Loading core/java/android/widget/RemoteViews.java +96 −5 Original line number Diff line number Diff line Loading @@ -159,6 +159,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; Loading Loading @@ -719,6 +720,11 @@ public class RemoteViews implements Parcelable, Filter { // Nothing to visit by default. } /** See {@link RemoteViews#visitIcons(Consumer)}. **/ public void visitIcons(@NonNull Consumer<Icon> visitor) { // Nothing to visit by default. } public abstract void writeToParcel(Parcel dest, int flags); /** Loading Loading @@ -848,6 +854,29 @@ public class RemoteViews implements Parcelable, Filter { } } /** * Note all {@link Icon} that are referenced internally. * @hide */ public void visitIcons(@NonNull Consumer<Icon> visitor) { if (mActions != null) { for (int i = 0; i < mActions.size(); i++) { mActions.get(i).visitIcons(visitor); } } if (mSizedRemoteViews != null) { for (int i = 0; i < mSizedRemoteViews.size(); i++) { mSizedRemoteViews.get(i).visitIcons(visitor); } } if (mLandscape != null) { mLandscape.visitIcons(visitor); } if (mPortrait != null) { mPortrait.visitIcons(visitor); } } /** * @hide * @return True if there is a change Loading Loading @@ -1311,6 +1340,19 @@ public class RemoteViews implements Parcelable, Filter { mItems.visitUris(visitor); } @Override public void visitIcons(Consumer<Icon> visitor) { if (mItems == null) { RemoteCollectionItems cachedItems = mCollectionCache.getItemsForId(mIntentId); if (cachedItems != null) { cachedItems.visitIcons(visitor); } return; } mItems.visitIcons(visitor); } @Override public boolean canWriteToProto() { // Skip actions that do not contain items (intent only actions) Loading Loading @@ -2385,7 +2427,7 @@ public class RemoteViews implements Parcelable, Filter { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) ArrayList<Bitmap> mBitmaps; SparseIntArray mBitmapHashes; int mBitmapMemory = -1; long mBitmapMemory = -1; public BitmapCache() { mBitmaps = new ArrayList<>(); Loading Loading @@ -2449,7 +2491,7 @@ public class RemoteViews implements Parcelable, Filter { } } public int getBitmapMemory() { public long getBitmapMemory() { if (mBitmapMemory < 0) { mBitmapMemory = 0; int count = mBitmaps.size(); Loading Loading @@ -2735,6 +2777,13 @@ public class RemoteViews implements Parcelable, Filter { // TODO(b/281044385): Should we do anything about type BUNDLE? } } @Override public void visitIcons(@NonNull Consumer<Icon> visitor) { if (mType == ICON && getParameterValue(null) instanceof Icon icon) { visitor.accept(icon); } } } /** Class for the reflection actions. */ Loading Loading @@ -4138,6 +4187,11 @@ public class RemoteViews implements Parcelable, Filter { mNestedViews.visitUris(visitor); } @Override public void visitIcons(@NonNull Consumer<Icon> visitor) { mNestedViews.visitIcons(visitor); } @Override public boolean canWriteToProto() { return true; Loading Loading @@ -6392,14 +6446,42 @@ public class RemoteViews implements Parcelable, Filter { } /** * Returns an estimate of the bitmap heap memory usage for this RemoteViews. * Returns an estimate of the bitmap heap memory usage by setBitmap and setImageViewBitmap in * this RemoteViews. * * @hide */ /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int estimateMemoryUsage() { public long estimateMemoryUsage() { return mBitmapCache.getBitmapMemory(); } /** * Returns an estimate of bitmap heap memory usage by setIcon and setImageViewIcon in this * RemoteViews. Note that this function will count duplicate Icons in its estimate. * * @hide */ public long estimateIconMemoryUsage() { AtomicLong total = new AtomicLong(0); visitIcons(icon -> { if (icon.getType() == Icon.TYPE_BITMAP || icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) { total.addAndGet(icon.getBitmap().getAllocationByteCount()); } }); return total.get(); } /** * Returns an estimate of the bitmap heap memory usage for all Icon and Bitmap actions in this * RemoteViews. * * @hide */ public long estimateTotalBitmapMemoryUsage() { return estimateMemoryUsage() + estimateIconMemoryUsage(); } /** * Add an action to be executed on the remote side when apply is called. * Loading Loading @@ -9768,6 +9850,15 @@ public class RemoteViews implements Parcelable, Filter { view.visitUris(visitor); } } /** * See {@link RemoteViews#visitIcons(Consumer)}. */ private void visitIcons(@NonNull Consumer<Icon> visitor) { for (RemoteViews view : mViews) { view.visitIcons(visitor); } } } /** Loading core/proto/android/service/appwidget.proto +1 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ message WidgetProto { optional int32 maxWidth = 8; optional int32 maxHeight = 9; optional bool restoreCompleted = 10; optional int32 views_bitmap_memory = 11; } // represents a set of widget previews for a particular provider Loading core/tests/coretests/src/android/widget/RemoteViewsTest.java +131 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.app.PendingIntent; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.AsyncTask; Loading Loading @@ -934,6 +935,136 @@ public class RemoteViewsTest { assertEquals(testText, replacedTextView.getText()); } @Test public void estimateMemoryUsage() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888); int b3Memory = b3.getAllocationByteCount(); final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); assertEquals(0, rv.estimateMemoryUsage()); assertEquals(0, rv.estimateIconMemoryUsage()); assertEquals(0, rv.estimateTotalBitmapMemoryUsage()); rv.setBitmap(R.id.view, "", b1); rv.setImageViewBitmap(R.id.view, b1); // second instance of b1 is cached rv.setBitmap(R.id.view, "", b2); assertEquals(b1Memory + b2Memory, rv.estimateMemoryUsage()); assertEquals(0, rv.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage()); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b3)); rv.setImageViewIcon(R.id.view, Icon.createWithBitmap(b3)); assertEquals(b1Memory + b2Memory, rv.estimateMemoryUsage()); assertEquals(b2Memory + (2 * b3Memory), rv.estimateIconMemoryUsage()); assertEquals(b1Memory + (2 * b2Memory) + (2 * b3Memory), rv.estimateTotalBitmapMemoryUsage()); } @Test public void estimateMemoryUsage_landscapePortrait() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888); int b3Memory = b3.getAllocationByteCount(); Bitmap b4 = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888); int b4Memory = b4.getAllocationByteCount(); // Landscape and portrait using same bitmaps get counted twice. final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setBitmap(R.id.view, "", b1); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); RemoteViews landscapePortraitViews = new RemoteViews(rv, rv); assertEquals(b1Memory, landscapePortraitViews.estimateMemoryUsage()); assertEquals(2 * b2Memory, landscapePortraitViews.estimateIconMemoryUsage()); assertEquals(b1Memory + (2 * b2Memory), landscapePortraitViews.estimateTotalBitmapMemoryUsage()); final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setBitmap(R.id.view, "", b3); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b4)); landscapePortraitViews = new RemoteViews(rv, rv2); assertEquals(b1Memory + b3Memory, landscapePortraitViews.estimateMemoryUsage()); assertEquals(b2Memory + b4Memory, landscapePortraitViews.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory + b3Memory + b4Memory, landscapePortraitViews.estimateTotalBitmapMemoryUsage()); } @Test public void estimateMemoryUsage_sizedViews() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888); int b3Memory = b3.getAllocationByteCount(); Bitmap b4 = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888); int b4Memory = b4.getAllocationByteCount(); // Sized views using same bitmaps do not get counted twice. final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setBitmap(R.id.view, "", b1); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); RemoteViews sizedViews = new RemoteViews( Map.of(new SizeF(0f, 0f), rv, new SizeF(1f, 1f), rv)); assertEquals(b1Memory, sizedViews.estimateMemoryUsage()); assertEquals(2 * b2Memory, sizedViews.estimateIconMemoryUsage()); assertEquals(b1Memory + (2 * b2Memory), sizedViews.estimateTotalBitmapMemoryUsage()); final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setBitmap(R.id.view, "", b3); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b4)); sizedViews = new RemoteViews(Map.of(new SizeF(0f, 0f), rv, new SizeF(1f, 1f), rv2)); assertEquals(b1Memory + b3Memory, sizedViews.estimateMemoryUsage()); assertEquals(b2Memory + b4Memory, sizedViews.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory + b3Memory + b4Memory, sizedViews.estimateTotalBitmapMemoryUsage()); } @Test public void estimateMemoryUsage_nestedViews() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); final RemoteViews rv1 = new RemoteViews(mPackage, R.layout.remote_views_test); rv1.setBitmap(R.id.view, "", b1); final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test); rv2.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); rv.addView(R.id.view, rv1); rv.addView(R.id.view, rv2); assertEquals(b1Memory, rv.estimateMemoryUsage()); assertEquals(b2Memory, rv.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage()); } @Test public void estimateMemoryUsage_remoteCollectionItems() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); final RemoteViews rv1 = new RemoteViews(mPackage, R.layout.remote_views_test); rv1.setBitmap(R.id.view, "", b1); final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test); rv2.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setRemoteAdapter(R.id.view, new RemoteViews.RemoteCollectionItems.Builder().addItem(0L, rv1).addItem(1L, rv2).build()); assertEquals(b1Memory, rv.estimateMemoryUsage()); assertEquals(b2Memory, rv.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage()); } private static LayoutInflater.Factory2 createLayoutInflaterFactory(String viewTypeToReplace, View replacementView) { return new LayoutInflater.Factory2() { Loading services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +7 −1 Original line number Diff line number Diff line Loading @@ -1095,6 +1095,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku proto.write(WidgetProto.MAX_HEIGHT, widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 0)); } if (widget.views != null) { proto.write(WidgetProto.VIEWS_BITMAP_MEMORY, widget.views.estimateTotalBitmapMemoryUsage()); } proto.end(token); } Loading Loading @@ -2846,7 +2850,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // For a full update we replace the RemoteViews completely. widget.views = views; } int memoryUsage; long memoryUsage; if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) && (widget.views != null) && ((memoryUsage = widget.views.estimateMemoryUsage()) > mMaxWidgetBitmapMemory)) { Loading Loading @@ -3503,6 +3507,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } if (widget.views != null) { pw.print(" views="); pw.println(widget.views); pw.print(" views_bitmap_memory="); pw.println(widget.views.estimateTotalBitmapMemoryUsage()); } } Loading services/core/java/com/android/server/notification/NotificationManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -8898,7 +8898,7 @@ public class NotificationManagerService extends SystemService { if (contentView == null) { return false; } final int contentViewSize = contentView.estimateMemoryUsage(); final long contentViewSize = contentView.estimateMemoryUsage(); if (contentViewSize > mWarnRemoteViewsSizeBytes && contentViewSize < mStripRemoteViewsSizeBytes) { Slog.w(TAG, "RemoteViews too large on pkg: " + pkg + " tag: " + tag + " id: " + id Loading Loading
core/java/android/widget/RemoteViews.java +96 −5 Original line number Diff line number Diff line Loading @@ -159,6 +159,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; Loading Loading @@ -719,6 +720,11 @@ public class RemoteViews implements Parcelable, Filter { // Nothing to visit by default. } /** See {@link RemoteViews#visitIcons(Consumer)}. **/ public void visitIcons(@NonNull Consumer<Icon> visitor) { // Nothing to visit by default. } public abstract void writeToParcel(Parcel dest, int flags); /** Loading Loading @@ -848,6 +854,29 @@ public class RemoteViews implements Parcelable, Filter { } } /** * Note all {@link Icon} that are referenced internally. * @hide */ public void visitIcons(@NonNull Consumer<Icon> visitor) { if (mActions != null) { for (int i = 0; i < mActions.size(); i++) { mActions.get(i).visitIcons(visitor); } } if (mSizedRemoteViews != null) { for (int i = 0; i < mSizedRemoteViews.size(); i++) { mSizedRemoteViews.get(i).visitIcons(visitor); } } if (mLandscape != null) { mLandscape.visitIcons(visitor); } if (mPortrait != null) { mPortrait.visitIcons(visitor); } } /** * @hide * @return True if there is a change Loading Loading @@ -1311,6 +1340,19 @@ public class RemoteViews implements Parcelable, Filter { mItems.visitUris(visitor); } @Override public void visitIcons(Consumer<Icon> visitor) { if (mItems == null) { RemoteCollectionItems cachedItems = mCollectionCache.getItemsForId(mIntentId); if (cachedItems != null) { cachedItems.visitIcons(visitor); } return; } mItems.visitIcons(visitor); } @Override public boolean canWriteToProto() { // Skip actions that do not contain items (intent only actions) Loading Loading @@ -2385,7 +2427,7 @@ public class RemoteViews implements Parcelable, Filter { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) ArrayList<Bitmap> mBitmaps; SparseIntArray mBitmapHashes; int mBitmapMemory = -1; long mBitmapMemory = -1; public BitmapCache() { mBitmaps = new ArrayList<>(); Loading Loading @@ -2449,7 +2491,7 @@ public class RemoteViews implements Parcelable, Filter { } } public int getBitmapMemory() { public long getBitmapMemory() { if (mBitmapMemory < 0) { mBitmapMemory = 0; int count = mBitmaps.size(); Loading Loading @@ -2735,6 +2777,13 @@ public class RemoteViews implements Parcelable, Filter { // TODO(b/281044385): Should we do anything about type BUNDLE? } } @Override public void visitIcons(@NonNull Consumer<Icon> visitor) { if (mType == ICON && getParameterValue(null) instanceof Icon icon) { visitor.accept(icon); } } } /** Class for the reflection actions. */ Loading Loading @@ -4138,6 +4187,11 @@ public class RemoteViews implements Parcelable, Filter { mNestedViews.visitUris(visitor); } @Override public void visitIcons(@NonNull Consumer<Icon> visitor) { mNestedViews.visitIcons(visitor); } @Override public boolean canWriteToProto() { return true; Loading Loading @@ -6392,14 +6446,42 @@ public class RemoteViews implements Parcelable, Filter { } /** * Returns an estimate of the bitmap heap memory usage for this RemoteViews. * Returns an estimate of the bitmap heap memory usage by setBitmap and setImageViewBitmap in * this RemoteViews. * * @hide */ /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int estimateMemoryUsage() { public long estimateMemoryUsage() { return mBitmapCache.getBitmapMemory(); } /** * Returns an estimate of bitmap heap memory usage by setIcon and setImageViewIcon in this * RemoteViews. Note that this function will count duplicate Icons in its estimate. * * @hide */ public long estimateIconMemoryUsage() { AtomicLong total = new AtomicLong(0); visitIcons(icon -> { if (icon.getType() == Icon.TYPE_BITMAP || icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) { total.addAndGet(icon.getBitmap().getAllocationByteCount()); } }); return total.get(); } /** * Returns an estimate of the bitmap heap memory usage for all Icon and Bitmap actions in this * RemoteViews. * * @hide */ public long estimateTotalBitmapMemoryUsage() { return estimateMemoryUsage() + estimateIconMemoryUsage(); } /** * Add an action to be executed on the remote side when apply is called. * Loading Loading @@ -9768,6 +9850,15 @@ public class RemoteViews implements Parcelable, Filter { view.visitUris(visitor); } } /** * See {@link RemoteViews#visitIcons(Consumer)}. */ private void visitIcons(@NonNull Consumer<Icon> visitor) { for (RemoteViews view : mViews) { view.visitIcons(visitor); } } } /** Loading
core/proto/android/service/appwidget.proto +1 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ message WidgetProto { optional int32 maxWidth = 8; optional int32 maxHeight = 9; optional bool restoreCompleted = 10; optional int32 views_bitmap_memory = 11; } // represents a set of widget previews for a particular provider Loading
core/tests/coretests/src/android/widget/RemoteViewsTest.java +131 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.app.PendingIntent; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.AsyncTask; Loading Loading @@ -934,6 +935,136 @@ public class RemoteViewsTest { assertEquals(testText, replacedTextView.getText()); } @Test public void estimateMemoryUsage() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888); int b3Memory = b3.getAllocationByteCount(); final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); assertEquals(0, rv.estimateMemoryUsage()); assertEquals(0, rv.estimateIconMemoryUsage()); assertEquals(0, rv.estimateTotalBitmapMemoryUsage()); rv.setBitmap(R.id.view, "", b1); rv.setImageViewBitmap(R.id.view, b1); // second instance of b1 is cached rv.setBitmap(R.id.view, "", b2); assertEquals(b1Memory + b2Memory, rv.estimateMemoryUsage()); assertEquals(0, rv.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage()); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b3)); rv.setImageViewIcon(R.id.view, Icon.createWithBitmap(b3)); assertEquals(b1Memory + b2Memory, rv.estimateMemoryUsage()); assertEquals(b2Memory + (2 * b3Memory), rv.estimateIconMemoryUsage()); assertEquals(b1Memory + (2 * b2Memory) + (2 * b3Memory), rv.estimateTotalBitmapMemoryUsage()); } @Test public void estimateMemoryUsage_landscapePortrait() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888); int b3Memory = b3.getAllocationByteCount(); Bitmap b4 = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888); int b4Memory = b4.getAllocationByteCount(); // Landscape and portrait using same bitmaps get counted twice. final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setBitmap(R.id.view, "", b1); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); RemoteViews landscapePortraitViews = new RemoteViews(rv, rv); assertEquals(b1Memory, landscapePortraitViews.estimateMemoryUsage()); assertEquals(2 * b2Memory, landscapePortraitViews.estimateIconMemoryUsage()); assertEquals(b1Memory + (2 * b2Memory), landscapePortraitViews.estimateTotalBitmapMemoryUsage()); final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setBitmap(R.id.view, "", b3); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b4)); landscapePortraitViews = new RemoteViews(rv, rv2); assertEquals(b1Memory + b3Memory, landscapePortraitViews.estimateMemoryUsage()); assertEquals(b2Memory + b4Memory, landscapePortraitViews.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory + b3Memory + b4Memory, landscapePortraitViews.estimateTotalBitmapMemoryUsage()); } @Test public void estimateMemoryUsage_sizedViews() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); Bitmap b3 = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888); int b3Memory = b3.getAllocationByteCount(); Bitmap b4 = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888); int b4Memory = b4.getAllocationByteCount(); // Sized views using same bitmaps do not get counted twice. final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setBitmap(R.id.view, "", b1); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); RemoteViews sizedViews = new RemoteViews( Map.of(new SizeF(0f, 0f), rv, new SizeF(1f, 1f), rv)); assertEquals(b1Memory, sizedViews.estimateMemoryUsage()); assertEquals(2 * b2Memory, sizedViews.estimateIconMemoryUsage()); assertEquals(b1Memory + (2 * b2Memory), sizedViews.estimateTotalBitmapMemoryUsage()); final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setBitmap(R.id.view, "", b3); rv.setIcon(R.id.view, "", Icon.createWithBitmap(b4)); sizedViews = new RemoteViews(Map.of(new SizeF(0f, 0f), rv, new SizeF(1f, 1f), rv2)); assertEquals(b1Memory + b3Memory, sizedViews.estimateMemoryUsage()); assertEquals(b2Memory + b4Memory, sizedViews.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory + b3Memory + b4Memory, sizedViews.estimateTotalBitmapMemoryUsage()); } @Test public void estimateMemoryUsage_nestedViews() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); final RemoteViews rv1 = new RemoteViews(mPackage, R.layout.remote_views_test); rv1.setBitmap(R.id.view, "", b1); final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test); rv2.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); rv.addView(R.id.view, rv1); rv.addView(R.id.view, rv2); assertEquals(b1Memory, rv.estimateMemoryUsage()); assertEquals(b2Memory, rv.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage()); } @Test public void estimateMemoryUsage_remoteCollectionItems() { Bitmap b1 = Bitmap.createBitmap(1024, 768, Bitmap.Config.ARGB_8888); int b1Memory = b1.getAllocationByteCount(); Bitmap b2 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888); int b2Memory = b2.getAllocationByteCount(); final RemoteViews rv1 = new RemoteViews(mPackage, R.layout.remote_views_test); rv1.setBitmap(R.id.view, "", b1); final RemoteViews rv2 = new RemoteViews(mPackage, R.layout.remote_views_test); rv2.setIcon(R.id.view, "", Icon.createWithBitmap(b2)); final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); rv.setRemoteAdapter(R.id.view, new RemoteViews.RemoteCollectionItems.Builder().addItem(0L, rv1).addItem(1L, rv2).build()); assertEquals(b1Memory, rv.estimateMemoryUsage()); assertEquals(b2Memory, rv.estimateIconMemoryUsage()); assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage()); } private static LayoutInflater.Factory2 createLayoutInflaterFactory(String viewTypeToReplace, View replacementView) { return new LayoutInflater.Factory2() { Loading
services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +7 −1 Original line number Diff line number Diff line Loading @@ -1095,6 +1095,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku proto.write(WidgetProto.MAX_HEIGHT, widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 0)); } if (widget.views != null) { proto.write(WidgetProto.VIEWS_BITMAP_MEMORY, widget.views.estimateTotalBitmapMemoryUsage()); } proto.end(token); } Loading Loading @@ -2846,7 +2850,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // For a full update we replace the RemoteViews completely. widget.views = views; } int memoryUsage; long memoryUsage; if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) && (widget.views != null) && ((memoryUsage = widget.views.estimateMemoryUsage()) > mMaxWidgetBitmapMemory)) { Loading Loading @@ -3503,6 +3507,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } if (widget.views != null) { pw.print(" views="); pw.println(widget.views); pw.print(" views_bitmap_memory="); pw.println(widget.views.estimateTotalBitmapMemoryUsage()); } } Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -8898,7 +8898,7 @@ public class NotificationManagerService extends SystemService { if (contentView == null) { return false; } final int contentViewSize = contentView.estimateMemoryUsage(); final long contentViewSize = contentView.estimateMemoryUsage(); if (contentViewSize > mWarnRemoteViewsSizeBytes && contentViewSize < mStripRemoteViewsSizeBytes) { Slog.w(TAG, "RemoteViews too large on pkg: " + pkg + " tag: " + tag + " id: " + id Loading