diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
2 files changed, 106 insertions, 59 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 55d5d974a..094afd4a5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -33,75 +33,33 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Showable;  import org.sufficientlysecure.keychain.ui.util.Notify.Style;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ParcelableCache;  import java.util.ArrayList;  import java.util.Arrays;  import java.util.Iterator;  import java.util.List; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -/** Represent the result of an operation. +/** + * Represent the result of an operation.   *   * This class holds a result and the log of an operation. It can be subclassed   * to include typed additional information specific to the operation. To keep   * the class structure (somewhat) simple, this class contains an exhaustive   * list (ie, enum) of all possible log types, which should in all cases be tied   * to string resource ids. - *   */  public abstract class OperationResult implements Parcelable {      public static final String EXTRA_RESULT = "operation_result"; -    public static final UUID NULL_UUID = new UUID(0,0);      /** -     * A HashMap of UUID:OperationLog which contains logs that we don't need -     * to care about. This is used such that when we become parceled, we are -     * well below the 1Mbit boundary that is specified. +     * Instead of parceling the logs, they are cached to overcome the 1 MB boundary of +     * Android's Binder. See ParcelableCache       */ -    private static ConcurrentHashMap<UUID, OperationLog> dehydratedLogs; +    private static ParcelableCache<OperationLog> logCache;      static { -        // Static initializer for ConcurrentHashMap -        dehydratedLogs = new ConcurrentHashMap<UUID,OperationLog>(); -    } - -    /** -     * Dehydrate a log (such that it is available after deparcelization) -     * -     * Returns the NULL uuid (0) if you hand it null. -     * @param log An OperationLog to dehydrate -     * @return a UUID, the ticket for your dehydrated log -     * -     */ -    private static UUID dehydrateLog(OperationLog log) { -        if(log == null) { -            return NULL_UUID; -        } -        else { -            UUID ticket = UUID.randomUUID(); -            dehydratedLogs.put(ticket, log); -            return ticket; -        } -    } - -    /*** -     * Rehydrate a log after going through parcelization, invalidating its place in the -     * dehydration pool. -     * This is used such that when parcelized, the parcel is no larger than 1mbit. -     * @param ticket A UUID ticket that identifies the log in question. -     * @return An OperationLog. -     */ -    private static OperationLog rehydrateLog(UUID ticket) { -        // UUID.equals isn't well documented; we use compareTo instead. -        if( NULL_UUID.compareTo(ticket) == 0 ) { -            return null; -        } -        else { -            OperationLog log = dehydratedLogs.get(ticket); -            dehydratedLogs.remove(ticket); -            return log; -        } +        logCache = new ParcelableCache<>();      }      /** Holds the overall result, the number specifying varying degrees of success: @@ -126,11 +84,8 @@ public abstract class OperationResult implements Parcelable {      public OperationResult(Parcel source) {          mResult = source.readInt(); -        long mostSig = source.readLong(); -        long leastSig = source.readLong(); -        UUID mTicket = new UUID(mostSig, leastSig); -        // fetch the dehydrated log out of storage (this removes it from the dehydration pool) -        mLog = rehydrateLog(mTicket); +        // get log out of cache based on UUID from source +        mLog = logCache.readFromParcelAndGetFromCache(source);      }      public int getResult() { @@ -813,11 +768,8 @@ public abstract class OperationResult implements Parcelable {      @Override      public void writeToParcel(Parcel dest, int flags) {          dest.writeInt(mResult); -        // Get a ticket for our log. -        UUID mTicket = dehydrateLog(mLog); -        // And write out the UUID most and least significant bits. -        dest.writeLong(mTicket.getMostSignificantBits()); -        dest.writeLong(mTicket.getLeastSignificantBits()); +        // cache log and write UUID to dest +        logCache.cacheAndWriteToParcel(mLog, dest);      }      public static class OperationLog implements Iterable<LogEntryParcel> { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableCache.java new file mode 100644 index 000000000..4d723cb30 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableCache.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.util; + +import android.os.Parcel; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * If Parcelables are above 1 MB, Android OS fails to send them via the Binder IPC: + * JavaBinder  E  !!! FAILED BINDER TRANSACTION !!! + * To overcome this issue this class allows to cache Parcelables, mapped by unique UUIDs, + * which are written to the parcel instead of the whole Parcelable. + */ +public class ParcelableCache<E extends Object> { + +    private static final UUID NULL_UUID = new UUID(0, 0); + +    /** +     * A HashMap of UUID:Object +     * This is used such that when we become parceled, we are +     * well below the 1 MB boundary that is specified. +     */ +    private ConcurrentHashMap<UUID, E> dehydratedLogs = new ConcurrentHashMap<>(); + +    /** +     * Dehydrate a Parcelable (such that it is available after deparcelization) +     * Returns the NULL uuid (0) if you hand it null. +     * +     * @param parcelable A Parcelable to dehydrate +     * @return a UUID, the ticket for your dehydrated Parcelable +     */ +    private UUID dehydrateParcelable(E parcelable) { +        if (parcelable == null) { +            return NULL_UUID; +        } else { +            UUID ticket = UUID.randomUUID(); +            dehydratedLogs.put(ticket, parcelable); +            return ticket; +        } +    } + +    /** +     * Rehydrate a Parcelable after going through parcelization, +     * invalidating its place in the dehydration pool. +     * This is used such that when parcelized, the Parcelable is no larger than 1 MB. +     * +     * @param ticket A UUID ticket that identifies the log in question. +     * @return An OperationLog. +     */ +    private E rehydrateParcelable(UUID ticket) { +        // UUID.equals isn't well documented; we use compareTo instead. +        if (NULL_UUID.compareTo(ticket) == 0) { +            return null; +        } else { +            E parcelable = dehydratedLogs.get(ticket); +            dehydratedLogs.remove(ticket); +            return parcelable; +        } +    } + +    public E readFromParcelAndGetFromCache(Parcel source) { +        long mostSig = source.readLong(); +        long leastSig = source.readLong(); +        UUID mTicket = new UUID(mostSig, leastSig); +        // fetch the dehydrated log out of storage (this removes it from the dehydration pool) +        return rehydrateParcelable(mTicket); +    } + + +    public void cacheAndWriteToParcel(E parcelable, Parcel dest) { +        // Get a ticket for our log. +        UUID mTicket = dehydrateParcelable(parcelable); +        // And write out the UUID most and least significant bits. +        dest.writeLong(mTicket.getMostSignificantBits()); +        dest.writeLong(mTicket.getLeastSignificantBits()); +    } + +}  | 
