summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/appwidget/flags.aconfig7
-rw-r--r--core/java/android/widget/RemoteViews.java279
-rw-r--r--core/proto/android/widget/remoteviews.proto60
-rw-r--r--core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java155
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java3
5 files changed, 503 insertions, 1 deletions
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 18cfca686107..4e0379e3dc3a 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -50,3 +50,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "remote_views_proto"
+ namespace: "app_widgets"
+ description: "Enable support for persisting RemoteViews previews to Protobuf"
+ bug: "306546610"
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5430f8f6add3..060c7dedf08a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,8 +17,10 @@
package android.widget;
import static android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL;
+import static android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO;
import static android.appwidget.flags.Flags.drawDataParcel;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
+import static android.util.proto.ProtoInputStream.NO_MORE_FIELDS;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
import android.annotation.AttrRes;
@@ -94,6 +96,9 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.TypedValue.ComplexDimensionUnit;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
@@ -7860,4 +7865,278 @@ public class RemoteViews implements Parcelable, Filter {
mClassCookies = classCookies;
}
}
+
+ /**
+ * Write this RemoteViews to proto.
+ * @hide
+ */
+ @FlaggedApi(FLAG_REMOTE_VIEWS_PROTO)
+ public void writePreviewToProto(@NonNull Context context, @NonNull ProtoOutputStream out) {
+ if (mApplication != null) {
+ // mApplication may be null if this was created with DrawInstructions constructor.
+ out.write(RemoteViewsProto.PACKAGE_NAME, mApplication.packageName);
+ }
+ Resources appResources = getContextForResourcesEnsuringCorrectCachedApkPaths(
+ context).getResources();
+ if (mLayoutId != 0) {
+ out.write(RemoteViewsProto.LAYOUT_ID, appResources.getResourceName(mLayoutId));
+ }
+ if (mLightBackgroundLayoutId != 0) {
+ out.write(RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID,
+ appResources.getResourceName(mLightBackgroundLayoutId));
+ }
+ if (mViewId != 0 && mViewId != -1) {
+ out.write(RemoteViewsProto.VIEW_ID, appResources.getResourceName(mViewId));
+ }
+ out.write(RemoteViewsProto.IS_ROOT, mIsRoot);
+ out.write(RemoteViewsProto.APPLY_FLAGS, mApplyFlags);
+ out.write(RemoteViewsProto.HAS_DRAW_INSTRUCTIONS, mHasDrawInstructions);
+ if (mProviderInstanceId != -1) {
+ out.write(RemoteViewsProto.PROVIDER_INSTANCE_ID, mProviderInstanceId);
+ }
+
+ if (!hasMultipleLayouts()) {
+ out.write(RemoteViewsProto.MODE, MODE_NORMAL);
+ if (mIdealSize != null) {
+ final long token = out.start(RemoteViewsProto.IDEAL_SIZE);
+ out.write(SizeFProto.WIDTH, mIdealSize.getWidth());
+ out.write(SizeFProto.HEIGHT, mIdealSize.getHeight());
+ out.end(token);
+ }
+ } else if (hasSizedRemoteViews()) {
+ out.write(RemoteViewsProto.MODE, MODE_HAS_SIZED_REMOTEVIEWS);
+ for (RemoteViews view : mSizedRemoteViews) {
+ final long sizedViewToken = out.start(RemoteViewsProto.SIZED_REMOTEVIEWS);
+ view.writePreviewToProto(context, out);
+ out.end(sizedViewToken);
+ }
+ } else {
+ out.write(RemoteViewsProto.MODE, MODE_HAS_LANDSCAPE_AND_PORTRAIT);
+ final long landscapeViewToken = out.start(RemoteViewsProto.LANDSCAPE_REMOTEVIEWS);
+ mLandscape.writePreviewToProto(context, out);
+ out.end(landscapeViewToken);
+ final long portraitViewToken = out.start(RemoteViewsProto.PORTRAIT_REMOTEVIEWS);
+ mPortrait.writePreviewToProto(context, out);
+ out.end(portraitViewToken);
+ }
+ }
+
+ /**
+ * Create a RemoteViews from proto input.
+ * @hide
+ */
+ @FlaggedApi(FLAG_REMOTE_VIEWS_PROTO)
+ public static RemoteViews createPreviewFromProto(Context context, ProtoInputStream in)
+ throws Exception {
+ return createFromProto(in).create(context, context.getResources(), /* rootData= */ null,
+ /* depth= */ 0);
+ }
+
+ private static PendingResources<RemoteViews> createFromProto(ProtoInputStream in)
+ throws Exception {
+ // Grouping these variables into an anonymous object allows us to access them through `ref`
+ // (which is final) later in the lambda.
+ final var ref = new Object() {
+ final RemoteViews mRv = new RemoteViews();
+ int mMode = 0;
+ int mApplyFlags = 0;
+ long mProviderInstanceId = -1;
+ String mPackageName = null;
+ SizeF mIdealSize = null;
+ String mLayoutResName = null;
+ String mLightBackgroundResName = null;
+ String mViewResName = null;
+ final List<PendingResources<RemoteViews>> mSizedRemoteViews = new ArrayList<>();
+ PendingResources<RemoteViews> mLandscapeViews = null;
+ PendingResources<RemoteViews> mPortraitViews = null;
+ boolean mIsRoot = false;
+ boolean mHasDrawInstructions = false;
+ };
+
+ try {
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.MODE:
+ ref.mMode = in.readInt(RemoteViewsProto.MODE);
+ break;
+ case (int) RemoteViewsProto.PACKAGE_NAME:
+ ref.mPackageName = in.readString(RemoteViewsProto.PACKAGE_NAME);
+ break;
+ case (int) RemoteViewsProto.IDEAL_SIZE:
+ final long idealSizeToken = in.start(RemoteViewsProto.IDEAL_SIZE);
+ ref.mIdealSize = createSizeFFromProto(in);
+ in.end(idealSizeToken);
+ break;
+ case (int) RemoteViewsProto.LAYOUT_ID:
+ ref.mLayoutResName = in.readString(RemoteViewsProto.LAYOUT_ID);
+ break;
+ case (int) RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID:
+ ref.mLightBackgroundResName = in.readString(
+ RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID);
+ break;
+ case (int) RemoteViewsProto.VIEW_ID:
+ ref.mViewResName = in.readString(RemoteViewsProto.VIEW_ID);
+ break;
+ case (int) RemoteViewsProto.APPLY_FLAGS:
+ ref.mApplyFlags = in.readInt(RemoteViewsProto.APPLY_FLAGS);
+ break;
+ case (int) RemoteViewsProto.PROVIDER_INSTANCE_ID:
+ ref.mProviderInstanceId = in.readInt(RemoteViewsProto.PROVIDER_INSTANCE_ID);
+ break;
+ case (int) RemoteViewsProto.SIZED_REMOTEVIEWS:
+ final long sizedToken = in.start(RemoteViewsProto.SIZED_REMOTEVIEWS);
+ ref.mSizedRemoteViews.add(createFromProto(in));
+ in.end(sizedToken);
+ break;
+ case (int) RemoteViewsProto.LANDSCAPE_REMOTEVIEWS:
+ final long landscapeToken = in.start(
+ RemoteViewsProto.LANDSCAPE_REMOTEVIEWS);
+ ref.mLandscapeViews = createFromProto(in);
+ in.end(landscapeToken);
+ break;
+ case (int) RemoteViewsProto.PORTRAIT_REMOTEVIEWS:
+ final long portraitToken = in.start(RemoteViewsProto.PORTRAIT_REMOTEVIEWS);
+ ref.mPortraitViews = createFromProto(in);
+ in.end(portraitToken);
+ break;
+ case (int) RemoteViewsProto.IS_ROOT:
+ ref.mIsRoot = in.readBoolean(RemoteViewsProto.IS_ROOT);
+ break;
+ case (int) RemoteViewsProto.HAS_DRAW_INSTRUCTIONS:
+ ref.mHasDrawInstructions = in.readBoolean(
+ RemoteViewsProto.HAS_DRAW_INSTRUCTIONS);
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return (context, resources, rootData, depth) -> {
+ if (depth > MAX_NESTED_VIEWS && (UserHandle.getAppId(Binder.getCallingUid())
+ != Process.SYSTEM_UID)) {
+ throw new IllegalArgumentException("Too many nested views.");
+ }
+ depth++;
+
+ RemoteViews rv = ref.mRv;
+ rv.mApplyFlags = ref.mApplyFlags;
+ rv.mIsRoot = ref.mIsRoot;
+ rv.mHasDrawInstructions = ref.mHasDrawInstructions;
+
+ // The root view will read its HierarchyRootData (bitmap cache, collection cache) from
+ // proto; all nested views will instead get it through the rootData parameter.
+ if (rootData == null) {
+ if (!rv.mIsRoot || depth != 1) {
+ throw new IllegalStateException(
+ "A nested view did not receive HierarchyRootData");
+ }
+ rootData = rv.getHierarchyRootData();
+ } else {
+ rv.configureAsChild(rootData);
+ }
+
+ Context appContext = null;
+ Resources appResources = null;
+ if (!ref.mHasDrawInstructions) {
+ checkProtoResultNotNull(ref.mPackageName, "No application info");
+ rv.mApplication = context.getPackageManager().getApplicationInfo(ref.mPackageName,
+ /* flags= */ 0);
+ appContext = rv.getContextForResourcesEnsuringCorrectCachedApkPaths(context);
+ appResources = appContext.getResources();
+
+ checkProtoResultNotNull(ref.mLayoutResName, "No layout id");
+ rv.mLayoutId = appResources.getIdentifier(ref.mLayoutResName, /* defType= */ null,
+ /* defPackage= */ null);
+ checkValidResource(rv.mLayoutId, "Invalid layout id", ref.mLayoutResName);
+
+ if (ref.mViewResName != null) {
+ rv.mViewId = appResources.getIdentifier(ref.mViewResName, /* defType= */ null,
+ /* defPackage= */ null);
+ checkValidResource(rv.mViewId, "Invalid view id", ref.mViewResName);
+ }
+
+ if (ref.mLightBackgroundResName != null) {
+ int lightBackgroundLayoutId = appResources.getIdentifier(
+ ref.mLightBackgroundResName,
+ /* defType= */ null, /* defPackage= */ null);
+ checkValidResource(lightBackgroundLayoutId,
+ "Invalid light background layout id", ref.mLightBackgroundResName);
+ rv.setLightBackgroundLayoutId(lightBackgroundLayoutId);
+ }
+ }
+ if (ref.mProviderInstanceId != -1) {
+ rv.mProviderInstanceId = ref.mProviderInstanceId;
+ }
+ if (ref.mMode == MODE_NORMAL) {
+ rv.setIdealSize(ref.mIdealSize);
+ return rv;
+ } else if (ref.mMode == MODE_HAS_SIZED_REMOTEVIEWS) {
+ List<RemoteViews> sizedViews = new ArrayList<>();
+ for (RemoteViews.PendingResources<RemoteViews> pendingViews :
+ ref.mSizedRemoteViews) {
+ RemoteViews views = pendingViews.create(context, resources, rootData, depth);
+ sizedViews.add(views);
+ }
+ rv.initializeSizedRemoteViews(sizedViews.iterator());
+ return rv;
+ } else if (ref.mMode == MODE_HAS_LANDSCAPE_AND_PORTRAIT) {
+ checkProtoResultNotNull(ref.mLandscapeViews, "Missing landscape views");
+ checkProtoResultNotNull(ref.mPortraitViews, "Missing portrait views");
+ RemoteViews parentRv = new RemoteViews(
+ ref.mLandscapeViews.create(context, resources, rootData, depth),
+ ref.mPortraitViews.create(context, resources, rootData, depth));
+ parentRv.initializeFrom(/* src= */ rv, /* hierarchyRoot= */ rv);
+ return parentRv;
+ } else {
+ throw new InvalidProtoException(ref.mMode + " is not a valid mode.");
+ }
+ };
+ }
+
+ private static class InvalidProtoException extends Exception {
+ InvalidProtoException(String message) {
+ super(message);
+ }
+ }
+
+ private interface PendingResources<T> {
+ T create(Context context, Resources appResources, HierarchyRootData rootData, int depth)
+ throws Exception;
+ }
+
+ private static void checkValidResource(int id, String message, String resName)
+ throws Exception {
+ if (id == 0) throw new Exception(message + ": " + resName);
+ }
+
+ private static void checkProtoResultNotNull(Object o, String message)
+ throws InvalidProtoException {
+ if (o == null) {
+ throw new InvalidProtoException(message);
+ }
+ }
+
+ private static SizeF createSizeFFromProto(ProtoInputStream in) throws Exception {
+ float width = 0;
+ float height = 0;
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) SizeFProto.WIDTH:
+ width = in.readFloat(SizeFProto.WIDTH);
+ break;
+ case (int) SizeFProto.HEIGHT:
+ height = in.readFloat(SizeFProto.HEIGHT);
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading SizeF proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+
+ return new SizeF(width, height);
+ }
}
diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto
new file mode 100644
index 000000000000..d24da0362e46
--- /dev/null
+++ b/core/proto/android/widget/remoteviews.proto
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless optional by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+
+package android.widget;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+/**
+ * An android.widget.RemoteViews object. This is used by RemoteViews.createPreviewFromProto
+ * and RemoteViews.writePreviewToProto.
+ *
+ * Any addition of fields here will require an update to the parsing code in RemoteViews.java.
+ * Otherwise the field will be ignored when parsing (with a logged warning).
+ *
+ * Do not change the tag number or type of any fields in order to maintain compatibility with
+ * previous versions. If a field is deleted, use `reserved` to mark its tag number.
+ */
+message RemoteViewsProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int32 mode = 1;
+ optional string package_name = 2;
+ optional string layout_id = 3;
+ optional string light_background_layout_id = 4;
+ optional string view_id = 5;
+ optional SizeFProto ideal_size = 6;
+ optional int32 apply_flags = 7;
+ optional int64 provider_instance_id = 8;
+ // RemoteViews for different sizes (created with RemoteViews(Map<SizeF, RemoteViews)
+ // constructor).
+ repeated RemoteViewsProto sized_remoteviews = 9;
+ // RemoteViews for portrait/landscape (created with RemoteViews(RemoteViews, RemoteViews)i
+ // constructor).
+ optional RemoteViewsProto portrait_remoteviews = 10;
+ optional RemoteViewsProto landscape_remoteviews = 11;
+ optional bool is_root = 12;
+ optional bool has_draw_instructions = 13;
+}
+
+
+message SizeFProto {
+ optional float width = 1;
+ optional float height = 2;
+}
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java b/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
new file mode 100644
index 000000000000..8e9ba7b008cd
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+import android.util.SizeF;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.view.View;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+/**
+ * Tests for RemoteViews.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RemoteViewsProtoTest {
+
+ // 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();
+
+ private Context mContext;
+ private String mPackage;
+ private LinearLayout mContainer;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getContext();
+ mPackage = mContext.getPackageName();
+ mContainer = new LinearLayout(mContext);
+ }
+
+ @Test
+ public void copy_canStillBeApplied() {
+ RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+ RemoteViews clone = recreateFromProto(original);
+
+ clone.apply(mContext, mContainer);
+ }
+
+ @SuppressWarnings("ReturnValueIgnored")
+ @Test
+ public void clone_repeatedly() {
+ RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+ recreateFromProto(original);
+ recreateFromProto(original);
+
+ original.apply(mContext, mContainer);
+ }
+
+ @Test
+ public void clone_chained() {
+ RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+ RemoteViews clone = recreateFromProto(recreateFromProto(original));
+
+
+ clone.apply(mContext, mContainer);
+ }
+
+ @Test
+ public void landscapePortraitViews_lightBackgroundLayoutFlag() {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(inner, inner);
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = recreateFromProto(parent).apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
+ @Test
+ public void sizedViews_lightBackgroundLayoutFlag() {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(
+ Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = recreateFromProto(parent).apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
+ @Test
+ public void nestedLandscapeViews() throws Exception {
+ RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+ for (int i = 0; i < 10; i++) {
+ views = new RemoteViews(views, new RemoteViews(mPackage, R.layout.remote_views_test));
+ }
+ // writeTo/createFromProto works
+ recreateFromProto(views);
+
+ views = new RemoteViews(mPackage, R.layout.remote_views_test);
+ for (int i = 0; i < 11; i++) {
+ views = new RemoteViews(views, new RemoteViews(mPackage, R.layout.remote_views_test));
+ }
+ // writeTo/createFromProto fails
+ exception.expect(IllegalArgumentException.class);
+ recreateFromProtoNoRethrow(views);
+ }
+
+ private RemoteViews recreateFromProto(RemoteViews views) {
+ try {
+ return recreateFromProtoNoRethrow(views);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private RemoteViews recreateFromProtoNoRethrow(RemoteViews views) throws Exception {
+ ProtoOutputStream out = new ProtoOutputStream();
+ views.writePreviewToProto(mContext, out);
+ ProtoInputStream in = new ProtoInputStream(out.getBytes());
+ return RemoteViews.createPreviewFromProto(mContext, in);
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 863cda4905f1..c2a923ed236d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -36,6 +36,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RemoteViews;
@@ -103,7 +104,7 @@ public class NotificationVisitUrisTest extends UiServiceTestCase {
private static final ImmutableSet<Class<?>> UNUSABLE_TYPES =
ImmutableSet.of(Consumer.class, IBinder.class, MediaSession.Token.class, Parcel.class,
PrintWriter.class, Resources.Theme.class, View.class,
- LayoutInflater.Factory2.class);
+ LayoutInflater.Factory2.class, ProtoOutputStream.class);
// Maximum number of times we allow generating the same class recursively.
// E.g. new RemoteViews.addView(new RemoteViews()) but stop there.