Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 234e3954 authored by Sihua Ma's avatar Sihua Ma
Browse files

Calculate maximum bitmap cache size when constructing collection items

Test: N/A
Bug: 348496628
Flag: android.appwidget.flags.remote_adapter_conversion
Change-Id: I2e9c6cfa3391d31f9049738d2cce00ab9051853a
parent ad1400b7
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -523,6 +523,8 @@ public class AppWidgetManager {
    private final IAppWidgetService mService;
    private final DisplayMetrics mDisplayMetrics;

    private int mMaxBitmapMemory = 0;

    private boolean mHasPostedLegacyLists = false;

    /**
@@ -548,6 +550,12 @@ public class AppWidgetManager {
        if (mService == null) {
            return;
        }
        // Allowing some buffer when estimating the maximum bitmap cache size
        try {
            mMaxBitmapMemory = (int) (mService.getMaxBitmapMemory() * 0.9);
        } catch (Exception e) {
            Log.e(TAG, "Error setting the maximum bitmap memory", e);
        }
        BackgroundThread.getExecutor().execute(() -> {
            try {
                mService.notifyProviderInheritance(getInstalledProvidersForPackage(mPackageName,
@@ -576,7 +584,7 @@ public class AppWidgetManager {
            final RemoteViews viewsCopy = new RemoteViews(original);
            Runnable updateWidgetWithTask = () -> {
                try {
                    viewsCopy.collectAllIntents().get();
                    viewsCopy.collectAllIntents(mMaxBitmapMemory).get();
                    action.acceptOrThrow(viewsCopy);
                } catch (Exception e) {
                    Log.e(TAG, failureMsg, e);
+66 −9
Original line number Diff line number Diff line
@@ -140,10 +140,12 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1236,8 +1238,8 @@ public class RemoteViews implements Parcelable, Filter {
    /**
     * @hide
     */
    public CompletableFuture<Void> collectAllIntents() {
        return mCollectionCache.collectAllIntentsNoComplete(this);
    public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit) {
        return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit);
    }

    private class RemoteCollectionCache {
@@ -1286,7 +1288,7 @@ public class RemoteViews implements Parcelable, Filter {
        }

        public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
                @NonNull RemoteViews inViews) {
                @NonNull RemoteViews inViews, int bitmapSizeLimit) {
            SparseArray<Intent> idToIntentMapping = new SparseArray<>();
            // Collect the number of uinque Intent (which is equal to the number of new connections
            // to make) for size allocation and exclude certain collections from being written to
@@ -1314,7 +1316,11 @@ public class RemoteViews implements Parcelable, Filter {
                    ? 0
                    : remainingSize / numOfIntents;

            return connectAllUniqueIntents(individualSize, idToIntentMapping);
            int individualBitmapSizeLimit = (bitmapSizeLimit - getBitmapMemoryUsedByActions())
                    / numOfIntents;

            return connectAllUniqueIntents(individualSize, individualBitmapSizeLimit,
                    idToIntentMapping);
        }

        private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
@@ -1380,13 +1386,13 @@ public class RemoteViews implements Parcelable, Filter {
        }

        private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
                @NonNull SparseArray<Intent> idToIntentMapping) {
                int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping) {
            List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
            for (int i = 0; i < idToIntentMapping.size(); i++) {
                String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
                Intent currentIntent = idToIntentMapping.valueAt(i);
                intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
                        individualSize)
                        individualSize, individualBitmapSize)
                        .thenAccept(items -> {
                            items.setHierarchyRootData(getHierarchyRootData());
                            mUriToCollectionMapping.put(currentIntentUri, items);
@@ -1397,7 +1403,7 @@ public class RemoteViews implements Parcelable, Filter {
        }

        private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
                Intent intent, int individualSize) {
                Intent intent, int individualSize, int individualBitmapSize) {
            if (intent == null) {
                Log.e(LOG_TAG, "Null intent received when generating adapter future");
                return CompletableFuture.completedFuture(new RemoteCollectionItems
@@ -1415,7 +1421,8 @@ public class RemoteViews implements Parcelable, Filter {
                            RemoteCollectionItems items;
                            try {
                                items = IRemoteViewsFactory.Stub.asInterface(iBinder)
                                        .getRemoteCollectionItems(individualSize);
                                        .getRemoteCollectionItems(individualSize,
                                                individualBitmapSize);
                            } catch (RemoteException re) {
                                items = new RemoteCollectionItems.Builder().build();
                                Log.e(LOG_TAG, "Error getting collection items from the"
@@ -2007,7 +2014,14 @@ public class RemoteViews implements Parcelable, Filter {
        }
    }

    private static class BitmapCache {
    /**
     * @hide
     */
    @NonNull BitmapCache getBitmapCache() {
        return mBitmapCache;
    }

    static class BitmapCache {
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        ArrayList<Bitmap> mBitmaps;
        SparseIntArray mBitmapHashes;
@@ -2029,6 +2043,11 @@ public class RemoteViews implements Parcelable, Filter {
            }
        }

        BitmapCache(BitmapCache other) {
            mBitmaps = new ArrayList<>(other.mBitmaps);
            mBitmapHashes = other.mBitmapHashes.clone();
        }

        public int getBitmapId(Bitmap b) {
            if (b == null) {
                return -1;
@@ -2071,6 +2090,12 @@ public class RemoteViews implements Parcelable, Filter {
            }
            return mBitmapMemory;
        }

        public void mergeWithCache(BitmapCache other) {
            for (int i = 0; i < other.mBitmaps.size(); i++) {
                getBitmapId(other.mBitmaps.get(i));
            }
        }
    }

    private class BitmapReflectionAction extends Action {
@@ -7343,6 +7368,38 @@ public class RemoteViews implements Parcelable, Filter {
        return true;
    }

    private int getBitmapMemoryUsedByActions() {
        Set<Integer> bitmapIdSet = getBitmapIdsUsedByActions(new HashSet<>());
        int result = 0;
        for (int bitmapId: bitmapIdSet) {
            result += mBitmapCache.getBitmapForId(bitmapId).getAllocationByteCount();
        }

        return result;
    }

    private Set<Integer> getBitmapIdsUsedByActions(@NonNull Set<Integer> intSet) {
        if (hasSizedRemoteViews()) {
            for (RemoteViews views: mSizedRemoteViews) {
                views.getBitmapIdsUsedByActions(intSet);
            }
        } else if (hasLandscapeAndPortraitLayouts()) {
            mLandscape.getBitmapIdsUsedByActions(intSet);
            mPortrait.getBitmapIdsUsedByActions(intSet);
        } else if (mActions != null) {
            for (Action action: mActions) {
                if (action instanceof ViewGroupActionAdd vgaa
                        && vgaa.mNestedViews != null) {
                    vgaa.mNestedViews.getBitmapIdsUsedByActions(intSet);
                } else if (action instanceof BitmapReflectionAction bitmapAction) {
                    intSet.add(bitmapAction.mBitmapId);
                }
            }
        }

        return intSet;
    }

    /** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
    public static final class RemoteCollectionItems implements Parcelable {
        private final long[] mIds;
+15 −3
Original line number Diff line number Diff line
@@ -128,7 +128,8 @@ public abstract class RemoteViewsService extends Service {
        /**
         * @hide
         */
        default RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
        default RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
                int capBitmapSize) {
            RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
                    .Builder().build();
            Parcel capSizeTestParcel = Parcel.obtain();
@@ -138,6 +139,7 @@ public abstract class RemoteViewsService extends Service {
            try {
                RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
                        new RemoteViews.RemoteCollectionItems.Builder();
                RemoteViews.BitmapCache testBitmapCache = null;
                onDataSetChanged();

                itemsBuilder.setHasStableIds(hasStableIds());
@@ -150,6 +152,15 @@ public abstract class RemoteViewsService extends Service {
                    if (capSizeTestParcel.dataSize() > capSize) {
                        break;
                    }
                    if (testBitmapCache == null) {
                        testBitmapCache = new RemoteViews.BitmapCache(currentView.getBitmapCache());
                    } else {
                        testBitmapCache.mergeWithCache(currentView.getBitmapCache());
                    }
                    if (testBitmapCache.getBitmapMemory() >= capBitmapSize) {
                        break;
                    }

                    itemsBuilder.addItem(currentItemId, currentView);
                }

@@ -266,11 +277,12 @@ public abstract class RemoteViewsService extends Service {
        }

        @Override
        public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
        public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
                int capBitmapSize) {
            RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
                    .Builder().build();
            try {
                items = mFactory.getRemoteCollectionItems(capSize);
                items = mFactory.getRemoteCollectionItems(capSize, capBitmapSize);
            } catch (Exception ex) {
                Thread t = Thread.currentThread();
                Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ interface IAppWidgetService {
    boolean bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
            IApplicationThread caller, IBinder token, IServiceConnection connection, long flags);
    void notifyProviderInheritance(in ComponentName[] componentNames);
    int getMaxBitmapMemory();

    @UnsupportedAppUsage
    int[] getAppWidgetIds(in ComponentName providerComponent);
+1 −1
Original line number Diff line number Diff line
@@ -39,6 +39,6 @@ interface IRemoteViewsFactory {
    boolean hasStableIds();
    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
    boolean isCreated();
    RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize);
    RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize, int capBitmapSize);
}
Loading