Loading core/java/android/appwidget/AppWidgetManager.java +9 −1 Original line number Diff line number Diff line Loading @@ -523,6 +523,8 @@ public class AppWidgetManager { private final IAppWidgetService mService; private final DisplayMetrics mDisplayMetrics; private int mMaxBitmapMemory = 0; private boolean mHasPostedLegacyLists = false; /** Loading @@ -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, Loading Loading @@ -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); Loading core/java/android/widget/RemoteViews.java +66 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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 Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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 Loading @@ -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" Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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; Loading core/java/android/widget/RemoteViewsService.java +15 −3 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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()); Loading @@ -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); } Loading Loading @@ -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); Loading core/java/com/android/internal/appwidget/IAppWidgetService.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -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); Loading core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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
core/java/android/appwidget/AppWidgetManager.java +9 −1 Original line number Diff line number Diff line Loading @@ -523,6 +523,8 @@ public class AppWidgetManager { private final IAppWidgetService mService; private final DisplayMetrics mDisplayMetrics; private int mMaxBitmapMemory = 0; private boolean mHasPostedLegacyLists = false; /** Loading @@ -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, Loading Loading @@ -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); Loading
core/java/android/widget/RemoteViews.java +66 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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 Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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 Loading @@ -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" Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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; Loading
core/java/android/widget/RemoteViewsService.java +15 −3 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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()); Loading @@ -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); } Loading Loading @@ -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); Loading
core/java/com/android/internal/appwidget/IAppWidgetService.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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); }