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

Commit 19f8c2c5 authored by Willie Koomson's avatar Willie Koomson Committed by Android (Google) Code Review
Browse files

Merge "Include bitmap memory estimate in AppWidgetServiceImpl dumpsys output" into main

parents 25d0404d 9ef0692a
Loading
Loading
Loading
Loading
+96 −5
Original line number Diff line number Diff line
@@ -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;
@@ -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);

        /**
@@ -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
@@ -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)
@@ -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<>();
@@ -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();
@@ -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. */
@@ -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;
@@ -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.
     *
@@ -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);
            }
        }
    }

    /**
+1 −0
Original line number Diff line number Diff line
@@ -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
+131 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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() {
+7 −1
Original line number Diff line number Diff line
@@ -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);
    }

@@ -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)) {
@@ -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());
        }
    }

+1 −1
Original line number Diff line number Diff line
@@ -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