Merge "Write RemoteViews actions to proto (1/3)" into main
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a395c1a..89ea852 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -153,6 +153,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 /**
@@ -712,6 +713,24 @@
         }
 
         public abstract void writeToParcel(Parcel dest, int flags);
+
+        /**
+         * Override to return true if this Action can be serialized to Protobuf, and implement
+         * writeToProto / createFromProto.
+         *
+         * If this returns false, then the action will be omitted from RemoteViews previews created
+         * with createPreviewFromProto / writePreviewToProto.
+         *
+         * Because Parcelables should not be serialized to disk, any action that contains an Intent,
+         * PendingIntent, or Bundle should return false here.
+         */
+        public boolean canWriteToProto() {
+            return false;
+        }
+
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            throw new UnsupportedOperationException();
+        }
     }
 
     /**
@@ -1506,6 +1525,7 @@
             switch (in.getFieldNumber()) {
                 case (int) RemoteViewsProto.RemoteCollectionCache.ENTRIES:
                     final LongSparseArray<Object> entry = new LongSparseArray<>();
+
                     final long entryToken = in.start(
                             RemoteViewsProto.RemoteCollectionCache.ENTRIES);
                     while (in.nextField() != NO_MORE_FIELDS) {
@@ -1533,10 +1553,12 @@
                         }
                     }
                     in.end(entryToken);
+
                     checkContainsKeys(entry,
                             new long[]{RemoteViewsProto.RemoteCollectionCache.Entry.ID,
                                     RemoteViewsProto.RemoteCollectionCache.Entry.URI,
                                     RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS});
+
                     entries.add(entry);
                     break;
                 default:
@@ -2247,6 +2269,62 @@
         public int getActionTag() {
             return BITMAP_REFLECTION_ACTION_TAG;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION);
+            out.write(RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME, mMethodName);
+            out.write(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID, mBitmapId);
+            out.end(token);
+        }
+    }
+
+    private PendingResources<Action> createFromBitmapReflectionActionFromProto(ProtoInputStream in)
+            throws Exception {
+        final LongSparseArray<Object> values = new LongSparseArray<>();
+
+        final long token = in.start(RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION);
+        while (in.nextField() != NO_MORE_FIELDS) {
+            switch (in.getFieldNumber()) {
+                case (int) RemoteViewsProto.BitmapReflectionAction.VIEW_ID:
+                    values.put(RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
+                            in.readString(RemoteViewsProto.BitmapReflectionAction.VIEW_ID));
+                    break;
+                case (int) RemoteViewsProto.BitmapReflectionAction.METHOD_NAME:
+                    values.put(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME,
+                            in.readString(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME));
+                    break;
+                case (int) RemoteViewsProto.BitmapReflectionAction.BITMAP_ID:
+                    values.put(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID,
+                            in.readInt(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID));
+                    break;
+                default:
+                    Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                            + ProtoUtils.currentFieldToString(in));
+            }
+        }
+        in.end(token);
+
+        checkContainsKeys(values, new long[]{RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
+                RemoteViewsProto.BitmapReflectionAction.METHOD_NAME});
+
+        return (context, resources, rootData, depth) -> {
+            int viewId = getAsIdentifier(resources, values,
+                    RemoteViewsProto.BitmapReflectionAction.VIEW_ID);
+            return new BitmapReflectionAction(viewId,
+                    (String) values.get(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME),
+                    rootData.mBitmapCache.getBitmapForId(
+                            (int) values.get(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID,
+                                    0)));
+        };
+
     }
 
     /**
@@ -2560,6 +2638,268 @@
         public int getActionTag() {
             return REFLECTION_ACTION_TAG;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.REFLECTION_ACTION);
+            out.write(RemoteViewsProto.ReflectionAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.ReflectionAction.METHOD_NAME, mMethodName);
+            out.write(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE, mType);
+            if (this.mValue != null) {
+                switch (this.mType) {
+                    case BOOLEAN:
+                        // ProtoOutputStream will omit this write if the value is false
+                        out.write(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE,
+                                (boolean) this.mValue);
+                        break;
+                    case BYTE:
+                        out.write(RemoteViewsProto.ReflectionAction.BYTE_VALUE,
+                                new byte[]{(byte) this.mValue});
+                        break;
+                    case SHORT:
+                        out.write(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
+                                (short) this.mValue);
+                        break;
+                    case INT:
+                        out.write(RemoteViewsProto.ReflectionAction.INT_VALUE, (int) this.mValue);
+                        break;
+                    case LONG:
+                        out.write(RemoteViewsProto.ReflectionAction.LONG_VALUE, (long) this.mValue);
+                        break;
+                    case FLOAT:
+                        out.write(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
+                                (float) this.mValue);
+                        break;
+                    case DOUBLE:
+                        out.write(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
+                                (double) this.mValue);
+                        break;
+                    case CHAR:
+                        out.write(RemoteViewsProto.ReflectionAction.CHAR_VALUE,
+                                (Character) this.mValue);
+                        break;
+                    case STRING:
+                        out.write(RemoteViewsProto.ReflectionAction.STRING_VALUE,
+                                (String) this.mValue);
+                        break;
+                    case CHAR_SEQUENCE:
+                        long csToken = out.start(
+                                RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE);
+                        RemoteViewsSerializers.writeCharSequenceToProto(out,
+                                (CharSequence) this.mValue);
+                        out.end(csToken);
+                        break;
+                    case URI:
+                        out.write(RemoteViewsProto.ReflectionAction.URI_VALUE,
+                                ((Uri) this.mValue).toString());
+                        break;
+                    case BITMAP:
+                        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+                        ((Bitmap) this.mValue).compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100,
+                                bytes);
+                        out.write(RemoteViewsProto.ReflectionAction.BITMAP_VALUE,
+                                bytes.toByteArray());
+                        break;
+                    case BLEND_MODE:
+                        out.write(RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE,
+                                BlendMode.toValue((BlendMode) this.mValue));
+                        break;
+                    case COLOR_STATE_LIST:
+                        writeColorStateListToProto(out, (ColorStateList) this.mValue,
+                                RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE);
+                        break;
+                    case ICON:
+                        writeIconToProto(out, appResources, (Icon) this.mValue,
+                                RemoteViewsProto.ReflectionAction.ICON_VALUE);
+                        break;
+                    case BUNDLE:
+                    case INTENT:
+                    default:
+                        break;
+                }
+            }
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(RemoteViewsProto.Action.REFLECTION_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.ReflectionAction.VIEW_ID:
+                        values.put(RemoteViewsProto.ReflectionAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.ReflectionAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.METHOD_NAME:
+                        values.put(RemoteViewsProto.ReflectionAction.METHOD_NAME,
+                                in.readString(RemoteViewsProto.ReflectionAction.METHOD_NAME));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.PARAMETER_TYPE:
+                        values.put(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE,
+                                in.readInt(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE,
+                                in.readBoolean(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.BYTE_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.BYTE_VALUE,
+                                in.readBytes(RemoteViewsProto.ReflectionAction.BYTE_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.SHORT_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
+                                (short) in.readInt(RemoteViewsProto.ReflectionAction.SHORT_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.INT_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.INT_VALUE,
+                                in.readInt(RemoteViewsProto.ReflectionAction.INT_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.LONG_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.LONG_VALUE,
+                                in.readLong(RemoteViewsProto.ReflectionAction.LONG_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.FLOAT_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
+                                in.readFloat(RemoteViewsProto.ReflectionAction.FLOAT_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.DOUBLE_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
+                                in.readDouble(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.CHAR_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.CHAR_VALUE,
+                                (char) in.readInt(RemoteViewsProto.ReflectionAction.CHAR_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.STRING_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.STRING_VALUE,
+                                in.readString(RemoteViewsProto.ReflectionAction.STRING_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE,
+                                createCharSequenceFromProto(in,
+                                        RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.URI_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.URI_VALUE,
+                                in.readString(RemoteViewsProto.ReflectionAction.URI_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.BITMAP_VALUE:
+                        byte[] bitmapData = in.readBytes(
+                                RemoteViewsProto.ReflectionAction.BITMAP_VALUE);
+                        values.put(RemoteViewsProto.ReflectionAction.BITMAP_VALUE,
+                                BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE,
+                                createColorStateListFromProto(in,
+                                        RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.ICON_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.ICON_VALUE,
+                                createIconFromProto(in,
+                                        RemoteViewsProto.ReflectionAction.ICON_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE:
+                        values.put(RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE,
+                                BlendMode.fromValue(in.readInt(
+                                        RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE)));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.ReflectionAction.VIEW_ID,
+                    RemoteViewsProto.ReflectionAction.METHOD_NAME,
+                    RemoteViewsProto.ReflectionAction.PARAMETER_TYPE});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.ReflectionAction.VIEW_ID);
+                Object value = null;
+                int parameterType = (int) values.get(
+                        RemoteViewsProto.ReflectionAction.PARAMETER_TYPE);
+                switch (parameterType) {
+                    case BOOLEAN:
+                        value = (boolean) values.get(
+                                RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE, false);
+                        break;
+                    case BYTE:
+                        byte[] bytes = (byte[]) values.get(
+                                RemoteViewsProto.ReflectionAction.BYTE_VALUE);
+                        if (bytes != null && bytes.length > 0) {
+                            value = bytes[0];
+                        }
+                        break;
+                    case SHORT:
+                        value = (short) values.get(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
+                                0);
+                        break;
+                    case INT:
+                        value = (int) values.get(RemoteViewsProto.ReflectionAction.INT_VALUE, 0);
+                        break;
+                    case LONG:
+                        value = (long) values.get(RemoteViewsProto.ReflectionAction.LONG_VALUE, 0);
+                        break;
+                    case FLOAT:
+                        value = (float) values.get(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
+                                0);
+                        break;
+                    case DOUBLE:
+                        value = (double) values.get(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
+                                0);
+                        break;
+                    case CHAR:
+                        value = (char) values.get(RemoteViewsProto.ReflectionAction.CHAR_VALUE, 0);
+                        break;
+                    case STRING:
+                        value = (String) values.get(RemoteViewsProto.ReflectionAction.STRING_VALUE);
+                        break;
+                    case CHAR_SEQUENCE:
+                        value = (CharSequence) values.get(
+                                RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE);
+                        break;
+                    case URI:
+                        value = Uri.parse(
+                                (String) values.get(RemoteViewsProto.ReflectionAction.URI_VALUE));
+                        break;
+                    case BITMAP:
+                        value = (Bitmap) values.get(RemoteViewsProto.ReflectionAction.BITMAP_VALUE);
+                        break;
+                    case BLEND_MODE:
+                        value = (BlendMode) values.get(
+                                RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE);
+                        break;
+                    case COLOR_STATE_LIST:
+                        value = (ColorStateList) values.get(
+                                RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE);
+                        break;
+                    case ICON:
+                        value = ((PendingResources<Icon>) values.get(
+                                RemoteViewsProto.ReflectionAction.ICON_VALUE)).create(context,
+                                resources, rootData, depth);
+                        break;
+                    case BUNDLE:
+                    case INTENT:
+                    default:
+                        // omit the action for unsupported parameter types
+                        return null;
+                }
+                return new ReflectionAction(viewId,
+                        (String) values.get(RemoteViewsProto.ReflectionAction.METHOD_NAME),
+                        parameterType, value);
+            };
+        }
     }
 
     private static final class ResourceReflectionAction extends BaseReflectionAction {
@@ -2740,7 +3080,87 @@
         public int getActionTag() {
             return ATTRIBUTE_REFLECTION_ACTION_TAG;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION);
+            out.write(RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME, mMethodName);
+            out.write(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE, mType);
+            out.write(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE, mResourceType);
+            if (mAttrId != 0) {
+                out.write(RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID,
+                        appResources.getResourceName(mAttrId));
+            }
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.AttributeReflectionAction.VIEW_ID: {
+                        values.put(RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.AttributeReflectionAction.VIEW_ID));
+                        break;
+                    }
+                    case (int) RemoteViewsProto.AttributeReflectionAction.METHOD_NAME:
+                        values.put(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME,
+                                in.readString(
+                                        RemoteViewsProto.AttributeReflectionAction.METHOD_NAME));
+                        break;
+                    case (int) RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID:
+                        values.put(RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID,
+                                in.readString(
+                                        RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID));
+                        break;
+                    case (int) RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE:
+                        values.put(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE,
+                                in.readInt(
+                                        RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE));
+                        break;
+                    case (int) RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE:
+                        values.put(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE,
+                                in.readInt(
+                                        RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
+                    RemoteViewsProto.AttributeReflectionAction.METHOD_NAME,
+                    RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE,
+                    RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.AttributeReflectionAction.VIEW_ID);
+                int attributeId = (values.indexOfKey(
+                        RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID) >= 0)
+                        ? getAsIdentifier(resources, values,
+                        RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID) : 0;
+                return new AttributeReflectionAction(viewId,
+                        (String) values.get(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME),
+                        (int) values.get(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE),
+                        (int) values.get(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE),
+                        attributeId);
+            };
+        }
     }
+
     private static final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
         private final float mValue;
         @ComplexDimensionUnit
@@ -2794,6 +3214,101 @@
         public int getActionTag() {
             return COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(
+                    RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION);
+            out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
+                    mMethodName);
+            out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE, mType);
+            out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
+                    mValue);
+            out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT, mUnit);
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(
+                    RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID:
+                        values.put(RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
+                                in.readString(
+                                        RemoteViewsProto
+                                                .ComplexUnitDimensionReflectionAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME:
+                        values.put(
+                                RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
+                                in.readString(
+                                        RemoteViewsProto
+                                                .ComplexUnitDimensionReflectionAction.METHOD_NAME));
+                        break;
+                    case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE:
+                        values.put(
+                                RemoteViewsProto
+                                        .ComplexUnitDimensionReflectionAction.PARAMETER_TYPE,
+                                in.readInt(
+                                        RemoteViewsProto
+                                                .ComplexUnitDimensionReflectionAction
+                                                .PARAMETER_TYPE));
+                        break;
+                    case (int) RemoteViewsProto
+                            .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE:
+                        values.put(
+                                RemoteViewsProto
+                                        .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
+                                in.readFloat(
+                                        RemoteViewsProto
+                                                .ComplexUnitDimensionReflectionAction
+                                                .DIMENSION_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT:
+                        values.put(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT,
+                                in.readInt(
+                                        RemoteViewsProto
+                                                .ComplexUnitDimensionReflectionAction.UNIT));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values,
+                    new long[]{RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
+                            RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
+                            RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID);
+                return new ComplexUnitDimensionReflectionAction(viewId, (String) values.get(
+                        RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME),
+                        (int) values.get(
+                                RemoteViewsProto
+                                        .ComplexUnitDimensionReflectionAction.PARAMETER_TYPE),
+                        (float) values.get(
+                                RemoteViewsProto
+                                        .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
+                                0),
+                        (int) values.get(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT,
+                                0));
+            };
+        }
     }
 
     private static final class NightModeReflectionAction extends BaseReflectionAction {
@@ -2868,6 +3383,145 @@
                 visitIconUri((Icon) mLightValue, visitor);
             }
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION);
+            out.write(RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.NightModeReflectionAction.METHOD_NAME, mMethodName);
+            out.write(RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE, mType);
+            switch (this.mType) {
+                case ICON:
+                    writeIconToProto(out, appResources, (Icon) mLightValue,
+                            RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON);
+                    writeIconToProto(out, appResources, (Icon) mDarkValue,
+                            RemoteViewsProto.NightModeReflectionAction.DARK_ICON);
+                    break;
+                case COLOR_STATE_LIST:
+                    writeColorStateListToProto(out, (ColorStateList) mLightValue,
+                            RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST);
+                    writeColorStateListToProto(out, (ColorStateList) mDarkValue,
+                            RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST);
+                    break;
+                case INT:
+                    out.write(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT,
+                            (int) mLightValue);
+                    out.write(RemoteViewsProto.NightModeReflectionAction.DARK_INT,
+                            (int) mDarkValue);
+                    break;
+            }
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.NightModeReflectionAction.VIEW_ID:
+                        values.put(RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.NightModeReflectionAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.NightModeReflectionAction.METHOD_NAME:
+                        values.put(RemoteViewsProto.NightModeReflectionAction.METHOD_NAME,
+                                in.readString(
+                                        RemoteViewsProto.NightModeReflectionAction.METHOD_NAME));
+                        break;
+                    case (int) RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE:
+                        values.put(RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE,
+                                in.readInt(
+                                        RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE));
+                        break;
+                    case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON:
+                        values.put(RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON,
+                                createIconFromProto(in,
+                                        RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON));
+                        break;
+                    case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST:
+                        values.put(
+                                RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST,
+                                createColorStateListFromProto(in,
+                                        RemoteViewsProto
+                                                .NightModeReflectionAction.LIGHT_COLOR_STATE_LIST));
+                        break;
+                    case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_INT:
+                        values.put(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT,
+                                in.readInt(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT));
+                        break;
+                    case (int) RemoteViewsProto.NightModeReflectionAction.DARK_ICON:
+                        values.put(RemoteViewsProto.NightModeReflectionAction.DARK_ICON,
+                                createIconFromProto(in,
+                                        RemoteViewsProto.NightModeReflectionAction.DARK_ICON));
+                        break;
+                    case (int) RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST:
+                        values.put(RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST,
+                                createColorStateListFromProto(in,
+                                        RemoteViewsProto
+                                                .NightModeReflectionAction.DARK_COLOR_STATE_LIST));
+                        break;
+                    case (int) RemoteViewsProto.NightModeReflectionAction.DARK_INT:
+                        values.put(RemoteViewsProto.NightModeReflectionAction.DARK_INT,
+                                in.readInt(RemoteViewsProto.NightModeReflectionAction.DARK_INT));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
+                    RemoteViewsProto.NightModeReflectionAction.METHOD_NAME,
+                    RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.NightModeReflectionAction.VIEW_ID);
+                String methodName = (String) values.get(
+                        RemoteViewsProto.NightModeReflectionAction.METHOD_NAME);
+                int parameterType = (int) values.get(
+                        RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE);
+                switch (parameterType) {
+                    case ICON:
+                        PendingResources<Icon> pendingLightIcon =
+                                (PendingResources<Icon>) values.get(
+                                        RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON);
+                        PendingResources<Icon> pendingDarkIcon =
+                                (PendingResources<Icon>) values.get(
+                                        RemoteViewsProto.NightModeReflectionAction.DARK_ICON);
+                        Icon lightIcon = pendingLightIcon != null ? pendingLightIcon.create(context,
+                                resources, rootData, depth) : null;
+                        Icon darkIcon = pendingDarkIcon != null ? pendingDarkIcon.create(context,
+                                resources, rootData, depth) : null;
+                        return new NightModeReflectionAction(viewId, methodName, parameterType,
+                                lightIcon, darkIcon);
+                    case COLOR_STATE_LIST:
+                        return new NightModeReflectionAction(viewId, methodName, parameterType,
+                                (ColorStateList) values.get(
+                                        RemoteViewsProto
+                                                .NightModeReflectionAction.LIGHT_COLOR_STATE_LIST),
+                                (ColorStateList) values.get(
+                                        RemoteViewsProto
+                                                .NightModeReflectionAction.DARK_COLOR_STATE_LIST));
+                    case INT:
+                        return new NightModeReflectionAction(viewId, methodName, parameterType,
+                                (int) values.get(
+                                        RemoteViewsProto.NightModeReflectionAction.LIGHT_INT, 0),
+                                (int) values.get(
+                                        RemoteViewsProto.NightModeReflectionAction.DARK_INT, 0));
+                    default:
+                        throw new RuntimeException("Unknown parameterType: " + parameterType);
+                }
+            };
+        }
     }
 
     /**
@@ -3353,6 +4007,46 @@
         public int mergeBehavior() {
             return MERGE_APPEND;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION);
+            out.write(RemoteViewsProto.RemoveFromParentAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.RemoveFromParentAction.VIEW_ID:
+                        values.put(RemoteViewsProto.RemoveFromParentAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.RemoveFromParentAction.VIEW_ID));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.RemoveFromParentAction.VIEW_ID});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.RemoveFromParentAction.VIEW_ID);
+                return new RemoveFromParentAction(viewId);
+            };
+        }
     }
 
     /**
@@ -3768,6 +4462,64 @@
         public String getUniqueKey() {
             return super.getUniqueKey() + mProperty;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.LAYOUT_PARAM_ACTION);
+            out.write(RemoteViewsProto.LayoutParamAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.LayoutParamAction.PROPERTY, mProperty);
+            out.write(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE, mValue);
+            out.write(RemoteViewsProto.LayoutParamAction.VALUE_TYPE, mValueType);
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(RemoteViewsProto.Action.LAYOUT_PARAM_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.LayoutParamAction.VIEW_ID:
+                        values.put(RemoteViewsProto.LayoutParamAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.LayoutParamAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.LayoutParamAction.PROPERTY:
+                        values.put(RemoteViewsProto.LayoutParamAction.PROPERTY,
+                                in.readInt(RemoteViewsProto.LayoutParamAction.PROPERTY));
+                        break;
+                    case (int) RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE:
+                        values.put(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE,
+                                in.readInt(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE));
+                        break;
+                    case (int) RemoteViewsProto.LayoutParamAction.VALUE_TYPE:
+                        values.put(RemoteViewsProto.LayoutParamAction.VALUE_TYPE,
+                                in.readInt(RemoteViewsProto.LayoutParamAction.VALUE_TYPE));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.LayoutParamAction.VIEW_ID});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.LayoutParamAction.VIEW_ID);
+                return new LayoutParamAction(viewId,
+                        (int) values.get(RemoteViewsProto.LayoutParamAction.PROPERTY, 0),
+                        (int) values.get(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE, 0),
+                        (int) values.get(RemoteViewsProto.LayoutParamAction.VALUE_TYPE, 0));
+            };
+        }
     }
 
     /**
@@ -7668,6 +8420,7 @@
             values.put(RemoteViewsProto.RemoteCollectionItems.IDS, new ArrayList<Long>());
             values.put(RemoteViewsProto.RemoteCollectionItems.VIEWS,
                     new ArrayList<PendingResources<RemoteViews>>());
+
             while (in.nextField() != NO_MORE_FIELDS) {
                 switch (in.getFieldNumber()) {
                     case (int) RemoteViewsProto.RemoteCollectionItems.IDS:
@@ -7704,6 +8457,7 @@
 
             checkContainsKeys(values,
                     new long[]{RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT});
+
             return (context, resources, rootData, depth) -> {
                 List<Long> idList = (List<Long>) values.get(
                         RemoteViewsProto.RemoteCollectionItems.IDS);
@@ -8149,6 +8903,16 @@
                 out.write(SizeFProto.HEIGHT, mIdealSize.getHeight());
                 out.end(token);
             }
+
+            if (mActions != null) {
+                for (Action action : mActions) {
+                    if (action.canWriteToProto()) {
+                        final long token = out.start(RemoteViewsProto.ACTIONS);
+                        action.writeToProto(out, context, appResources);
+                        out.end(token);
+                    }
+                }
+            }
         } else if (hasSizedRemoteViews()) {
             out.write(RemoteViewsProto.MODE, MODE_HAS_SIZED_REMOTEVIEWS);
             for (RemoteViews view : mSizedRemoteViews) {
@@ -8192,6 +8956,7 @@
             String mLayoutResName = null;
             String mLightBackgroundResName = null;
             String mViewResName = null;
+            final List<PendingResources<Action>> mActions = new ArrayList<>();
             final List<PendingResources<RemoteViews>> mSizedRemoteViews = new ArrayList<>();
             PendingResources<RemoteViews> mLandscapeViews = null;
             PendingResources<RemoteViews> mPortraitViews = null;
@@ -8230,6 +8995,14 @@
                     case (int) RemoteViewsProto.PROVIDER_INSTANCE_ID:
                         ref.mProviderInstanceId = in.readInt(RemoteViewsProto.PROVIDER_INSTANCE_ID);
                         break;
+                    case (int) RemoteViewsProto.ACTIONS:
+                        final long actionsToken = in.start(RemoteViewsProto.ACTIONS);
+                        final PendingResources<Action> action = createActionFromProto(ref.mRv, in);
+                        if (action != null) {
+                            ref.mActions.add(action);
+                        }
+                        in.end(actionsToken);
+                        break;
                     case (int) RemoteViewsProto.SIZED_REMOTEVIEWS:
                         final long sizedToken = in.start(RemoteViewsProto.SIZED_REMOTEVIEWS);
                         ref.mSizedRemoteViews.add(createFromProto(in));
@@ -8328,19 +9101,27 @@
                 }
             }
             if (ref.mPopulateRemoteCollectionCache != null) {
-                ref.mPopulateRemoteCollectionCache.create(context, resources, rootData, depth);
+                ref.mPopulateRemoteCollectionCache.create(appContext, appResources, rootData,
+                        depth);
             }
             if (ref.mProviderInstanceId != -1) {
                 rv.mProviderInstanceId = ref.mProviderInstanceId;
             }
             if (ref.mMode == MODE_NORMAL) {
                 rv.setIdealSize(ref.mIdealSize);
+                for (PendingResources<Action> pendingAction : ref.mActions) {
+                    Action action = pendingAction.create(appContext, appResources, rootData, depth);
+                    if (action != null) {
+                        rv.addAction(action);
+                    }
+                }
                 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);
+                    RemoteViews views = pendingViews.create(appContext, appResources, rootData,
+                            depth);
                     sizedViews.add(views);
                 }
                 rv.initializeSizedRemoteViews(sizedViews.iterator());
@@ -8349,8 +9130,8 @@
                 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));
+                        ref.mLandscapeViews.create(appContext, appResources, rootData, depth),
+                        ref.mPortraitViews.create(appContext, appResources, rootData, depth));
                 parentRv.initializeFrom(/* src= */ rv, /* hierarchyRoot= */ rv);
                 return parentRv;
             } else {
@@ -8370,6 +9151,35 @@
                 throws Exception;
     }
 
+    @Nullable
+    private static PendingResources<Action> createActionFromProto(RemoteViews rv,
+            ProtoInputStream in) throws Exception {
+        int actionFieldId = in.nextField();
+        if (actionFieldId == NO_MORE_FIELDS) {
+            // action was omitted
+            return null;
+        }
+        switch (actionFieldId) {
+            case (int) RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION:
+                return AttributeReflectionAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION:
+                return rv.createFromBitmapReflectionActionFromProto(in);
+            case (int) RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION:
+                return ComplexUnitDimensionReflectionAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.LAYOUT_PARAM_ACTION:
+                return LayoutParamAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION:
+                return NightModeReflectionAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.REFLECTION_ACTION:
+                return ReflectionAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION:
+                return RemoveFromParentAction.createFromProto(in);
+            default:
+                throw new RuntimeException("Unhandled field while reading Action proto!\n"
+                        + ProtoUtils.currentFieldToString(in));
+        }
+    }
+
     private static void checkValidResource(int id, String message, String resName)
             throws Exception {
         if (id == 0) throw new Exception(message + ": " + resName);
@@ -8392,6 +9202,22 @@
         }
     }
 
+    private static int getAsIdentifier(Resources resources, LongSparseArray<?> array, long fieldId)
+            throws Exception {
+        String resName = (String) array.get(fieldId);
+        int id = resources.getIdentifier(resName, /* defType= */ null, /* defPackage= */ null);
+        checkValidResource(id, "Invalid id", resName);
+        return id;
+    }
+
+    private static int getAsIdentifier(Resources resources, SparseArray<?> array, int key)
+            throws Exception {
+        String resName = (String) array.get(key);
+        int id = resources.getIdentifier(resName, /* defType= */ null, /* defPackage= */ null);
+        checkValidResource(id, "Invalid id", resName);
+        return id;
+    }
+
     private static SizeF createSizeFFromProto(ProtoInputStream in) throws Exception {
         float width = 0;
         float height = 0;
@@ -8411,4 +9237,43 @@
 
         return new SizeF(width, height);
     }
+
+    private static void writeIconToProto(ProtoOutputStream out, Resources appResources, Icon icon,
+            long fieldId) {
+        long token = out.start(fieldId);
+        RemoteViewsSerializers.writeIconToProto(out, appResources, icon);
+        out.end(token);
+    }
+
+    private static PendingResources<Icon> createIconFromProto(ProtoInputStream in, long fieldId)
+            throws Exception {
+        long token = in.start(fieldId);
+        Function<Resources, Icon> icon = RemoteViewsSerializers.createIconFromProto(in);
+        in.end(token);
+        return (context, resources, rootData, depth) -> icon.apply(resources);
+    }
+
+    private static void writeColorStateListToProto(ProtoOutputStream out,
+            ColorStateList colorStateList, long fieldId) {
+        long token = out.start(fieldId);
+        colorStateList.writeToProto(out);
+        out.end(token);
+    }
+
+    private static ColorStateList createColorStateListFromProto(ProtoInputStream in, long fieldId)
+            throws Exception {
+        long token = in.start(fieldId);
+        ColorStateList colorStateList = ColorStateList.createFromProto(in);
+        in.end(token);
+        return colorStateList;
+    }
+
+    private static CharSequence createCharSequenceFromProto(ProtoInputStream in, long fieldId)
+            throws Exception {
+        long token = in.start(fieldId);
+        CharSequence cs = RemoteViewsSerializers.createCharSequenceFromProto(in);
+        in.end(token);
+        return cs;
+    }
+
 }
diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto
index 5892396..47c97b0 100644
--- a/core/proto/android/widget/remoteviews.proto
+++ b/core/proto/android/widget/remoteviews.proto
@@ -54,6 +54,7 @@
     optional bool has_draw_instructions = 13;
     repeated bytes bitmap_cache = 14;
     optional RemoteCollectionCache remote_collection_cache = 15;
+    repeated Action actions = 16;
 
     message RemoteCollectionCache {
         message Entry {
@@ -288,6 +289,91 @@
             }
         }
     }
+
+    message Action {
+        oneof action {
+            AttributeReflectionAction attribute_reflection_action = 1;
+            BitmapReflectionAction bitmap_reflection_action = 2;
+            ComplexUnitDimensionReflectionAction complex_unit_dimension_reflection_action = 3;
+            LayoutParamAction layout_param_action = 4;
+            NightModeReflectionAction night_mode_reflection_action = 5;
+            ReflectionAction reflection_action = 6;
+            RemoveFromParentAction remove_from_parent_action = 7;
+        }
+    }
+
+    message AttributeReflectionAction {
+        optional string view_id = 1;
+        optional string method_name = 2;
+        optional int32 parameter_type = 3;
+        optional int32 resource_type = 4;
+        optional string attribute_id = 5;
+    }
+
+    message BitmapReflectionAction {
+        optional string view_id = 1;
+        optional string method_name = 2;
+        optional int32 bitmap_id = 3;
+    }
+
+    message ComplexUnitDimensionReflectionAction {
+        optional string view_id = 1;
+        optional string method_name = 2;
+        optional int32 parameter_type = 3;
+        optional float dimension_value = 4;
+        optional int32 unit = 5;
+    }
+
+    message LayoutParamAction {
+        optional string view_id = 1;
+        optional int32 property = 2;
+        optional int32 layout_value = 3;
+        optional int32 value_type = 4;
+    }
+
+    message NightModeReflectionAction {
+        optional string view_id = 1;
+        optional string method_name = 2;
+        optional int32 parameter_type = 3;
+        oneof light {
+            Icon light_icon = 4;
+            android.content.res.ColorStateListProto light_color_state_list = 5;
+            int32 light_int = 6;
+        }
+        oneof dark {
+            Icon dark_icon = 7;
+            android.content.res.ColorStateListProto dark_color_state_list = 8;
+            int32 dark_int = 9;
+        }
+    }
+
+    message ReflectionAction {
+        optional string view_id = 1;
+        optional string method_name = 2;
+        optional int32 parameter_type = 3;
+        oneof reflection_value {
+            bool boolean_value = 4;
+            bytes byte_value = 5;
+            int32 short_value = 6;
+            int32 int_value = 7;
+            int64 long_value = 8;
+            float float_value = 9;
+            double double_value = 10;
+            int32 char_value = 11;
+            string string_value = 12;
+            CharSequence char_sequence_value = 13;
+            string uri_value = 14;
+            bytes bitmap_value = 15;
+            android.content.res.ColorStateListProto color_state_list_value = 16;
+            Icon icon_value = 17;
+            int32 blend_mode_value = 18;
+            // Intent and Bundle values are excluded.
+        }
+    }
+
+    message RemoveFromParentAction {
+        optional string view_id = 1;
+    }
 }
 
 
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java b/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
deleted file mode 100644
index 7c14032..0000000
--- a/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-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);
-    }
-}