Loading core/java/android/widget/RemoteViews.java +36 −13 Original line number Diff line number Diff line Loading @@ -324,6 +324,14 @@ public class RemoteViews implements Parcelable, Filter { return false; } /** * Overridden by subclasses which have (or inherit) an ApplicationInfo instance * as member variable */ public boolean hasSameAppInfo(ApplicationInfo parentInfo) { return true; } int viewId; } Loading Loading @@ -1521,11 +1529,11 @@ public class RemoteViews implements Parcelable, Filter { } } public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) { ViewGroupAction(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info) { viewId = parcel.readInt(); boolean nestedViewsNull = parcel.readInt() == 0; if (!nestedViewsNull) { nestedViews = new RemoteViews(parcel, bitmapCache); nestedViews = new RemoteViews(parcel, bitmapCache, info); } else { nestedViews = null; } Loading @@ -1543,6 +1551,13 @@ public class RemoteViews implements Parcelable, Filter { } } @Override public boolean hasSameAppInfo(ApplicationInfo parentInfo) { return nestedViews != null && nestedViews.mApplication.packageName.equals(parentInfo.packageName) && nestedViews.mApplication.uid == parentInfo.uid; } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final Context context = root.getContext(); Loading Loading @@ -2195,10 +2210,10 @@ public class RemoteViews implements Parcelable, Filter { * @param parcel */ public RemoteViews(Parcel parcel) { this(parcel, null); this(parcel, null, null); } private RemoteViews(Parcel parcel, BitmapCache bitmapCache) { private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info) { int mode = parcel.readInt(); // We only store a bitmap cache in the root of the RemoteViews. Loading @@ -2210,7 +2225,8 @@ public class RemoteViews implements Parcelable, Filter { } if (mode == MODE_NORMAL) { mApplication = parcel.readParcelable(null); mApplication = parcel.readInt() == 0 ? info : ApplicationInfo.CREATOR.createFromParcel(parcel); mLayoutId = parcel.readInt(); mIsWidgetCollectionChild = parcel.readInt() == 1; Loading @@ -2230,7 +2246,7 @@ public class RemoteViews implements Parcelable, Filter { mActions.add(new ReflectionAction(parcel)); break; case ViewGroupAction.TAG: mActions.add(new ViewGroupAction(parcel, mBitmapCache)); mActions.add(new ViewGroupAction(parcel, mBitmapCache, mApplication)); break; case ReflectionActionWithoutParams.TAG: mActions.add(new ReflectionActionWithoutParams(parcel)); Loading Loading @@ -2278,8 +2294,8 @@ public class RemoteViews implements Parcelable, Filter { } } else { // MODE_HAS_LANDSCAPE_AND_PORTRAIT mLandscape = new RemoteViews(parcel, mBitmapCache); mPortrait = new RemoteViews(parcel, mBitmapCache); mLandscape = new RemoteViews(parcel, mBitmapCache, info); mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication); mApplication = mPortrait.mApplication; mLayoutId = mPortrait.getLayoutId(); } Loading @@ -2299,11 +2315,11 @@ public class RemoteViews implements Parcelable, Filter { // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps. // Instead pretend we're not owning the cache while parceling. mIsRoot = false; writeToParcel(p, 0); writeToParcel(p, PARCELABLE_ELIDE_DUPLICATES); p.setDataPosition(0); mIsRoot = true; RemoteViews rv = new RemoteViews(p, mBitmapCache.clone()); RemoteViews rv = new RemoteViews(p, mBitmapCache.clone(), mApplication); rv.mIsRoot = true; p.recycle(); Loading Loading @@ -3536,7 +3552,12 @@ public class RemoteViews implements Parcelable, Filter { if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); } dest.writeParcelable(mApplication, flags); if (!mIsRoot && (flags & PARCELABLE_ELIDE_DUPLICATES) != 0) { dest.writeInt(0); } else { dest.writeInt(1); mApplication.writeToParcel(dest, flags); } dest.writeInt(mLayoutId); dest.writeInt(mIsWidgetCollectionChild ? 1 : 0); int count; Loading @@ -3548,7 +3569,8 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(count); for (int i=0; i<count; i++) { Action a = mActions.get(i); a.writeToParcel(dest, 0); a.writeToParcel(dest, a.hasSameAppInfo(mApplication) ? PARCELABLE_ELIDE_DUPLICATES : 0); } } else { dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT); Loading @@ -3558,7 +3580,8 @@ public class RemoteViews implements Parcelable, Filter { mBitmapCache.writeBitmapsToParcel(dest, flags); } mLandscape.writeToParcel(dest, flags); mPortrait.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES); } } Loading core/tests/coretests/src/android/widget/RemoteViewsTest.java +45 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; Loading @@ -35,6 +36,7 @@ import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; /** * Tests for RemoteViews. Loading @@ -43,6 +45,9 @@ import static org.junit.Assert.assertSame; @SmallTest public class RemoteViewsTest { // This can point to any other package which exists on the device. private static final String OTHER_PACKAGE = "com.android.systemui"; @Rule public final ExpectedException exception = ExpectedException.none(); Loading Loading @@ -121,4 +126,44 @@ public class RemoteViewsTest { clone.apply(mContext, mContainer); } @Test public void parcelSize_nestedViews() { RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); // We don't care about the actual layout id. RemoteViews child = new RemoteViews(mPackage, 33); int expectedSize = getParcelSize(original) + getParcelSize(child); original.addView(R.id.layout, child); // The application info will get written only once. assertTrue(getParcelSize(original) < expectedSize); assertEquals(getParcelSize(original), getParcelSize(original.clone())); original = new RemoteViews(mPackage, R.layout.remote_views_test); child = new RemoteViews(OTHER_PACKAGE, 33); expectedSize = getParcelSize(original) + getParcelSize(child); original.addView(R.id.layout, child); // Both the views will get written completely along with an additional view operation assertTrue(getParcelSize(original) > expectedSize); assertEquals(getParcelSize(original), getParcelSize(original.clone())); } @Test public void parcelSize_differentOrientation() { RemoteViews landscape = new RemoteViews(mPackage, R.layout.remote_views_test); RemoteViews portrait = new RemoteViews(mPackage, 33); // The application info will get written only once. RemoteViews views = new RemoteViews(landscape, portrait); assertTrue(getParcelSize(views) < (getParcelSize(landscape) + getParcelSize(portrait))); assertEquals(getParcelSize(views), getParcelSize(views.clone())); } private int getParcelSize(RemoteViews view) { Parcel parcel = Parcel.obtain(); view.writeToParcel(parcel, 0); int size = parcel.dataSize(); parcel.recycle(); return size; } } Loading
core/java/android/widget/RemoteViews.java +36 −13 Original line number Diff line number Diff line Loading @@ -324,6 +324,14 @@ public class RemoteViews implements Parcelable, Filter { return false; } /** * Overridden by subclasses which have (or inherit) an ApplicationInfo instance * as member variable */ public boolean hasSameAppInfo(ApplicationInfo parentInfo) { return true; } int viewId; } Loading Loading @@ -1521,11 +1529,11 @@ public class RemoteViews implements Parcelable, Filter { } } public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) { ViewGroupAction(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info) { viewId = parcel.readInt(); boolean nestedViewsNull = parcel.readInt() == 0; if (!nestedViewsNull) { nestedViews = new RemoteViews(parcel, bitmapCache); nestedViews = new RemoteViews(parcel, bitmapCache, info); } else { nestedViews = null; } Loading @@ -1543,6 +1551,13 @@ public class RemoteViews implements Parcelable, Filter { } } @Override public boolean hasSameAppInfo(ApplicationInfo parentInfo) { return nestedViews != null && nestedViews.mApplication.packageName.equals(parentInfo.packageName) && nestedViews.mApplication.uid == parentInfo.uid; } @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final Context context = root.getContext(); Loading Loading @@ -2195,10 +2210,10 @@ public class RemoteViews implements Parcelable, Filter { * @param parcel */ public RemoteViews(Parcel parcel) { this(parcel, null); this(parcel, null, null); } private RemoteViews(Parcel parcel, BitmapCache bitmapCache) { private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info) { int mode = parcel.readInt(); // We only store a bitmap cache in the root of the RemoteViews. Loading @@ -2210,7 +2225,8 @@ public class RemoteViews implements Parcelable, Filter { } if (mode == MODE_NORMAL) { mApplication = parcel.readParcelable(null); mApplication = parcel.readInt() == 0 ? info : ApplicationInfo.CREATOR.createFromParcel(parcel); mLayoutId = parcel.readInt(); mIsWidgetCollectionChild = parcel.readInt() == 1; Loading @@ -2230,7 +2246,7 @@ public class RemoteViews implements Parcelable, Filter { mActions.add(new ReflectionAction(parcel)); break; case ViewGroupAction.TAG: mActions.add(new ViewGroupAction(parcel, mBitmapCache)); mActions.add(new ViewGroupAction(parcel, mBitmapCache, mApplication)); break; case ReflectionActionWithoutParams.TAG: mActions.add(new ReflectionActionWithoutParams(parcel)); Loading Loading @@ -2278,8 +2294,8 @@ public class RemoteViews implements Parcelable, Filter { } } else { // MODE_HAS_LANDSCAPE_AND_PORTRAIT mLandscape = new RemoteViews(parcel, mBitmapCache); mPortrait = new RemoteViews(parcel, mBitmapCache); mLandscape = new RemoteViews(parcel, mBitmapCache, info); mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication); mApplication = mPortrait.mApplication; mLayoutId = mPortrait.getLayoutId(); } Loading @@ -2299,11 +2315,11 @@ public class RemoteViews implements Parcelable, Filter { // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps. // Instead pretend we're not owning the cache while parceling. mIsRoot = false; writeToParcel(p, 0); writeToParcel(p, PARCELABLE_ELIDE_DUPLICATES); p.setDataPosition(0); mIsRoot = true; RemoteViews rv = new RemoteViews(p, mBitmapCache.clone()); RemoteViews rv = new RemoteViews(p, mBitmapCache.clone(), mApplication); rv.mIsRoot = true; p.recycle(); Loading Loading @@ -3536,7 +3552,12 @@ public class RemoteViews implements Parcelable, Filter { if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); } dest.writeParcelable(mApplication, flags); if (!mIsRoot && (flags & PARCELABLE_ELIDE_DUPLICATES) != 0) { dest.writeInt(0); } else { dest.writeInt(1); mApplication.writeToParcel(dest, flags); } dest.writeInt(mLayoutId); dest.writeInt(mIsWidgetCollectionChild ? 1 : 0); int count; Loading @@ -3548,7 +3569,8 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(count); for (int i=0; i<count; i++) { Action a = mActions.get(i); a.writeToParcel(dest, 0); a.writeToParcel(dest, a.hasSameAppInfo(mApplication) ? PARCELABLE_ELIDE_DUPLICATES : 0); } } else { dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT); Loading @@ -3558,7 +3580,8 @@ public class RemoteViews implements Parcelable, Filter { mBitmapCache.writeBitmapsToParcel(dest, flags); } mLandscape.writeToParcel(dest, flags); mPortrait.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES); } } Loading
core/tests/coretests/src/android/widget/RemoteViewsTest.java +45 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; Loading @@ -35,6 +36,7 @@ import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; /** * Tests for RemoteViews. Loading @@ -43,6 +45,9 @@ import static org.junit.Assert.assertSame; @SmallTest public class RemoteViewsTest { // This can point to any other package which exists on the device. private static final String OTHER_PACKAGE = "com.android.systemui"; @Rule public final ExpectedException exception = ExpectedException.none(); Loading Loading @@ -121,4 +126,44 @@ public class RemoteViewsTest { clone.apply(mContext, mContainer); } @Test public void parcelSize_nestedViews() { RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); // We don't care about the actual layout id. RemoteViews child = new RemoteViews(mPackage, 33); int expectedSize = getParcelSize(original) + getParcelSize(child); original.addView(R.id.layout, child); // The application info will get written only once. assertTrue(getParcelSize(original) < expectedSize); assertEquals(getParcelSize(original), getParcelSize(original.clone())); original = new RemoteViews(mPackage, R.layout.remote_views_test); child = new RemoteViews(OTHER_PACKAGE, 33); expectedSize = getParcelSize(original) + getParcelSize(child); original.addView(R.id.layout, child); // Both the views will get written completely along with an additional view operation assertTrue(getParcelSize(original) > expectedSize); assertEquals(getParcelSize(original), getParcelSize(original.clone())); } @Test public void parcelSize_differentOrientation() { RemoteViews landscape = new RemoteViews(mPackage, R.layout.remote_views_test); RemoteViews portrait = new RemoteViews(mPackage, 33); // The application info will get written only once. RemoteViews views = new RemoteViews(landscape, portrait); assertTrue(getParcelSize(views) < (getParcelSize(landscape) + getParcelSize(portrait))); assertEquals(getParcelSize(views), getParcelSize(views.clone())); } private int getParcelSize(RemoteViews view) { Parcel parcel = Parcel.obtain(); view.writeToParcel(parcel, 0); int size = parcel.dataSize(); parcel.recycle(); return size; } }