• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.widget;
18 
19 import static android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL;
20 import static android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO;
21 import static android.appwidget.flags.Flags.drawDataParcel;
22 import static android.appwidget.flags.Flags.remoteAdapterConversion;
23 import static android.content.res.Flags.FLAG_SELF_TARGETING_ANDROID_RESOURCE_FRRO;
24 import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
25 import static android.util.proto.ProtoInputStream.NO_MORE_FIELDS;
26 import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
27 
28 import android.annotation.AttrRes;
29 import android.annotation.ColorInt;
30 import android.annotation.ColorRes;
31 import android.annotation.DimenRes;
32 import android.annotation.DrawableRes;
33 import android.annotation.FlaggedApi;
34 import android.annotation.IdRes;
35 import android.annotation.IntDef;
36 import android.annotation.LayoutRes;
37 import android.annotation.NonNull;
38 import android.annotation.Nullable;
39 import android.annotation.Px;
40 import android.annotation.StringRes;
41 import android.annotation.StyleRes;
42 import android.annotation.SuppressLint;
43 import android.app.Activity;
44 import android.app.ActivityOptions;
45 import android.app.ActivityThread;
46 import android.app.Application;
47 import android.app.PendingIntent;
48 import android.app.RemoteInput;
49 import android.appwidget.AppWidgetHostView;
50 import android.appwidget.AppWidgetManager.ServiceCollectionCache;
51 import android.appwidget.flags.Flags;
52 import android.compat.annotation.UnsupportedAppUsage;
53 import android.content.ComponentName;
54 import android.content.Context;
55 import android.content.ContextWrapper;
56 import android.content.Intent;
57 import android.content.IntentSender;
58 import android.content.om.FabricatedOverlay;
59 import android.content.om.OverlayInfo;
60 import android.content.om.OverlayManager;
61 import android.content.om.OverlayManagerTransaction;
62 import android.content.pm.ApplicationInfo;
63 import android.content.pm.PackageManager.NameNotFoundException;
64 import android.content.res.ColorStateList;
65 import android.content.res.Configuration;
66 import android.content.res.Resources;
67 import android.content.res.TypedArray;
68 import android.content.res.loader.ResourcesLoader;
69 import android.content.res.loader.ResourcesProvider;
70 import android.graphics.Bitmap;
71 import android.graphics.BitmapFactory;
72 import android.graphics.BlendMode;
73 import android.graphics.Outline;
74 import android.graphics.PorterDuff;
75 import android.graphics.Rect;
76 import android.graphics.drawable.Drawable;
77 import android.graphics.drawable.Icon;
78 import android.graphics.drawable.RippleDrawable;
79 import android.net.Uri;
80 import android.os.AsyncTask;
81 import android.os.Binder;
82 import android.os.Build;
83 import android.os.Bundle;
84 import android.os.CancellationSignal;
85 import android.os.Parcel;
86 import android.os.ParcelFileDescriptor;
87 import android.os.Parcelable;
88 import android.os.Process;
89 import android.os.RemoteException;
90 import android.os.StrictMode;
91 import android.os.Trace;
92 import android.os.UserHandle;
93 import android.system.Os;
94 import android.text.TextUtils;
95 import android.util.ArrayMap;
96 import android.util.DisplayMetrics;
97 import android.util.IntArray;
98 import android.util.Log;
99 import android.util.LongArray;
100 import android.util.LongSparseArray;
101 import android.util.Pair;
102 import android.util.SizeF;
103 import android.util.SparseArray;
104 import android.util.SparseIntArray;
105 import android.util.TypedValue;
106 import android.util.TypedValue.ComplexDimensionUnit;
107 import android.util.proto.ProtoInputStream;
108 import android.util.proto.ProtoOutputStream;
109 import android.util.proto.ProtoStream;
110 import android.util.proto.ProtoUtils;
111 import android.view.ContextThemeWrapper;
112 import android.view.LayoutInflater;
113 import android.view.LayoutInflater.Filter;
114 import android.view.MotionEvent;
115 import android.view.RemotableViewMethod;
116 import android.view.View;
117 import android.view.ViewGroup;
118 import android.view.ViewGroup.MarginLayoutParams;
119 import android.view.ViewManager;
120 import android.view.ViewOutlineProvider;
121 import android.view.ViewParent;
122 import android.view.ViewStub;
123 import android.widget.AdapterView.OnItemClickListener;
124 import android.widget.CompoundButton.OnCheckedChangeListener;
125 
126 import com.android.internal.R;
127 import com.android.internal.util.Preconditions;
128 import com.android.internal.widget.IRemoteViewsFactory;
129 import com.android.internal.widget.remotecompose.core.CoreDocument;
130 import com.android.internal.widget.remotecompose.core.operations.Theme;
131 import com.android.internal.widget.remotecompose.player.RemoteComposeDocument;
132 import com.android.internal.widget.remotecompose.player.RemoteComposePlayer;
133 
134 import java.io.ByteArrayInputStream;
135 import java.io.ByteArrayOutputStream;
136 import java.io.FileDescriptor;
137 import java.io.FileOutputStream;
138 import java.io.IOException;
139 import java.io.InputStream;
140 import java.io.OutputStream;
141 import java.lang.annotation.ElementType;
142 import java.lang.annotation.Retention;
143 import java.lang.annotation.RetentionPolicy;
144 import java.lang.annotation.Target;
145 import java.lang.invoke.MethodHandle;
146 import java.lang.invoke.MethodHandles;
147 import java.lang.invoke.MethodType;
148 import java.lang.reflect.Method;
149 import java.util.ArrayDeque;
150 import java.util.ArrayList;
151 import java.util.Arrays;
152 import java.util.HashMap;
153 import java.util.HashSet;
154 import java.util.Iterator;
155 import java.util.List;
156 import java.util.Map;
157 import java.util.Objects;
158 import java.util.Set;
159 import java.util.concurrent.CompletableFuture;
160 import java.util.concurrent.Executor;
161 import java.util.concurrent.TimeUnit;
162 import java.util.concurrent.atomic.AtomicLong;
163 import java.util.function.Consumer;
164 import java.util.function.Function;
165 import java.util.function.Predicate;
166 
167 /**
168  * A class that describes a view hierarchy that can be displayed in
169  * another process. The hierarchy is inflated from a layout resource
170  * file, and this class provides some basic operations for modifying
171  * the content of the inflated hierarchy.
172  *
173  * <p>{@code RemoteViews} is limited to support for the following layouts:</p>
174  * <ul>
175  *   <li>{@link android.widget.AdapterViewFlipper}</li>
176  *   <li>{@link android.widget.FrameLayout}</li>
177  *   <li>{@link android.widget.GridLayout}</li>
178  *   <li>{@link android.widget.GridView}</li>
179  *   <li>{@link android.widget.LinearLayout}</li>
180  *   <li>{@link android.widget.ListView}</li>
181  *   <li>{@link android.widget.RelativeLayout}</li>
182  *   <li>{@link android.widget.StackView}</li>
183  *   <li>{@link android.widget.ViewFlipper}</li>
184  * </ul>
185  * <p>And the following widgets:</p>
186  * <ul>
187  *   <li>{@link android.widget.AnalogClock}</li>
188  *   <li>{@link android.widget.Button}</li>
189  *   <li>{@link android.widget.Chronometer}</li>
190  *   <li>{@link android.widget.ImageButton}</li>
191  *   <li>{@link android.widget.ImageView}</li>
192  *   <li>{@link android.widget.ProgressBar}</li>
193  *   <li>{@link android.widget.TextClock}</li>
194  *   <li>{@link android.widget.TextView}</li>
195  * </ul>
196  * <p>As of API 31, the following widgets and layouts may also be used:</p>
197  * <ul>
198  *     <li>{@link android.widget.CheckBox}</li>
199  *     <li>{@link android.widget.RadioButton}</li>
200  *     <li>{@link android.widget.RadioGroup}</li>
201  *     <li>{@link android.widget.Switch}</li>
202  * </ul>
203  * <p>Descendants of these classes are not supported.</p>
204  */
205 public class RemoteViews implements Parcelable, Filter {
206 
207     private static final String LOG_TAG = "RemoteViews";
208 
209     /** The intent extra for whether the view whose checked state changed is currently checked. */
210     public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED";
211 
212     /**
213      * The intent extra that contains the appWidgetId.
214      * @hide
215      */
216     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
217 
218     /**
219      * The intent extra that contains {@code true} if inflating as dak text theme.
220      * @hide
221      */
222     static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground";
223 
224     /**
225      * The intent extra that contains the bounds for all shared elements.
226      */
227     public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
228             "android.widget.extra.SHARED_ELEMENT_BOUNDS";
229 
230     /**
231      * Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and
232      * {@link #RemoteViews(RemoteViews, RemoteViews)}.
233      */
234     private static final int MAX_NESTED_VIEWS = 10;
235 
236     /**
237      * Maximum number of RemoteViews that can be specified in constructor.
238      */
239     private static final int MAX_INIT_VIEW_COUNT = 16;
240 
241     // The unique identifiers for each custom {@link Action}.
242     private static final int SET_ON_CLICK_RESPONSE_TAG = 1;
243     private static final int REFLECTION_ACTION_TAG = 2;
244     private static final int SET_DRAWABLE_TINT_TAG = 3;
245     private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
246     private static final int VIEW_CONTENT_NAVIGATION_TAG = 5;
247     private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
248     private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
249     private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
250     private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
251     private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
252     private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
253     private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13;
254     private static final int VIEW_PADDING_ACTION_TAG = 14;
255     private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
256     private static final int LAYOUT_PARAM_ACTION_TAG = 19;
257     private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
258     private static final int SET_INT_TAG_TAG = 22;
259     private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
260     private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
261     private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
262     private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26;
263     private static final int SET_RADIO_GROUP_CHECKED = 27;
264     private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
265     private static final int SET_ON_CHECKED_CHANGE_RESPONSE_TAG = 29;
266     private static final int NIGHT_MODE_REFLECTION_ACTION_TAG = 30;
267     private static final int SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG = 31;
268     private static final int ATTRIBUTE_REFLECTION_ACTION_TAG = 32;
269     private static final int SET_REMOTE_ADAPTER_TAG = 33;
270     private static final int SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG = 34;
271     private static final int SET_DRAW_INSTRUCTION_TAG = 35;
272 
273     /** @hide **/
274     @IntDef(prefix = "MARGIN_", value = {
275             MARGIN_LEFT,
276             MARGIN_TOP,
277             MARGIN_RIGHT,
278             MARGIN_BOTTOM,
279             MARGIN_START,
280             MARGIN_END
281     })
282     @Retention(RetentionPolicy.SOURCE)
283     public @interface MarginType {}
284     /** The value will apply to the marginLeft. */
285     public static final int MARGIN_LEFT = 0;
286     /** The value will apply to the marginTop. */
287     public static final int MARGIN_TOP = 1;
288     /** The value will apply to the marginRight. */
289     public static final int MARGIN_RIGHT = 2;
290     /** The value will apply to the marginBottom. */
291     public static final int MARGIN_BOTTOM = 3;
292     /** The value will apply to the marginStart. */
293     public static final int MARGIN_START = 4;
294     /** The value will apply to the marginEnd. */
295     public static final int MARGIN_END = 5;
296 
297     @IntDef(prefix = "VALUE_TYPE_", value = {
298             VALUE_TYPE_RAW,
299             VALUE_TYPE_COMPLEX_UNIT,
300             VALUE_TYPE_RESOURCE,
301             VALUE_TYPE_ATTRIBUTE
302     })
303     @Retention(RetentionPolicy.SOURCE)
304     @interface ValueType {}
305     static final int VALUE_TYPE_RAW = 1;
306     static final int VALUE_TYPE_COMPLEX_UNIT = 2;
307     static final int VALUE_TYPE_RESOURCE = 3;
308     static final int VALUE_TYPE_ATTRIBUTE = 4;
309 
310     /** @hide **/
311     @IntDef(flag = true, value = {
312             FLAG_REAPPLY_DISALLOWED,
313             FLAG_WIDGET_IS_COLLECTION_CHILD,
314             FLAG_USE_LIGHT_BACKGROUND_LAYOUT
315     })
316     @Retention(RetentionPolicy.SOURCE)
317     public @interface ApplyFlags {}
318     /**
319      * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
320      * the layout in a way that isn't recoverable, since views are being removed.
321      * @hide
322      */
323     public static final int FLAG_REAPPLY_DISALLOWED = 1;
324     /**
325      * This flag indicates whether this RemoteViews object is being created from a
326      * RemoteViewsService for use as a child of a widget collection. This flag is used
327      * to determine whether or not certain features are available, in particular,
328      * setting on click extras and setting on click pending intents. The former is enabled,
329      * and the latter disabled when this flag is true.
330      * @hide
331      */
332     public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2;
333     /**
334      * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead
335      * of {link #mLayoutId}
336      * @hide
337      */
338     public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
339 
340     /**
341      * This mask determines which flags are propagated to nested RemoteViews (either added by
342      * addView, or set as portrait/landscape/sized RemoteViews).
343      */
344     static final int FLAG_MASK_TO_PROPAGATE =
345             FLAG_WIDGET_IS_COLLECTION_CHILD | FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
346 
347     /**
348      * A ReadWriteHelper which has the same behavior as ReadWriteHelper.DEFAULT, but which is
349      * intentionally a different instance in order to trick Bundle reader so that it doesn't allow
350      * lazy initialization.
351      */
352     private static final Parcel.ReadWriteHelper ALTERNATIVE_DEFAULT = new Parcel.ReadWriteHelper();
353 
354     /**
355      * Used to restrict the views which can be inflated
356      *
357      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
358      */
359     private static final LayoutInflater.Filter INFLATER_FILTER =
360             (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
361 
362     /**
363      * The maximum waiting time for remote adapter conversion in milliseconds
364      *
365      * @hide
366      */
367     private static final int MAX_ADAPTER_CONVERSION_WAITING_TIME_MS = 20_000;
368 
369     /**
370      * Application that hosts the remote views.
371      *
372      * @hide
373      */
374     @UnsupportedAppUsage
375     public ApplicationInfo mApplication;
376 
377     /**
378      * The resource ID of the layout file. (Added to the parcel)
379      */
380     @UnsupportedAppUsage
381     private int mLayoutId;
382 
383     /**
384      * The resource ID of the layout file in dark text mode. (Added to the parcel)
385      */
386     private int mLightBackgroundLayoutId = 0;
387 
388     /**
389      * An array of actions to perform on the view tree once it has been
390      * inflated
391      */
392     @UnsupportedAppUsage
393     private ArrayList<Action> mActions;
394 
395     /**
396      * Maps bitmaps to unique indicies to avoid Bitmap duplication.
397      */
398     @UnsupportedAppUsage
399     private BitmapCache mBitmapCache = new BitmapCache();
400 
401     /**
402      * Maps Intent ID to RemoteCollectionItems to avoid duplicate items
403      */
404     private @NonNull RemoteCollectionCache mCollectionCache = new RemoteCollectionCache();
405 
406     /** Cache of ApplicationInfos used by collection items. */
407     private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache();
408 
409     /**
410      * Indicates whether or not this RemoteViews object is contained as a child of any other
411      * RemoteViews.
412      */
413     private boolean mIsRoot = true;
414 
415     /**
416      * Constants to whether or not this RemoteViews is composed of a landscape and portrait
417      * RemoteViews.
418      */
419     private static final int MODE_NORMAL = 0;
420     private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
421     private static final int MODE_HAS_SIZED_REMOTEVIEWS = 2;
422 
423     /**
424      * Used in conjunction with the special constructor
425      * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait
426      * RemoteViews.
427      */
428     private RemoteViews mLandscape = null;
429     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
430     private RemoteViews mPortrait = null;
431     /**
432      * List of RemoteViews with their ideal size. There must be at least two if the map is not null.
433      *
434      * The smallest remote view is always the last element in the list.
435      */
436     private List<RemoteViews> mSizedRemoteViews = null;
437 
438     /**
439      * Ideal size for this RemoteViews.
440      *
441      * Only to be used on children views used in a {@link RemoteViews} with
442      * {@link RemoteViews#hasSizedRemoteViews()}.
443      */
444     private SizeF mIdealSize = null;
445 
446     @ApplyFlags
447     private int mApplyFlags = 0;
448 
449     /**
450      * Id to use to override the ID of the top-level view in this RemoteViews.
451      *
452      * Only used if this RemoteViews is defined from a XML layout value.
453      */
454     private int mViewId = View.NO_ID;
455 
456     /**
457      * Id used to uniquely identify a {@link RemoteViews} instance coming from a given provider.
458      */
459     private long mProviderInstanceId = -1;
460 
461     /** Class cookies of the Parcel this instance was read from. */
462     private Map<Class, Object> mClassCookies;
463 
464     /**
465      * {@link LayoutInflater.Factory2} which will be passed into a {@link LayoutInflater} instance
466      * used by this class.
467      */
468     @Nullable
469     private LayoutInflater.Factory2 mLayoutInflaterFactory2;
470 
471     /**
472      * Indicates whether this {@link RemoteViews} was instantiated with a {@link DrawInstructions}
473      * object. {@link DrawInstructions} serves as an alternative protocol for the host process
474      * to render.
475      */
476     private boolean mHasDrawInstructions;
477 
478     @Nullable
479     private SparseArray<PendingIntent> mPendingIntentTemplate;
480 
481     @Nullable
482     private SparseArray<Intent> mFillInIntent;
483 
484     private static final InteractionHandler DEFAULT_INTERACTION_HANDLER =
485             (view, pendingIntent, response) ->
486                     startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
487 
488     private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
489 
490     /**
491      * This key is used to perform lookups in sMethods without causing allocations.
492      */
493     private static final MethodKey sLookupKey = new MethodKey();
494 
495     /**
496      * @hide
497      */
setRemoteInputs(@dRes int viewId, RemoteInput[] remoteInputs)498     public void setRemoteInputs(@IdRes int viewId, RemoteInput[] remoteInputs) {
499         mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
500     }
501 
502     /**
503      * Sets {@link LayoutInflater.Factory2} to be passed into {@link LayoutInflater} used
504      * by this class instance. It has to be set before the views are inflated to have any effect.
505      *
506      * The factory callbacks will be called on the background thread so the implementation needs
507      * to be thread safe.
508      *
509      * @hide
510      */
setLayoutInflaterFactory(@ullable LayoutInflater.Factory2 factory)511     public void setLayoutInflaterFactory(@Nullable LayoutInflater.Factory2 factory) {
512         mLayoutInflaterFactory2 = factory;
513     }
514 
515     /**
516      * Returns currently set {@link LayoutInflater.Factory2}.
517      *
518      * @hide
519      */
520     @Nullable
getLayoutInflaterFactory()521     public LayoutInflater.Factory2 getLayoutInflaterFactory() {
522         return mLayoutInflaterFactory2;
523     }
524 
525     /**
526      * Reduces all images and ensures that they are all below the given sizes.
527      *
528      * @param maxWidth the maximum width allowed
529      * @param maxHeight the maximum height allowed
530      *
531      * @hide
532      */
reduceImageSizes(int maxWidth, int maxHeight)533     public void reduceImageSizes(int maxWidth, int maxHeight) {
534         ArrayList<Bitmap> cache = mBitmapCache.mBitmaps;
535         for (int i = 0; i < cache.size(); i++) {
536             Bitmap bitmap = cache.get(i);
537             cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight));
538         }
539     }
540 
541     /**
542      * Sets an integer tag to the view.
543      *
544      * @hide
545      */
setIntTag(@dRes int viewId, @IdRes int key, int tag)546     public void setIntTag(@IdRes int viewId, @IdRes int key, int tag) {
547         addAction(new SetIntTagAction(viewId, key, tag));
548     }
549 
550     /**
551      * Set a view tag associating a View with an ID to be used for widget interaction usage events
552      * ({@link android.app.usage.UsageEvents.Event}). When this RemoteViews is applied to a bound
553      * widget, any clicks or scrolls on the tagged view will be reported to
554      * {@link android.app.usage.UsageStatsManager} using this tag.
555      *
556      * @param viewId ID of the View whose tag will be set
557      * @param tag The integer tag to use for the event
558      *
559      * @see android.appwidget.AppWidgetManager#EVENT_TYPE_WIDGET_INTERACTION
560      * @see android.appwidget.AppWidgetManager#EXTRA_EVENT_CLICKED_VIEWS
561      * @see android.appwidget.AppWidgetManager#EXTRA_EVENT_SCROLLED_VIEWS
562      * @see android.app.usage.UsageStatsManager#queryEventsForSelf
563      */
564     @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
setUsageEventTag(@dRes int viewId, int tag)565     public void setUsageEventTag(@IdRes int viewId, int tag) {
566         addAction(new SetIntTagAction(viewId, com.android.internal.R.id.remoteViewsMetricsId, tag));
567     }
568 
569     /**
570      * Set that it is disallowed to reapply another remoteview with the same layout as this view.
571      * This should be done if an action is destroying the view tree of the base layout.
572      *
573      * @hide
574      */
addFlags(@pplyFlags int flags)575     public void addFlags(@ApplyFlags int flags) {
576         mApplyFlags = mApplyFlags | flags;
577 
578         int flagsToPropagate = flags & FLAG_MASK_TO_PROPAGATE;
579         if (flagsToPropagate != 0) {
580             if (hasSizedRemoteViews()) {
581                 for (RemoteViews remoteView : mSizedRemoteViews) {
582                     remoteView.addFlags(flagsToPropagate);
583                 }
584             } else if (hasLandscapeAndPortraitLayouts()) {
585                 mLandscape.addFlags(flagsToPropagate);
586                 mPortrait.addFlags(flagsToPropagate);
587             }
588         }
589     }
590 
591     /**
592      * @hide
593      */
hasFlags(@pplyFlags int flag)594     public boolean hasFlags(@ApplyFlags int flag) {
595         return (mApplyFlags & flag) == flag;
596     }
597 
598     /**
599      * Stores information related to reflection method lookup.
600      */
601     static class MethodKey {
602         public Class targetClass;
603         public Class paramClass;
604         public String methodName;
605 
606         @Override
equals(@ullable Object o)607         public boolean equals(@Nullable Object o) {
608             if (!(o instanceof MethodKey)) {
609                 return false;
610             }
611             MethodKey p = (MethodKey) o;
612             return Objects.equals(p.targetClass, targetClass)
613                     && Objects.equals(p.paramClass, paramClass)
614                     && Objects.equals(p.methodName, methodName);
615         }
616 
617         @Override
hashCode()618         public int hashCode() {
619             return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
620                     ^ Objects.hashCode(methodName);
621         }
622 
set(Class targetClass, Class paramClass, String methodName)623         public void set(Class targetClass, Class paramClass, String methodName) {
624             this.targetClass = targetClass;
625             this.paramClass = paramClass;
626             this.methodName = methodName;
627         }
628     }
629 
630 
631     /**
632      * Stores information related to reflection method lookup result.
633      */
634     static class MethodArgs {
635         public MethodHandle syncMethod;
636         public MethodHandle asyncMethod;
637         public String asyncMethodName;
638     }
639 
640     /**
641      * This annotation indicates that a subclass of View is allowed to be used
642      * with the {@link RemoteViews} mechanism.
643      */
644     @Target({ ElementType.TYPE })
645     @Retention(RetentionPolicy.RUNTIME)
646     public @interface RemoteView {
647     }
648 
649     /**
650      * Exception to send when something goes wrong executing an action
651      *
652      */
653     public static class ActionException extends RuntimeException {
ActionException(Exception ex)654         public ActionException(Exception ex) {
655             super(ex);
656         }
ActionException(String message)657         public ActionException(String message) {
658             super(message);
659         }
660         /**
661          * @hide
662          */
ActionException(Throwable t)663         public ActionException(Throwable t) {
664             super(t);
665         }
666     }
667 
668     /**
669      * Handler for view interactions (such as clicks) within a RemoteViews.
670      *
671      * @hide
672      */
673     public interface InteractionHandler {
674         /**
675          * Invoked when the user performs an interaction on the View.
676          *
677          * @param view the View with which the user interacted
678          * @param pendingIntent the base PendingIntent associated with the view
679          * @param response the response to the interaction, which knows how to fill in the
680          *                 attached PendingIntent
681          *
682          * @hide
683          */
onInteraction( View view, PendingIntent pendingIntent, RemoteResponse response)684         boolean onInteraction(
685                 View view,
686                 PendingIntent pendingIntent,
687                 RemoteResponse response);
688 
689         /**
690          * Invoked when an AbsListView is scrolled.
691          * @param view view that was scrolled
692          *
693          * @hide
694          */
onScroll(@onNull AbsListView view)695         default void onScroll(@NonNull AbsListView view) {}
696     }
697 
698     /**
699      * Base class for all actions that can be performed on an
700      * inflated view.
701      *
702      * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
703      */
704     private abstract static class Action {
705         @IdRes
706         @UnsupportedAppUsage
707         int mViewId;
708 
apply(View root, ViewGroup rootParent, ActionApplyParams params)709         public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params)
710                 throws ActionException;
711 
712         public static final int MERGE_REPLACE = 0;
713         public static final int MERGE_APPEND = 1;
714         public static final int MERGE_IGNORE = 2;
715 
setHierarchyRootData(HierarchyRootData root)716         public void setHierarchyRootData(HierarchyRootData root) {
717             // Do nothing
718         }
719 
720         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
mergeBehavior()721         public int mergeBehavior() {
722             return MERGE_REPLACE;
723         }
724 
getActionTag()725         public abstract int getActionTag();
726 
getUniqueKey()727         public String getUniqueKey() {
728             return (getActionTag() + "_" + mViewId);
729         }
730 
731         /**
732          * This is called on the background thread. It should perform any non-ui computations
733          * and return the final action which will run on the UI thread.
734          * Override this if some of the tasks can be performed async.
735          */
initActionAsync(ViewTree root, ViewGroup rootParent, ActionApplyParams params)736         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
737                 ActionApplyParams params) {
738             return this;
739         }
740 
prefersAsyncApply()741         public boolean prefersAsyncApply() {
742             return false;
743         }
744 
745         /** See {@link RemoteViews#visitUris(Consumer)}. **/
visitUris(@onNull Consumer<Uri> visitor)746         public void visitUris(@NonNull Consumer<Uri> visitor) {
747             // Nothing to visit by default.
748         }
749 
750         /** See {@link RemoteViews#visitIcons(Consumer)}. **/
visitIcons(@onNull Consumer<Icon> visitor)751         public void visitIcons(@NonNull Consumer<Icon> visitor) {
752             // Nothing to visit by default.
753         }
754 
writeToParcel(Parcel dest, int flags)755         public abstract void writeToParcel(Parcel dest, int flags);
756 
757         /**
758          * Override to return true if this Action can be serialized to Protobuf, and implement
759          * writeToProto / createFromProto.
760          *
761          * If this returns false, then the action will be omitted from RemoteViews previews created
762          * with createPreviewFromProto / writePreviewToProto.
763          *
764          * Because Parcelables should not be serialized to disk, any action that contains an Intent,
765          * PendingIntent, or Bundle should return false here.
766          */
canWriteToProto()767         public boolean canWriteToProto() {
768             return false;
769         }
770 
writeToProto(ProtoOutputStream out, Context context, Resources appResources)771         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
772             throw new UnsupportedOperationException();
773         }
774     }
775 
776     /**
777      * Action class used during async inflation of RemoteViews. Subclasses are not parcelable.
778      */
779     private abstract static class RuntimeAction extends Action {
780         @Override
getActionTag()781         public final int getActionTag() {
782             return 0;
783         }
784 
785         @Override
writeToParcel(Parcel dest, int flags)786         public final void writeToParcel(Parcel dest, int flags) {
787             throw new UnsupportedOperationException();
788         }
789     }
790 
791     // Constant used during async execution. It is not parcelable.
792     private static final Action ACTION_NOOP = new RuntimeAction() {
793         @Override
794         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { }
795     };
796 
797     /**
798      * Merges the passed RemoteViews actions with this RemoteViews actions according to
799      * action-specific merge rules.
800      *
801      * @param newRv
802      *
803      * @hide
804      */
805     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
mergeRemoteViews(RemoteViews newRv)806     public void mergeRemoteViews(RemoteViews newRv) {
807         if (newRv == null) return;
808         // We first copy the new RemoteViews, as the process of merging modifies the way the actions
809         // reference the bitmap cache. We don't want to modify the object as it may need to
810         // be merged and applied multiple times.
811         RemoteViews copy = new RemoteViews(newRv);
812 
813         HashMap<String, Action> map = new HashMap<String, Action>();
814         if (mActions == null) {
815             mActions = new ArrayList<Action>();
816         }
817 
818         int count = mActions.size();
819         for (int i = 0; i < count; i++) {
820             Action a = mActions.get(i);
821             map.put(a.getUniqueKey(), a);
822         }
823 
824         ArrayList<Action> newActions = copy.mActions;
825         if (newActions == null) return;
826         count = newActions.size();
827         for (int i = 0; i < count; i++) {
828             Action a = newActions.get(i);
829             String key = newActions.get(i).getUniqueKey();
830             int mergeBehavior = newActions.get(i).mergeBehavior();
831             if (map.containsKey(key) && mergeBehavior == Action.MERGE_REPLACE) {
832                 mActions.remove(map.get(key));
833                 map.remove(key);
834             }
835 
836             // If the merge behavior is ignore, we don't bother keeping the extra action
837             if (mergeBehavior == Action.MERGE_REPLACE || mergeBehavior == Action.MERGE_APPEND) {
838                 mActions.add(a);
839             }
840         }
841 
842         // Because pruning can remove the need for bitmaps, we reconstruct the caches.
843         reconstructCaches();
844     }
845 
846     /**
847      * Return {@code true} only if this {@code RemoteViews} is a legacy list widget that uses
848      * {@code Intent} for inflating child entries.
849      *
850      * @hide
851      */
isLegacyListRemoteViews()852     public boolean isLegacyListRemoteViews() {
853         return mCollectionCache.mIdToUriMapping.size() > 0;
854     }
855 
856     /**
857      * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission
858      * grants will need to be issued to ensure the recipient of this object is able to render its
859      * contents.
860      * See b/281044385 for more context and examples about what happens when this isn't done
861      * correctly.
862      *
863      * @hide
864      */
visitUris(@onNull Consumer<Uri> visitor)865     public void visitUris(@NonNull Consumer<Uri> visitor) {
866         if (mActions != null) {
867             for (int i = 0; i < mActions.size(); i++) {
868                 mActions.get(i).visitUris(visitor);
869             }
870         }
871         if (mSizedRemoteViews != null) {
872             for (int i = 0; i < mSizedRemoteViews.size(); i++) {
873                 mSizedRemoteViews.get(i).visitUris(visitor);
874             }
875         }
876         if (mLandscape != null) {
877             mLandscape.visitUris(visitor);
878         }
879         if (mPortrait != null) {
880             mPortrait.visitUris(visitor);
881         }
882     }
883 
884     /**
885      * Note all {@link Icon} that are referenced internally.
886      * @hide
887      */
visitIcons(@onNull Consumer<Icon> visitor)888     public void visitIcons(@NonNull Consumer<Icon> visitor) {
889         if (mActions != null) {
890             for (int i = 0; i < mActions.size(); i++) {
891                 mActions.get(i).visitIcons(visitor);
892             }
893         }
894         if (mSizedRemoteViews != null) {
895             for (int i = 0; i < mSizedRemoteViews.size(); i++) {
896                 mSizedRemoteViews.get(i).visitIcons(visitor);
897             }
898         }
899         if (mLandscape != null) {
900             mLandscape.visitIcons(visitor);
901         }
902         if (mPortrait != null) {
903             mPortrait.visitIcons(visitor);
904         }
905     }
906 
907     /**
908      * @hide
909      * @return True if there is a change
910      */
replaceRemoteCollections(int viewId)911     public boolean replaceRemoteCollections(int viewId) {
912         boolean isActionReplaced = false;
913         if (mActions != null) {
914             for (int i = 0; i < mActions.size(); i++) {
915                 Action action = mActions.get(i);
916                 if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
917                         && itemsAction.mViewId == viewId
918                         && itemsAction.mServiceIntent != null) {
919                     SetRemoteCollectionItemListAdapterAction newCollectionAction =
920                             new SetRemoteCollectionItemListAdapterAction(
921                                     itemsAction.mViewId, itemsAction.mServiceIntent);
922                     newCollectionAction.mIntentId = itemsAction.mIntentId;
923                     newCollectionAction.mIsReplacedIntoAction = true;
924                     mActions.set(i, newCollectionAction);
925                     isActionReplaced = true;
926                 } else if (action instanceof SetRemoteViewsAdapterIntent intentAction
927                         && intentAction.mViewId == viewId) {
928                     mActions.set(i, new SetRemoteCollectionItemListAdapterAction(
929                             intentAction.mViewId, intentAction.mIntent));
930                     isActionReplaced = true;
931                 } else if (action instanceof ViewGroupActionAdd groupAction
932                         && groupAction.mNestedViews != null) {
933                     isActionReplaced |= groupAction.mNestedViews.replaceRemoteCollections(viewId);
934                 }
935             }
936         }
937         if (mSizedRemoteViews != null) {
938             for (int i = 0; i < mSizedRemoteViews.size(); i++) {
939                 isActionReplaced |= mSizedRemoteViews.get(i).replaceRemoteCollections(viewId);
940             }
941         }
942         if (mLandscape != null) {
943             isActionReplaced |= mLandscape.replaceRemoteCollections(viewId);
944         }
945         if (mPortrait != null) {
946             isActionReplaced |= mPortrait.replaceRemoteCollections(viewId);
947         }
948 
949         return isActionReplaced;
950     }
951 
952     /**
953      * @return True if has set remote adapter using service intent
954      * @hide
955      */
hasLegacyLists()956     public boolean hasLegacyLists() {
957         if (mActions != null) {
958             for (int i = 0; i < mActions.size(); i++) {
959                 Action action = mActions.get(i);
960                 if ((action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
961                         && itemsAction.mServiceIntent != null)
962                         || (action instanceof SetRemoteViewsAdapterIntent intentAction
963                                 && intentAction.mIntent != null)
964                         || (action instanceof ViewGroupActionAdd groupAction
965                                 && groupAction.mNestedViews != null
966                                 && groupAction.mNestedViews.hasLegacyLists())) {
967                     return true;
968                 }
969             }
970         }
971         if (mSizedRemoteViews != null) {
972             for (int i = 0; i < mSizedRemoteViews.size(); i++) {
973                 if (mSizedRemoteViews.get(i).hasLegacyLists()) {
974                     return true;
975                 }
976             }
977         }
978         if (mLandscape != null && mLandscape.hasLegacyLists()) {
979             return true;
980         }
981         return mPortrait != null && mPortrait.hasLegacyLists();
982     }
983 
visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor)984     private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
985         if (icon != null && (icon.getType() == Icon.TYPE_URI
986                 || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
987             visitor.accept(icon.getUri());
988         }
989     }
990 
991     private static class RemoteViewsContextWrapper extends ContextWrapper {
992         private final Context mContextForResources;
993 
RemoteViewsContextWrapper(Context context, Context contextForResources)994         RemoteViewsContextWrapper(Context context, Context contextForResources) {
995             super(context);
996             mContextForResources = contextForResources;
997         }
998 
999         @Override
getResources()1000         public Resources getResources() {
1001             return mContextForResources.getResources();
1002         }
1003 
1004         @Override
getTheme()1005         public Resources.Theme getTheme() {
1006             return mContextForResources.getTheme();
1007         }
1008 
1009         @Override
getPackageName()1010         public String getPackageName() {
1011             return mContextForResources.getPackageName();
1012         }
1013 
1014         @Override
getUser()1015         public UserHandle getUser() {
1016             return mContextForResources.getUser();
1017         }
1018 
1019         @Override
getUserId()1020         public int getUserId() {
1021             return mContextForResources.getUserId();
1022         }
1023 
1024         @Override
isRestricted()1025         public boolean isRestricted() {
1026             // Override isRestricted and direct to resource's implementation. The isRestricted is
1027             // used for determining the risky resources loading, e.g. fonts, thus direct to context
1028             // for resource.
1029             return mContextForResources.isRestricted();
1030         }
1031     }
1032 
1033     private static class SetEmptyView extends Action {
1034         int mEmptyViewId;
1035 
SetEmptyView(@dRes int viewId, @IdRes int emptyViewId)1036         SetEmptyView(@IdRes int viewId, @IdRes int emptyViewId) {
1037             this.mViewId = viewId;
1038             this.mEmptyViewId = emptyViewId;
1039         }
1040 
SetEmptyView(Parcel in)1041         SetEmptyView(Parcel in) {
1042             this.mViewId = in.readInt();
1043             this.mEmptyViewId = in.readInt();
1044         }
1045 
writeToParcel(Parcel out, int flags)1046         public void writeToParcel(Parcel out, int flags) {
1047             out.writeInt(this.mViewId);
1048             out.writeInt(this.mEmptyViewId);
1049         }
1050 
1051         @Override
apply(View root, ViewGroup rootParent, ActionApplyParams params)1052         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
1053             final View view = root.findViewById(mViewId);
1054             if (!(view instanceof AdapterView<?>)) return;
1055 
1056             AdapterView<?> adapterView = (AdapterView<?>) view;
1057 
1058             final View emptyView = root.findViewById(mEmptyViewId);
1059             if (emptyView == null) return;
1060 
1061             adapterView.setEmptyView(emptyView);
1062         }
1063 
1064         @Override
getActionTag()1065         public int getActionTag() {
1066             return SET_EMPTY_VIEW_ACTION_TAG;
1067         }
1068 
1069         @Override
canWriteToProto()1070         public boolean canWriteToProto() {
1071             return true;
1072         }
1073 
1074         @Override
writeToProto(ProtoOutputStream out, Context context, Resources appResources)1075         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
1076             final long token = out.start(RemoteViewsProto.Action.SET_EMPTY_VIEW_ACTION);
1077             out.write(RemoteViewsProto.SetEmptyViewAction.VIEW_ID,
1078                     appResources.getResourceName(mViewId));
1079             out.write(RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID,
1080                     appResources.getResourceName(mViewId));
1081             out.end(token);
1082         }
1083 
createFromProto(ProtoInputStream in)1084         public static PendingResources<Action> createFromProto(ProtoInputStream in)
1085                 throws Exception {
1086             final LongSparseArray<Object> values = new LongSparseArray<>();
1087 
1088             final long token = in.start(RemoteViewsProto.Action.SET_EMPTY_VIEW_ACTION);
1089             while (in.nextField() != NO_MORE_FIELDS) {
1090                 switch (in.getFieldNumber()) {
1091                     case (int) RemoteViewsProto.SetEmptyViewAction.VIEW_ID:
1092                         values.put(RemoteViewsProto.SetEmptyViewAction.VIEW_ID,
1093                                 in.readString(RemoteViewsProto.SetEmptyViewAction.VIEW_ID));
1094                         break;
1095                     case (int) RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID:
1096                         values.put(RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID,
1097                                 in.readString(RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID));
1098                         break;
1099                     default:
1100                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
1101                                 + ProtoUtils.currentFieldToString(in));
1102                 }
1103             }
1104             in.end(token);
1105 
1106             checkContainsKeys(values, new long[]{RemoteViewsProto.SetEmptyViewAction.VIEW_ID,
1107                     RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID});
1108 
1109             return (context, resources, rootData, depth) -> {
1110                 int viewId = getAsIdentifier(resources, values,
1111                         RemoteViewsProto.SetEmptyViewAction.VIEW_ID);
1112                 int emptyViewId = getAsIdentifier(resources, values,
1113                         RemoteViewsProto.SetEmptyViewAction.EMPTY_VIEW_ID);
1114                 return new SetEmptyView(viewId, emptyViewId);
1115             };
1116         }
1117     }
1118 
1119     private static class SetPendingIntentTemplate extends Action {
1120         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1121         PendingIntent mPendingIntentTemplate;
1122 
SetPendingIntentTemplate(@dRes int id, PendingIntent pendingIntentTemplate)1123         public SetPendingIntentTemplate(@IdRes int id, PendingIntent pendingIntentTemplate) {
1124             this.mViewId = id;
1125             this.mPendingIntentTemplate = pendingIntentTemplate;
1126         }
1127 
SetPendingIntentTemplate(Parcel parcel)1128         public SetPendingIntentTemplate(Parcel parcel) {
1129             mViewId = parcel.readInt();
1130             mPendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
1131         }
1132 
writeToParcel(Parcel dest, int flags)1133         public void writeToParcel(Parcel dest, int flags) {
1134             dest.writeInt(mViewId);
1135             PendingIntent.writePendingIntentOrNullToParcel(mPendingIntentTemplate, dest);
1136         }
1137 
1138         @Override
apply(View root, ViewGroup rootParent, ActionApplyParams params)1139         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
1140             final View target = root.findViewById(mViewId);
1141             if (target == null) return;
1142 
1143             // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
1144             if (target instanceof AdapterView<?>) {
1145                 AdapterView<?> av = (AdapterView<?>) target;
1146                 // The PendingIntent template is stored in the view's tag.
1147                 OnItemClickListener listener = (parent, view, position, id) -> {
1148                     RemoteResponse response = findRemoteResponseTag(view);
1149                     if (response != null) {
1150                         response.handleViewInteraction(view, params.handler);
1151                     }
1152                 };
1153                 av.setOnItemClickListener(listener);
1154                 av.setTag(mPendingIntentTemplate);
1155             } else {
1156                 Log.e(LOG_TAG, "Cannot setPendingIntentTemplate on a view which is not" +
1157                         "an AdapterView (id: " + mViewId + ")");
1158                 return;
1159             }
1160         }
1161 
1162         @Nullable
findRemoteResponseTag(@ullable View rootView)1163         private RemoteResponse findRemoteResponseTag(@Nullable View rootView) {
1164             if (rootView == null) return null;
1165 
1166             ArrayDeque<View> viewsToCheck = new ArrayDeque<>();
1167             viewsToCheck.addLast(rootView);
1168 
1169             while (!viewsToCheck.isEmpty()) {
1170                 View view = viewsToCheck.removeFirst();
1171                 Object tag = view.getTag(R.id.fillInIntent);
1172                 if (tag instanceof RemoteResponse) return (RemoteResponse) tag;
1173                 if (!(view instanceof ViewGroup)) continue;
1174 
1175                 ViewGroup viewGroup = (ViewGroup) view;
1176                 for (int i = 0; i < viewGroup.getChildCount(); i++) {
1177                     viewsToCheck.addLast(viewGroup.getChildAt(i));
1178                 }
1179             }
1180 
1181             return null;
1182         }
1183 
1184         @Override
getActionTag()1185         public int getActionTag() {
1186             return SET_PENDING_INTENT_TEMPLATE_TAG;
1187         }
1188     }
1189 
1190     /**
1191      * Cache of {@link ApplicationInfo}s that can be used to ensure that the same
1192      * {@link ApplicationInfo} instance is used throughout the RemoteViews.
1193      */
1194     private static class ApplicationInfoCache {
1195         private final Map<Pair<String, Integer>, ApplicationInfo> mPackageUserToApplicationInfo;
1196 
ApplicationInfoCache()1197         ApplicationInfoCache() {
1198             mPackageUserToApplicationInfo = new ArrayMap<>();
1199         }
1200 
1201         /**
1202          * Adds the {@link ApplicationInfo} to the cache if it's not present. Returns either the
1203          * provided {@code applicationInfo} or a previously added value with the same package name
1204          * and uid.
1205          */
1206         @Nullable
getOrPut(@ullable ApplicationInfo applicationInfo)1207         ApplicationInfo getOrPut(@Nullable ApplicationInfo applicationInfo) {
1208             Pair<String, Integer> key = getPackageUserKey(applicationInfo);
1209             if (key == null) return null;
1210             return mPackageUserToApplicationInfo.computeIfAbsent(key, ignored -> applicationInfo);
1211         }
1212 
1213         /** Puts the {@link ApplicationInfo} in the cache, replacing any previously stored value. */
put(@ullable ApplicationInfo applicationInfo)1214         void put(@Nullable ApplicationInfo applicationInfo) {
1215             Pair<String, Integer> key = getPackageUserKey(applicationInfo);
1216             if (key == null) return;
1217             mPackageUserToApplicationInfo.put(key, applicationInfo);
1218         }
1219 
1220         /**
1221          * Returns the currently stored {@link ApplicationInfo} from the cache matching
1222          * {@code  applicationInfo}, or null if there wasn't any.
1223          */
get(@ullable ApplicationInfo applicationInfo)1224         @Nullable ApplicationInfo get(@Nullable ApplicationInfo applicationInfo) {
1225             Pair<String, Integer> key = getPackageUserKey(applicationInfo);
1226             if (key == null) return null;
1227             return mPackageUserToApplicationInfo.get(key);
1228         }
1229     }
1230 
1231     private class SetRemoteCollectionItemListAdapterAction extends Action {
1232         private @Nullable RemoteCollectionItems mItems;
1233         final Intent mServiceIntent;
1234         int mIntentId = -1;
1235         boolean mIsReplacedIntoAction = false;
1236 
SetRemoteCollectionItemListAdapterAction(@dRes int id, @NonNull RemoteCollectionItems items)1237         SetRemoteCollectionItemListAdapterAction(@IdRes int id,
1238                 @NonNull RemoteCollectionItems items) {
1239             mViewId = id;
1240             items.setHierarchyRootData(getHierarchyRootData());
1241             mItems = items;
1242             mServiceIntent = null;
1243         }
1244 
SetRemoteCollectionItemListAdapterAction(@dRes int id, Intent intent)1245         SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) {
1246             mViewId = id;
1247             mItems = null;
1248             mServiceIntent = intent;
1249         }
1250 
SetRemoteCollectionItemListAdapterAction(Parcel parcel)1251         SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
1252             mViewId = parcel.readInt();
1253             mIntentId = parcel.readInt();
1254             mIsReplacedIntoAction = parcel.readBoolean();
1255             mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
1256             mItems = mServiceIntent != null
1257                     ? null
1258                     : new RemoteCollectionItems(parcel, getHierarchyRootData());
1259         }
1260 
1261         @Override
setHierarchyRootData(HierarchyRootData rootData)1262         public void setHierarchyRootData(HierarchyRootData rootData) {
1263             if (mItems != null) {
1264                 mItems.setHierarchyRootData(rootData);
1265                 return;
1266             }
1267 
1268             if (mIntentId != -1) {
1269                 // Set the root data for items in the cache instead
1270                 mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
1271             }
1272         }
1273 
1274         @Override
writeToParcel(Parcel dest, int flags)1275         public void writeToParcel(Parcel dest, int flags) {
1276             dest.writeInt(mViewId);
1277             dest.writeInt(mIntentId);
1278             dest.writeBoolean(mIsReplacedIntoAction);
1279             dest.writeTypedObject(mServiceIntent, flags);
1280             if (mItems != null) {
1281                 mItems.writeToParcel(dest, flags, /* attached= */ true);
1282             }
1283         }
1284 
1285         @Override
apply(View root, ViewGroup rootParent, ActionApplyParams params)1286         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
1287                 throws ActionException {
1288             View target = root.findViewById(mViewId);
1289             if (target == null) return;
1290 
1291             RemoteCollectionItems items = mIntentId == -1
1292                     ? mItems == null
1293                             ? new RemoteCollectionItems.Builder().build()
1294                             : mItems
1295                     : mCollectionCache.getItemsForId(mIntentId);
1296 
1297             // Ensure that we are applying to an AppWidget root
1298             if (!(rootParent instanceof AppWidgetHostView)) {
1299                 Log.e(LOG_TAG, "setRemoteAdapter can only be used for "
1300                         + "AppWidgets (root id: " + mViewId + ")");
1301                 return;
1302             }
1303 
1304             if (!(target instanceof AdapterView)) {
1305                 Log.e(LOG_TAG, "Cannot call setRemoteAdapter on a view which is not "
1306                         + "an AdapterView (id: " + mViewId + ")");
1307                 return;
1308             }
1309 
1310             AdapterView adapterView = (AdapterView) target;
1311             Adapter adapter = adapterView.getAdapter();
1312             boolean onLightBackground = hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
1313             // We can reuse the adapter if it's a RemoteCollectionItemsAdapter and the view type
1314             // count hasn't increased. Note that AbsListView allocates a fixed size array for view
1315             // recycling in setAdapter, so we must call setAdapter again if the number of view types
1316             // increases.
1317             if (adapter instanceof RemoteCollectionItemsAdapter
1318                     && adapter.getViewTypeCount() >= items.getViewTypeCount()) {
1319                 try {
1320                     ((RemoteCollectionItemsAdapter) adapter)
1321                             .setData(
1322                                     items,
1323                                     params.handler,
1324                                     params.colorResources,
1325                                     onLightBackground);
1326                 } catch (Throwable throwable) {
1327                     // setData should never failed with the validation in the items builder, but if
1328                     // it does, catch and rethrow.
1329                     throw new ActionException(throwable);
1330                 }
1331                 return;
1332             }
1333 
1334             try {
1335                 adapterView.setAdapter(
1336                         new RemoteCollectionItemsAdapter(
1337                                 items, params.handler, params.colorResources, onLightBackground));
1338             } catch (Throwable throwable) {
1339                 // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
1340                 // a type error.
1341                 throw new ActionException(throwable);
1342             }
1343             if (adapterView instanceof AbsListView listView) {
1344                 listView.setOnScrollListener(new AbsListView.OnScrollListener() {
1345                     @Override
1346                     public void onScrollStateChanged(AbsListView view, int scrollState) {
1347                         if (scrollState != SCROLL_STATE_IDLE) {
1348                             params.handler.onScroll(view);
1349                         }
1350                     }
1351 
1352                     @Override
1353                     public void onScroll(AbsListView view, int firstVisibleItem,
1354                             int visibleItemCount, int totalItemCount) {
1355                     }
1356                 });
1357             }
1358         }
1359 
1360         @Override
getActionTag()1361         public int getActionTag() {
1362             return SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG;
1363         }
1364 
1365         @Override
getUniqueKey()1366         public String getUniqueKey() {
1367             return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId);
1368         }
1369 
1370         @Override
visitUris(@onNull Consumer<Uri> visitor)1371         public void visitUris(@NonNull Consumer<Uri> visitor) {
1372             if (mItems == null) {
1373                 // Null item indicates adapter conversion took place, so the URIs in cached items
1374                 // need to be validated.
1375                 RemoteCollectionItems cachedItems = mCollectionCache.getItemsForId(mIntentId);
1376                 if (cachedItems != null) {
1377                     cachedItems.visitUris(visitor);
1378                 }
1379                 return;
1380             }
1381 
1382             mItems.visitUris(visitor);
1383         }
1384 
1385         @Override
visitIcons(Consumer<Icon> visitor)1386         public void visitIcons(Consumer<Icon> visitor) {
1387             if (mItems == null) {
1388                 RemoteCollectionItems cachedItems = mCollectionCache.getItemsForId(mIntentId);
1389                 if (cachedItems != null) {
1390                     cachedItems.visitIcons(visitor);
1391                 }
1392                 return;
1393             }
1394 
1395             mItems.visitIcons(visitor);
1396         }
1397 
1398         @Override
canWriteToProto()1399         public boolean canWriteToProto() {
1400             // Skip actions that do not contain items (intent only actions)
1401             return mItems != null;
1402         }
1403 
1404         @Override
writeToProto(ProtoOutputStream out, Context context, Resources appResources)1405         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
1406             if (mItems == null) return;
1407             final long token = out.start(
1408                     RemoteViewsProto.Action.SET_REMOTE_COLLECTION_ITEM_LIST_ADAPTER_ACTION);
1409             out.write(RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID,
1410                     appResources.getResourceName(mViewId));
1411             final long itemsToken = out.start(
1412                     RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS);
1413             mItems.writeToProto(context, out, /* attached= */ true);
1414             out.end(itemsToken);
1415             out.end(token);
1416         }
1417     }
1418 
createSetRemoteCollectionItemListAdapterActionFromProto( ProtoInputStream in)1419     private PendingResources<Action> createSetRemoteCollectionItemListAdapterActionFromProto(
1420             ProtoInputStream in) throws Exception {
1421         final LongSparseArray<Object> values = new LongSparseArray<>();
1422 
1423         final long token = in.start(
1424                 RemoteViewsProto.Action.SET_REMOTE_COLLECTION_ITEM_LIST_ADAPTER_ACTION);
1425         while (in.nextField() != NO_MORE_FIELDS) {
1426             switch (in.getFieldNumber()) {
1427                 case (int) RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID:
1428                     values.put(RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID,
1429                             in.readString(
1430                                     RemoteViewsProto
1431                                             .SetRemoteCollectionItemListAdapterAction.VIEW_ID));
1432                     break;
1433                 case (int) RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS:
1434                     final long itemsToken = in.start(
1435                             RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS);
1436                     values.put(RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS,
1437                             RemoteCollectionItems.createFromProto(in));
1438                     in.end(itemsToken);
1439                     break;
1440                 default:
1441                     Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
1442                             + ProtoUtils.currentFieldToString(in));
1443             }
1444         }
1445         in.end(token);
1446 
1447         checkContainsKeys(values,
1448                 new long[]{RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID,
1449                         RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS});
1450 
1451         return (context, resources, rootData, depth) -> {
1452             int viewId = getAsIdentifier(resources, values,
1453                     RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.VIEW_ID);
1454             return new SetRemoteCollectionItemListAdapterAction(viewId,
1455                     ((PendingResources<RemoteCollectionItems>) values.get(
1456                             RemoteViewsProto.SetRemoteCollectionItemListAdapterAction.ITEMS))
1457                             .create(context, resources, rootData, depth));
1458         };
1459     }
1460 
1461     /**
1462      * The maximum size for RemoteViews with converted RemoteCollectionItemsAdapter.
1463      * When converting RemoteViewsAdapter to RemoteCollectionItemsAdapter, we want to put size
1464      * limits on each unique RemoteCollectionItems in order to not exceed the transaction size limit
1465      * for each parcel (typically 1 MB). We leave a certain ratio of the maximum size as a buffer
1466      * for missing calculations of certain parameters (e.g. writing a RemoteCollectionItems to the
1467      * parcel will write its Id array as well, but that is missing when writing itschild RemoteViews
1468      * directly to the parcel as we did in RemoteViewsService)
1469      *
1470      * @hide
1471      */
1472     private static final int MAX_SINGLE_PARCEL_SIZE = (int) (1_000_000 * 0.8);
1473 
1474     /**
1475      * @hide
1476      */
1477     public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit,
1478             @NonNull ServiceCollectionCache collectionCache) {
1479         return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit,
1480                 collectionCache);
1481     }
1482 
1483     private class RemoteCollectionCache {
1484         private final SparseArray<String> mIdToUriMapping = new SparseArray<>();
1485         private final Map<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>();
1486 
1487         RemoteCollectionCache() { }
1488 
1489         RemoteCollectionCache(RemoteCollectionCache src) {
1490             for (int i = 0; i < src.mIdToUriMapping.size(); i++) {
1491                 String uri = src.mIdToUriMapping.valueAt(i);
1492                 mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri);
1493                 mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
1494             }
1495         }
1496 
1497         RemoteCollectionCache(Parcel in) {
1498             int cacheSize = in.readInt();
1499             HierarchyRootData currentRootData = new HierarchyRootData(mBitmapCache,
1500                     this,
1501                     mApplicationInfoCache,
1502                     mClassCookies);
1503             for (int i = 0; i < cacheSize; i++) {
1504                 int intentId = in.readInt();
1505                 String intentUri = in.readString8();
1506                 RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData);
1507                 addMapping(intentId, intentUri, items);
1508             }
1509         }
1510 
1511         void addMapping(int intentId, String intentUri, RemoteCollectionItems items) {
1512             mIdToUriMapping.put(intentId, intentUri);
1513             mUriToCollectionMapping.put(intentUri, items);
1514         }
1515 
1516 
1517         void setHierarchyDataForId(int intentId, HierarchyRootData data) {
1518             String uri = mIdToUriMapping.get(intentId);
1519             if (mUriToCollectionMapping.get(uri) == null) {
1520                 Log.e(LOG_TAG, "Error setting hierarchy data for id=" + intentId);
1521                 return;
1522             }
1523 
1524             RemoteCollectionItems items = mUriToCollectionMapping.get(uri);
1525             items.setHierarchyRootData(data);
1526         }
1527 
1528         RemoteCollectionItems getItemsForId(int intentId) {
1529             String uri = mIdToUriMapping.get(intentId);
1530             return mUriToCollectionMapping.get(uri);
1531         }
1532 
1533         public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
1534                 @NonNull RemoteViews inViews, int bitmapSizeLimit,
1535                 @NonNull ServiceCollectionCache collectionCache) {
1536             SparseArray<Intent> idToIntentMapping = new SparseArray<>();
1537             // Collect the number of uinque Intent (which is equal to the number of new connections
1538             // to make) for size allocation and exclude certain collections from being written to
1539             // the parcel to better estimate the space left for reallocation.
1540             collectAllIntentsInternal(inViews, idToIntentMapping);
1541 
1542             // Calculate the individual size here
1543             int numOfIntents = idToIntentMapping.size();
1544             if (numOfIntents == 0) {
1545                 Log.e(LOG_TAG, "Possibly notifying updates for nonexistent view Id");
1546                 return CompletableFuture.completedFuture(null);
1547             }
1548 
1549             Parcel sizeTestParcel = Parcel.obtain();
1550             // Write self RemoteViews to the parcel, which includes the actions/bitmaps/collection
1551             // cache to see how much space is left for the RemoteCollectionItems that are to be
1552             // updated.
1553             RemoteViews.this.writeToParcel(sizeTestParcel,
1554                     /* flags= */ 0,
1555                     /* intentsToIgnore= */ idToIntentMapping);
1556             int remainingSize = MAX_SINGLE_PARCEL_SIZE - sizeTestParcel.dataSize();
1557             sizeTestParcel.recycle();
1558 
1559             int individualSize = remainingSize < 0
1560                     ? 0
1561                     : remainingSize / numOfIntents;
1562 
1563             int individualBitmapSizeLimit = (bitmapSizeLimit - getBitmapMemoryUsedByActions())
1564                     / numOfIntents;
1565 
1566             return connectAllUniqueIntents(individualSize, individualBitmapSizeLimit,
1567                     idToIntentMapping, collectionCache);
1568         }
1569 
1570         private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
1571                 @NonNull SparseArray<Intent> idToIntentMapping) {
1572             if (inViews.hasSizedRemoteViews()) {
1573                 for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
1574                     collectAllIntentsInternal(remoteViews, idToIntentMapping);
1575                 }
1576             } else if (inViews.hasLandscapeAndPortraitLayouts()) {
1577                 collectAllIntentsInternal(inViews.mLandscape, idToIntentMapping);
1578                 collectAllIntentsInternal(inViews.mPortrait, idToIntentMapping);
1579             } else if (inViews.mActions != null) {
1580                 for (Action action : inViews.mActions) {
1581                     if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
1582                         // Deal with the case where the intent is replaced into the action list
1583                         if (rca.mIntentId != -1 && !rca.mIsReplacedIntoAction) {
1584                             continue;
1585                         }
1586 
1587                         if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
1588                             rca.mIsReplacedIntoAction = false;
1589 
1590                             // Avoid redundant connections for the same intent. Also making sure
1591                             // that the number of connections we are making is always equal to the
1592                             // nmuber of unique intents that are being used for the updates.
1593                             if (idToIntentMapping.contains(rca.mIntentId)) {
1594                                 continue;
1595                             }
1596 
1597                             idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent);
1598                             rca.mItems = null;
1599                             continue;
1600                         }
1601 
1602                         // Differentiate between the normal collection actions and the ones with
1603                         // intents.
1604                         if (rca.mServiceIntent != null) {
1605                             final String uri = rca.mServiceIntent.toUri(0);
1606                             int index = mIdToUriMapping.indexOfValueByValue(uri);
1607                             if (index == -1) {
1608                                 int newIntentId = mIdToUriMapping.size();
1609                                 rca.mIntentId = newIntentId;
1610                                 mIdToUriMapping.put(newIntentId, uri);
1611                             } else {
1612                                 rca.mIntentId = mIdToUriMapping.keyAt(index);
1613                                 rca.mItems = null;
1614                                 continue;
1615                             }
1616 
1617                             idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent);
1618                             rca.mItems = null;
1619                         } else {
1620                             for (RemoteViews views : rca.mItems.mViews) {
1621                                 collectAllIntentsInternal(views, idToIntentMapping);
1622                             }
1623                         }
1624                     } else if (action instanceof ViewGroupActionAdd vgaa
1625                             && vgaa.mNestedViews != null) {
1626                         collectAllIntentsInternal(vgaa.mNestedViews, idToIntentMapping);
1627                     }
1628                 }
1629             }
1630         }
1631 
1632         private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
1633                 int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping,
1634                 @NonNull ServiceCollectionCache collectionCache) {
1635             List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
1636             for (int i = 0; i < idToIntentMapping.size(); i++) {
1637                 String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
1638                 Intent currentIntent = idToIntentMapping.valueAt(i);
1639                 intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
1640                         individualSize, individualBitmapSize, collectionCache)
1641                         .thenAccept(items -> {
1642                             items.setHierarchyRootData(getHierarchyRootData());
1643                             mUriToCollectionMapping.put(currentIntentUri, items);
1644                         }));
1645             }
1646 
1647             return CompletableFuture.allOf(intentFutureList.toArray(CompletableFuture[]::new));
1648         }
1649 
1650         private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
1651                 Intent intent, int individualSize, int individualBitmapSize,
1652                 @NonNull ServiceCollectionCache collectionCache) {
1653             if (intent == null) {
1654                 Log.e(LOG_TAG, "Null intent received when generating adapter future");
1655                 return CompletableFuture.completedFuture(new RemoteCollectionItems
1656                         .Builder().build());
1657             }
1658 
1659             final Context context = ActivityThread.currentApplication();
1660 
1661             final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
1662             String contextPackageName = context.getPackageName();
1663             ComponentName intentComponent = intent.getComponent();
1664             if (contextPackageName != null
1665                     && intentComponent != null
1666                     && (!contextPackageName.equals(intentComponent.getPackageName()))) {
1667                 // We shouldn't allow for connections to other packages
1668                 result.complete(new RemoteCollectionItems.Builder().build());
1669                 return result;
1670             }
1671 
1672             collectionCache.connectAndConsume(intent, iBinder -> {
1673                 RemoteCollectionItems items;
1674                 try {
1675                     items = IRemoteViewsFactory.Stub.asInterface(iBinder)
1676                             .getRemoteCollectionItems(individualSize,
1677                                     individualBitmapSize);
1678                 } catch (RemoteException re) {
1679                     items = new RemoteCollectionItems.Builder().build();
1680                     Log.e(LOG_TAG, "Error getting collection items from the"
1681                             + " factory", re);
1682                 }
1683 
1684                 if (items == null) {
1685                     items = new RemoteCollectionItems.Builder().build();
1686                 }
1687 
1688                 result.complete(items);
1689             }, result.defaultExecutor());
1690 
1691             result.completeOnTimeout(
1692                     new RemoteCollectionItems.Builder().build(),
1693                     MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);
1694 
1695             return result;
1696         }
1697 
1698         public void writeToParcel(Parcel out, int flags,
1699                 @Nullable SparseArray<Intent> intentsToIgnore) {
1700             out.writeInt(mIdToUriMapping.size());
1701             for (int i = 0; i < mIdToUriMapping.size(); i++) {
1702                 int currentIntentId = mIdToUriMapping.keyAt(i);
1703                 if (intentsToIgnore != null && intentsToIgnore.contains(currentIntentId)) {
1704                     // Skip writing collections that are to be updated in the following steps to
1705                     // better estimate the RemoteViews size.
1706                     continue;
1707                 }
1708                 out.writeInt(currentIntentId);
1709                 String intentUri = mIdToUriMapping.valueAt(i);
1710                 out.writeString8(intentUri);
1711                 mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true);
1712             }
1713         }
1714 
1715         public void writeToProto(Context context, ProtoOutputStream out) {
1716             final long token = out.start(RemoteViewsProto.REMOTE_COLLECTION_CACHE);
1717             for (int i = 0; i < mIdToUriMapping.size(); i++) {
1718                 final long entryToken = out.start(RemoteViewsProto.RemoteCollectionCache.ENTRIES);
1719                 out.write(RemoteViewsProto.RemoteCollectionCache.Entry.ID,
1720                         mIdToUriMapping.keyAt(i));
1721                 String intentUri = mIdToUriMapping.valueAt(i);
1722                 out.write(RemoteViewsProto.RemoteCollectionCache.Entry.URI, intentUri);
1723                 final long itemsToken = out.start(
1724                         RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS);
1725                 mUriToCollectionMapping.get(intentUri).writeToProto(context, out, /* attached= */
1726                         true);
1727                 out.end(itemsToken);
1728                 out.end(entryToken);
1729             }
1730             out.end(token);
1731         }
1732     }
1733 
1734     private PendingResources<RemoteCollectionCache> populateRemoteCollectionCacheFromProto(
1735             ProtoInputStream in) throws Exception {
1736         final ArrayList<LongSparseArray<Object>> entries = new ArrayList<>();
1737         final long token = in.start(RemoteViewsProto.REMOTE_COLLECTION_CACHE);
1738         while (in.nextField() != NO_MORE_FIELDS) {
1739             switch (in.getFieldNumber()) {
1740                 case (int) RemoteViewsProto.RemoteCollectionCache.ENTRIES:
1741                     final LongSparseArray<Object> entry = new LongSparseArray<>();
1742 
1743                     final long entryToken = in.start(
1744                             RemoteViewsProto.RemoteCollectionCache.ENTRIES);
1745                     while (in.nextField() != NO_MORE_FIELDS) {
1746                         switch (in.getFieldNumber()) {
1747                             case (int) RemoteViewsProto.RemoteCollectionCache.Entry.ID:
1748                                 entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.ID,
1749                                         in.readInt(
1750                                                 RemoteViewsProto.RemoteCollectionCache.Entry.ID));
1751                                 break;
1752                             case (int) RemoteViewsProto.RemoteCollectionCache.Entry.URI:
1753                                 entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.URI,
1754                                         in.readString(
1755                                                 RemoteViewsProto.RemoteCollectionCache.Entry.URI));
1756                                 break;
1757                             case (int) RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS:
1758                                 final long itemsToken = in.start(
1759                                         RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS);
1760                                 entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS,
1761                                         RemoteCollectionItems.createFromProto(in));
1762                                 in.end(itemsToken);
1763                                 break;
1764                             default:
1765                                 Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
1766                                         + ProtoUtils.currentFieldToString(in));
1767                         }
1768                     }
1769                     in.end(entryToken);
1770 
1771                     checkContainsKeys(entry,
1772                             new long[]{RemoteViewsProto.RemoteCollectionCache.Entry.ID,
1773                                     RemoteViewsProto.RemoteCollectionCache.Entry.URI,
1774                                     RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS});
1775 
1776                     entries.add(entry);
1777                     break;
1778                 default:
1779                     Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
1780                             + ProtoUtils.currentFieldToString(in));
1781             }
1782         }
1783         in.end(token);
1784 
1785         return (context, resources, rootData, depth) -> {
1786             for (LongSparseArray<Object> entry : entries) {
1787                 int id = (int) entry.get(RemoteViewsProto.RemoteCollectionCache.Entry.ID);
1788                 String uri = (String) entry.get(RemoteViewsProto.RemoteCollectionCache.Entry.URI);
1789                 // Depth resets to 0 for RemoteCollectionItems
1790                 RemoteCollectionItems items = ((PendingResources<RemoteCollectionItems>) entry.get(
1791                         RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS)).create(context,
1792                         resources, rootData, depth);
1793                 rootData.mRemoteCollectionCache.addMapping(id, uri, items);
1794             }
1795             // Redundant return, but type signature requires we return something.
1796             return rootData.mRemoteCollectionCache;
1797         };
1798     }
1799 
1800     private class SetRemoteViewsAdapterIntent extends Action {
1801         Intent mIntent;
1802         boolean mIsAsync = false;
1803 
1804         public SetRemoteViewsAdapterIntent(@IdRes int id, Intent intent) {
1805             this.mViewId = id;
1806             this.mIntent = intent;
1807         }
1808 
1809         public SetRemoteViewsAdapterIntent(Parcel parcel) {
1810             mViewId = parcel.readInt();
1811             mIntent = parcel.readTypedObject(Intent.CREATOR);
1812         }
1813 
1814         public void writeToParcel(Parcel dest, int flags) {
1815             dest.writeInt(mViewId);
1816             dest.writeTypedObject(mIntent, flags);
1817         }
1818 
1819         @Override
1820         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
1821             final View target = root.findViewById(mViewId);
1822             if (target == null) return;
1823 
1824             // Ensure that we are applying to an AppWidget root
1825             if (!(rootParent instanceof AppWidgetHostView)) {
1826                 Log.e(LOG_TAG, "setRemoteAdapter can only be used for "
1827                         + "AppWidgets (root id: " + mViewId + ")");
1828                 return;
1829             }
1830 
1831             // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
1832             if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
1833                 Log.e(LOG_TAG, "Cannot setRemoteAdapter on a view which is not "
1834                         + "an AbsListView or AdapterViewAnimator (id: " + mViewId + ")");
1835                 return;
1836             }
1837 
1838             // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
1839             // RemoteViewsService
1840             AppWidgetHostView host = (AppWidgetHostView) rootParent;
1841             mIntent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
1842                     .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
1843                             hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
1844 
1845             if (target instanceof AbsListView) {
1846                 AbsListView v = (AbsListView) target;
1847                 v.setRemoteViewsAdapter(mIntent, mIsAsync);
1848                 v.setRemoteViewsInteractionHandler(params.handler);
1849                 v.setOnScrollListener(new AbsListView.OnScrollListener() {
1850                     @Override
1851                     public void onScrollStateChanged(AbsListView view, int scrollState) {
1852                         if (scrollState != SCROLL_STATE_IDLE) {
1853                             params.handler.onScroll(view);
1854                         }
1855                     }
1856 
1857                     @Override
1858                     public void onScroll(AbsListView view, int firstVisibleItem,
1859                             int visibleItemCount, int totalItemCount) {
1860                     }
1861                 });
1862             } else if (target instanceof AdapterViewAnimator) {
1863                 AdapterViewAnimator v = (AdapterViewAnimator) target;
1864                 v.setRemoteViewsAdapter(mIntent, mIsAsync);
1865                 v.setRemoteViewsOnClickHandler(params.handler);
1866             }
1867         }
1868 
1869         @Override
1870         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
1871                 ActionApplyParams params) {
1872             SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(mViewId, mIntent);
1873             copy.mIsAsync = true;
1874             return copy;
1875         }
1876 
1877         @Override
1878         public int getActionTag() {
1879             return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
1880         }
1881     }
1882 
1883     /**
1884      * Equivalent to calling
1885      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
1886      * to launch the provided {@link PendingIntent}.
1887      */
1888     private class SetOnClickResponse extends Action {
1889         final RemoteResponse mResponse;
1890 
1891         SetOnClickResponse(@IdRes int id, RemoteResponse response) {
1892             this.mViewId = id;
1893             this.mResponse = response;
1894         }
1895 
1896         SetOnClickResponse(Parcel parcel) {
1897             mViewId = parcel.readInt();
1898             mResponse = new RemoteResponse();
1899             mResponse.readFromParcel(parcel);
1900         }
1901 
1902         public void writeToParcel(Parcel dest, int flags) {
1903             dest.writeInt(mViewId);
1904             mResponse.writeToParcel(dest, flags);
1905         }
1906 
1907         @Override
1908         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
1909             if (hasDrawInstructions() && root instanceof RemoteComposePlayer) {
1910                 return;
1911             }
1912             final View target = root.findViewById(mViewId);
1913             if (target == null) return;
1914 
1915             if (mResponse.mPendingIntent != null) {
1916                 // If the view is an AdapterView, setting a PendingIntent on click doesn't make
1917                 // much sense, do they mean to set a PendingIntent template for the
1918                 // AdapterView's children?
1919                 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
1920                     Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
1921                             + "(id: " + mViewId + ")");
1922                     ApplicationInfo appInfo = root.getContext().getApplicationInfo();
1923 
1924                     // We let this slide for HC and ICS so as to not break compatibility. It should
1925                     // have been disabled from the outset, but was left open by accident.
1926                     if (appInfo != null
1927                             && appInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
1928                         return;
1929                     }
1930                 }
1931                 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
1932             } else if (mResponse.mFillIntent != null) {
1933                 if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
1934                     Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
1935                             + "only from RemoteViewsFactory (ie. on collection items).");
1936                     return;
1937                 }
1938                 if (target == root) {
1939                     // Target is a root node of an AdapterView child. Set the response in the tag.
1940                     // Actual click handling is done by OnItemClickListener in
1941                     // SetPendingIntentTemplate, which uses this tag information.
1942                     target.setTagInternal(com.android.internal.R.id.fillInIntent, mResponse);
1943                     return;
1944                 }
1945             } else {
1946                 // No intent to apply, clear the listener and any tags that were previously set.
1947                 target.setOnClickListener(null);
1948                 target.setTagInternal(R.id.pending_intent_tag, null);
1949                 target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
1950                 return;
1951             }
1952             target.setOnClickListener(v ->
1953                     mResponse.handleViewInteraction(v, params.handler));
1954         }
1955 
1956         @Override
1957         public int getActionTag() {
1958             return SET_ON_CLICK_RESPONSE_TAG;
1959         }
1960     }
1961 
1962     /** Helper action to configure handwriting delegation via {@link PendingIntent}. */
1963     private class SetOnStylusHandwritingResponse extends Action {
1964         final PendingIntent mPendingIntent;
1965 
1966         SetOnStylusHandwritingResponse(@IdRes int id, @Nullable PendingIntent pendingIntent) {
1967             this.mViewId = id;
1968             this.mPendingIntent = pendingIntent;
1969         }
1970 
1971         SetOnStylusHandwritingResponse(@NonNull Parcel parcel) {
1972             mViewId = parcel.readInt();
1973             mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
1974         }
1975 
1976         public void writeToParcel(@NonNull Parcel dest, int flags) {
1977             dest.writeInt(mViewId);
1978             PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
1979         }
1980 
1981         @Override
1982         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
1983             final View target = root.findViewById(mViewId);
1984             if (target == null) return;
1985 
1986             if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
1987                 Log.w(LOG_TAG, "Cannot use setOnStylusHandwritingPendingIntent for collection item "
1988                         + "(id: " + mViewId + ")");
1989                 return;
1990             }
1991 
1992             if (mPendingIntent != null) {
1993                 RemoteResponse response = RemoteResponse.fromPendingIntent(mPendingIntent);
1994                 target.setHandwritingDelegatorCallback(
1995                         () -> response.handleViewInteraction(target, params.handler));
1996                 target.setAllowedHandwritingDelegatePackage(mPendingIntent.getCreatorPackage());
1997             } else {
1998                 target.setHandwritingDelegatorCallback(null);
1999                 target.setAllowedHandwritingDelegatePackage(null);
2000             }
2001         }
2002 
2003         @Override
2004         public int getActionTag() {
2005             return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG;
2006         }
2007     }
2008 
2009     /**
2010      * Equivalent to calling
2011      * {@link android.widget.CompoundButton#setOnCheckedChangeListener(
2012      * android.widget.CompoundButton.OnCheckedChangeListener)}
2013      * to launch the provided {@link PendingIntent}.
2014      */
2015     private class SetOnCheckedChangeResponse extends Action {
2016         private final RemoteResponse mResponse;
2017 
2018         SetOnCheckedChangeResponse(@IdRes int id, RemoteResponse response) {
2019             this.mViewId = id;
2020             this.mResponse = response;
2021         }
2022 
2023         SetOnCheckedChangeResponse(Parcel parcel) {
2024             mViewId = parcel.readInt();
2025             mResponse = new RemoteResponse();
2026             mResponse.readFromParcel(parcel);
2027         }
2028 
2029         public void writeToParcel(Parcel dest, int flags) {
2030             dest.writeInt(mViewId);
2031             mResponse.writeToParcel(dest, flags);
2032         }
2033 
2034         @Override
2035         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
2036             final View target = root.findViewById(mViewId);
2037             if (target == null) return;
2038             if (!(target instanceof CompoundButton)) {
2039                 Log.w(LOG_TAG, "setOnCheckedChange methods cannot be used on "
2040                         + "non-CompoundButton child (id: " + mViewId + ")");
2041                 return;
2042             }
2043             CompoundButton button = (CompoundButton) target;
2044 
2045             if (mResponse.mPendingIntent != null) {
2046                 // setOnCheckedChangePendingIntent cannot be used with collection children, which
2047                 // must use setOnCheckedChangeFillInIntent instead.
2048                 if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
2049                     Log.w(LOG_TAG, "Cannot setOnCheckedChangePendingIntent for collection item "
2050                             + "(id: " + mViewId + ")");
2051                     return;
2052                 }
2053                 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
2054             } else if (mResponse.mFillIntent != null) {
2055                 if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
2056                     Log.e(LOG_TAG, "The method setOnCheckedChangeFillInIntent is available "
2057                             + "only from RemoteViewsFactory (ie. on collection items).");
2058                     return;
2059                 }
2060             } else {
2061                 // No intent to apply, clear any existing listener or tag.
2062                 button.setOnCheckedChangeListener(null);
2063                 button.setTagInternal(R.id.remote_checked_change_listener_tag, null);
2064                 return;
2065             }
2066 
2067             OnCheckedChangeListener onCheckedChangeListener =
2068                     (v, isChecked) -> mResponse.handleViewInteraction(v, params.handler);
2069             button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener);
2070             button.setOnCheckedChangeListener(onCheckedChangeListener);
2071         }
2072 
2073         @Override
2074         public int getActionTag() {
2075             return SET_ON_CHECKED_CHANGE_RESPONSE_TAG;
2076         }
2077     }
2078 
2079     /** @hide **/
2080     public static Rect getSourceBounds(View v) {
2081         final float appScale = v.getContext().getResources()
2082                 .getCompatibilityInfo().applicationScale;
2083         final int[] pos = new int[2];
2084         v.getLocationOnScreen(pos);
2085 
2086         final Rect rect = new Rect();
2087         rect.left = (int) (pos[0] * appScale + 0.5f);
2088         rect.top = (int) (pos[1] * appScale + 0.5f);
2089         rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
2090         rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
2091         return rect;
2092     }
2093 
2094     @Nullable
2095     private static Class<?> getParameterType(int type) {
2096         switch (type) {
2097             case BaseReflectionAction.BOOLEAN:
2098                 return boolean.class;
2099             case BaseReflectionAction.BYTE:
2100                 return byte.class;
2101             case BaseReflectionAction.SHORT:
2102                 return short.class;
2103             case BaseReflectionAction.INT:
2104                 return int.class;
2105             case BaseReflectionAction.LONG:
2106                 return long.class;
2107             case BaseReflectionAction.FLOAT:
2108                 return float.class;
2109             case BaseReflectionAction.DOUBLE:
2110                 return double.class;
2111             case BaseReflectionAction.CHAR:
2112                 return char.class;
2113             case BaseReflectionAction.STRING:
2114                 return String.class;
2115             case BaseReflectionAction.CHAR_SEQUENCE:
2116                 return CharSequence.class;
2117             case BaseReflectionAction.URI:
2118                 return Uri.class;
2119             case BaseReflectionAction.BITMAP:
2120                 return Bitmap.class;
2121             case BaseReflectionAction.BUNDLE:
2122                 return Bundle.class;
2123             case BaseReflectionAction.INTENT:
2124                 return Intent.class;
2125             case BaseReflectionAction.COLOR_STATE_LIST:
2126                 return ColorStateList.class;
2127             case BaseReflectionAction.ICON:
2128                 return Icon.class;
2129             case BaseReflectionAction.BLEND_MODE:
2130                 return BlendMode.class;
2131             default:
2132                 return null;
2133         }
2134     }
2135 
2136     @Nullable
2137     private static MethodHandle getMethod(View view, String methodName, Class<?> paramType,
2138             boolean async) {
2139         MethodArgs result;
2140         Class<? extends View> klass = view.getClass();
2141 
2142         synchronized (sMethods) {
2143             // The key is defined by the view class, param class and method name.
2144             sLookupKey.set(klass, paramType, methodName);
2145             result = sMethods.get(sLookupKey);
2146 
2147             if (result == null) {
2148                 Method method;
2149                 try {
2150                     if (paramType == null) {
2151                         method = klass.getMethod(methodName);
2152                     } else {
2153                         method = klass.getMethod(methodName, paramType);
2154                     }
2155                     if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
2156                         throw new ActionException("view: " + klass.getName()
2157                                 + " can't use method with RemoteViews: "
2158                                 + methodName + getParameters(paramType));
2159                     }
2160 
2161                     result = new MethodArgs();
2162                     result.syncMethod = MethodHandles.publicLookup().unreflect(method);
2163                     result.asyncMethodName =
2164                             method.getAnnotation(RemotableViewMethod.class).asyncImpl();
2165                 } catch (NoSuchMethodException | IllegalAccessException ex) {
2166                     throw new ActionException("view: " + klass.getName() + " doesn't have method: "
2167                             + methodName + getParameters(paramType));
2168                 }
2169 
2170                 MethodKey key = new MethodKey();
2171                 key.set(klass, paramType, methodName);
2172                 sMethods.put(key, result);
2173             }
2174 
2175             if (!async) {
2176                 return result.syncMethod;
2177             }
2178             // Check this so see if async method is implemented or not.
2179             if (result.asyncMethodName.isEmpty()) {
2180                 return null;
2181             }
2182             // Async method is lazily loaded. If it is not yet loaded, load now.
2183             if (result.asyncMethod == null) {
2184                 MethodType asyncType = result.syncMethod.type()
2185                         .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
2186                 try {
2187                     result.asyncMethod = MethodHandles.publicLookup().findVirtual(
2188                             klass, result.asyncMethodName, asyncType);
2189                 } catch (NoSuchMethodException | IllegalAccessException ex) {
2190                     throw new ActionException("Async implementation declared as "
2191                             + result.asyncMethodName + " but not defined for " + methodName
2192                             + ": public Runnable " + result.asyncMethodName + " ("
2193                             + TextUtils.join(",", asyncType.parameterArray()) + ")");
2194                 }
2195             }
2196             return result.asyncMethod;
2197         }
2198     }
2199 
2200     private static String getParameters(Class<?> paramType) {
2201         if (paramType == null) return "()";
2202         return "(" + paramType + ")";
2203     }
2204 
2205     /**
2206      * Equivalent to calling
2207      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
2208      * on the {@link Drawable} of a given view.
2209      * <p>
2210      * The operation will be performed on the {@link Drawable} returned by the
2211      * target {@link View#getBackground()} by default.  If targetBackground is false,
2212      * we assume the target is an {@link ImageView} and try applying the operations
2213      * to {@link ImageView#getDrawable()}.
2214      * <p>
2215      */
2216     private static class SetDrawableTint extends Action {
2217         boolean mTargetBackground;
2218         @ColorInt int mColorFilter;
2219         PorterDuff.Mode mFilterMode;
2220 
2221         SetDrawableTint(@IdRes int id, boolean targetBackground,
2222                 @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) {
2223             this.mViewId = id;
2224             this.mTargetBackground = targetBackground;
2225             this.mColorFilter = colorFilter;
2226             this.mFilterMode = mode;
2227         }
2228 
2229         SetDrawableTint(Parcel parcel) {
2230             mViewId = parcel.readInt();
2231             mTargetBackground = parcel.readInt() != 0;
2232             mColorFilter = parcel.readInt();
2233             mFilterMode = PorterDuff.intToMode(parcel.readInt());
2234         }
2235 
2236         public void writeToParcel(Parcel dest, int flags) {
2237             dest.writeInt(mViewId);
2238             dest.writeInt(mTargetBackground ? 1 : 0);
2239             dest.writeInt(mColorFilter);
2240             dest.writeInt(PorterDuff.modeToInt(mFilterMode));
2241         }
2242 
2243         @Override
2244         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
2245             final View target = root.findViewById(mViewId);
2246             if (target == null) return;
2247 
2248             // Pick the correct drawable to modify for this view
2249             Drawable targetDrawable = null;
2250             if (mTargetBackground) {
2251                 targetDrawable = target.getBackground();
2252             } else if (target instanceof ImageView) {
2253                 ImageView imageView = (ImageView) target;
2254                 targetDrawable = imageView.getDrawable();
2255             }
2256 
2257             if (targetDrawable != null) {
2258                 targetDrawable.mutate().setColorFilter(mColorFilter, mFilterMode);
2259             }
2260         }
2261 
2262         @Override
2263         public int getActionTag() {
2264             return SET_DRAWABLE_TINT_TAG;
2265         }
2266 
2267         @Override
2268         public boolean canWriteToProto() {
2269             return true;
2270         }
2271 
2272         @Override
2273         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
2274             final long token = out.start(RemoteViewsProto.Action.SET_DRAWABLE_TINT_ACTION);
2275             out.write(RemoteViewsProto.SetDrawableTintAction.VIEW_ID,
2276                     appResources.getResourceName(mViewId));
2277             out.write(RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER, mColorFilter);
2278             out.write(RemoteViewsProto.SetDrawableTintAction.FILTER_MODE,
2279                     PorterDuff.modeToInt(mFilterMode));
2280             out.write(RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND, mTargetBackground);
2281             out.end(token);
2282         }
2283 
2284         public static PendingResources<Action> createFromProto(ProtoInputStream in)
2285                 throws Exception {
2286             final LongSparseArray<Object> values = new LongSparseArray<>();
2287 
2288             final long token = in.start(RemoteViewsProto.Action.SET_DRAWABLE_TINT_ACTION);
2289             while (in.nextField() != NO_MORE_FIELDS) {
2290                 switch (in.getFieldNumber()) {
2291                     case (int) RemoteViewsProto.SetDrawableTintAction.VIEW_ID:
2292                         values.put(RemoteViewsProto.SetDrawableTintAction.VIEW_ID,
2293                                 in.readString(RemoteViewsProto.SetDrawableTintAction.VIEW_ID));
2294                         break;
2295                     case (int) RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND:
2296                         values.put(RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND,
2297                                 in.readBoolean(
2298                                         RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND));
2299                         break;
2300                     case (int) RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER:
2301                         values.put(RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER,
2302                                 in.readInt(RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER));
2303                         break;
2304                     case (int) RemoteViewsProto.SetDrawableTintAction.FILTER_MODE:
2305                         values.put(RemoteViewsProto.SetDrawableTintAction.FILTER_MODE,
2306                                 PorterDuff.intToMode(in.readInt(
2307                                         RemoteViewsProto.SetDrawableTintAction.FILTER_MODE)));
2308                         break;
2309                     default:
2310                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
2311                                 + ProtoUtils.currentFieldToString(in));
2312                 }
2313             }
2314             in.end(token);
2315 
2316             checkContainsKeys(values, new long[]{RemoteViewsProto.SetDrawableTintAction.VIEW_ID});
2317 
2318             return (context, resources, rootData, depth) -> {
2319                 int viewId = getAsIdentifier(resources, values,
2320                         RemoteViewsProto.SetDrawableTintAction.VIEW_ID);
2321                 return new SetDrawableTint(viewId, (boolean) values.get(
2322                         RemoteViewsProto.SetDrawableTintAction.TARGET_BACKGROUND, false),
2323                         (int) values.get(RemoteViewsProto.SetDrawableTintAction.COLOR_FILTER, 0),
2324                         (PorterDuff.Mode) values.get(
2325                                 RemoteViewsProto.SetDrawableTintAction.FILTER_MODE));
2326             };
2327         }
2328     }
2329 
2330     /**
2331      * Equivalent to calling
2332      * {@link RippleDrawable#setColor(ColorStateList)},
2333      * on the {@link Drawable} of a given view.
2334      * <p>
2335      * The operation will be performed on the {@link Drawable} returned by the
2336      * target {@link View#getBackground()}.
2337      * <p>
2338      */
2339     private static class SetRippleDrawableColor extends Action {
2340         ColorStateList mColorStateList;
2341 
2342         SetRippleDrawableColor(@IdRes int id, ColorStateList colorStateList) {
2343             this.mViewId = id;
2344             this.mColorStateList = colorStateList;
2345         }
2346 
2347         SetRippleDrawableColor(Parcel parcel) {
2348             mViewId = parcel.readInt();
2349             mColorStateList = parcel.readParcelable(null, android.content.res.ColorStateList.class);
2350         }
2351 
2352         public void writeToParcel(Parcel dest, int flags) {
2353             dest.writeInt(mViewId);
2354             dest.writeParcelable(mColorStateList, 0);
2355         }
2356 
2357         @Override
2358         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
2359             final View target = root.findViewById(mViewId);
2360             if (target == null) return;
2361 
2362             // Pick the correct drawable to modify for this view
2363             Drawable targetDrawable = target.getBackground();
2364 
2365             if (targetDrawable instanceof RippleDrawable) {
2366                 ((RippleDrawable) targetDrawable.mutate()).setColor(mColorStateList);
2367             }
2368         }
2369 
2370         @Override
2371         public int getActionTag() {
2372             return SET_RIPPLE_DRAWABLE_COLOR_TAG;
2373         }
2374 
2375         @Override
2376         public boolean canWriteToProto() {
2377             return true;
2378         }
2379 
2380         @Override
2381         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
2382             final long token = out.start(RemoteViewsProto.Action.SET_RIPPLE_DRAWABLE_COLOR_ACTION);
2383             out.write(RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID,
2384                     appResources.getResourceName(mViewId));
2385             writeColorStateListToProto(out, mColorStateList,
2386                     RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST);
2387             out.end(token);
2388         }
2389 
2390         public static PendingResources<Action> createFromProto(ProtoInputStream in)
2391                 throws Exception {
2392             final LongSparseArray<Object> values = new LongSparseArray<>();
2393 
2394             final long token = in.start(RemoteViewsProto.Action.SET_RIPPLE_DRAWABLE_COLOR_ACTION);
2395             while (in.nextField() != NO_MORE_FIELDS) {
2396                 switch (in.getFieldNumber()) {
2397                     case (int) RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID:
2398                         values.put(RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID,
2399                                 in.readString(
2400                                         RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID));
2401                         break;
2402                     case (int) RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST:
2403                         values.put(RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST,
2404                                 createColorStateListFromProto(in,
2405                                         RemoteViewsProto
2406                                                 .SetRippleDrawableColorAction.COLOR_STATE_LIST));
2407                         break;
2408                     default:
2409                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
2410                                 + ProtoUtils.currentFieldToString(in));
2411                 }
2412             }
2413             in.end(token);
2414 
2415             checkContainsKeys(values,
2416                     new long[]{RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID,
2417                             RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST});
2418 
2419             return (context, resources, rootData, depth) -> {
2420                 int viewId = getAsIdentifier(resources, values,
2421                         RemoteViewsProto.SetRippleDrawableColorAction.VIEW_ID);
2422                 return new SetRippleDrawableColor(viewId, (ColorStateList) values.get(
2423                         RemoteViewsProto.SetRippleDrawableColorAction.COLOR_STATE_LIST));
2424             };
2425         }
2426     }
2427 
2428     /**
2429      * @deprecated As RemoteViews may be reapplied frequently, it is preferable to call
2430      * {@link #setDisplayedChild(int, int)} to ensure that the adapter index does not change
2431      * unexpectedly.
2432      */
2433     @Deprecated
2434     private final class ViewContentNavigation extends Action {
2435         final boolean mNext;
2436 
2437         ViewContentNavigation(@IdRes int viewId, boolean next) {
2438             this.mViewId = viewId;
2439             this.mNext = next;
2440         }
2441 
2442         ViewContentNavigation(Parcel in) {
2443             this.mViewId = in.readInt();
2444             this.mNext = in.readBoolean();
2445         }
2446 
2447         public void writeToParcel(Parcel out, int flags) {
2448             out.writeInt(this.mViewId);
2449             out.writeBoolean(this.mNext);
2450         }
2451 
2452         @Override
2453         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
2454             final View view = root.findViewById(mViewId);
2455             if (view == null) return;
2456 
2457             try {
2458                 getMethod(view,
2459                         mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view);
2460             } catch (Throwable ex) {
2461                 throw new ActionException(ex);
2462             }
2463         }
2464 
2465         public int mergeBehavior() {
2466             return MERGE_IGNORE;
2467         }
2468 
2469         @Override
2470         public int getActionTag() {
2471             return VIEW_CONTENT_NAVIGATION_TAG;
2472         }
2473     }
2474 
2475     /**
2476      * @hide
2477      */
2478     @NonNull BitmapCache getBitmapCache() {
2479         return mBitmapCache;
2480     }
2481 
2482     static class BitmapCache {
2483         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2484         ArrayList<Bitmap> mBitmaps;
2485         SparseIntArray mBitmapHashes;
2486         long mBitmapMemory = -1;
2487 
2488         public BitmapCache() {
2489             mBitmaps = new ArrayList<>();
2490             mBitmapHashes = new SparseIntArray();
2491         }
2492 
2493         public BitmapCache(Parcel source) {
2494             mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
2495             mBitmapHashes = new SparseIntArray();
2496             for (int i = 0; i < mBitmaps.size(); i++) {
2497                 Bitmap b = mBitmaps.get(i);
2498                 if (b != null) {
2499                     mBitmapHashes.put(b.hashCode(), i);
2500                 }
2501             }
2502         }
2503 
2504         BitmapCache(BitmapCache other) {
2505             mBitmaps = new ArrayList<>(other.mBitmaps);
2506             mBitmapHashes = other.mBitmapHashes.clone();
2507         }
2508 
2509         public int getBitmapId(Bitmap b) {
2510             if (b == null) {
2511                 return -1;
2512             } else {
2513                 int hash = b.hashCode();
2514                 int hashId = mBitmapHashes.get(hash, -1);
2515                 if (hashId != -1) {
2516                     return hashId;
2517                 } else {
2518                     if (b.isMutable()) {
2519                         b = b.asShared();
2520                     }
2521                     mBitmaps.add(b);
2522                     mBitmapHashes.put(hash, mBitmaps.size() - 1);
2523                     mBitmapMemory = -1;
2524                     return (mBitmaps.size() - 1);
2525                 }
2526             }
2527         }
2528 
2529         @Nullable
2530         public Bitmap getBitmapForId(int id) {
2531             if (id == -1 || id >= mBitmaps.size()) {
2532                 return null;
2533             }
2534             return mBitmaps.get(id);
2535         }
2536 
2537         public void writeBitmapsToParcel(Parcel dest, int flags) {
2538             dest.writeTypedList(mBitmaps, flags);
2539         }
2540 
2541         public void writeBitmapsToProto(ProtoOutputStream out) {
2542             for (int i = 0; i < mBitmaps.size(); i++) {
2543                 final Bitmap bitmap = mBitmaps.get(i);
2544                 final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
2545                 bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, bytes);
2546                 out.write(RemoteViewsProto.BITMAP_CACHE, bytes.toByteArray());
2547             }
2548         }
2549 
2550         public long getBitmapMemory() {
2551             if (mBitmapMemory < 0) {
2552                 mBitmapMemory = 0;
2553                 int count = mBitmaps.size();
2554                 for (int i = 0; i < count; i++) {
2555                     mBitmapMemory += mBitmaps.get(i).getAllocationByteCount();
2556                 }
2557             }
2558             return mBitmapMemory;
2559         }
2560 
2561         public void mergeWithCache(BitmapCache other) {
2562             for (int i = 0; i < other.mBitmaps.size(); i++) {
2563                 getBitmapId(other.mBitmaps.get(i));
2564             }
2565         }
2566     }
2567 
2568     private class BitmapReflectionAction extends Action {
2569         int mBitmapId;
2570         @UnsupportedAppUsage
2571         Bitmap mBitmap;
2572         @UnsupportedAppUsage
2573         String mMethodName;
2574 
2575         BitmapReflectionAction(@IdRes int viewId, String methodName, Bitmap bitmap) {
2576             this.mBitmap = bitmap;
2577             this.mViewId = viewId;
2578             this.mMethodName = methodName;
2579             mBitmapId = mBitmapCache.getBitmapId(bitmap);
2580         }
2581 
2582         BitmapReflectionAction(Parcel in) {
2583             mViewId = in.readInt();
2584             mMethodName = in.readString8();
2585             mBitmapId = in.readInt();
2586             mBitmap = mBitmapCache.getBitmapForId(mBitmapId);
2587         }
2588 
2589         @Override
2590         public void writeToParcel(Parcel dest, int flags) {
2591             dest.writeInt(mViewId);
2592             dest.writeString8(mMethodName);
2593             dest.writeInt(mBitmapId);
2594         }
2595 
2596         @Override
2597         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
2598                 throws ActionException {
2599             ReflectionAction ra = new ReflectionAction(mViewId, mMethodName,
2600                     BaseReflectionAction.BITMAP,
2601                     mBitmap);
2602             ra.apply(root, rootParent, params);
2603         }
2604 
2605         @Override
2606         public void setHierarchyRootData(HierarchyRootData rootData) {
2607             mBitmapId = rootData.mBitmapCache.getBitmapId(mBitmap);
2608         }
2609 
2610         @Override
2611         public int getActionTag() {
2612             return BITMAP_REFLECTION_ACTION_TAG;
2613         }
2614 
2615         @Override
2616         public boolean canWriteToProto() {
2617             return true;
2618         }
2619 
2620         @Override
2621         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
2622             final long token = out.start(RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION);
2623             out.write(RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
2624                     appResources.getResourceName(mViewId));
2625             out.write(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME, mMethodName);
2626             out.write(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID, mBitmapId);
2627             out.end(token);
2628         }
2629     }
2630 
2631     private PendingResources<Action> createFromBitmapReflectionActionFromProto(ProtoInputStream in)
2632             throws Exception {
2633         final LongSparseArray<Object> values = new LongSparseArray<>();
2634 
2635         final long token = in.start(RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION);
2636         while (in.nextField() != NO_MORE_FIELDS) {
2637             switch (in.getFieldNumber()) {
2638                 case (int) RemoteViewsProto.BitmapReflectionAction.VIEW_ID:
2639                     values.put(RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
2640                             in.readString(RemoteViewsProto.BitmapReflectionAction.VIEW_ID));
2641                     break;
2642                 case (int) RemoteViewsProto.BitmapReflectionAction.METHOD_NAME:
2643                     values.put(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME,
2644                             in.readString(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME));
2645                     break;
2646                 case (int) RemoteViewsProto.BitmapReflectionAction.BITMAP_ID:
2647                     values.put(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID,
2648                             in.readInt(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID));
2649                     break;
2650                 default:
2651                     Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
2652                             + ProtoUtils.currentFieldToString(in));
2653             }
2654         }
2655         in.end(token);
2656 
2657         checkContainsKeys(values, new long[]{RemoteViewsProto.BitmapReflectionAction.VIEW_ID,
2658                 RemoteViewsProto.BitmapReflectionAction.METHOD_NAME});
2659 
2660         return (context, resources, rootData, depth) -> {
2661             int viewId = getAsIdentifier(resources, values,
2662                     RemoteViewsProto.BitmapReflectionAction.VIEW_ID);
2663             return new BitmapReflectionAction(viewId,
2664                     (String) values.get(RemoteViewsProto.BitmapReflectionAction.METHOD_NAME),
2665                     rootData.mBitmapCache.getBitmapForId(
2666                             (int) values.get(RemoteViewsProto.BitmapReflectionAction.BITMAP_ID,
2667                                     0)));
2668         };
2669 
2670     }
2671 
2672     /**
2673      * Base class for the reflection actions.
2674      */
2675     private abstract static class BaseReflectionAction extends Action {
2676         static final int BOOLEAN = 1;
2677         static final int BYTE = 2;
2678         static final int SHORT = 3;
2679         static final int INT = 4;
2680         static final int LONG = 5;
2681         static final int FLOAT = 6;
2682         static final int DOUBLE = 7;
2683         static final int CHAR = 8;
2684         static final int STRING = 9;
2685         static final int CHAR_SEQUENCE = 10;
2686         static final int URI = 11;
2687         // BITMAP actions are never stored in the list of actions. They are only used locally
2688         // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.
2689         static final int BITMAP = 12;
2690         static final int BUNDLE = 13;
2691         static final int INTENT = 14;
2692         static final int COLOR_STATE_LIST = 15;
2693         static final int ICON = 16;
2694         static final int BLEND_MODE = 17;
2695 
2696         @UnsupportedAppUsage
2697         String mMethodName;
2698         int mType;
2699 
2700         BaseReflectionAction(@IdRes int viewId, String methodName, int type) {
2701             this.mViewId = viewId;
2702             this.mMethodName = methodName;
2703             this.mType = type;
2704         }
2705 
2706         BaseReflectionAction(Parcel in) {
2707             this.mViewId = in.readInt();
2708             this.mMethodName = in.readString8();
2709             this.mType = in.readInt();
2710             //noinspection ConstantIfStatement
2711             if (false) {
2712                 Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.mViewId)
2713                         + " methodName=" + this.mMethodName + " type=" + this.mType);
2714             }
2715         }
2716 
2717         public void writeToParcel(Parcel out, int flags) {
2718             out.writeInt(this.mViewId);
2719             out.writeString8(this.mMethodName);
2720             out.writeInt(this.mType);
2721         }
2722 
2723         /**
2724          * Returns the value to use as parameter for the method.
2725          *
2726          * The view might be passed as {@code null} if the parameter value is requested outside of
2727          * inflation. If the parameter cannot be determined at that time, the method should return
2728          * {@code null} but not raise any exception.
2729          */
2730         @Nullable
2731         protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
2732 
2733         @Override
2734         public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
2735             final View view = root.findViewById(mViewId);
2736             if (view == null) return;
2737 
2738             Class<?> param = getParameterType(this.mType);
2739             if (param == null) {
2740                 throw new ActionException("bad type: " + this.mType);
2741             }
2742             Object value = getParameterValue(view);
2743             try {
2744                 getMethod(view, this.mMethodName, param, false /* async */).invoke(view, value);
2745             } catch (Throwable ex) {
2746                 throw new ActionException(ex);
2747             }
2748         }
2749 
2750         @Override
2751         public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
2752                 ActionApplyParams params) {
2753             final View view = root.findViewById(mViewId);
2754             if (view == null) return ACTION_NOOP;
2755 
2756             Class<?> param = getParameterType(this.mType);
2757             if (param == null) {
2758                 throw new ActionException("bad type: " + this.mType);
2759             }
2760 
2761             Object value = getParameterValue(view);
2762             try {
2763                 MethodHandle method = getMethod(view, this.mMethodName, param, true /* async */);
2764                 // Upload the bitmap to GPU if the parameter is of type Bitmap or Icon.
2765                 // Since bitmaps in framework are seldomly modified, this is supposed to accelerate
2766                 // the operations.
2767                 if (value instanceof Bitmap bitmap) {
2768                     bitmap.prepareToDraw();
2769                 }
2770 
2771                 if (value instanceof Icon icon
2772                         && (icon.getType() == Icon.TYPE_BITMAP
2773                                 || icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP)) {
2774                     Bitmap bitmap = icon.getBitmap();
2775                     if (bitmap != null) {
2776                         bitmap.prepareToDraw();
2777                     }
2778                 }
2779 
2780                 if (method != null) {
2781                     Runnable endAction = (Runnable) method.invoke(view, value);
2782                     if (endAction == null) {
2783                         return ACTION_NOOP;
2784                     }
2785                     // Special case view stub
2786                     if (endAction instanceof ViewStub.ViewReplaceRunnable) {
2787                         root.createTree();
2788                         // Replace child tree
2789                         root.findViewTreeById(mViewId).replaceView(
2790                                 ((ViewStub.ViewReplaceRunnable) endAction).view);
2791                     }
2792                     return new RunnableAction(endAction);
2793                 }
2794             } catch (Throwable ex) {
2795                 throw new ActionException(ex);
2796             }
2797 
2798             return this;
2799         }
2800 
2801         public final int mergeBehavior() {
2802             // smoothScrollBy is cumulative, everything else overwites.
2803             if (mMethodName.equals("smoothScrollBy")) {
2804                 return MERGE_APPEND;
2805             } else {
2806                 return MERGE_REPLACE;
2807             }
2808         }
2809 
2810         @Override
2811         public final String getUniqueKey() {
2812             // Each type of reflection action corresponds to a setter, so each should be seen as
2813             // unique from the standpoint of merging.
2814             return super.getUniqueKey() + this.mMethodName + this.mType;
2815         }
2816 
2817         @Override
2818         public final boolean prefersAsyncApply() {
2819             return this.mType == URI || this.mType == ICON;
2820         }
2821 
2822         @Override
2823         public void visitUris(@NonNull Consumer<Uri> visitor) {
2824             switch (this.mType) {
2825                 case URI:
2826                     final Uri uri = (Uri) getParameterValue(null);
2827                     if (uri != null) visitor.accept(uri);
2828                     break;
2829                 case ICON:
2830                     final Icon icon = (Icon) getParameterValue(null);
2831                     if (icon != null) visitIconUri(icon, visitor);
2832                     break;
2833                 // TODO(b/281044385): Should we do anything about type BUNDLE?
2834             }
2835         }
2836 
2837         @Override
2838         public void visitIcons(@NonNull Consumer<Icon> visitor) {
2839             if (mType == ICON && getParameterValue(null) instanceof Icon icon) {
2840                 visitor.accept(icon);
2841             }
2842         }
2843     }
2844 
2845     /** Class for the reflection actions. */
2846     private static final class ReflectionAction extends BaseReflectionAction {
2847         @UnsupportedAppUsage
2848         Object mValue;
2849 
2850         ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
2851             super(viewId, methodName, type);
2852             this.mValue = value;
2853         }
2854 
2855         ReflectionAction(Parcel in) {
2856             super(in);
2857             // For some values that may have been null, we first check a flag to see if they were
2858             // written to the parcel.
2859             switch (this.mType) {
2860                 case BOOLEAN:
2861                     this.mValue = in.readBoolean();
2862                     break;
2863                 case BYTE:
2864                     this.mValue = in.readByte();
2865                     break;
2866                 case SHORT:
2867                     this.mValue = (short) in.readInt();
2868                     break;
2869                 case INT:
2870                     this.mValue = in.readInt();
2871                     break;
2872                 case LONG:
2873                     this.mValue = in.readLong();
2874                     break;
2875                 case FLOAT:
2876                     this.mValue = in.readFloat();
2877                     break;
2878                 case DOUBLE:
2879                     this.mValue = in.readDouble();
2880                     break;
2881                 case CHAR:
2882                     this.mValue = (char) in.readInt();
2883                     break;
2884                 case STRING:
2885                     this.mValue = in.readString8();
2886                     break;
2887                 case CHAR_SEQUENCE:
2888                     this.mValue = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
2889                     break;
2890                 case URI:
2891                     this.mValue = in.readTypedObject(Uri.CREATOR);
2892                     break;
2893                 case BITMAP:
2894                     this.mValue = in.readTypedObject(Bitmap.CREATOR);
2895                     break;
2896                 case BUNDLE:
2897                     // Because we use Parcel.allowSquashing() when writing, and that affects
2898                     //  how the contents of Bundles are written, we need to ensure the bundle is
2899                     //  unparceled immediately, not lazily.  Setting a custom ReadWriteHelper
2900                     //  just happens to have that effect on Bundle.readFromParcel().
2901                     // TODO(b/212731590): build this state tracking into Bundle
2902                     if (in.hasReadWriteHelper()) {
2903                         this.mValue = in.readBundle();
2904                     } else {
2905                         in.setReadWriteHelper(ALTERNATIVE_DEFAULT);
2906                         this.mValue = in.readBundle();
2907                         in.setReadWriteHelper(null);
2908                     }
2909                     break;
2910                 case INTENT:
2911                     this.mValue = in.readTypedObject(Intent.CREATOR);
2912                     break;
2913                 case COLOR_STATE_LIST:
2914                     this.mValue = in.readTypedObject(ColorStateList.CREATOR);
2915                     break;
2916                 case ICON:
2917                     this.mValue = in.readTypedObject(Icon.CREATOR);
2918                     break;
2919                 case BLEND_MODE:
2920                     this.mValue = BlendMode.fromValue(in.readInt());
2921                     break;
2922                 default:
2923                     break;
2924             }
2925         }
2926 
2927         public void writeToParcel(Parcel out, int flags) {
2928             super.writeToParcel(out, flags);
2929             // For some values which are null, we record an integer flag to indicate whether
2930             // we have written a valid value to the parcel.
2931             switch (this.mType) {
2932                 case BOOLEAN:
2933                     out.writeBoolean((Boolean) this.mValue);
2934                     break;
2935                 case BYTE:
2936                     out.writeByte((Byte) this.mValue);
2937                     break;
2938                 case SHORT:
2939                     out.writeInt((Short) this.mValue);
2940                     break;
2941                 case INT:
2942                     out.writeInt((Integer) this.mValue);
2943                     break;
2944                 case LONG:
2945                     out.writeLong((Long) this.mValue);
2946                     break;
2947                 case FLOAT:
2948                     out.writeFloat((Float) this.mValue);
2949                     break;
2950                 case DOUBLE:
2951                     out.writeDouble((Double) this.mValue);
2952                     break;
2953                 case CHAR:
2954                     out.writeInt((int) ((Character) this.mValue).charValue());
2955                     break;
2956                 case STRING:
2957                     out.writeString8((String) this.mValue);
2958                     break;
2959                 case CHAR_SEQUENCE:
2960                     TextUtils.writeToParcel((CharSequence) this.mValue, out, flags);
2961                     break;
2962                 case BUNDLE:
2963                     out.writeBundle((Bundle) this.mValue);
2964                     break;
2965                 case BLEND_MODE:
2966                     out.writeInt(BlendMode.toValue((BlendMode) this.mValue));
2967                     break;
2968                 case URI:
2969                 case BITMAP:
2970                 case INTENT:
2971                 case COLOR_STATE_LIST:
2972                 case ICON:
2973                     out.writeTypedObject((Parcelable) this.mValue, flags);
2974                     break;
2975                 default:
2976                     break;
2977             }
2978         }
2979 
2980         @Nullable
2981         @Override
2982         protected Object getParameterValue(@Nullable View view) throws ActionException {
2983             return this.mValue;
2984         }
2985 
2986         @Override
2987         public int getActionTag() {
2988             return REFLECTION_ACTION_TAG;
2989         }
2990 
2991         @Override
2992         public boolean canWriteToProto() {
2993             return true;
2994         }
2995 
2996         @Override
2997         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
2998             final long token = out.start(RemoteViewsProto.Action.REFLECTION_ACTION);
2999             out.write(RemoteViewsProto.ReflectionAction.VIEW_ID,
3000                     appResources.getResourceName(mViewId));
3001             out.write(RemoteViewsProto.ReflectionAction.METHOD_NAME, mMethodName);
3002             out.write(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE, mType);
3003             if (this.mValue != null) {
3004                 switch (this.mType) {
3005                     case BOOLEAN:
3006                         // ProtoOutputStream will omit this write if the value is false
3007                         out.write(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE,
3008                                 (boolean) this.mValue);
3009                         break;
3010                     case BYTE:
3011                         out.write(RemoteViewsProto.ReflectionAction.BYTE_VALUE,
3012                                 new byte[]{(byte) this.mValue});
3013                         break;
3014                     case SHORT:
3015                         out.write(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
3016                                 (short) this.mValue);
3017                         break;
3018                     case INT:
3019                         out.write(RemoteViewsProto.ReflectionAction.INT_VALUE, (int) this.mValue);
3020                         break;
3021                     case LONG:
3022                         out.write(RemoteViewsProto.ReflectionAction.LONG_VALUE, (long) this.mValue);
3023                         break;
3024                     case FLOAT:
3025                         out.write(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
3026                                 (float) this.mValue);
3027                         break;
3028                     case DOUBLE:
3029                         out.write(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
3030                                 (double) this.mValue);
3031                         break;
3032                     case CHAR:
3033                         out.write(RemoteViewsProto.ReflectionAction.CHAR_VALUE,
3034                                 (Character) this.mValue);
3035                         break;
3036                     case STRING:
3037                         out.write(RemoteViewsProto.ReflectionAction.STRING_VALUE,
3038                                 (String) this.mValue);
3039                         break;
3040                     case CHAR_SEQUENCE:
3041                         long csToken = out.start(
3042                                 RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE);
3043                         RemoteViewsSerializers.writeCharSequenceToProto(out,
3044                                 (CharSequence) this.mValue);
3045                         out.end(csToken);
3046                         break;
3047                     case URI:
3048                         out.write(RemoteViewsProto.ReflectionAction.URI_VALUE,
3049                                 ((Uri) this.mValue).toString());
3050                         break;
3051                     case BITMAP:
3052                         final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
3053                         ((Bitmap) this.mValue).compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100,
3054                                 bytes);
3055                         out.write(RemoteViewsProto.ReflectionAction.BITMAP_VALUE,
3056                                 bytes.toByteArray());
3057                         break;
3058                     case BLEND_MODE:
3059                         out.write(RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE,
3060                                 BlendMode.toValue((BlendMode) this.mValue));
3061                         break;
3062                     case COLOR_STATE_LIST:
3063                         writeColorStateListToProto(out, (ColorStateList) this.mValue,
3064                                 RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE);
3065                         break;
3066                     case ICON:
3067                         writeIconToProto(out, appResources, (Icon) this.mValue,
3068                                 RemoteViewsProto.ReflectionAction.ICON_VALUE);
3069                         break;
3070                     case BUNDLE:
3071                     case INTENT:
3072                     default:
3073                         break;
3074                 }
3075             }
3076             out.end(token);
3077         }
3078 
3079         public static PendingResources<Action> createFromProto(ProtoInputStream in)
3080                 throws Exception {
3081             final LongSparseArray<Object> values = new LongSparseArray<>();
3082 
3083             final long token = in.start(RemoteViewsProto.Action.REFLECTION_ACTION);
3084             while (in.nextField() != NO_MORE_FIELDS) {
3085                 switch (in.getFieldNumber()) {
3086                     case (int) RemoteViewsProto.ReflectionAction.VIEW_ID:
3087                         values.put(RemoteViewsProto.ReflectionAction.VIEW_ID,
3088                                 in.readString(RemoteViewsProto.ReflectionAction.VIEW_ID));
3089                         break;
3090                     case (int) RemoteViewsProto.ReflectionAction.METHOD_NAME:
3091                         values.put(RemoteViewsProto.ReflectionAction.METHOD_NAME,
3092                                 in.readString(RemoteViewsProto.ReflectionAction.METHOD_NAME));
3093                         break;
3094                     case (int) RemoteViewsProto.ReflectionAction.PARAMETER_TYPE:
3095                         values.put(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE,
3096                                 in.readInt(RemoteViewsProto.ReflectionAction.PARAMETER_TYPE));
3097                         break;
3098                     case (int) RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE:
3099                         values.put(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE,
3100                                 in.readBoolean(RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE));
3101                         break;
3102                     case (int) RemoteViewsProto.ReflectionAction.BYTE_VALUE:
3103                         values.put(RemoteViewsProto.ReflectionAction.BYTE_VALUE,
3104                                 in.readBytes(RemoteViewsProto.ReflectionAction.BYTE_VALUE));
3105                         break;
3106                     case (int) RemoteViewsProto.ReflectionAction.SHORT_VALUE:
3107                         values.put(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
3108                                 (short) in.readInt(RemoteViewsProto.ReflectionAction.SHORT_VALUE));
3109                         break;
3110                     case (int) RemoteViewsProto.ReflectionAction.INT_VALUE:
3111                         values.put(RemoteViewsProto.ReflectionAction.INT_VALUE,
3112                                 in.readInt(RemoteViewsProto.ReflectionAction.INT_VALUE));
3113                         break;
3114                     case (int) RemoteViewsProto.ReflectionAction.LONG_VALUE:
3115                         values.put(RemoteViewsProto.ReflectionAction.LONG_VALUE,
3116                                 in.readLong(RemoteViewsProto.ReflectionAction.LONG_VALUE));
3117                         break;
3118                     case (int) RemoteViewsProto.ReflectionAction.FLOAT_VALUE:
3119                         values.put(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
3120                                 in.readFloat(RemoteViewsProto.ReflectionAction.FLOAT_VALUE));
3121                         break;
3122                     case (int) RemoteViewsProto.ReflectionAction.DOUBLE_VALUE:
3123                         values.put(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
3124                                 in.readDouble(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE));
3125                         break;
3126                     case (int) RemoteViewsProto.ReflectionAction.CHAR_VALUE:
3127                         values.put(RemoteViewsProto.ReflectionAction.CHAR_VALUE,
3128                                 (char) in.readInt(RemoteViewsProto.ReflectionAction.CHAR_VALUE));
3129                         break;
3130                     case (int) RemoteViewsProto.ReflectionAction.STRING_VALUE:
3131                         values.put(RemoteViewsProto.ReflectionAction.STRING_VALUE,
3132                                 in.readString(RemoteViewsProto.ReflectionAction.STRING_VALUE));
3133                         break;
3134                     case (int) RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE:
3135                         values.put(RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE,
3136                                 createCharSequenceFromProto(in,
3137                                         RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE));
3138                         break;
3139                     case (int) RemoteViewsProto.ReflectionAction.URI_VALUE:
3140                         values.put(RemoteViewsProto.ReflectionAction.URI_VALUE,
3141                                 in.readString(RemoteViewsProto.ReflectionAction.URI_VALUE));
3142                         break;
3143                     case (int) RemoteViewsProto.ReflectionAction.BITMAP_VALUE:
3144                         byte[] bitmapData = in.readBytes(
3145                                 RemoteViewsProto.ReflectionAction.BITMAP_VALUE);
3146                         values.put(RemoteViewsProto.ReflectionAction.BITMAP_VALUE,
3147                                 BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length));
3148                         break;
3149                     case (int) RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE:
3150                         values.put(RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE,
3151                                 createColorStateListFromProto(in,
3152                                         RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE));
3153                         break;
3154                     case (int) RemoteViewsProto.ReflectionAction.ICON_VALUE:
3155                         values.put(RemoteViewsProto.ReflectionAction.ICON_VALUE,
3156                                 createIconFromProto(in,
3157                                         RemoteViewsProto.ReflectionAction.ICON_VALUE));
3158                         break;
3159                     case (int) RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE:
3160                         values.put(RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE,
3161                                 BlendMode.fromValue(in.readInt(
3162                                         RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE)));
3163                         break;
3164                     default:
3165                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
3166                                 + ProtoUtils.currentFieldToString(in));
3167                 }
3168             }
3169             in.end(token);
3170 
3171             checkContainsKeys(values, new long[]{RemoteViewsProto.ReflectionAction.VIEW_ID,
3172                     RemoteViewsProto.ReflectionAction.METHOD_NAME,
3173                     RemoteViewsProto.ReflectionAction.PARAMETER_TYPE});
3174 
3175             return (context, resources, rootData, depth) -> {
3176                 int viewId = getAsIdentifier(resources, values,
3177                         RemoteViewsProto.ReflectionAction.VIEW_ID);
3178                 Object value = null;
3179                 int parameterType = (int) values.get(
3180                         RemoteViewsProto.ReflectionAction.PARAMETER_TYPE);
3181                 switch (parameterType) {
3182                     case BOOLEAN:
3183                         value = (boolean) values.get(
3184                                 RemoteViewsProto.ReflectionAction.BOOLEAN_VALUE, false);
3185                         break;
3186                     case BYTE:
3187                         byte[] bytes = (byte[]) values.get(
3188                                 RemoteViewsProto.ReflectionAction.BYTE_VALUE);
3189                         if (bytes != null && bytes.length > 0) {
3190                             value = bytes[0];
3191                         }
3192                         break;
3193                     case SHORT:
3194                         value = (short) values.get(RemoteViewsProto.ReflectionAction.SHORT_VALUE,
3195                                 0);
3196                         break;
3197                     case INT:
3198                         value = (int) values.get(RemoteViewsProto.ReflectionAction.INT_VALUE, 0);
3199                         break;
3200                     case LONG:
3201                         value = (long) values.get(RemoteViewsProto.ReflectionAction.LONG_VALUE, 0);
3202                         break;
3203                     case FLOAT:
3204                         value = (float) values.get(RemoteViewsProto.ReflectionAction.FLOAT_VALUE,
3205                                 0);
3206                         break;
3207                     case DOUBLE:
3208                         value = (double) values.get(RemoteViewsProto.ReflectionAction.DOUBLE_VALUE,
3209                                 0);
3210                         break;
3211                     case CHAR:
3212                         value = (char) values.get(RemoteViewsProto.ReflectionAction.CHAR_VALUE, 0);
3213                         break;
3214                     case STRING:
3215                         value = (String) values.get(RemoteViewsProto.ReflectionAction.STRING_VALUE);
3216                         break;
3217                     case CHAR_SEQUENCE:
3218                         value = (CharSequence) values.get(
3219                                 RemoteViewsProto.ReflectionAction.CHAR_SEQUENCE_VALUE);
3220                         break;
3221                     case URI:
3222                         value = Uri.parse(
3223                                 (String) values.get(RemoteViewsProto.ReflectionAction.URI_VALUE));
3224                         break;
3225                     case BITMAP:
3226                         value = (Bitmap) values.get(RemoteViewsProto.ReflectionAction.BITMAP_VALUE);
3227                         break;
3228                     case BLEND_MODE:
3229                         value = (BlendMode) values.get(
3230                                 RemoteViewsProto.ReflectionAction.BLEND_MODE_VALUE);
3231                         break;
3232                     case COLOR_STATE_LIST:
3233                         value = (ColorStateList) values.get(
3234                                 RemoteViewsProto.ReflectionAction.COLOR_STATE_LIST_VALUE);
3235                         break;
3236                     case ICON:
3237                         value = ((PendingResources<Icon>) values.get(
3238                                 RemoteViewsProto.ReflectionAction.ICON_VALUE)).create(context,
3239                                 resources, rootData, depth);
3240                         break;
3241                     case BUNDLE:
3242                     case INTENT:
3243                     default:
3244                         // omit the action for unsupported parameter types
3245                         return null;
3246                 }
3247                 return new ReflectionAction(viewId,
3248                         (String) values.get(RemoteViewsProto.ReflectionAction.METHOD_NAME),
3249                         parameterType, value);
3250             };
3251         }
3252     }
3253 
3254     private static final class ResourceReflectionAction extends BaseReflectionAction {
3255         static final int DIMEN_RESOURCE = 1;
3256         static final int COLOR_RESOURCE = 2;
3257         static final int STRING_RESOURCE = 3;
3258 
3259         private final int mResourceType;
3260         private final int mResId;
3261 
3262         ResourceReflectionAction(@IdRes int viewId, String methodName, int parameterType,
3263                 int resourceType, int resId) {
3264             super(viewId, methodName, parameterType);
3265             this.mResourceType = resourceType;
3266             this.mResId = resId;
3267         }
3268 
3269         ResourceReflectionAction(Parcel in) {
3270             super(in);
3271             this.mResourceType = in.readInt();
3272             this.mResId = in.readInt();
3273         }
3274 
3275         @Override
3276         public void writeToParcel(Parcel dest, int flags) {
3277             super.writeToParcel(dest, flags);
3278             dest.writeInt(this.mResourceType);
3279             dest.writeInt(this.mResId);
3280         }
3281 
3282         @Nullable
3283         @Override
3284         protected Object getParameterValue(@Nullable View view) throws ActionException {
3285             if (view == null) return null;
3286 
3287             Resources resources = view.getContext().getResources();
3288             try {
3289                 switch (this.mResourceType) {
3290                     case DIMEN_RESOURCE:
3291                         switch (this.mType) {
3292                             case BaseReflectionAction.INT:
3293                                 return mResId == 0 ? 0 : resources.getDimensionPixelSize(mResId);
3294                             case BaseReflectionAction.FLOAT:
3295                                 return mResId == 0 ? 0f : resources.getDimension(mResId);
3296                             default:
3297                                 throw new ActionException(
3298                                         "dimen resources must be used as INT or FLOAT, "
3299                                                 + "not " + this.mType);
3300                         }
3301                     case COLOR_RESOURCE:
3302                         switch (this.mType) {
3303                             case BaseReflectionAction.INT:
3304                                 return mResId == 0 ? 0 : view.getContext().getColor(mResId);
3305                             case BaseReflectionAction.COLOR_STATE_LIST:
3306                                 return mResId == 0
3307                                         ? null : view.getContext().getColorStateList(mResId);
3308                             default:
3309                                 throw new ActionException(
3310                                         "color resources must be used as INT or COLOR_STATE_LIST,"
3311                                                 + " not " + this.mType);
3312                         }
3313                     case STRING_RESOURCE:
3314                         switch (this.mType) {
3315                             case BaseReflectionAction.CHAR_SEQUENCE:
3316                                 return mResId == 0 ? null : resources.getText(mResId);
3317                             case BaseReflectionAction.STRING:
3318                                 return mResId == 0 ? null : resources.getString(mResId);
3319                             default:
3320                                 throw new ActionException(
3321                                         "string resources must be used as STRING or CHAR_SEQUENCE,"
3322                                                 + " not " + this.mType);
3323                         }
3324                     default:
3325                         throw new ActionException("unknown resource type: " + this.mResourceType);
3326                 }
3327             } catch (ActionException ex) {
3328                 throw ex;
3329             } catch (Throwable t) {
3330                 throw new ActionException(t);
3331             }
3332         }
3333 
3334         @Override
3335         public int getActionTag() {
3336             return RESOURCE_REFLECTION_ACTION_TAG;
3337         }
3338 
3339         @Override
3340         public boolean canWriteToProto() {
3341             return true;
3342         }
3343 
3344         @Override
3345         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
3346             final long token = out.start(RemoteViewsProto.Action.RESOURCE_REFLECTION_ACTION);
3347             out.write(RemoteViewsProto.ResourceReflectionAction.VIEW_ID,
3348                     appResources.getResourceName(mViewId));
3349             out.write(RemoteViewsProto.ResourceReflectionAction.METHOD_NAME, mMethodName);
3350             out.write(RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE, mType);
3351             out.write(RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE, mResourceType);
3352             if (mResId != 0) {
3353                 out.write(RemoteViewsProto.ResourceReflectionAction.RES_ID,
3354                         appResources.getResourceName(mResId));
3355             }
3356             out.end(token);
3357         }
3358 
3359         public static PendingResources<Action> createFromProto(ProtoInputStream in)
3360                 throws Exception {
3361             final LongSparseArray<Object> values = new LongSparseArray<>();
3362 
3363             final long token = in.start(RemoteViewsProto.Action.RESOURCE_REFLECTION_ACTION);
3364             while (in.nextField() != NO_MORE_FIELDS) {
3365                 switch (in.getFieldNumber()) {
3366                     case (int) RemoteViewsProto.ResourceReflectionAction.VIEW_ID:
3367                         values.put(RemoteViewsProto.ResourceReflectionAction.VIEW_ID,
3368                                 in.readString(RemoteViewsProto.ResourceReflectionAction.VIEW_ID));
3369                         break;
3370                     case (int) RemoteViewsProto.ResourceReflectionAction.METHOD_NAME:
3371                         values.put(RemoteViewsProto.ResourceReflectionAction.METHOD_NAME,
3372                                 in.readString(
3373                                         RemoteViewsProto.ResourceReflectionAction.METHOD_NAME));
3374                         break;
3375                     case (int) RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE:
3376                         values.put(RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE,
3377                                 in.readInt(
3378                                         RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE));
3379                         break;
3380                     case (int) RemoteViewsProto.ResourceReflectionAction.RES_ID:
3381                         values.put(RemoteViewsProto.ResourceReflectionAction.RES_ID,
3382                                 in.readString(RemoteViewsProto.ResourceReflectionAction.RES_ID));
3383                         break;
3384                     case (int) RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE:
3385                         values.put(RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE,
3386                                 in.readInt(
3387                                         RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE));
3388                         break;
3389                     default:
3390                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
3391                                 + ProtoUtils.currentFieldToString(in));
3392                 }
3393             }
3394             in.end(token);
3395 
3396             checkContainsKeys(values, new long[]{RemoteViewsProto.ResourceReflectionAction.VIEW_ID,
3397                     RemoteViewsProto.ResourceReflectionAction.METHOD_NAME,
3398                     RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE});
3399 
3400             return (context, resources, rootData, depth) -> {
3401                 int viewId = getAsIdentifier(resources, values,
3402                         RemoteViewsProto.ResourceReflectionAction.VIEW_ID);
3403 
3404                 int resId = (values.indexOfKey(RemoteViewsProto.ResourceReflectionAction.RES_ID)
3405                         >= 0) ? getAsIdentifier(resources, values,
3406                         RemoteViewsProto.ResourceReflectionAction.RES_ID) : 0;
3407                 return new ResourceReflectionAction(viewId,
3408                         (String) values.get(RemoteViewsProto.ResourceReflectionAction.METHOD_NAME),
3409                         (int) values.get(RemoteViewsProto.ResourceReflectionAction.PARAMETER_TYPE),
3410                         (int) values.get(RemoteViewsProto.ResourceReflectionAction.RESOURCE_TYPE,
3411                                 0), resId);
3412             };
3413         }
3414     }
3415 
3416     private static final class AttributeReflectionAction extends BaseReflectionAction {
3417         static final int DIMEN_RESOURCE = 1;
3418         static final int COLOR_RESOURCE = 2;
3419         static final int STRING_RESOURCE = 3;
3420 
3421         private final int mResourceType;
3422         private final int mAttrId;
3423 
3424         AttributeReflectionAction(@IdRes int viewId, String methodName, int parameterType,
3425                 int resourceType, int attrId) {
3426             super(viewId, methodName, parameterType);
3427             this.mResourceType = resourceType;
3428             this.mAttrId = attrId;
3429         }
3430 
3431         AttributeReflectionAction(Parcel in) {
3432             super(in);
3433             this.mResourceType = in.readInt();
3434             this.mAttrId = in.readInt();
3435         }
3436 
3437         @Override
3438         public void writeToParcel(Parcel dest, int flags) {
3439             super.writeToParcel(dest, flags);
3440             dest.writeInt(this.mResourceType);
3441             dest.writeInt(this.mAttrId);
3442         }
3443 
3444         @Override
3445         protected Object getParameterValue(View view) throws ActionException {
3446             TypedArray typedArray = view.getContext().obtainStyledAttributes(new int[]{mAttrId});
3447             try {
3448                 // When mAttrId == 0, we will depend on the default values below
3449                 if (mAttrId != 0 && typedArray.getType(0) == TypedValue.TYPE_NULL) {
3450                     throw new ActionException("Attribute 0x" + Integer.toHexString(this.mAttrId)
3451                             + " is not defined");
3452                 }
3453                 switch (this.mResourceType) {
3454                     case DIMEN_RESOURCE:
3455                         switch (this.mType) {
3456                             case BaseReflectionAction.INT:
3457                                 return typedArray.getDimensionPixelSize(0, 0);
3458                             case BaseReflectionAction.FLOAT:
3459                                 return typedArray.getDimension(0, 0);
3460                             default:
3461                                 throw new ActionException(
3462                                         "dimen attribute 0x" + Integer.toHexString(this.mAttrId)
3463                                                 + " must be used as INT or FLOAT,"
3464                                                 + " not " + this.mType);
3465                         }
3466                     case COLOR_RESOURCE:
3467                         switch (this.mType) {
3468                             case BaseReflectionAction.INT:
3469                                 return typedArray.getColor(0, 0);
3470                             case BaseReflectionAction.COLOR_STATE_LIST:
3471                                 return typedArray.getColorStateList(0);
3472                             default:
3473                                 throw new ActionException(
3474                                         "color attribute 0x" + Integer.toHexString(this.mAttrId)
3475                                                 + " must be used as INT or COLOR_STATE_LIST,"
3476                                                 + " not " + this.mType);
3477                         }
3478                     case STRING_RESOURCE:
3479                         switch (this.mType) {
3480                             case BaseReflectionAction.CHAR_SEQUENCE:
3481                                 return typedArray.getText(0);
3482                             case BaseReflectionAction.STRING:
3483                                 return typedArray.getString(0);
3484                             default:
3485                                 throw new ActionException(
3486                                         "string attribute 0x" + Integer.toHexString(this.mAttrId)
3487                                                 + " must be used as STRING or CHAR_SEQUENCE,"
3488                                                 + " not " + this.mType);
3489                         }
3490                     default:
3491                         // Note: This can only be an implementation error.
3492                         throw new ActionException(
3493                                 "Unknown resource type: " + this.mResourceType);
3494                 }
3495             } catch (ActionException ex) {
3496                 throw ex;
3497             } catch (Throwable t) {
3498                 throw new ActionException(t);
3499             } finally {
3500                 typedArray.recycle();
3501             }
3502         }
3503 
3504         @Override
3505         public int getActionTag() {
3506             return ATTRIBUTE_REFLECTION_ACTION_TAG;
3507         }
3508 
3509         @Override
3510         public boolean canWriteToProto() {
3511             return true;
3512         }
3513 
3514         @Override
3515         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
3516             final long token = out.start(RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION);
3517             out.write(RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
3518                     appResources.getResourceName(mViewId));
3519             out.write(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME, mMethodName);
3520             out.write(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE, mType);
3521             out.write(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE, mResourceType);
3522             if (mAttrId != 0) {
3523                 out.write(RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID,
3524                         appResources.getResourceName(mAttrId));
3525             }
3526             out.end(token);
3527         }
3528 
3529         public static PendingResources<Action> createFromProto(ProtoInputStream in)
3530                 throws Exception {
3531             final LongSparseArray<Object> values = new LongSparseArray<>();
3532 
3533             final long token = in.start(RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION);
3534             while (in.nextField() != NO_MORE_FIELDS) {
3535                 switch (in.getFieldNumber()) {
3536                     case (int) RemoteViewsProto.AttributeReflectionAction.VIEW_ID: {
3537                         values.put(RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
3538                                 in.readString(RemoteViewsProto.AttributeReflectionAction.VIEW_ID));
3539                         break;
3540                     }
3541                     case (int) RemoteViewsProto.AttributeReflectionAction.METHOD_NAME:
3542                         values.put(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME,
3543                                 in.readString(
3544                                         RemoteViewsProto.AttributeReflectionAction.METHOD_NAME));
3545                         break;
3546                     case (int) RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID:
3547                         values.put(RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID,
3548                                 in.readString(
3549                                         RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID));
3550                         break;
3551                     case (int) RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE:
3552                         values.put(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE,
3553                                 in.readInt(
3554                                         RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE));
3555                         break;
3556                     case (int) RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE:
3557                         values.put(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE,
3558                                 in.readInt(
3559                                         RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE));
3560                         break;
3561                     default:
3562                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
3563                                 + ProtoUtils.currentFieldToString(in));
3564                 }
3565             }
3566             in.end(token);
3567 
3568             checkContainsKeys(values, new long[]{RemoteViewsProto.AttributeReflectionAction.VIEW_ID,
3569                     RemoteViewsProto.AttributeReflectionAction.METHOD_NAME,
3570                     RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE,
3571                     RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE});
3572 
3573             return (context, resources, rootData, depth) -> {
3574                 int viewId = getAsIdentifier(resources, values,
3575                         RemoteViewsProto.AttributeReflectionAction.VIEW_ID);
3576                 int attributeId = (values.indexOfKey(
3577                         RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID) >= 0)
3578                         ? getAsIdentifier(resources, values,
3579                         RemoteViewsProto.AttributeReflectionAction.ATTRIBUTE_ID) : 0;
3580                 return new AttributeReflectionAction(viewId,
3581                         (String) values.get(RemoteViewsProto.AttributeReflectionAction.METHOD_NAME),
3582                         (int) values.get(RemoteViewsProto.AttributeReflectionAction.PARAMETER_TYPE),
3583                         (int) values.get(RemoteViewsProto.AttributeReflectionAction.RESOURCE_TYPE),
3584                         attributeId);
3585             };
3586         }
3587     }
3588 
3589     private static final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
3590         private final float mValue;
3591         @ComplexDimensionUnit
3592         private final int mUnit;
3593 
3594         ComplexUnitDimensionReflectionAction(int viewId, String methodName, int parameterType,
3595                 float value, @ComplexDimensionUnit int unit) {
3596             super(viewId, methodName, parameterType);
3597             this.mValue = value;
3598             this.mUnit = unit;
3599         }
3600 
3601         ComplexUnitDimensionReflectionAction(Parcel in) {
3602             super(in);
3603             this.mValue = in.readFloat();
3604             this.mUnit = in.readInt();
3605         }
3606 
3607         @Override
3608         public void writeToParcel(Parcel dest, int flags) {
3609             super.writeToParcel(dest, flags);
3610             dest.writeFloat(this.mValue);
3611             dest.writeInt(this.mUnit);
3612         }
3613 
3614         @Nullable
3615         @Override
3616         protected Object getParameterValue(@Nullable View view) throws ActionException {
3617             if (view == null) return null;
3618 
3619             DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics();
3620             try {
3621                 int data = TypedValue.createComplexDimension(this.mValue, this.mUnit);
3622                 switch (this.mType) {
3623                     case ReflectionAction.INT:
3624                         return TypedValue.complexToDimensionPixelSize(data, dm);
3625                     case ReflectionAction.FLOAT:
3626                         return TypedValue.complexToDimension(data, dm);
3627                     default:
3628                         throw new ActionException(
3629                                 "parameter type must be INT or FLOAT, not " + this.mType);
3630                 }
3631             } catch (ActionException ex) {
3632                 throw ex;
3633             } catch (Throwable t) {
3634                 throw new ActionException(t);
3635             }
3636         }
3637 
3638         @Override
3639         public int getActionTag() {
3640             return COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG;
3641         }
3642 
3643         @Override
3644         public boolean canWriteToProto() {
3645             return true;
3646         }
3647 
3648         @Override
3649         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
3650             final long token = out.start(
3651                     RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION);
3652             out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
3653                     appResources.getResourceName(mViewId));
3654             out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
3655                     mMethodName);
3656             out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE, mType);
3657             out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
3658                     mValue);
3659             out.write(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT, mUnit);
3660             out.end(token);
3661         }
3662 
3663         public static PendingResources<Action> createFromProto(ProtoInputStream in)
3664                 throws Exception {
3665             final LongSparseArray<Object> values = new LongSparseArray<>();
3666 
3667             final long token = in.start(
3668                     RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION);
3669             while (in.nextField() != NO_MORE_FIELDS) {
3670                 switch (in.getFieldNumber()) {
3671                     case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID:
3672                         values.put(RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
3673                                 in.readString(
3674                                         RemoteViewsProto
3675                                                 .ComplexUnitDimensionReflectionAction.VIEW_ID));
3676                         break;
3677                     case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME:
3678                         values.put(
3679                                 RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
3680                                 in.readString(
3681                                         RemoteViewsProto
3682                                                 .ComplexUnitDimensionReflectionAction.METHOD_NAME));
3683                         break;
3684                     case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE:
3685                         values.put(
3686                                 RemoteViewsProto
3687                                         .ComplexUnitDimensionReflectionAction.PARAMETER_TYPE,
3688                                 in.readInt(
3689                                         RemoteViewsProto
3690                                                 .ComplexUnitDimensionReflectionAction
3691                                                 .PARAMETER_TYPE));
3692                         break;
3693                     case (int) RemoteViewsProto
3694                             .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE:
3695                         values.put(
3696                                 RemoteViewsProto
3697                                         .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
3698                                 in.readFloat(
3699                                         RemoteViewsProto
3700                                                 .ComplexUnitDimensionReflectionAction
3701                                                 .DIMENSION_VALUE));
3702                         break;
3703                     case (int) RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT:
3704                         values.put(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT,
3705                                 in.readInt(
3706                                         RemoteViewsProto
3707                                                 .ComplexUnitDimensionReflectionAction.UNIT));
3708                         break;
3709                     default:
3710                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
3711                                 + ProtoUtils.currentFieldToString(in));
3712                 }
3713             }
3714             in.end(token);
3715 
3716             checkContainsKeys(values,
3717                     new long[]{RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID,
3718                             RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME,
3719                             RemoteViewsProto.ComplexUnitDimensionReflectionAction.PARAMETER_TYPE});
3720 
3721             return (context, resources, rootData, depth) -> {
3722                 int viewId = getAsIdentifier(resources, values,
3723                         RemoteViewsProto.ComplexUnitDimensionReflectionAction.VIEW_ID);
3724                 return new ComplexUnitDimensionReflectionAction(viewId, (String) values.get(
3725                         RemoteViewsProto.ComplexUnitDimensionReflectionAction.METHOD_NAME),
3726                         (int) values.get(
3727                                 RemoteViewsProto
3728                                         .ComplexUnitDimensionReflectionAction.PARAMETER_TYPE),
3729                         (float) values.get(
3730                                 RemoteViewsProto
3731                                         .ComplexUnitDimensionReflectionAction.DIMENSION_VALUE,
3732                                 0),
3733                         (int) values.get(RemoteViewsProto.ComplexUnitDimensionReflectionAction.UNIT,
3734                                 0));
3735             };
3736         }
3737     }
3738 
3739     private static final class NightModeReflectionAction extends BaseReflectionAction {
3740         private final Object mLightValue;
3741         private final Object mDarkValue;
3742 
3743         NightModeReflectionAction(
3744                 @IdRes int viewId,
3745                 String methodName,
3746                 int type,
3747                 Object lightValue,
3748                 Object darkValue) {
3749             super(viewId, methodName, type);
3750             mLightValue = lightValue;
3751             mDarkValue = darkValue;
3752         }
3753 
3754         NightModeReflectionAction(Parcel in) {
3755             super(in);
3756             switch (this.mType) {
3757                 case ICON:
3758                     mLightValue = in.readTypedObject(Icon.CREATOR);
3759                     mDarkValue = in.readTypedObject(Icon.CREATOR);
3760                     break;
3761                 case COLOR_STATE_LIST:
3762                     mLightValue = in.readTypedObject(ColorStateList.CREATOR);
3763                     mDarkValue = in.readTypedObject(ColorStateList.CREATOR);
3764                     break;
3765                 case INT:
3766                     mLightValue = in.readInt();
3767                     mDarkValue = in.readInt();
3768                     break;
3769                 default:
3770                     throw new ActionException("Unexpected night mode action type: " + this.mType);
3771             }
3772         }
3773 
3774         @Override
3775         public void writeToParcel(Parcel out, int flags) {
3776             super.writeToParcel(out, flags);
3777             switch (this.mType) {
3778                 case ICON:
3779                 case COLOR_STATE_LIST:
3780                     out.writeTypedObject((Parcelable) mLightValue, flags);
3781                     out.writeTypedObject((Parcelable) mDarkValue, flags);
3782                     break;
3783                 case INT:
3784                     out.writeInt((int) mLightValue);
3785                     out.writeInt((int) mDarkValue);
3786                     break;
3787             }
3788         }
3789 
3790         @Nullable
3791         @Override
3792         protected Object getParameterValue(@Nullable View view) throws ActionException {
3793             if (view == null) return null;
3794 
3795             Configuration configuration = view.getResources().getConfiguration();
3796             return configuration.isNightModeActive() ? mDarkValue : mLightValue;
3797         }
3798 
3799         @Override
3800         public int getActionTag() {
3801             return NIGHT_MODE_REFLECTION_ACTION_TAG;
3802         }
3803 
3804         @Override
3805         public void visitUris(@NonNull Consumer<Uri> visitor) {
3806             if (this.mType == ICON) {
3807                 visitIconUri((Icon) mDarkValue, visitor);
3808                 visitIconUri((Icon) mLightValue, visitor);
3809             }
3810         }
3811 
3812         @Override
3813         public boolean canWriteToProto() {
3814             return true;
3815         }
3816 
3817         @Override
3818         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
3819             final long token = out.start(RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION);
3820             out.write(RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
3821                     appResources.getResourceName(mViewId));
3822             out.write(RemoteViewsProto.NightModeReflectionAction.METHOD_NAME, mMethodName);
3823             out.write(RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE, mType);
3824             switch (this.mType) {
3825                 case ICON:
3826                     writeIconToProto(out, appResources, (Icon) mLightValue,
3827                             RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON);
3828                     writeIconToProto(out, appResources, (Icon) mDarkValue,
3829                             RemoteViewsProto.NightModeReflectionAction.DARK_ICON);
3830                     break;
3831                 case COLOR_STATE_LIST:
3832                     writeColorStateListToProto(out, (ColorStateList) mLightValue,
3833                             RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST);
3834                     writeColorStateListToProto(out, (ColorStateList) mDarkValue,
3835                             RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST);
3836                     break;
3837                 case INT:
3838                     out.write(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT,
3839                             (int) mLightValue);
3840                     out.write(RemoteViewsProto.NightModeReflectionAction.DARK_INT,
3841                             (int) mDarkValue);
3842                     break;
3843             }
3844             out.end(token);
3845         }
3846 
3847         public static PendingResources<Action> createFromProto(ProtoInputStream in)
3848                 throws Exception {
3849             final LongSparseArray<Object> values = new LongSparseArray<>();
3850 
3851             final long token = in.start(RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION);
3852             while (in.nextField() != NO_MORE_FIELDS) {
3853                 switch (in.getFieldNumber()) {
3854                     case (int) RemoteViewsProto.NightModeReflectionAction.VIEW_ID:
3855                         values.put(RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
3856                                 in.readString(RemoteViewsProto.NightModeReflectionAction.VIEW_ID));
3857                         break;
3858                     case (int) RemoteViewsProto.NightModeReflectionAction.METHOD_NAME:
3859                         values.put(RemoteViewsProto.NightModeReflectionAction.METHOD_NAME,
3860                                 in.readString(
3861                                         RemoteViewsProto.NightModeReflectionAction.METHOD_NAME));
3862                         break;
3863                     case (int) RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE:
3864                         values.put(RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE,
3865                                 in.readInt(
3866                                         RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE));
3867                         break;
3868                     case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON:
3869                         values.put(RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON,
3870                                 createIconFromProto(in,
3871                                         RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON));
3872                         break;
3873                     case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST:
3874                         values.put(
3875                                 RemoteViewsProto.NightModeReflectionAction.LIGHT_COLOR_STATE_LIST,
3876                                 createColorStateListFromProto(in,
3877                                         RemoteViewsProto
3878                                                 .NightModeReflectionAction.LIGHT_COLOR_STATE_LIST));
3879                         break;
3880                     case (int) RemoteViewsProto.NightModeReflectionAction.LIGHT_INT:
3881                         values.put(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT,
3882                                 in.readInt(RemoteViewsProto.NightModeReflectionAction.LIGHT_INT));
3883                         break;
3884                     case (int) RemoteViewsProto.NightModeReflectionAction.DARK_ICON:
3885                         values.put(RemoteViewsProto.NightModeReflectionAction.DARK_ICON,
3886                                 createIconFromProto(in,
3887                                         RemoteViewsProto.NightModeReflectionAction.DARK_ICON));
3888                         break;
3889                     case (int) RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST:
3890                         values.put(RemoteViewsProto.NightModeReflectionAction.DARK_COLOR_STATE_LIST,
3891                                 createColorStateListFromProto(in,
3892                                         RemoteViewsProto
3893                                                 .NightModeReflectionAction.DARK_COLOR_STATE_LIST));
3894                         break;
3895                     case (int) RemoteViewsProto.NightModeReflectionAction.DARK_INT:
3896                         values.put(RemoteViewsProto.NightModeReflectionAction.DARK_INT,
3897                                 in.readInt(RemoteViewsProto.NightModeReflectionAction.DARK_INT));
3898                         break;
3899                     default:
3900                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
3901                                 + ProtoUtils.currentFieldToString(in));
3902                 }
3903             }
3904             in.end(token);
3905 
3906             checkContainsKeys(values, new long[]{RemoteViewsProto.NightModeReflectionAction.VIEW_ID,
3907                     RemoteViewsProto.NightModeReflectionAction.METHOD_NAME,
3908                     RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE});
3909 
3910             return (context, resources, rootData, depth) -> {
3911                 int viewId = getAsIdentifier(resources, values,
3912                         RemoteViewsProto.NightModeReflectionAction.VIEW_ID);
3913                 String methodName = (String) values.get(
3914                         RemoteViewsProto.NightModeReflectionAction.METHOD_NAME);
3915                 int parameterType = (int) values.get(
3916                         RemoteViewsProto.NightModeReflectionAction.PARAMETER_TYPE);
3917                 switch (parameterType) {
3918                     case ICON:
3919                         PendingResources<Icon> pendingLightIcon =
3920                                 (PendingResources<Icon>) values.get(
3921                                         RemoteViewsProto.NightModeReflectionAction.LIGHT_ICON);
3922                         PendingResources<Icon> pendingDarkIcon =
3923                                 (PendingResources<Icon>) values.get(
3924                                         RemoteViewsProto.NightModeReflectionAction.DARK_ICON);
3925                         Icon lightIcon = pendingLightIcon != null ? pendingLightIcon.create(context,
3926                                 resources, rootData, depth) : null;
3927                         Icon darkIcon = pendingDarkIcon != null ? pendingDarkIcon.create(context,
3928                                 resources, rootData, depth) : null;
3929                         return new NightModeReflectionAction(viewId, methodName, parameterType,
3930                                 lightIcon, darkIcon);
3931                     case COLOR_STATE_LIST:
3932                         return new NightModeReflectionAction(viewId, methodName, parameterType,
3933                                 (ColorStateList) values.get(
3934                                         RemoteViewsProto
3935                                                 .NightModeReflectionAction.LIGHT_COLOR_STATE_LIST),
3936                                 (ColorStateList) values.get(
3937                                         RemoteViewsProto
3938                                                 .NightModeReflectionAction.DARK_COLOR_STATE_LIST));
3939                     case INT:
3940                         return new NightModeReflectionAction(viewId, methodName, parameterType,
3941                                 (int) values.get(
3942                                         RemoteViewsProto.NightModeReflectionAction.LIGHT_INT, 0),
3943                                 (int) values.get(
3944                                         RemoteViewsProto.NightModeReflectionAction.DARK_INT, 0));
3945                     default:
3946                         throw new RuntimeException("Unknown parameterType: " + parameterType);
3947                 }
3948             };
3949         }
3950     }
3951 
3952     /**
3953      * This is only used for async execution of actions and it not parcelable.
3954      */
3955     private static final class RunnableAction extends RuntimeAction {
3956         private final Runnable mRunnable;
3957 
3958         RunnableAction(Runnable r) {
3959             mRunnable = r;
3960         }
3961 
3962         @Override
3963         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
3964             mRunnable.run();
3965         }
3966     }
3967 
3968     private static boolean hasStableId(View view) {
3969         Object tag = view.getTag(com.android.internal.R.id.remote_views_stable_id);
3970         return tag != null;
3971     }
3972 
3973     private static int getStableId(View view) {
3974         Integer id = (Integer) view.getTag(com.android.internal.R.id.remote_views_stable_id);
3975         return id == null ? ViewGroupActionAdd.NO_ID : id;
3976     }
3977 
3978     private static void setStableId(View view, int stableId) {
3979         view.setTagInternal(com.android.internal.R.id.remote_views_stable_id, stableId);
3980     }
3981 
3982     // Returns the next recyclable child of the view group, or -1 if there are none.
3983     private static int getNextRecyclableChild(ViewGroup vg) {
3984         Integer tag = (Integer) vg.getTag(com.android.internal.R.id.remote_views_next_child);
3985         return tag == null ? -1 : tag;
3986     }
3987 
3988     private static int getViewLayoutId(View v) {
3989         return (Integer) v.getTag(R.id.widget_frame);
3990     }
3991 
3992     private static void setNextRecyclableChild(ViewGroup vg, int nextChild, int numChildren) {
3993         if (nextChild < 0 || nextChild >= numChildren) {
3994             vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, -1);
3995         } else {
3996             vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, nextChild);
3997         }
3998     }
3999 
4000     private void finalizeViewRecycling(ViewGroup root) {
4001         // Remove any recyclable children that were not used. nextChild should either be -1 or point
4002         // to the next recyclable child that hasn't been recycled.
4003         int nextChild = getNextRecyclableChild(root);
4004         if (nextChild >= 0 && nextChild < root.getChildCount()) {
4005             root.removeViews(nextChild, root.getChildCount() - nextChild);
4006         }
4007         // Make sure on the next round, we don't try to recycle if removeAllViews is not called.
4008         setNextRecyclableChild(root, -1, 0);
4009         // Traverse the view tree.
4010         for (int i = 0; i < root.getChildCount(); i++) {
4011             View child = root.getChildAt(i);
4012             if (child instanceof ViewGroup && !child.isRootNamespace()) {
4013                 finalizeViewRecycling((ViewGroup) child);
4014             }
4015         }
4016     }
4017 
4018     /**
4019      * ViewGroup methods that are related to adding Views.
4020      */
4021     private class ViewGroupActionAdd extends Action {
4022         static final int NO_ID = -1;
4023         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
4024         private RemoteViews mNestedViews;
4025         private int mIndex;
4026         private int mStableId;
4027 
4028         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews) {
4029             this(viewId, nestedViews, -1 /* index */, NO_ID /* nestedViewId */);
4030         }
4031 
4032         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index) {
4033             this(viewId, nestedViews, index, NO_ID /* nestedViewId */);
4034         }
4035 
4036         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index, int stableId) {
4037             this.mViewId = viewId;
4038             mNestedViews = nestedViews;
4039             mIndex = index;
4040             mStableId = stableId;
4041             nestedViews.configureAsChild(getHierarchyRootData());
4042         }
4043 
4044         ViewGroupActionAdd(Parcel parcel, ApplicationInfo info, int depth) {
4045             mViewId = parcel.readInt();
4046             mIndex = parcel.readInt();
4047             mStableId = parcel.readInt();
4048             mNestedViews = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
4049             mNestedViews.addFlags(mApplyFlags);
4050         }
4051 
4052         public void writeToParcel(Parcel dest, int flags) {
4053             dest.writeInt(mViewId);
4054             dest.writeInt(mIndex);
4055             dest.writeInt(mStableId);
4056             mNestedViews.writeToParcel(dest, flags);
4057         }
4058 
4059         @Override
4060         public void setHierarchyRootData(HierarchyRootData root) {
4061             mNestedViews.configureAsChild(root);
4062         }
4063 
4064         private int findViewIndexToRecycle(ViewGroup target, RemoteViews newContent) {
4065             for (int nextChild = getNextRecyclableChild(target); nextChild < target.getChildCount();
4066                     nextChild++) {
4067                 View child = target.getChildAt(nextChild);
4068                 if (getStableId(child) == mStableId) {
4069                     return nextChild;
4070                 }
4071             }
4072             return -1;
4073         }
4074 
4075         @Override
4076         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
4077             final Context context = root.getContext();
4078             final ViewGroup target = root.findViewById(mViewId);
4079 
4080             if (target == null) {
4081                 return;
4082             }
4083 
4084             // If removeAllViews was called, this returns the next potential recycled view.
4085             // If there are no more views to recycle (or removeAllViews was not called), this
4086             // will return -1.
4087             final int nextChild = getNextRecyclableChild(target);
4088             RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
4089 
4090             int flagsToPropagate = mApplyFlags & FLAG_MASK_TO_PROPAGATE;
4091             if (flagsToPropagate != 0) rvToApply.addFlags(flagsToPropagate);
4092 
4093             if (nextChild >= 0 && mStableId != NO_ID) {
4094                 // At that point, the views starting at index nextChild are the ones recyclable but
4095                 // not yet recycled. All views added on that round of application are placed before.
4096                 // Find the next view with the same stable id, or -1.
4097                 int recycledViewIndex = findViewIndexToRecycle(target, rvToApply);
4098                 if (recycledViewIndex >= 0) {
4099                     View child = target.getChildAt(recycledViewIndex);
4100                     if (rvToApply.canRecycleView(child)) {
4101                         if (nextChild < recycledViewIndex) {
4102                             target.removeViews(nextChild, recycledViewIndex - nextChild);
4103                         }
4104                         setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
4105                         rvToApply.reapplyNestedViews(context, child, rootParent, params);
4106                         return;
4107                     }
4108                     // If we cannot recycle the views, we still remove all views in between to
4109                     // avoid weird behaviors and insert the new view in place of the old one.
4110                     target.removeViews(nextChild, recycledViewIndex - nextChild + 1);
4111                 }
4112             }
4113             // If we cannot recycle, insert the new view before the next recyclable child.
4114 
4115             // Inflate nested views and add as children
4116             View nestedView = rvToApply.apply(context, target, rootParent, null /* size */, params);
4117             if (mStableId != NO_ID) {
4118                 setStableId(nestedView, mStableId);
4119             }
4120             target.addView(nestedView, mIndex >= 0 ? mIndex : nextChild);
4121             if (nextChild >= 0) {
4122                 // If we are at the end, there is no reason to try to recycle anymore
4123                 setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
4124             }
4125         }
4126 
4127         @Override
4128         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
4129                 ActionApplyParams params) {
4130             // In the async implementation, update the view tree so that subsequent calls to
4131             // findViewById return the current view.
4132             root.createTree();
4133             ViewTree target = root.findViewTreeById(mViewId);
4134             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
4135                 return ACTION_NOOP;
4136             }
4137             final ViewGroup targetVg = (ViewGroup) target.mRoot;
4138 
4139             // Inflate nested views and perform all the async tasks for the child remoteView.
4140             final Context context = root.mRoot.getContext();
4141 
4142             // If removeAllViews was called, this returns the next potential recycled view.
4143             // If there are no more views to recycle (or removeAllViews was not called), this
4144             // will return -1.
4145             final int nextChild = getNextRecyclableChild(targetVg);
4146             if (nextChild >= 0 && mStableId != NO_ID) {
4147                 RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
4148                 final int recycledViewIndex = target.findChildIndex(nextChild,
4149                         view -> getStableId(view) == mStableId);
4150                 if (recycledViewIndex >= 0) {
4151                     // At that point, the views starting at index nextChild are the ones
4152                     // recyclable but not yet recycled. All views added on that round of
4153                     // application are placed before.
4154                     ViewTree recycled = target.mChildren.get(recycledViewIndex);
4155                     // We can only recycle the view if the layout id is the same.
4156                     if (rvToApply.canRecycleView(recycled.mRoot)) {
4157                         if (recycledViewIndex > nextChild) {
4158                             target.removeChildren(nextChild, recycledViewIndex - nextChild);
4159                         }
4160                         setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
4161                         final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
4162                                 context,
4163                                 targetVg, null /* listener */, params, null /* size */,
4164                                 recycled.mRoot);
4165                         final ViewTree tree = reapplyTask.doInBackground();
4166                         if (tree == null) {
4167                             throw new ActionException(reapplyTask.mError);
4168                         }
4169                         return new RuntimeAction() {
4170                             @Override
4171                             public void apply(View root, ViewGroup rootParent,
4172                                     ActionApplyParams params) throws ActionException {
4173                                 reapplyTask.onPostExecute(tree);
4174                                 if (recycledViewIndex > nextChild) {
4175                                     targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
4176                                 }
4177                             }
4178                         };
4179                     }
4180                     // If the layout id is different, still remove the children as if we recycled
4181                     // the view, to insert at the same place.
4182                     target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
4183                     return insertNewView(context, target, params,
4184                             () -> targetVg.removeViews(nextChild,
4185                                     recycledViewIndex - nextChild + 1));
4186 
4187                 }
4188             }
4189             // If we cannot recycle, simply add the view at the same available slot.
4190             return insertNewView(context, target, params, () -> {});
4191         }
4192 
4193         private Action insertNewView(Context context, ViewTree target,
4194                 ActionApplyParams params, Runnable finalizeAction) {
4195             ViewGroup targetVg = (ViewGroup) target.mRoot;
4196             int nextChild = getNextRecyclableChild(targetVg);
4197             final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
4198                     null /* listener */, params, null /* size */,  null /* result */);
4199             final ViewTree tree = task.doInBackground();
4200 
4201             if (tree == null) {
4202                 throw new ActionException(task.mError);
4203             }
4204             if (mStableId != NO_ID) {
4205                 setStableId(task.mResult, mStableId);
4206             }
4207 
4208             // Update the global view tree, so that next call to findViewTreeById
4209             // goes through the subtree as well.
4210             final int insertIndex = mIndex >= 0 ? mIndex : nextChild;
4211             target.addChild(tree, insertIndex);
4212             if (nextChild >= 0) {
4213                 setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
4214             }
4215 
4216             return new RuntimeAction() {
4217                 @Override
4218                 public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
4219                     task.onPostExecute(tree);
4220                     finalizeAction.run();
4221                     targetVg.addView(task.mResult, insertIndex);
4222                 }
4223             };
4224         }
4225 
4226         @Override
4227         public int mergeBehavior() {
4228             return MERGE_APPEND;
4229         }
4230 
4231         @Override
4232         public boolean prefersAsyncApply() {
4233             return mNestedViews.prefersAsyncApply();
4234         }
4235 
4236         @Override
4237         public int getActionTag() {
4238             return VIEW_GROUP_ACTION_ADD_TAG;
4239         }
4240 
4241         @Override
4242         public void visitUris(@NonNull Consumer<Uri> visitor) {
4243             mNestedViews.visitUris(visitor);
4244         }
4245 
4246         @Override
4247         public void visitIcons(@NonNull Consumer<Icon> visitor) {
4248             mNestedViews.visitIcons(visitor);
4249         }
4250 
4251         @Override
4252         public boolean canWriteToProto() {
4253             return true;
4254         }
4255 
4256         @Override
4257         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
4258             if (!Flags.remoteViewsProto()) return;
4259             final long token = out.start(RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION);
4260             out.write(RemoteViewsProto.ViewGroupAddAction.VIEW_ID,
4261                     appResources.getResourceName(mViewId));
4262             out.write(RemoteViewsProto.ViewGroupAddAction.INDEX, mIndex);
4263             out.write(RemoteViewsProto.ViewGroupAddAction.STABLE_ID, mStableId);
4264             long rvToken = out.start(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS);
4265             mNestedViews.writePreviewToProto(context, out);
4266             out.end(rvToken);
4267             out.end(token);
4268         }
4269     }
4270 
4271     private PendingResources<Action> createViewGroupActionAddFromProto(ProtoInputStream in)
4272             throws Exception {
4273         final LongSparseArray<Object> values = new LongSparseArray<>();
4274 
4275         final long token = in.start(RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION);
4276         while (in.nextField() != NO_MORE_FIELDS) {
4277             switch (in.getFieldNumber()) {
4278                 case (int) RemoteViewsProto.ViewGroupAddAction.VIEW_ID:
4279                     values.put(RemoteViewsProto.ViewGroupAddAction.VIEW_ID,
4280                             in.readString(RemoteViewsProto.ViewGroupAddAction.VIEW_ID));
4281                     break;
4282                 case (int) RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS:
4283                     final long nvToken = in.start(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS);
4284                     values.put(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS,
4285                             createFromProto(in));
4286                     in.end(nvToken);
4287                     break;
4288                 case (int) RemoteViewsProto.ViewGroupAddAction.INDEX:
4289                     values.put(RemoteViewsProto.ViewGroupAddAction.INDEX,
4290                             in.readInt(RemoteViewsProto.ViewGroupAddAction.INDEX));
4291                     break;
4292                 case (int) RemoteViewsProto.ViewGroupAddAction.STABLE_ID:
4293                     values.put(RemoteViewsProto.ViewGroupAddAction.STABLE_ID,
4294                             in.readInt(RemoteViewsProto.ViewGroupAddAction.STABLE_ID));
4295                     break;
4296                 default:
4297                     Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
4298                             + ProtoUtils.currentFieldToString(in));
4299             }
4300         }
4301         in.end(token);
4302 
4303         checkContainsKeys(values, new long[]{RemoteViewsProto.ViewGroupAddAction.VIEW_ID,
4304                 RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS});
4305 
4306         return (context, resources, rootData, depth) -> {
4307             int viewId = getAsIdentifier(resources, values,
4308                     RemoteViewsProto.ViewGroupAddAction.VIEW_ID);
4309             return new ViewGroupActionAdd(viewId, ((PendingResources<RemoteViews>) values.get(
4310                     RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS)).create(context, resources,
4311                     rootData, depth),
4312                     (int) values.get(RemoteViewsProto.ViewGroupAddAction.INDEX, 0),
4313                     (int) values.get(RemoteViewsProto.ViewGroupAddAction.STABLE_ID, 0));
4314         };
4315     }
4316 
4317     /**
4318      * ViewGroup methods related to removing child views.
4319      */
4320     private static class ViewGroupActionRemove extends Action {
4321         /**
4322          * Id that indicates that all child views of the affected ViewGroup should be removed.
4323          *
4324          * <p>Using -2 because the default id is -1. This avoids accidentally matching that.
4325          */
4326         private static final int REMOVE_ALL_VIEWS_ID = -2;
4327 
4328         private int mViewIdToKeep;
4329 
4330         ViewGroupActionRemove(@IdRes int viewId) {
4331             this(viewId, REMOVE_ALL_VIEWS_ID);
4332         }
4333 
4334         ViewGroupActionRemove(@IdRes int viewId, @IdRes int viewIdToKeep) {
4335             this.mViewId = viewId;
4336             mViewIdToKeep = viewIdToKeep;
4337         }
4338 
4339         ViewGroupActionRemove(Parcel parcel) {
4340             mViewId = parcel.readInt();
4341             mViewIdToKeep = parcel.readInt();
4342         }
4343 
4344         public void writeToParcel(Parcel dest, int flags) {
4345             dest.writeInt(mViewId);
4346             dest.writeInt(mViewIdToKeep);
4347         }
4348 
4349         @Override
4350         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
4351             final ViewGroup target = root.findViewById(mViewId);
4352 
4353             if (target == null) {
4354                 return;
4355             }
4356 
4357             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
4358                 // Remote any view without a stable id
4359                 for (int i = target.getChildCount() - 1; i >= 0; i--) {
4360                     if (!hasStableId(target.getChildAt(i))) {
4361                         target.removeViewAt(i);
4362                     }
4363                 }
4364                 // In the end, only children with a stable id (i.e. recyclable) are left.
4365                 setNextRecyclableChild(target, 0, target.getChildCount());
4366                 return;
4367             }
4368 
4369             removeAllViewsExceptIdToKeep(target);
4370         }
4371 
4372         @Override
4373         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
4374                 ActionApplyParams params) {
4375             // In the async implementation, update the view tree so that subsequent calls to
4376             // findViewById return the current view.
4377             root.createTree();
4378             ViewTree target = root.findViewTreeById(mViewId);
4379 
4380             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
4381                 return ACTION_NOOP;
4382             }
4383 
4384             final ViewGroup targetVg = (ViewGroup) target.mRoot;
4385 
4386             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
4387                 target.mChildren.removeIf(childTree -> !hasStableId(childTree.mRoot));
4388                 setNextRecyclableChild(targetVg, 0, target.mChildren.size());
4389             } else {
4390                 // Remove just the children which don't match the excepted view
4391                 target.mChildren.removeIf(childTree -> childTree.mRoot.getId() != mViewIdToKeep);
4392                 if (target.mChildren.isEmpty()) {
4393                     target.mChildren = null;
4394                 }
4395             }
4396             return new RuntimeAction() {
4397                 @Override
4398                 public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
4399                     if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
4400                         for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
4401                             if (!hasStableId(targetVg.getChildAt(i))) {
4402                                 targetVg.removeViewAt(i);
4403                             }
4404                         }
4405                         return;
4406                     }
4407 
4408                     removeAllViewsExceptIdToKeep(targetVg);
4409                 }
4410             };
4411         }
4412 
4413         /**
4414          * Iterates through the children in the given ViewGroup and removes all the views that
4415          * do not have an id of {@link #mViewIdToKeep}.
4416          */
4417         private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) {
4418             // Otherwise, remove all the views that do not match the id to keep.
4419             int index = viewGroup.getChildCount() - 1;
4420             while (index >= 0) {
4421                 if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) {
4422                     viewGroup.removeViewAt(index);
4423                 }
4424                 index--;
4425             }
4426         }
4427 
4428         @Override
4429         public int getActionTag() {
4430             return VIEW_GROUP_ACTION_REMOVE_TAG;
4431         }
4432 
4433         @Override
4434         public int mergeBehavior() {
4435             return MERGE_APPEND;
4436         }
4437 
4438         @Override
4439         public boolean canWriteToProto() {
4440             return true;
4441         }
4442 
4443         @Override
4444         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
4445             final long token = out.start(RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION);
4446             out.write(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID,
4447                     appResources.getResourceName(mViewId));
4448             if (mViewIdToKeep != REMOVE_ALL_VIEWS_ID) {
4449                 out.write(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP,
4450                         appResources.getResourceName(mViewIdToKeep));
4451             }
4452             out.end(token);
4453         }
4454 
4455         public static PendingResources<Action> createFromProto(ProtoInputStream in)
4456                 throws Exception {
4457             final LongSparseArray<Object> values = new LongSparseArray<>();
4458 
4459             final long token = in.start(RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION);
4460             while (in.nextField() != NO_MORE_FIELDS) {
4461                 switch (in.getFieldNumber()) {
4462                     case (int) RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID:
4463                         values.put(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID,
4464                                 in.readString(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID));
4465                         break;
4466                     case (int) RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP:
4467                         values.put(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP,
4468                                 in.readString(
4469                                         RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP));
4470                         break;
4471                     default:
4472                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
4473                                 + ProtoUtils.currentFieldToString(in));
4474                 }
4475             }
4476             in.end(token);
4477 
4478             checkContainsKeys(values, new long[]{RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID});
4479 
4480             return (context, resources, rootData, depth) -> {
4481                 int viewId = getAsIdentifier(resources, values,
4482                         RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID);
4483                 int viewIdToKeep = (values.indexOfKey(
4484                         RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP) >= 0)
4485                         ? getAsIdentifier(resources, values,
4486                         RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP)
4487                         : REMOVE_ALL_VIEWS_ID;
4488                 return new ViewGroupActionRemove(viewId, viewIdToKeep);
4489             };
4490         }
4491     }
4492 
4493     /**
4494      * Action to remove a view from its parent.
4495      */
4496     private static class RemoveFromParentAction extends Action {
4497         RemoveFromParentAction(@IdRes int viewId) {
4498             this.mViewId = viewId;
4499         }
4500 
4501         RemoveFromParentAction(Parcel parcel) {
4502             mViewId = parcel.readInt();
4503         }
4504 
4505         public void writeToParcel(Parcel dest, int flags) {
4506             dest.writeInt(mViewId);
4507         }
4508 
4509         @Override
4510         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
4511             final View target = root.findViewById(mViewId);
4512 
4513             if (target == null || target == root) {
4514                 return;
4515             }
4516 
4517             ViewParent parent = target.getParent();
4518             if (parent instanceof ViewManager) {
4519                 ((ViewManager) parent).removeView(target);
4520             }
4521         }
4522 
4523         @Override
4524         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
4525                 ActionApplyParams params) {
4526             // In the async implementation, update the view tree so that subsequent calls to
4527             // findViewById return the correct view.
4528             root.createTree();
4529             ViewTree target = root.findViewTreeById(mViewId);
4530 
4531             if (target == null || target == root) {
4532                 return ACTION_NOOP;
4533             }
4534 
4535             ViewTree parent = root.findViewTreeParentOf(target);
4536             if (parent == null || !(parent.mRoot instanceof ViewManager)) {
4537                 return ACTION_NOOP;
4538             }
4539             final ViewManager parentVg = (ViewManager) parent.mRoot;
4540 
4541             parent.mChildren.remove(target);
4542             return new RuntimeAction() {
4543                 @Override
4544                 public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
4545                     parentVg.removeView(target.mRoot);
4546                 }
4547             };
4548         }
4549 
4550         @Override
4551         public int getActionTag() {
4552             return REMOVE_FROM_PARENT_ACTION_TAG;
4553         }
4554 
4555         @Override
4556         public int mergeBehavior() {
4557             return MERGE_APPEND;
4558         }
4559 
4560         @Override
4561         public boolean canWriteToProto() {
4562             return true;
4563         }
4564 
4565         @Override
4566         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
4567             final long token = out.start(RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION);
4568             out.write(RemoteViewsProto.RemoveFromParentAction.VIEW_ID,
4569                     appResources.getResourceName(mViewId));
4570             out.end(token);
4571         }
4572 
4573         public static PendingResources<Action> createFromProto(ProtoInputStream in)
4574                 throws Exception {
4575             final LongSparseArray<Object> values = new LongSparseArray<>();
4576 
4577             final long token = in.start(RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION);
4578             while (in.nextField() != NO_MORE_FIELDS) {
4579                 switch (in.getFieldNumber()) {
4580                     case (int) RemoteViewsProto.RemoveFromParentAction.VIEW_ID:
4581                         values.put(RemoteViewsProto.RemoveFromParentAction.VIEW_ID,
4582                                 in.readString(RemoteViewsProto.RemoveFromParentAction.VIEW_ID));
4583                         break;
4584                     default:
4585                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
4586                                 + ProtoUtils.currentFieldToString(in));
4587                 }
4588             }
4589             in.end(token);
4590 
4591             checkContainsKeys(values, new long[]{RemoteViewsProto.RemoveFromParentAction.VIEW_ID});
4592 
4593             return (context, resources, rootData, depth) -> {
4594                 int viewId = getAsIdentifier(resources, values,
4595                         RemoteViewsProto.RemoveFromParentAction.VIEW_ID);
4596                 return new RemoveFromParentAction(viewId);
4597             };
4598         }
4599     }
4600 
4601     /**
4602      * Helper action to set compound drawables on a TextView. Supports relative
4603      * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
4604      */
4605     private static class TextViewDrawableAction extends Action {
4606         boolean mIsRelative = false;
4607         boolean mUseIcons = false;
4608         int mD1, mD2, mD3, mD4;
4609         Icon mI1, mI2, mI3, mI4;
4610 
4611         boolean mDrawablesLoaded = false;
4612         Drawable mId1, mId2, mId3, mId4;
4613 
4614         public TextViewDrawableAction(@IdRes int viewId, boolean isRelative, @DrawableRes int d1,
4615                 @DrawableRes int d2, @DrawableRes int d3, @DrawableRes int d4) {
4616             this.mViewId = viewId;
4617             this.mIsRelative = isRelative;
4618             this.mUseIcons = false;
4619             this.mD1 = d1;
4620             this.mD2 = d2;
4621             this.mD3 = d3;
4622             this.mD4 = d4;
4623         }
4624 
4625         public TextViewDrawableAction(@IdRes int viewId, boolean isRelative,
4626                 Icon i1, Icon i2, Icon i3, Icon i4) {
4627             this.mViewId = viewId;
4628             this.mIsRelative = isRelative;
4629             this.mUseIcons = true;
4630             this.mI1 = i1;
4631             this.mI2 = i2;
4632             this.mI3 = i3;
4633             this.mI4 = i4;
4634         }
4635 
4636         public TextViewDrawableAction(Parcel parcel) {
4637             mViewId = parcel.readInt();
4638             mIsRelative = (parcel.readInt() != 0);
4639             mUseIcons = (parcel.readInt() != 0);
4640             if (mUseIcons) {
4641                 mI1 = parcel.readTypedObject(Icon.CREATOR);
4642                 mI2 = parcel.readTypedObject(Icon.CREATOR);
4643                 mI3 = parcel.readTypedObject(Icon.CREATOR);
4644                 mI4 = parcel.readTypedObject(Icon.CREATOR);
4645             } else {
4646                 mD1 = parcel.readInt();
4647                 mD2 = parcel.readInt();
4648                 mD3 = parcel.readInt();
4649                 mD4 = parcel.readInt();
4650             }
4651         }
4652 
4653         public void writeToParcel(Parcel dest, int flags) {
4654             dest.writeInt(mViewId);
4655             dest.writeInt(mIsRelative ? 1 : 0);
4656             dest.writeInt(mUseIcons ? 1 : 0);
4657             if (mUseIcons) {
4658                 dest.writeTypedObject(mI1, 0);
4659                 dest.writeTypedObject(mI2, 0);
4660                 dest.writeTypedObject(mI3, 0);
4661                 dest.writeTypedObject(mI4, 0);
4662             } else {
4663                 dest.writeInt(mD1);
4664                 dest.writeInt(mD2);
4665                 dest.writeInt(mD3);
4666                 dest.writeInt(mD4);
4667             }
4668         }
4669 
4670         @Override
4671         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
4672             final TextView target = root.findViewById(mViewId);
4673             if (target == null) return;
4674             if (mDrawablesLoaded) {
4675                 if (mIsRelative) {
4676                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(mId1, mId2, mId3, mId4);
4677                 } else {
4678                     target.setCompoundDrawablesWithIntrinsicBounds(mId1, mId2, mId3, mId4);
4679                 }
4680             } else if (mUseIcons) {
4681                 final Context ctx = target.getContext();
4682                 final Drawable id1 = mI1 == null ? null : mI1.loadDrawable(ctx);
4683                 final Drawable id2 = mI2 == null ? null : mI2.loadDrawable(ctx);
4684                 final Drawable id3 = mI3 == null ? null : mI3.loadDrawable(ctx);
4685                 final Drawable id4 = mI4 == null ? null : mI4.loadDrawable(ctx);
4686                 if (mIsRelative) {
4687                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(id1, id2, id3, id4);
4688                 } else {
4689                     target.setCompoundDrawablesWithIntrinsicBounds(id1, id2, id3, id4);
4690                 }
4691             } else {
4692                 if (mIsRelative) {
4693                     target.setCompoundDrawablesRelativeWithIntrinsicBounds(mD1, mD2, mD3, mD4);
4694                 } else {
4695                     target.setCompoundDrawablesWithIntrinsicBounds(mD1, mD2, mD3, mD4);
4696                 }
4697             }
4698         }
4699 
4700         @Override
4701         public Action initActionAsync(ViewTree root, ViewGroup rootParent,
4702                 ActionApplyParams params) {
4703             final TextView target = root.findViewById(mViewId);
4704             if (target == null) return ACTION_NOOP;
4705 
4706             TextViewDrawableAction copy = mUseIcons
4707                     ? new TextViewDrawableAction(mViewId, mIsRelative, mI1, mI2, mI3, mI4)
4708                     : new TextViewDrawableAction(mViewId, mIsRelative, mD1, mD2, mD3, mD4);
4709 
4710             // Load the drawables on the background thread.
4711             copy.mDrawablesLoaded = true;
4712             final Context ctx = target.getContext();
4713 
4714             if (mUseIcons) {
4715                 copy.mId1 = mI1 == null ? null : mI1.loadDrawable(ctx);
4716                 copy.mId2 = mI2 == null ? null : mI2.loadDrawable(ctx);
4717                 copy.mId3 = mI3 == null ? null : mI3.loadDrawable(ctx);
4718                 copy.mId4 = mI4 == null ? null : mI4.loadDrawable(ctx);
4719             } else {
4720                 copy.mId1 = mD1 == 0 ? null : ctx.getDrawable(mD1);
4721                 copy.mId2 = mD2 == 0 ? null : ctx.getDrawable(mD2);
4722                 copy.mId3 = mD3 == 0 ? null : ctx.getDrawable(mD3);
4723                 copy.mId4 = mD4 == 0 ? null : ctx.getDrawable(mD4);
4724             }
4725             return copy;
4726         }
4727 
4728         @Override
4729         public boolean prefersAsyncApply() {
4730             return mUseIcons;
4731         }
4732 
4733         @Override
4734         public int getActionTag() {
4735             return TEXT_VIEW_DRAWABLE_ACTION_TAG;
4736         }
4737 
4738         @Override
4739         public void visitUris(@NonNull Consumer<Uri> visitor) {
4740             if (mUseIcons) {
4741                 visitIconUri(mI1, visitor);
4742                 visitIconUri(mI2, visitor);
4743                 visitIconUri(mI3, visitor);
4744                 visitIconUri(mI4, visitor);
4745             }
4746         }
4747 
4748         @Override
4749         public boolean canWriteToProto() {
4750             return true;
4751         }
4752 
4753         @Override
4754         public void writeToProto(ProtoOutputStream out, Context context,
4755                 Resources appResources) { // rebase
4756             final long token = out.start(RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION);
4757             out.write(RemoteViewsProto.TextViewDrawableAction.VIEW_ID,
4758                     appResources.getResourceName(mViewId));
4759             out.write(RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE, mIsRelative);
4760             if (mUseIcons) {
4761                 long iconsToken = out.start(RemoteViewsProto.TextViewDrawableAction.ICONS);
4762                 if (mI1 != null) {
4763                     writeIconToProto(out, appResources, mI1,
4764                             RemoteViewsProto.TextViewDrawableAction.Icons.ONE);
4765                 }
4766                 if (mI2 != null) {
4767                     writeIconToProto(out, appResources, mI2,
4768                             RemoteViewsProto.TextViewDrawableAction.Icons.TWO);
4769                 }
4770                 if (mI3 != null) {
4771                     writeIconToProto(out, appResources, mI3,
4772                             RemoteViewsProto.TextViewDrawableAction.Icons.THREE);
4773                 }
4774                 if (mI4 != null) {
4775                     writeIconToProto(out, appResources, mI4,
4776                             RemoteViewsProto.TextViewDrawableAction.Icons.FOUR);
4777                 }
4778                 out.end(iconsToken);
4779             } else {
4780                 long resourcesToken = out.start(RemoteViewsProto.TextViewDrawableAction.RESOURCES);
4781                 if (mD1 != 0) {
4782                     out.write(RemoteViewsProto.TextViewDrawableAction.Resources.ONE,
4783                             appResources.getResourceName(mD1));
4784                 }
4785                 if (mD2 != 0) {
4786                     out.write(RemoteViewsProto.TextViewDrawableAction.Resources.TWO,
4787                             appResources.getResourceName(mD2));
4788                 }
4789                 if (mD3 != 0) {
4790                     out.write(RemoteViewsProto.TextViewDrawableAction.Resources.THREE,
4791                             appResources.getResourceName(mD3));
4792                 }
4793                 if (mD4 != 0) {
4794                     out.write(RemoteViewsProto.TextViewDrawableAction.Resources.FOUR,
4795                             appResources.getResourceName(mD4));
4796                 }
4797                 out.end(resourcesToken);
4798             }
4799             out.end(token);
4800         }
4801 
4802         public static PendingResources<Action> createFromProto(ProtoInputStream in)
4803                 throws Exception {
4804             final LongSparseArray<Object> values = new LongSparseArray<>();
4805 
4806             values.put(RemoteViewsProto.TextViewDrawableAction.ICONS,
4807                     new SparseArray<PendingResources<Icon>>());
4808             values.put(RemoteViewsProto.TextViewDrawableAction.RESOURCES,
4809                     new SparseArray<String>());
4810             final long token = in.start(RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION);
4811             while (in.nextField() != NO_MORE_FIELDS) {
4812                 switch (in.getFieldNumber()) {
4813                     case (int) RemoteViewsProto.TextViewDrawableAction.VIEW_ID:
4814                         values.put(RemoteViewsProto.TextViewDrawableAction.VIEW_ID,
4815                                 in.readString(RemoteViewsProto.TextViewDrawableAction.VIEW_ID));
4816                         break;
4817                     case (int) RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE:
4818                         values.put(RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE,
4819                                 in.readBoolean(
4820                                         RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE));
4821                         break;
4822                     case (int) RemoteViewsProto.TextViewDrawableAction.RESOURCES:
4823                         final long resourcesToken = in.start(
4824                                 RemoteViewsProto.TextViewDrawableAction.RESOURCES);
4825                         while (in.nextField() != NO_MORE_FIELDS) {
4826                             switch (in.getFieldNumber()) {
4827                                 case (int) RemoteViewsProto.TextViewDrawableAction.Resources.ONE:
4828                                     ((SparseArray<String>) values.get(
4829                                             RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put(
4830                                             1, in.readString(
4831                                                     RemoteViewsProto
4832                                                             .TextViewDrawableAction.Resources.ONE));
4833                                     break;
4834                                 case (int) RemoteViewsProto.TextViewDrawableAction.Resources.TWO:
4835                                     ((SparseArray<String>) values.get(
4836                                             RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put(
4837                                             2, in.readString(
4838                                                     RemoteViewsProto
4839                                                             .TextViewDrawableAction.Resources.TWO));
4840                                     break;
4841                                 case (int) RemoteViewsProto.TextViewDrawableAction.Resources.THREE:
4842                                     ((SparseArray<String>) values.get(
4843                                             RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put(
4844                                             3, in.readString(
4845                                                     RemoteViewsProto
4846                                                             .TextViewDrawableAction
4847                                                             .Resources.THREE));
4848                                     break;
4849                                 case (int) RemoteViewsProto.TextViewDrawableAction.Resources.FOUR:
4850                                     ((SparseArray<String>) values.get(
4851                                             RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put(
4852                                             4, in.readString(
4853                                                     RemoteViewsProto
4854                                                             .TextViewDrawableAction
4855                                                             .Resources.FOUR));
4856                                     break;
4857                                 default:
4858                                     Log.w(LOG_TAG,
4859                                             "Unhandled field while reading RemoteViews proto!\n"
4860                                                     + ProtoUtils.currentFieldToString(in));
4861                             }
4862                         }
4863                         in.end(resourcesToken);
4864                         break;
4865                     case (int) RemoteViewsProto.TextViewDrawableAction.ICONS:
4866                         final long iconsToken = in.start(
4867                                 RemoteViewsProto.TextViewDrawableAction.ICONS);
4868                         while (in.nextField() != NO_MORE_FIELDS) {
4869                             switch (in.getFieldNumber()) {
4870                                 case (int) RemoteViewsProto.TextViewDrawableAction.Icons.ONE:
4871                                     ((SparseArray<PendingResources<Icon>>) values.get(
4872                                             RemoteViewsProto.TextViewDrawableAction.ICONS)).put(1,
4873                                             createIconFromProto(in,
4874                                                     RemoteViewsProto
4875                                                             .TextViewDrawableAction.Icons.ONE));
4876                                     break;
4877                                 case (int) RemoteViewsProto.TextViewDrawableAction.Icons.TWO:
4878                                     ((SparseArray<PendingResources<Icon>>) values.get(
4879                                             RemoteViewsProto.TextViewDrawableAction.ICONS)).put(2,
4880                                             createIconFromProto(in,
4881                                                     RemoteViewsProto
4882                                                             .TextViewDrawableAction.Icons.TWO));
4883                                     break;
4884                                 case (int) RemoteViewsProto.TextViewDrawableAction.Icons.THREE:
4885                                     ((SparseArray<PendingResources<Icon>>) values.get(
4886                                             RemoteViewsProto.TextViewDrawableAction.ICONS)).put(3,
4887                                             createIconFromProto(in,
4888                                                     RemoteViewsProto
4889                                                             .TextViewDrawableAction.Icons.THREE));
4890                                     break;
4891                                 case (int) RemoteViewsProto.TextViewDrawableAction.Icons.FOUR:
4892                                     ((SparseArray<PendingResources<Icon>>) values.get(
4893                                             RemoteViewsProto.TextViewDrawableAction.ICONS)).put(4,
4894                                             createIconFromProto(in,
4895                                                     RemoteViewsProto
4896                                                             .TextViewDrawableAction.Icons.FOUR));
4897                                     break;
4898                                 default:
4899                                     Log.w(LOG_TAG,
4900                                             "Unhandled field while reading RemoteViews proto!\n"
4901                                                     + ProtoUtils.currentFieldToString(in));
4902                             }
4903                         }
4904                         in.end(iconsToken);
4905                         break;
4906                     default:
4907                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
4908                                 + ProtoUtils.currentFieldToString(in));
4909                 }
4910             }
4911             in.end(token);
4912 
4913             checkContainsKeys(values, new long[]{RemoteViewsProto.TextViewDrawableAction.VIEW_ID});
4914 
4915             return (context, resources, rootData, depth) -> {
4916                 int viewId = getAsIdentifier(resources, values,
4917                         RemoteViewsProto.TextViewDrawableAction.VIEW_ID);
4918                 SparseArray<PendingResources<Icon>> icons =
4919                         (SparseArray<PendingResources<Icon>>) values.get(
4920                                 RemoteViewsProto.TextViewDrawableAction.ICONS);
4921                 SparseArray<String> resArray = (SparseArray<String>) values.get(
4922                         RemoteViewsProto.TextViewDrawableAction.RESOURCES);
4923                 boolean isRelative = (boolean) values.get(
4924                         RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE, false);
4925                 if (icons.size() > 0) {
4926                     return new TextViewDrawableAction(viewId, isRelative,
4927                             icons.get(1).create(context, resources, rootData, depth),
4928                             icons.get(2).create(context, resources, rootData, depth),
4929                             icons.get(3).create(context, resources, rootData, depth),
4930                             icons.get(4).create(context, resources, rootData, depth));
4931                 } else {
4932                     int first = resArray.contains(1) ? getAsIdentifier(resources, resArray, 1) : 0;
4933                     int second = resArray.contains(2) ? getAsIdentifier(resources, resArray, 2) : 0;
4934                     int third = resArray.contains(3) ? getAsIdentifier(resources, resArray, 3) : 0;
4935                     int fourth = resArray.contains(4) ? getAsIdentifier(resources, resArray, 4) : 0;
4936                     return new TextViewDrawableAction(viewId, isRelative, first, second, third,
4937                             fourth);
4938                 }
4939             };
4940         }
4941     }
4942 
4943     /**
4944      * Helper action to set text size on a TextView in any supported units.
4945      */
4946     private static class TextViewSizeAction extends Action {
4947         int mUnits;
4948         float mSize;
4949 
4950         TextViewSizeAction(@IdRes int viewId, @ComplexDimensionUnit int units, float size) {
4951             this.mViewId = viewId;
4952             this.mUnits = units;
4953             this.mSize = size;
4954         }
4955 
4956         TextViewSizeAction(Parcel parcel) {
4957             mViewId = parcel.readInt();
4958             mUnits = parcel.readInt();
4959             mSize = parcel.readFloat();
4960         }
4961 
4962         public void writeToParcel(Parcel dest, int flags) {
4963             dest.writeInt(mViewId);
4964             dest.writeInt(mUnits);
4965             dest.writeFloat(mSize);
4966         }
4967 
4968         @Override
4969         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
4970             final TextView target = root.findViewById(mViewId);
4971             if (target == null) return;
4972             target.setTextSize(mUnits, mSize);
4973         }
4974 
4975         @Override
4976         public int getActionTag() {
4977             return TEXT_VIEW_SIZE_ACTION_TAG;
4978         }
4979 
4980         @Override
4981         public boolean canWriteToProto() {
4982             return true;
4983         }
4984 
4985         @Override
4986         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
4987             final long token = out.start(RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION);
4988             out.write(RemoteViewsProto.TextViewSizeAction.VIEW_ID,
4989                     appResources.getResourceName(mViewId));
4990             out.write(RemoteViewsProto.TextViewSizeAction.UNITS, mUnits);
4991             out.write(RemoteViewsProto.TextViewSizeAction.SIZE, mSize);
4992             out.end(token);
4993         }
4994 
4995         public static PendingResources<Action> createFromProto(ProtoInputStream in)
4996                 throws Exception {
4997             final LongSparseArray<Object> values = new LongSparseArray<>();
4998 
4999             final long token = in.start(RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION);
5000             while (in.nextField() != NO_MORE_FIELDS) {
5001                 switch (in.getFieldNumber()) {
5002                     case (int) RemoteViewsProto.TextViewSizeAction.VIEW_ID:
5003                         values.put(RemoteViewsProto.TextViewSizeAction.VIEW_ID,
5004                                 in.readString(RemoteViewsProto.TextViewSizeAction.VIEW_ID));
5005                         break;
5006                     case (int) RemoteViewsProto.TextViewSizeAction.UNITS:
5007                         values.put(RemoteViewsProto.TextViewSizeAction.UNITS,
5008                                 in.readInt(RemoteViewsProto.TextViewSizeAction.UNITS));
5009                         break;
5010                     case (int) RemoteViewsProto.TextViewSizeAction.SIZE:
5011                         values.put(RemoteViewsProto.TextViewSizeAction.SIZE,
5012                                 in.readFloat(RemoteViewsProto.TextViewSizeAction.SIZE));
5013                         break;
5014                     default:
5015                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
5016                                 + ProtoUtils.currentFieldToString(in));
5017                 }
5018             }
5019             in.end(token);
5020 
5021             checkContainsKeys(values, new long[]{RemoteViewsProto.TextViewSizeAction.VIEW_ID});
5022 
5023             return (context, resources, rootData, depth) -> {
5024                 int viewId = getAsIdentifier(resources, values,
5025                         RemoteViewsProto.TextViewSizeAction.VIEW_ID);
5026                 return new TextViewSizeAction(viewId,
5027                         (int) values.get(RemoteViewsProto.TextViewSizeAction.UNITS, 0),
5028                         (float) values.get(RemoteViewsProto.TextViewSizeAction.SIZE, 0));
5029             };
5030         }
5031     }
5032 
5033     /**
5034      * Helper action to set padding on a View.
5035      */
5036     private static class ViewPaddingAction extends Action {
5037         @Px int mLeft, mTop, mRight, mBottom;
5038 
5039         public ViewPaddingAction(@IdRes int viewId, @Px int left, @Px int top,
5040                 @Px int right, @Px int bottom) {
5041             this.mViewId = viewId;
5042             this.mLeft = left;
5043             this.mTop = top;
5044             this.mRight = right;
5045             this.mBottom = bottom;
5046         }
5047 
5048         public ViewPaddingAction(Parcel parcel) {
5049             mViewId = parcel.readInt();
5050             mLeft = parcel.readInt();
5051             mTop = parcel.readInt();
5052             mRight = parcel.readInt();
5053             mBottom = parcel.readInt();
5054         }
5055 
5056         public void writeToParcel(Parcel dest, int flags) {
5057             dest.writeInt(mViewId);
5058             dest.writeInt(mLeft);
5059             dest.writeInt(mTop);
5060             dest.writeInt(mRight);
5061             dest.writeInt(mBottom);
5062         }
5063 
5064         @Override
5065         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
5066             final View target = root.findViewById(mViewId);
5067             if (target == null) return;
5068             target.setPadding(mLeft, mTop, mRight, mBottom);
5069         }
5070 
5071         @Override
5072         public int getActionTag() {
5073             return VIEW_PADDING_ACTION_TAG;
5074         }
5075 
5076         @Override
5077         public boolean canWriteToProto() {
5078             return true;
5079         }
5080 
5081         @Override
5082         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
5083             final long token = out.start(RemoteViewsProto.Action.VIEW_PADDING_ACTION);
5084             out.write(RemoteViewsProto.ViewPaddingAction.VIEW_ID,
5085                     appResources.getResourceName(mViewId));
5086             out.write(RemoteViewsProto.ViewPaddingAction.LEFT, mLeft);
5087             out.write(RemoteViewsProto.ViewPaddingAction.RIGHT, mRight);
5088             out.write(RemoteViewsProto.ViewPaddingAction.TOP, mTop);
5089             out.write(RemoteViewsProto.ViewPaddingAction.BOTTOM, mBottom);
5090             out.end(token);
5091         }
5092 
5093         public static PendingResources<Action> createFromProto(ProtoInputStream in)
5094                 throws Exception {
5095             final LongSparseArray<Object> values = new LongSparseArray<>();
5096 
5097             final long token = in.start(RemoteViewsProto.Action.VIEW_PADDING_ACTION);
5098             while (in.nextField() != NO_MORE_FIELDS) {
5099                 switch (in.getFieldNumber()) {
5100                     case (int) RemoteViewsProto.ViewPaddingAction.VIEW_ID:
5101                         values.put(RemoteViewsProto.ViewPaddingAction.VIEW_ID,
5102                                 in.readString(RemoteViewsProto.ViewPaddingAction.VIEW_ID));
5103                         break;
5104                     case (int) RemoteViewsProto.ViewPaddingAction.LEFT:
5105                         values.put(RemoteViewsProto.ViewPaddingAction.LEFT,
5106                                 in.readInt(RemoteViewsProto.ViewPaddingAction.LEFT));
5107                         break;
5108                     case (int) RemoteViewsProto.ViewPaddingAction.RIGHT:
5109                         values.put(RemoteViewsProto.ViewPaddingAction.RIGHT,
5110                                 in.readInt(RemoteViewsProto.ViewPaddingAction.RIGHT));
5111                         break;
5112                     case (int) RemoteViewsProto.ViewPaddingAction.TOP:
5113                         values.put(RemoteViewsProto.ViewPaddingAction.TOP,
5114                                 in.readInt(RemoteViewsProto.ViewPaddingAction.TOP));
5115                         break;
5116                     case (int) RemoteViewsProto.ViewPaddingAction.BOTTOM:
5117                         values.put(RemoteViewsProto.ViewPaddingAction.BOTTOM,
5118                                 in.readInt(RemoteViewsProto.ViewPaddingAction.BOTTOM));
5119                         break;
5120                     default:
5121                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
5122                                 + ProtoUtils.currentFieldToString(in));
5123                 }
5124             }
5125             in.end(token);
5126 
5127             checkContainsKeys(values, new long[]{RemoteViewsProto.ViewPaddingAction.VIEW_ID});
5128 
5129             return (context, resources, rootData, depth) -> {
5130                 int viewId = getAsIdentifier(resources, values,
5131                         RemoteViewsProto.ViewPaddingAction.VIEW_ID);
5132                 return new ViewPaddingAction(viewId,
5133                         (int) values.get(RemoteViewsProto.ViewPaddingAction.LEFT, 0),
5134                         (int) values.get(RemoteViewsProto.ViewPaddingAction.TOP, 0),
5135                         (int) values.get(RemoteViewsProto.ViewPaddingAction.RIGHT, 0),
5136                         (int) values.get(RemoteViewsProto.ViewPaddingAction.BOTTOM, 0));
5137             };
5138         }
5139     }
5140 
5141     /**
5142      * Helper action to set layout params on a View.
5143      */
5144     private static class LayoutParamAction extends Action {
5145         static final int LAYOUT_MARGIN_LEFT = MARGIN_LEFT;
5146         static final int LAYOUT_MARGIN_TOP = MARGIN_TOP;
5147         static final int LAYOUT_MARGIN_RIGHT = MARGIN_RIGHT;
5148         static final int LAYOUT_MARGIN_BOTTOM = MARGIN_BOTTOM;
5149         static final int LAYOUT_MARGIN_START = MARGIN_START;
5150         static final int LAYOUT_MARGIN_END = MARGIN_END;
5151         static final int LAYOUT_WIDTH = 8;
5152         static final int LAYOUT_HEIGHT = 9;
5153 
5154         final int mProperty;
5155         final int mValueType;
5156         final int mValue;
5157 
5158         /**
5159          * @param viewId ID of the view alter
5160          * @param property which layout parameter to alter
5161          * @param value new value of the layout parameter
5162          * @param units the units of the given value
5163          */
5164         LayoutParamAction(@IdRes int viewId, int property, float value,
5165                 @ComplexDimensionUnit int units) {
5166             this.mViewId = viewId;
5167             this.mProperty = property;
5168             this.mValueType = VALUE_TYPE_COMPLEX_UNIT;
5169             this.mValue = TypedValue.createComplexDimension(value, units);
5170         }
5171 
5172         /**
5173          * @param viewId ID of the view alter
5174          * @param property which layout parameter to alter
5175          * @param value value to set.
5176          * @param valueType must be one of {@link #VALUE_TYPE_COMPLEX_UNIT},
5177          *   {@link #VALUE_TYPE_RESOURCE}, {@link #VALUE_TYPE_ATTRIBUTE} or
5178          *   {@link #VALUE_TYPE_RAW}.
5179          */
5180         LayoutParamAction(@IdRes int viewId, int property, int value, @ValueType int valueType) {
5181             this.mViewId = viewId;
5182             this.mProperty = property;
5183             this.mValueType = valueType;
5184             this.mValue = value;
5185         }
5186 
5187         public LayoutParamAction(Parcel parcel) {
5188             mViewId = parcel.readInt();
5189             mProperty = parcel.readInt();
5190             mValueType = parcel.readInt();
5191             mValue = parcel.readInt();
5192         }
5193 
5194         public void writeToParcel(Parcel dest, int flags) {
5195             dest.writeInt(mViewId);
5196             dest.writeInt(mProperty);
5197             dest.writeInt(mValueType);
5198             dest.writeInt(mValue);
5199         }
5200 
5201         @Override
5202         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
5203             final View target = root.findViewById(mViewId);
5204             if (target == null) {
5205                 return;
5206             }
5207             ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
5208             if (layoutParams == null) {
5209                 return;
5210             }
5211             switch (mProperty) {
5212                 case LAYOUT_MARGIN_LEFT:
5213                     if (layoutParams instanceof MarginLayoutParams) {
5214                         ((MarginLayoutParams) layoutParams).leftMargin = getPixelOffset(target);
5215                         target.setLayoutParams(layoutParams);
5216                     }
5217                     break;
5218                 case LAYOUT_MARGIN_TOP:
5219                     if (layoutParams instanceof MarginLayoutParams) {
5220                         ((MarginLayoutParams) layoutParams).topMargin = getPixelOffset(target);
5221                         target.setLayoutParams(layoutParams);
5222                     }
5223                     break;
5224                 case LAYOUT_MARGIN_RIGHT:
5225                     if (layoutParams instanceof MarginLayoutParams) {
5226                         ((MarginLayoutParams) layoutParams).rightMargin = getPixelOffset(target);
5227                         target.setLayoutParams(layoutParams);
5228                     }
5229                     break;
5230                 case LAYOUT_MARGIN_BOTTOM:
5231                     if (layoutParams instanceof MarginLayoutParams) {
5232                         ((MarginLayoutParams) layoutParams).bottomMargin = getPixelOffset(target);
5233                         target.setLayoutParams(layoutParams);
5234                     }
5235                     break;
5236                 case LAYOUT_MARGIN_START:
5237                     if (layoutParams instanceof MarginLayoutParams) {
5238                         ((MarginLayoutParams) layoutParams).setMarginStart(getPixelOffset(target));
5239                         target.setLayoutParams(layoutParams);
5240                     }
5241                     break;
5242                 case LAYOUT_MARGIN_END:
5243                     if (layoutParams instanceof MarginLayoutParams) {
5244                         ((MarginLayoutParams) layoutParams).setMarginEnd(getPixelOffset(target));
5245                         target.setLayoutParams(layoutParams);
5246                     }
5247                     break;
5248                 case LAYOUT_WIDTH:
5249                     layoutParams.width = getPixelSize(target);
5250                     target.setLayoutParams(layoutParams);
5251                     break;
5252                 case LAYOUT_HEIGHT:
5253                     layoutParams.height = getPixelSize(target);
5254                     target.setLayoutParams(layoutParams);
5255                     break;
5256                 default:
5257                     throw new IllegalArgumentException("Unknown property " + mProperty);
5258             }
5259         }
5260 
5261         private int getPixelOffset(View target) {
5262             try {
5263                 switch (mValueType) {
5264                     case VALUE_TYPE_ATTRIBUTE:
5265                         TypedArray typedArray = target.getContext().obtainStyledAttributes(
5266                                 new int[]{this.mValue});
5267                         try {
5268                             return typedArray.getDimensionPixelOffset(0, 0);
5269                         } finally {
5270                             typedArray.recycle();
5271                         }
5272                     case VALUE_TYPE_RESOURCE:
5273                         if (mValue == 0) {
5274                             return 0;
5275                         }
5276                         return target.getResources().getDimensionPixelOffset(mValue);
5277                     case VALUE_TYPE_COMPLEX_UNIT:
5278                         return TypedValue.complexToDimensionPixelOffset(mValue,
5279                                 target.getResources().getDisplayMetrics());
5280                     default:
5281                         return mValue;
5282                 }
5283             } catch (Throwable t) {
5284                 throw new ActionException(t);
5285             }
5286         }
5287 
5288         private int getPixelSize(View target) {
5289             try {
5290                 switch (mValueType) {
5291                     case VALUE_TYPE_ATTRIBUTE:
5292                         TypedArray typedArray = target.getContext().obtainStyledAttributes(
5293                                 new int[]{this.mValue});
5294                         try {
5295                             return typedArray.getDimensionPixelSize(0, 0);
5296                         } finally {
5297                             typedArray.recycle();
5298                         }
5299                     case VALUE_TYPE_RESOURCE:
5300                         if (mValue == 0) {
5301                             return 0;
5302                         }
5303                         return target.getResources().getDimensionPixelSize(mValue);
5304                     case VALUE_TYPE_COMPLEX_UNIT:
5305                         return TypedValue.complexToDimensionPixelSize(mValue,
5306                                 target.getResources().getDisplayMetrics());
5307                     default:
5308                         return mValue;
5309                 }
5310             } catch (Throwable t) {
5311                 throw new ActionException(t);
5312             }
5313         }
5314 
5315         @Override
5316         public int getActionTag() {
5317             return LAYOUT_PARAM_ACTION_TAG;
5318         }
5319 
5320         @Override
5321         public String getUniqueKey() {
5322             return super.getUniqueKey() + mProperty;
5323         }
5324 
5325         @Override
5326         public boolean canWriteToProto() {
5327             return true;
5328         }
5329 
5330         @Override
5331         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
5332             final long token = out.start(RemoteViewsProto.Action.LAYOUT_PARAM_ACTION);
5333             out.write(RemoteViewsProto.LayoutParamAction.VIEW_ID,
5334                     appResources.getResourceName(mViewId));
5335             out.write(RemoteViewsProto.LayoutParamAction.PROPERTY, mProperty);
5336             out.write(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE, mValue);
5337             out.write(RemoteViewsProto.LayoutParamAction.VALUE_TYPE, mValueType);
5338             out.end(token);
5339         }
5340 
5341         public static PendingResources<Action> createFromProto(ProtoInputStream in)
5342                 throws Exception {
5343             final LongSparseArray<Object> values = new LongSparseArray<>();
5344 
5345             final long token = in.start(RemoteViewsProto.Action.LAYOUT_PARAM_ACTION);
5346             while (in.nextField() != NO_MORE_FIELDS) {
5347                 switch (in.getFieldNumber()) {
5348                     case (int) RemoteViewsProto.LayoutParamAction.VIEW_ID:
5349                         values.put(RemoteViewsProto.LayoutParamAction.VIEW_ID,
5350                                 in.readString(RemoteViewsProto.LayoutParamAction.VIEW_ID));
5351                         break;
5352                     case (int) RemoteViewsProto.LayoutParamAction.PROPERTY:
5353                         values.put(RemoteViewsProto.LayoutParamAction.PROPERTY,
5354                                 in.readInt(RemoteViewsProto.LayoutParamAction.PROPERTY));
5355                         break;
5356                     case (int) RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE:
5357                         values.put(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE,
5358                                 in.readInt(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE));
5359                         break;
5360                     case (int) RemoteViewsProto.LayoutParamAction.VALUE_TYPE:
5361                         values.put(RemoteViewsProto.LayoutParamAction.VALUE_TYPE,
5362                                 in.readInt(RemoteViewsProto.LayoutParamAction.VALUE_TYPE));
5363                         break;
5364                     default:
5365                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
5366                                 + ProtoUtils.currentFieldToString(in));
5367                 }
5368             }
5369             in.end(token);
5370 
5371             checkContainsKeys(values, new long[]{RemoteViewsProto.LayoutParamAction.VIEW_ID});
5372 
5373             return (context, resources, rootData, depth) -> {
5374                 int viewId = getAsIdentifier(resources, values,
5375                         RemoteViewsProto.LayoutParamAction.VIEW_ID);
5376                 return new LayoutParamAction(viewId,
5377                         (int) values.get(RemoteViewsProto.LayoutParamAction.PROPERTY, 0),
5378                         (int) values.get(RemoteViewsProto.LayoutParamAction.LAYOUT_VALUE, 0),
5379                         (int) values.get(RemoteViewsProto.LayoutParamAction.VALUE_TYPE, 0));
5380             };
5381         }
5382     }
5383 
5384     /**
5385      * Helper action to add a view tag with RemoteInputs.
5386      */
5387     private static class SetRemoteInputsAction extends Action {
5388         final Parcelable[] mRemoteInputs;
5389 
5390         public SetRemoteInputsAction(@IdRes int viewId, RemoteInput[] remoteInputs) {
5391             this.mViewId = viewId;
5392             this.mRemoteInputs = remoteInputs;
5393         }
5394 
5395         public SetRemoteInputsAction(Parcel parcel) {
5396             mViewId = parcel.readInt();
5397             mRemoteInputs = parcel.createTypedArray(RemoteInput.CREATOR);
5398         }
5399 
5400         public void writeToParcel(Parcel dest, int flags) {
5401             dest.writeInt(mViewId);
5402             dest.writeTypedArray(mRemoteInputs, flags);
5403         }
5404 
5405         @Override
5406         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
5407             final View target = root.findViewById(mViewId);
5408             if (target == null) return;
5409 
5410             target.setTagInternal(R.id.remote_input_tag, mRemoteInputs);
5411         }
5412 
5413         @Override
5414         public int getActionTag() {
5415             return SET_REMOTE_INPUTS_ACTION_TAG;
5416         }
5417     }
5418 
5419     private static class SetIntTagAction extends Action {
5420         @IdRes private final int mViewId;
5421         @IdRes private final int mKey;
5422         private final int mTag;
5423 
5424         SetIntTagAction(@IdRes int viewId, @IdRes int key, int tag) {
5425             mViewId = viewId;
5426             mKey = key;
5427             mTag = tag;
5428         }
5429 
5430         SetIntTagAction(Parcel parcel) {
5431             mViewId = parcel.readInt();
5432             mKey = parcel.readInt();
5433             mTag = parcel.readInt();
5434         }
5435 
5436         public void writeToParcel(Parcel dest, int flags) {
5437             dest.writeInt(mViewId);
5438             dest.writeInt(mKey);
5439             dest.writeInt(mTag);
5440         }
5441 
5442         @Override
5443         public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
5444             final View target = root.findViewById(mViewId);
5445             if (target == null) return;
5446 
5447             target.setTagInternal(mKey, mTag);
5448         }
5449 
5450         @Override
5451         public int getActionTag() {
5452             return SET_INT_TAG_TAG;
5453         }
5454 
5455         @Override
5456         public boolean canWriteToProto() {
5457             return true;
5458         }
5459 
5460         @Override
5461         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
5462             final long token = out.start(RemoteViewsProto.Action.SET_INT_TAG_ACTION);
5463             out.write(RemoteViewsProto.SetIntTagAction.VIEW_ID,
5464                     appResources.getResourceName(mViewId));
5465             out.write(RemoteViewsProto.SetIntTagAction.KEY,
5466                     appResources.getResourceName(mKey)); // rebase
5467             out.write(RemoteViewsProto.SetIntTagAction.TAG, mTag);
5468             out.end(token);
5469         }
5470 
5471         public static PendingResources<Action> createFromProto(ProtoInputStream in)
5472                 throws Exception {
5473             final LongSparseArray<Object> values = new LongSparseArray<>();
5474 
5475             final long token = in.start(RemoteViewsProto.Action.SET_INT_TAG_ACTION);
5476             while (in.nextField() != NO_MORE_FIELDS) {
5477                 switch (in.getFieldNumber()) {
5478                     case (int) RemoteViewsProto.SetIntTagAction.VIEW_ID:
5479                         values.put(RemoteViewsProto.SetIntTagAction.VIEW_ID,
5480                                 in.readString(RemoteViewsProto.SetIntTagAction.VIEW_ID));
5481                         break;
5482                     case (int) RemoteViewsProto.SetIntTagAction.KEY:
5483                         values.put(RemoteViewsProto.SetIntTagAction.KEY,
5484                                 in.readString(RemoteViewsProto.SetIntTagAction.KEY));
5485                         break;
5486                     case (int) RemoteViewsProto.SetIntTagAction.TAG:
5487                         values.put(RemoteViewsProto.SetIntTagAction.TAG,
5488                                 in.readInt(RemoteViewsProto.SetIntTagAction.TAG));
5489                         break;
5490                     default:
5491                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
5492                                 + ProtoUtils.currentFieldToString(in));
5493                 }
5494             }
5495             in.end(token);
5496 
5497             checkContainsKeys(values, new long[]{RemoteViewsProto.SetIntTagAction.VIEW_ID,
5498                     RemoteViewsProto.SetIntTagAction.KEY});
5499 
5500             return (context, resources, rootData, depth) -> {
5501                 int viewId = getAsIdentifier(resources, values,
5502                         RemoteViewsProto.SetIntTagAction.VIEW_ID);
5503                 int keyId = getAsIdentifier(resources, values,
5504                         RemoteViewsProto.SetIntTagAction.KEY);
5505                 return new SetIntTagAction(viewId, keyId,
5506                         (int) values.get(RemoteViewsProto.SetIntTagAction.TAG, 0));
5507             };
5508         }
5509     }
5510 
5511     private static class SetCompoundButtonCheckedAction extends Action {
5512         private final boolean mChecked;
5513 
5514         SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) {
5515             this.mViewId = viewId;
5516             mChecked = checked;
5517         }
5518 
5519         SetCompoundButtonCheckedAction(Parcel in) {
5520             mViewId = in.readInt();
5521             mChecked = in.readBoolean();
5522         }
5523 
5524         @Override
5525         public void writeToParcel(Parcel dest, int flags) {
5526             dest.writeInt(mViewId);
5527             dest.writeBoolean(mChecked);
5528         }
5529 
5530         @Override
5531         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
5532                 throws ActionException {
5533             final View target = root.findViewById(mViewId);
5534             if (target == null) return;
5535 
5536             if (!(target instanceof CompoundButton)) {
5537                 Log.w(LOG_TAG, "Cannot set checked to view "
5538                         + mViewId + " because it is not a CompoundButton");
5539                 return;
5540             }
5541 
5542             CompoundButton button = (CompoundButton) target;
5543             Object tag = button.getTag(R.id.remote_checked_change_listener_tag);
5544             // Temporarily unset the checked change listener so calling setChecked doesn't launch
5545             // the intent.
5546             if (tag instanceof OnCheckedChangeListener) {
5547                 button.setOnCheckedChangeListener(null);
5548                 button.setChecked(mChecked);
5549                 button.setOnCheckedChangeListener((OnCheckedChangeListener) tag);
5550             } else {
5551                 button.setChecked(mChecked);
5552             }
5553         }
5554 
5555         @Override
5556         public int getActionTag() {
5557             return SET_COMPOUND_BUTTON_CHECKED_TAG;
5558         }
5559 
5560         @Override
5561         public boolean canWriteToProto() {
5562             return true;
5563         }
5564 
5565         @Override
5566         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
5567             final long token = out.start(
5568                     RemoteViewsProto.Action.SET_COMPOUND_BUTTON_CHECKED_ACTION);
5569             out.write(RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID,
5570                     appResources.getResourceName(mViewId));
5571             out.write(RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED, mChecked);
5572             out.end(token);
5573         }
5574 
5575         public static PendingResources<Action> createFromProto(ProtoInputStream in)
5576                 throws Exception {
5577             final LongSparseArray<Object> values = new LongSparseArray<>();
5578 
5579             final long token = in.start(RemoteViewsProto.Action.SET_COMPOUND_BUTTON_CHECKED_ACTION);
5580             while (in.nextField() != NO_MORE_FIELDS) {
5581                 switch (in.getFieldNumber()) {
5582                     case (int) RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID:
5583                         values.put(RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID,
5584                                 in.readString(
5585                                         RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID));
5586                         break;
5587                     case (int) RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED:
5588                         values.put(RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED,
5589                                 in.readBoolean(
5590                                         RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED));
5591                         break;
5592                     default:
5593                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
5594                                 + ProtoUtils.currentFieldToString(in));
5595                 }
5596             }
5597             in.end(token);
5598 
5599             checkContainsKeys(values,
5600                     new long[]{RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID});
5601 
5602             return (context, resources, rootData, depth) -> {
5603                 int viewId = getAsIdentifier(resources, values,
5604                         RemoteViewsProto.SetCompoundButtonCheckedAction.VIEW_ID);
5605                 return new SetCompoundButtonCheckedAction(viewId, (boolean) values.get(
5606                         RemoteViewsProto.SetCompoundButtonCheckedAction.CHECKED, false));
5607             };
5608         }
5609     }
5610 
5611     private static class SetRadioGroupCheckedAction extends Action {
5612         @IdRes private final int mCheckedId;
5613 
5614         SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) {
5615             this.mViewId = viewId;
5616             mCheckedId = checkedId;
5617         }
5618 
5619         SetRadioGroupCheckedAction(Parcel in) {
5620             mViewId = in.readInt();
5621             mCheckedId = in.readInt();
5622         }
5623 
5624         @Override
5625         public void writeToParcel(Parcel dest, int flags) {
5626             dest.writeInt(mViewId);
5627             dest.writeInt(mCheckedId);
5628         }
5629 
5630         @Override
5631         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
5632                 throws ActionException {
5633             final View target = root.findViewById(mViewId);
5634             if (target == null) return;
5635 
5636             if (!(target instanceof RadioGroup)) {
5637                 Log.w(LOG_TAG, "Cannot check " + mViewId + " because it's not a RadioGroup");
5638                 return;
5639             }
5640 
5641             RadioGroup group = (RadioGroup) target;
5642 
5643             // Temporarily unset all the checked change listeners while we check the group.
5644             for (int i = 0; i < group.getChildCount(); i++) {
5645                 View child = group.getChildAt(i);
5646                 if (!(child instanceof CompoundButton)) continue;
5647 
5648                 Object tag = child.getTag(R.id.remote_checked_change_listener_tag);
5649                 if (!(tag instanceof OnCheckedChangeListener)) continue;
5650 
5651                 // Clear the checked change listener, we'll restore it after the check.
5652                 ((CompoundButton) child).setOnCheckedChangeListener(null);
5653             }
5654 
5655             group.check(mCheckedId);
5656 
5657             // Loop through the children again and restore the checked change listeners.
5658             for (int i = 0; i < group.getChildCount(); i++) {
5659                 View child = group.getChildAt(i);
5660                 if (!(child instanceof CompoundButton)) continue;
5661 
5662                 Object tag = child.getTag(R.id.remote_checked_change_listener_tag);
5663                 if (!(tag instanceof OnCheckedChangeListener)) continue;
5664 
5665                 ((CompoundButton) child).setOnCheckedChangeListener((OnCheckedChangeListener) tag);
5666             }
5667         }
5668 
5669         @Override
5670         public int getActionTag() {
5671             return SET_RADIO_GROUP_CHECKED;
5672         }
5673 
5674         @Override
5675         public boolean canWriteToProto() {
5676             return true;
5677         }
5678 
5679         @Override
5680         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
5681             final long token = out.start(RemoteViewsProto.Action.SET_RADIO_GROUP_CHECKED_ACTION);
5682             out.write(RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID,
5683                     appResources.getResourceName(mViewId));
5684             if (mCheckedId != -1) {
5685                 out.write(RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID,
5686                         appResources.getResourceName(mCheckedId));
5687             }
5688             out.end(token);
5689         }
5690 
5691         public static PendingResources<Action> createFromProto(ProtoInputStream in)
5692                 throws Exception {
5693             final LongSparseArray<Object> values = new LongSparseArray<>();
5694 
5695             final long token = in.start(RemoteViewsProto.Action.SET_RADIO_GROUP_CHECKED_ACTION);
5696             while (in.nextField() != NO_MORE_FIELDS) {
5697                 switch (in.getFieldNumber()) {
5698                     case (int) RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID:
5699                         values.put(RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID,
5700                                 in.readString(RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID));
5701                         break;
5702                     case (int) RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID:
5703                         values.put(RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID,
5704                                 in.readString(
5705                                         RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID));
5706                         break;
5707                     default:
5708                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
5709                                 + ProtoUtils.currentFieldToString(in));
5710                 }
5711             }
5712             in.end(token);
5713 
5714             checkContainsKeys(values,
5715                     new long[]{RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID});
5716 
5717             return (context, resources, rootData, depth) -> {
5718                 int viewId = getAsIdentifier(resources, values,
5719                         RemoteViewsProto.SetRadioGroupCheckedAction.VIEW_ID);
5720 
5721                 int checkedId = (values.indexOfKey(
5722                         RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID) >= 0)
5723                         ? getAsIdentifier(resources, values,
5724                         RemoteViewsProto.SetRadioGroupCheckedAction.CHECKED_ID) : -1;
5725                 return new SetRadioGroupCheckedAction(viewId, checkedId);
5726             };
5727         }
5728     }
5729 
5730     private static class SetViewOutlinePreferredRadiusAction extends Action {
5731         @ValueType
5732         private final int mValueType;
5733         private final int mValue;
5734 
5735         SetViewOutlinePreferredRadiusAction(@IdRes int viewId, int value,
5736                 @ValueType int valueType) {
5737             this.mViewId = viewId;
5738             this.mValueType = valueType;
5739             this.mValue = value;
5740         }
5741 
5742         SetViewOutlinePreferredRadiusAction(
5743                 @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
5744             this.mViewId = viewId;
5745             this.mValueType = VALUE_TYPE_COMPLEX_UNIT;
5746             this.mValue = TypedValue.createComplexDimension(radius, units);
5747 
5748         }
5749 
5750         SetViewOutlinePreferredRadiusAction(Parcel in) {
5751             mViewId = in.readInt();
5752             mValueType = in.readInt();
5753             mValue = in.readInt();
5754         }
5755 
5756         @Override
5757         public void writeToParcel(Parcel dest, int flags) {
5758             dest.writeInt(mViewId);
5759             dest.writeInt(mValueType);
5760             dest.writeInt(mValue);
5761         }
5762 
5763         @Override
5764         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
5765                 throws ActionException {
5766             final View target = root.findViewById(mViewId);
5767             if (target == null) return;
5768 
5769             try {
5770                 float radius;
5771                 switch (mValueType) {
5772                     case VALUE_TYPE_ATTRIBUTE:
5773                         TypedArray typedArray = target.getContext().obtainStyledAttributes(
5774                                 new int[]{mValue});
5775                         try {
5776                             radius = typedArray.getDimension(0, 0);
5777                         } finally {
5778                             typedArray.recycle();
5779                         }
5780                         break;
5781                     case VALUE_TYPE_RESOURCE:
5782                         radius = mValue == 0 ? 0 : target.getResources().getDimension(mValue);
5783                         break;
5784                     case VALUE_TYPE_COMPLEX_UNIT:
5785                         radius = TypedValue.complexToDimension(mValue,
5786                                 target.getResources().getDisplayMetrics());
5787                         break;
5788                     default:
5789                         radius = mValue;
5790                 }
5791                 target.setOutlineProvider(new RemoteViewOutlineProvider(radius));
5792             } catch (Throwable t) {
5793                 throw new ActionException(t);
5794             }
5795         }
5796 
5797         @Override
5798         public int getActionTag() {
5799             return SET_VIEW_OUTLINE_RADIUS_TAG;
5800         }
5801 
5802         @Override
5803         public boolean canWriteToProto() {
5804             return true;
5805         }
5806 
5807         @Override
5808         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
5809             final long token = out.start(
5810                     RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION);
5811             out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID,
5812                     appResources.getResourceName(mViewId));
5813             out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE, mValueType);
5814             out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE, mValue);
5815             out.end(token);
5816         }
5817 
5818         public static PendingResources<Action> createFromProto(ProtoInputStream in)
5819                 throws Exception {
5820             final LongSparseArray<Object> values = new LongSparseArray<>();
5821 
5822             final long token = in.start(
5823                     RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION);
5824             while (in.nextField() != NO_MORE_FIELDS) {
5825                 switch (in.getFieldNumber()) {
5826                     case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID:
5827                         values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID,
5828                                 in.readString(
5829                                         RemoteViewsProto
5830                                                 .SetViewOutlinePreferredRadiusAction.VIEW_ID));
5831                         break;
5832                     case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE:
5833                         values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE,
5834                                 in.readInt(
5835                                         RemoteViewsProto
5836                                                 .SetViewOutlinePreferredRadiusAction.VALUE_TYPE));
5837                         break;
5838                     case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE:
5839                         values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE,
5840                                 in.readInt(
5841                                         RemoteViewsProto
5842                                                 .SetViewOutlinePreferredRadiusAction.VALUE));
5843                         break;
5844                     default:
5845                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
5846                                 + ProtoUtils.currentFieldToString(in));
5847                 }
5848             }
5849             in.end(token);
5850 
5851             checkContainsKeys(values,
5852                     new long[]{RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID,
5853                             RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE});
5854 
5855             return (context, resources, rootData, depth) -> {
5856                 int viewId = getAsIdentifier(resources, values,
5857                         RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID);
5858                 return new SetViewOutlinePreferredRadiusAction(viewId,
5859                         (int) values.get(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE,
5860                                 0), (int) values.get(
5861                         RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE));
5862             };
5863         }
5864     }
5865 
5866     /**
5867      * OutlineProvider for a view with a radius set by
5868      * {@link #setViewOutlinePreferredRadius(int, float, int)}.
5869      */
5870     public static final class RemoteViewOutlineProvider extends ViewOutlineProvider {
5871         private final float mRadius;
5872 
5873         public RemoteViewOutlineProvider(float radius) {
5874             mRadius = radius;
5875         }
5876 
5877         /** Returns the corner radius used when providing the view outline. */
5878         public float getRadius() {
5879             return mRadius;
5880         }
5881 
5882         @Override
5883         public void getOutline(@NonNull View view, @NonNull Outline outline) {
5884             outline.setRoundRect(
5885                     0 /*left*/,
5886                     0 /* top */,
5887                     view.getWidth() /* right */,
5888                     view.getHeight() /* bottom */,
5889                     mRadius);
5890         }
5891     }
5892 
5893     private class SetDrawInstructionAction extends Action {
5894 
5895         @Nullable
5896         private final DrawInstructions mInstructions;
5897 
5898         SetDrawInstructionAction(@NonNull final DrawInstructions instructions) {
5899             mInstructions = instructions;
5900         }
5901 
5902         SetDrawInstructionAction(@NonNull final Parcel in) {
5903             if (drawDataParcel()) {
5904                 mInstructions = DrawInstructions.readFromParcel(in);
5905             } else {
5906                 mInstructions = null;
5907             }
5908         }
5909 
5910         @Override
5911         public void writeToParcel(Parcel dest, int flags) {
5912             if (drawDataParcel()) {
5913                 DrawInstructions.writeToParcel(mInstructions, dest, flags);
5914             }
5915         }
5916 
5917         @Override
5918         public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
5919                 throws ActionException {
5920             if (drawDataParcel() && mInstructions != null
5921                     && root instanceof RemoteComposePlayer player) {
5922                 final List<byte[]> bytes = mInstructions.mInstructions;
5923                 if (bytes.isEmpty()) {
5924                     return;
5925                 }
5926                 try (ByteArrayInputStream is = new ByteArrayInputStream(bytes.get(0))) {
5927                     player.setDocument(new RemoteComposeDocument(is));
5928                     player.addIdActionListener((viewId, metadata) -> {
5929                         mActions.forEach(action -> {
5930                             if (viewId == action.mViewId
5931                                     && action instanceof SetOnClickResponse setOnClickResponse) {
5932                                 final RemoteResponse response = setOnClickResponse.mResponse;
5933                                 if (response.mFillIntent == null) {
5934                                     response.mFillIntent = new Intent();
5935                                 }
5936                                 response.mFillIntent.putExtra(
5937                                         "remotecompose_metadata", metadata);
5938                                 response.handleViewInteraction(player, params.handler);
5939                             }
5940                         });
5941                     });
5942                 } catch (IOException e) {
5943                     Log.e(LOG_TAG, "Failed to render draw instructions", e);
5944                 }
5945             }
5946         }
5947 
5948         @Override
5949         public int getActionTag() {
5950             return SET_DRAW_INSTRUCTION_TAG;
5951         }
5952 
5953         @Override
5954         public boolean canWriteToProto() {
5955             return drawDataParcel();
5956         }
5957 
5958         @Override
5959         public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
5960             if (!drawDataParcel()) return;
5961             final long token = out.start(RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION);
5962             if (mInstructions != null) {
5963                 for (byte[] bytes : mInstructions.mInstructions) {
5964                     out.write(RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS, bytes);
5965                 }
5966             }
5967             out.end(token);
5968         }
5969     }
5970 
5971     @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
5972     private PendingResources<Action> createSetDrawInstructionActionFromProto(ProtoInputStream in)
5973             throws Exception {
5974         List<byte[]> instructions = new ArrayList<byte[]>();
5975 
5976         final long token = in.start(RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION);
5977         while (in.nextField() != NO_MORE_FIELDS) {
5978             switch (in.getFieldNumber()) {
5979                 case (int) RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS:
5980                     instructions.add(
5981                             in.readBytes(RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS));
5982                     break;
5983                 default:
5984                     Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
5985                             + ProtoUtils.currentFieldToString(in));
5986             }
5987         }
5988         in.end(token);
5989 
5990         return (context, resources, rootData, depth) -> new SetDrawInstructionAction(
5991                 new DrawInstructions.Builder(instructions).build());
5992     }
5993 
5994     /**
5995      * Create a new RemoteViews object that will display the views contained
5996      * in the specified layout file.
5997      *
5998      * @param packageName Name of the package that contains the layout resource
5999      * @param layoutId The id of the layout resource
6000      */
6001     public RemoteViews(String packageName, int layoutId) {
6002         this(getApplicationInfo(packageName, UserHandle.myUserId()), layoutId);
6003     }
6004 
6005     /**
6006      * Create a new RemoteViews object that will display the views contained
6007      * in the specified layout file and change the id of the root view to the specified one.
6008      *
6009      * @param packageName Name of the package that contains the layout resource
6010      * @param layoutId The id of the layout resource
6011      */
6012     public RemoteViews(@NonNull String packageName, @LayoutRes int layoutId, @IdRes int viewId) {
6013         this(packageName, layoutId);
6014         this.mViewId = viewId;
6015     }
6016 
6017     /**
6018      * Create a new RemoteViews object that will display the views contained
6019      * in the specified layout file.
6020      *
6021      * @param application The application whose content is shown by the views.
6022      * @param layoutId The id of the layout resource.
6023      *
6024      * @hide
6025      */
6026     protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
6027         mApplication = application;
6028         mLayoutId = layoutId;
6029         mApplicationInfoCache.put(application);
6030     }
6031 
6032     private boolean hasMultipleLayouts() {
6033         return hasLandscapeAndPortraitLayouts() || hasSizedRemoteViews();
6034     }
6035 
6036     private boolean hasLandscapeAndPortraitLayouts() {
6037         return (mLandscape != null) && (mPortrait != null);
6038     }
6039 
6040     private boolean hasSizedRemoteViews() {
6041         return mSizedRemoteViews != null;
6042     }
6043 
6044     @Nullable
6045     private SizeF getIdealSize() {
6046         return mIdealSize;
6047     }
6048 
6049     private void setIdealSize(@Nullable SizeF size) {
6050         mIdealSize = size;
6051     }
6052 
6053     /**
6054      * Finds the smallest view in {@code mSizedRemoteViews}.
6055      * This method must not be called if {@code mSizedRemoteViews} is null.
6056      */
6057     private RemoteViews findSmallestRemoteView() {
6058         return mSizedRemoteViews.get(mSizedRemoteViews.size() - 1);
6059     }
6060 
6061     /**
6062      * Create a new RemoteViews object that will inflate as the specified
6063      * landspace or portrait RemoteViews, depending on the current configuration.
6064      *
6065      * @param landscape The RemoteViews to inflate in landscape configuration
6066      * @param portrait The RemoteViews to inflate in portrait configuration
6067      * @throws IllegalArgumentException if either landscape or portrait are null or if they are
6068      *   not from the same application
6069      */
6070     public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
6071         if (landscape == null || portrait == null) {
6072             throw new IllegalArgumentException("Both RemoteViews must be non-null");
6073         }
6074         if (!landscape.hasSameAppInfo(portrait.mApplication)) {
6075             throw new IllegalArgumentException(
6076                     "Both RemoteViews must share the same package and user");
6077         }
6078         mApplication = portrait.mApplication;
6079         mLayoutId = portrait.mLayoutId;
6080         mViewId = portrait.mViewId;
6081         mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
6082 
6083         mLandscape = landscape;
6084         mPortrait = portrait;
6085 
6086         mClassCookies = (portrait.mClassCookies != null)
6087                 ? portrait.mClassCookies : landscape.mClassCookies;
6088 
6089         configureDescendantsAsChildren();
6090     }
6091 
6092     /**
6093      * Create a new RemoteViews object that will inflate the layout with the closest size
6094      * specification.
6095      *
6096      * The default remote views in that case is always the one with the smallest area.
6097      *
6098      * If the {@link RemoteViews} host provides the size of the view, the layout with the largest
6099      * area that fits entirely in the provided size will be used (i.e. the width and height of
6100      * the layout must be less than the size of the view, with a 1dp margin to account for
6101      * rounding). If no layout fits in the view, the layout with the smallest area will be used.
6102      *
6103      * @param remoteViews Mapping of size to layout.
6104      * @throws IllegalArgumentException if the map is empty, there are more than
6105      *   MAX_INIT_VIEW_COUNT layouts or the remote views are not all from the same application.
6106      */
6107     public RemoteViews(@NonNull Map<SizeF, RemoteViews> remoteViews) {
6108         if (remoteViews.isEmpty()) {
6109             throw new IllegalArgumentException("The set of RemoteViews cannot be empty");
6110         }
6111         if (remoteViews.size() > MAX_INIT_VIEW_COUNT) {
6112             throw new IllegalArgumentException("Too many RemoteViews in constructor");
6113         }
6114         if (remoteViews.size() == 1) {
6115             // If the map only contains a single mapping, treat this as if that RemoteViews was
6116             // passed as the top-level RemoteViews.
6117             RemoteViews single = remoteViews.values().iterator().next();
6118             initializeFrom(single, /* hierarchyRoot= */ single);
6119             return;
6120         }
6121         mClassCookies = initializeSizedRemoteViews(
6122                 remoteViews.entrySet().stream().map(
6123                         entry -> {
6124                             entry.getValue().setIdealSize(entry.getKey());
6125                             return entry.getValue();
6126                         }
6127                 ).iterator()
6128         );
6129 
6130         RemoteViews smallestView = findSmallestRemoteView();
6131         mApplication = smallestView.mApplication;
6132         mLayoutId = smallestView.mLayoutId;
6133         mViewId = smallestView.mViewId;
6134         mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
6135 
6136         configureDescendantsAsChildren();
6137     }
6138 
6139     // Initialize mSizedRemoteViews and return the class cookies.
6140     private Map<Class, Object> initializeSizedRemoteViews(Iterator<RemoteViews> remoteViews) {
6141         List<RemoteViews> sizedRemoteViews = new ArrayList<>();
6142         Map<Class, Object> classCookies = null;
6143         float viewArea = Float.MAX_VALUE;
6144         RemoteViews smallestView = null;
6145         while (remoteViews.hasNext()) {
6146             RemoteViews view = remoteViews.next();
6147             SizeF size = view.getIdealSize();
6148             if (size == null) {
6149                 throw new IllegalStateException("Expected RemoteViews to have ideal size");
6150             }
6151             float newViewArea = size.getWidth() * size.getHeight();
6152             if (smallestView != null && !view.hasSameAppInfo(smallestView.mApplication)) {
6153                 throw new IllegalArgumentException(
6154                         "All RemoteViews must share the same package and user");
6155             }
6156             if (smallestView == null || newViewArea < viewArea) {
6157                 if (smallestView != null) {
6158                     sizedRemoteViews.add(smallestView);
6159                 }
6160                 viewArea = newViewArea;
6161                 smallestView = view;
6162             } else {
6163                 sizedRemoteViews.add(view);
6164             }
6165             view.setIdealSize(size);
6166             if (classCookies == null) {
6167                 classCookies = view.mClassCookies;
6168             }
6169         }
6170         sizedRemoteViews.add(smallestView);
6171         mSizedRemoteViews = sizedRemoteViews;
6172         return classCookies;
6173     }
6174 
6175     /**
6176      * Creates a copy of another RemoteViews.
6177      */
6178     public RemoteViews(RemoteViews src) {
6179         initializeFrom(src, /* hierarchyRoot= */ null);
6180     }
6181 
6182     /**
6183      * No-arg constructor for use with {@link #initializeFrom(RemoteViews, RemoteViews)}. A
6184      * constructor taking two RemoteViews parameters would clash with the landscape/portrait
6185      * constructor.
6186      */
6187     private RemoteViews() {}
6188 
6189     private static RemoteViews createInitializedFrom(@NonNull RemoteViews src,
6190             @Nullable RemoteViews hierarchyRoot) {
6191         RemoteViews child = new RemoteViews();
6192         child.initializeFrom(src, hierarchyRoot);
6193         return child;
6194     }
6195 
6196     private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
6197         if (hierarchyRoot == null) {
6198             mBitmapCache = src.mBitmapCache;
6199             // We need to create a new instance because we don't reconstruct collection cache
6200             mCollectionCache = new RemoteCollectionCache(src.mCollectionCache);
6201             mApplicationInfoCache = src.mApplicationInfoCache;
6202         } else {
6203             mBitmapCache = hierarchyRoot.mBitmapCache;
6204             mCollectionCache = hierarchyRoot.mCollectionCache;
6205             mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
6206         }
6207         if (hierarchyRoot == null || src.mIsRoot) {
6208             // If there's no provided root, or if src was itself a root, then this RemoteViews is
6209             // the root of the new hierarchy.
6210             mIsRoot = true;
6211             hierarchyRoot = this;
6212         } else {
6213             // Otherwise, we're a descendant in the hierarchy.
6214             mIsRoot = false;
6215         }
6216         mApplication = src.mApplication;
6217         mLayoutId = src.mLayoutId;
6218         mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
6219         mApplyFlags = src.mApplyFlags;
6220         mClassCookies = src.mClassCookies;
6221         mIdealSize = src.mIdealSize;
6222         mProviderInstanceId = src.mProviderInstanceId;
6223         mHasDrawInstructions = src.mHasDrawInstructions;
6224 
6225         if (src.hasLandscapeAndPortraitLayouts()) {
6226             mLandscape = createInitializedFrom(src.mLandscape, hierarchyRoot);
6227             mPortrait = createInitializedFrom(src.mPortrait, hierarchyRoot);
6228         }
6229 
6230         if (src.hasSizedRemoteViews()) {
6231             mSizedRemoteViews = new ArrayList<>(src.mSizedRemoteViews.size());
6232             for (RemoteViews srcView : src.mSizedRemoteViews) {
6233                 mSizedRemoteViews.add(createInitializedFrom(srcView, hierarchyRoot));
6234             }
6235         }
6236 
6237         if (src.mActions != null) {
6238             Parcel p = Parcel.obtain();
6239             p.putClassCookies(mClassCookies);
6240             src.writeActionsToParcel(p, /* flags= */ 0);
6241             p.setDataPosition(0);
6242             // Since src is already in memory, we do not care about stack overflow as it has
6243             // already been read once.
6244             readActionsFromParcel(p, 0);
6245             p.recycle();
6246         }
6247 
6248         // Now that everything is initialized and duplicated, create new caches for this
6249         // RemoteViews and recursively set up all descendants.
6250         if (mIsRoot) {
6251             reconstructCaches();
6252         }
6253     }
6254 
6255     /**
6256      * Reads a RemoteViews object from a parcel.
6257      *
6258      * @param parcel the parcel object
6259      */
6260     public RemoteViews(Parcel parcel) {
6261         this(parcel, /* rootData= */ null, /* info= */ null, /* depth= */ 0);
6262     }
6263 
6264     /**
6265      * Instantiates a RemoteViews object using {@link DrawInstructions}, which serves as an
6266      * alternative to XML layout. {@link DrawInstructions} objects contains the instructions which
6267      * can be interpreted and rendered accordingly in the host process.
6268      *
6269      * @param drawInstructions The {@link DrawInstructions} object
6270      */
6271     @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
6272     public RemoteViews(@NonNull final DrawInstructions drawInstructions) {
6273         Objects.requireNonNull(drawInstructions);
6274         mHasDrawInstructions = true;
6275         addAction(new SetDrawInstructionAction(drawInstructions));
6276     }
6277 
6278     private RemoteViews(@NonNull Parcel parcel, @Nullable HierarchyRootData rootData,
6279             @Nullable ApplicationInfo info, int depth) {
6280         if (depth > MAX_NESTED_VIEWS
6281                 && (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
6282             throw new IllegalArgumentException("Too many nested views.");
6283         }
6284         depth++;
6285 
6286         int mode = parcel.readInt();
6287 
6288         if (rootData == null) {
6289             // We only store a bitmap cache in the root of the RemoteViews.
6290             mBitmapCache = new BitmapCache(parcel);
6291             // Store the class cookies such that they are available when we clone this RemoteView.
6292             mClassCookies = parcel.copyClassCookies();
6293             mCollectionCache = new RemoteCollectionCache(parcel);
6294         } else {
6295             configureAsChild(rootData);
6296         }
6297 
6298         if (mode == MODE_NORMAL) {
6299             mApplication = parcel.readTypedObject(ApplicationInfo.CREATOR);
6300             mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
6301             mLayoutId = parcel.readInt();
6302             mViewId = parcel.readInt();
6303             mLightBackgroundLayoutId = parcel.readInt();
6304 
6305             readActionsFromParcel(parcel, depth);
6306         } else if (mode == MODE_HAS_SIZED_REMOTEVIEWS) {
6307             int numViews = parcel.readInt();
6308             if (numViews > MAX_INIT_VIEW_COUNT) {
6309                 throw new IllegalArgumentException(
6310                         "Too many views in mapping from size to RemoteViews.");
6311             }
6312             List<RemoteViews> remoteViews = new ArrayList<>(numViews);
6313             for (int i = 0; i < numViews; i++) {
6314                 RemoteViews view = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
6315                 info = view.mApplication;
6316                 remoteViews.add(view);
6317             }
6318             initializeSizedRemoteViews(remoteViews.iterator());
6319             RemoteViews smallestView = findSmallestRemoteView();
6320             mApplication = smallestView.mApplication;
6321             mLayoutId = smallestView.mLayoutId;
6322             mViewId = smallestView.mViewId;
6323             mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
6324         } else {
6325             // MODE_HAS_LANDSCAPE_AND_PORTRAIT
6326             mLandscape = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
6327             mPortrait =
6328                     new RemoteViews(parcel, getHierarchyRootData(), mLandscape.mApplication, depth);
6329             mApplication = mPortrait.mApplication;
6330             mLayoutId = mPortrait.mLayoutId;
6331             mViewId = mPortrait.mViewId;
6332             mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
6333         }
6334         mApplyFlags = parcel.readInt();
6335         mProviderInstanceId = parcel.readLong();
6336         mHasDrawInstructions = parcel.readBoolean();
6337 
6338         // Ensure that all descendants have their caches set up recursively.
6339         if (mIsRoot) {
6340             configureDescendantsAsChildren();
6341         }
6342     }
6343 
6344     private void readActionsFromParcel(Parcel parcel, int depth) {
6345         int count = parcel.readInt();
6346         if (count > 0) {
6347             mActions = new ArrayList<>(count);
6348             for (int i = 0; i < count; i++) {
6349                 mActions.add(getActionFromParcel(parcel, depth));
6350             }
6351         }
6352     }
6353 
6354     private Action getActionFromParcel(Parcel parcel, int depth) {
6355         int tag = parcel.readInt();
6356         switch (tag) {
6357             case SET_ON_CLICK_RESPONSE_TAG:
6358                 return new SetOnClickResponse(parcel);
6359             case SET_DRAWABLE_TINT_TAG:
6360                 return new SetDrawableTint(parcel);
6361             case REFLECTION_ACTION_TAG:
6362                 return new ReflectionAction(parcel);
6363             case VIEW_GROUP_ACTION_ADD_TAG:
6364                 return new ViewGroupActionAdd(parcel, mApplication, depth);
6365             case VIEW_GROUP_ACTION_REMOVE_TAG:
6366                 return new ViewGroupActionRemove(parcel);
6367             case VIEW_CONTENT_NAVIGATION_TAG:
6368                 return new ViewContentNavigation(parcel);
6369             case SET_EMPTY_VIEW_ACTION_TAG:
6370                 return new SetEmptyView(parcel);
6371             case SET_PENDING_INTENT_TEMPLATE_TAG:
6372                 return new SetPendingIntentTemplate(parcel);
6373             case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
6374                 return new SetRemoteViewsAdapterIntent(parcel);
6375             case TEXT_VIEW_DRAWABLE_ACTION_TAG:
6376                 return new TextViewDrawableAction(parcel);
6377             case TEXT_VIEW_SIZE_ACTION_TAG:
6378                 return new TextViewSizeAction(parcel);
6379             case VIEW_PADDING_ACTION_TAG:
6380                 return new ViewPaddingAction(parcel);
6381             case BITMAP_REFLECTION_ACTION_TAG:
6382                 return new BitmapReflectionAction(parcel);
6383             case SET_REMOTE_INPUTS_ACTION_TAG:
6384                 return new SetRemoteInputsAction(parcel);
6385             case LAYOUT_PARAM_ACTION_TAG:
6386                 return new LayoutParamAction(parcel);
6387             case SET_RIPPLE_DRAWABLE_COLOR_TAG:
6388                 return new SetRippleDrawableColor(parcel);
6389             case SET_INT_TAG_TAG:
6390                 return new SetIntTagAction(parcel);
6391             case REMOVE_FROM_PARENT_ACTION_TAG:
6392                 return new RemoveFromParentAction(parcel);
6393             case RESOURCE_REFLECTION_ACTION_TAG:
6394                 return new ResourceReflectionAction(parcel);
6395             case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
6396                 return new ComplexUnitDimensionReflectionAction(parcel);
6397             case SET_COMPOUND_BUTTON_CHECKED_TAG:
6398                 return new SetCompoundButtonCheckedAction(parcel);
6399             case SET_RADIO_GROUP_CHECKED:
6400                 return new SetRadioGroupCheckedAction(parcel);
6401             case SET_VIEW_OUTLINE_RADIUS_TAG:
6402                 return new SetViewOutlinePreferredRadiusAction(parcel);
6403             case SET_ON_CHECKED_CHANGE_RESPONSE_TAG:
6404                 return new SetOnCheckedChangeResponse(parcel);
6405             case NIGHT_MODE_REFLECTION_ACTION_TAG:
6406                 return new NightModeReflectionAction(parcel);
6407             case SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG:
6408                 return new SetRemoteCollectionItemListAdapterAction(parcel);
6409             case ATTRIBUTE_REFLECTION_ACTION_TAG:
6410                 return new AttributeReflectionAction(parcel);
6411             case SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG:
6412                 return new SetOnStylusHandwritingResponse(parcel);
6413             case SET_DRAW_INSTRUCTION_TAG:
6414                 return new SetDrawInstructionAction(parcel);
6415             default:
6416                 throw new ActionException("Tag " + tag + " not found");
6417         }
6418     }
6419 
6420     /**
6421      * Returns a deep copy of the RemoteViews object. The RemoteView may not be
6422      * attached to another RemoteView -- it must be the root of a hierarchy.
6423      *
6424      * @deprecated use {@link #RemoteViews(RemoteViews)} instead.
6425      * @throws IllegalStateException if this is not the root of a RemoteView
6426      *         hierarchy
6427      */
6428     @Override
6429     @Deprecated
6430     public RemoteViews clone() {
6431         Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. "
6432                 + "May only clone the root of a RemoteView hierarchy.");
6433 
6434         return new RemoteViews(this);
6435     }
6436 
6437     public String getPackage() {
6438         return (mApplication != null) ? mApplication.packageName : null;
6439     }
6440 
6441     /**
6442      * Returns the layout id of the root layout associated with this RemoteViews. In the case
6443      * that the RemoteViews has both a landscape and portrait root, this will return the layout
6444      * id associated with the portrait layout.
6445      *
6446      * @return the layout id.
6447      */
6448     public int getLayoutId() {
6449         return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
6450                 ? mLightBackgroundLayoutId : mLayoutId;
6451     }
6452 
6453     /**
6454      * Sets the root of the hierarchy and then recursively traverses the tree to update the root
6455      * and populate caches for all descendants.
6456      */
6457     private void configureAsChild(@NonNull HierarchyRootData rootData) {
6458         mIsRoot = false;
6459         mBitmapCache = rootData.mBitmapCache;
6460         mCollectionCache = rootData.mRemoteCollectionCache;
6461         mApplicationInfoCache = rootData.mApplicationInfoCache;
6462         mClassCookies = rootData.mClassCookies;
6463         configureDescendantsAsChildren();
6464     }
6465 
6466     /**
6467      * Recursively traverses the tree to update the root and populate caches for all descendants.
6468      */
6469     private void configureDescendantsAsChildren() {
6470         // Before propagating down the tree, replace our application from the root application info
6471         // cache, to ensure the same instance is present throughout the hierarchy to allow for
6472         // squashing.
6473         mApplication = mApplicationInfoCache.getOrPut(mApplication);
6474 
6475         HierarchyRootData rootData = getHierarchyRootData();
6476         if (hasSizedRemoteViews()) {
6477             for (RemoteViews remoteView : mSizedRemoteViews) {
6478                 remoteView.configureAsChild(rootData);
6479             }
6480         } else if (hasLandscapeAndPortraitLayouts()) {
6481             mLandscape.configureAsChild(rootData);
6482             mPortrait.configureAsChild(rootData);
6483         } else {
6484             if (mActions != null) {
6485                 for (Action action : mActions) {
6486                     action.setHierarchyRootData(rootData);
6487                 }
6488             }
6489         }
6490     }
6491 
6492     /**
6493      * Recreates caches at the root level of the hierarchy, then recursively populates the caches
6494      * down the hierarchy.
6495      */
6496     private void reconstructCaches() {
6497         if (!mIsRoot) return;
6498         mBitmapCache = new BitmapCache();
6499         mApplicationInfoCache = new ApplicationInfoCache();
6500         mApplication = mApplicationInfoCache.getOrPut(mApplication);
6501         configureDescendantsAsChildren();
6502     }
6503 
6504     /**
6505      * Returns an estimate of the bitmap heap memory usage by setBitmap and setImageViewBitmap in
6506      * this RemoteViews.
6507      *
6508      * @hide
6509      */
6510     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
6511     public long estimateMemoryUsage() {
6512         return mBitmapCache.getBitmapMemory();
6513     }
6514 
6515     /**
6516      * Returns an estimate of bitmap heap memory usage by setIcon and setImageViewIcon in this
6517      * RemoteViews. Note that this function will count duplicate Icons in its estimate.
6518      *
6519      * @hide
6520      */
6521     public long estimateIconMemoryUsage() {
6522         AtomicLong total = new AtomicLong(0);
6523         visitIcons(icon -> {
6524             if (icon.getType() == Icon.TYPE_BITMAP || icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
6525                 total.addAndGet(icon.getBitmap().getAllocationByteCount());
6526             }
6527         });
6528         return total.get();
6529     }
6530 
6531     /**
6532      * Returns an estimate of the bitmap heap memory usage for all Icon and Bitmap actions in this
6533      * RemoteViews.
6534      *
6535      * @hide
6536      */
6537     public long estimateTotalBitmapMemoryUsage() {
6538         return estimateMemoryUsage() + estimateIconMemoryUsage();
6539     }
6540 
6541     /**
6542      * Add an action to be executed on the remote side when apply is called.
6543      *
6544      * @param a The action to add
6545      */
6546     private void addAction(Action a) {
6547         if (hasMultipleLayouts()) {
6548             throw new RuntimeException("RemoteViews specifying separate layouts for orientation"
6549                     + " or size cannot be modified. Instead, fully configure each layouts"
6550                     + " individually before constructing the combined layout.");
6551         }
6552         if (mActions == null) {
6553             mActions = new ArrayList<>();
6554         }
6555         mActions.add(a);
6556     }
6557 
6558     /**
6559      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
6560      * given {@link RemoteViews}. This allows users to build "nested"
6561      * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may
6562      * recycle layouts, use {@link #removeAllViews(int)} to clear any existing
6563      * children.
6564      *
6565      * @param viewId The id of the parent {@link ViewGroup} to add child into.
6566      * @param nestedView {@link RemoteViews} that describes the child.
6567      */
6568     public void addView(@IdRes int viewId, RemoteViews nestedView) {
6569         // Clear all children when nested views omitted
6570         addAction(nestedView == null
6571                 ? new ViewGroupActionRemove(viewId)
6572                 : new ViewGroupActionAdd(viewId, nestedView));
6573     }
6574 
6575     /**
6576      * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the given
6577      * {@link RemoteViews}. If the {@link RemoteViews} may be re-inflated or updated,
6578      * {@link #removeAllViews(int)} must be called on the same {@code viewId
6579      * } before the first call to this method for the behavior of this method to be predictable.
6580      *
6581      * The {@code stableId} will be used to identify a potential view to recycled when the remote
6582      * view is inflated. Views can be re-used if inserted in the same order, potentially with
6583      * some views appearing / disappearing. To be recycled the view must not change the layout
6584      * used to inflate it or its view id (see {@link RemoteViews#RemoteViews(String, int, int)}).
6585      *
6586      * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
6587      * are not reset, so what was applied in previous round will have an effect. As a view may be
6588      * re-created at any time by the host, the RemoteViews should not rely on keeping information
6589      * from previous applications and always re-set all the properties they need.
6590      *
6591      * @param viewId The id of the parent {@link ViewGroup} to add child into.
6592      * @param nestedView {@link RemoteViews} that describes the child.
6593      * @param stableId An id that is stable across different versions of RemoteViews.
6594      */
6595     public void addStableView(@IdRes int viewId, @NonNull RemoteViews nestedView, int stableId) {
6596         addAction(new ViewGroupActionAdd(viewId, nestedView, -1 /* index */, stableId));
6597     }
6598 
6599     /**
6600      * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
6601      * given {@link RemoteViews}.
6602      *
6603      * @param viewId The id of the parent {@link ViewGroup} to add the child into.
6604      * @param nestedView {@link RemoteViews} of the child to add.
6605      * @param index The position at which to add the child.
6606      *
6607      * @hide
6608      */
6609     @UnsupportedAppUsage
6610     public void addView(@IdRes int viewId, RemoteViews nestedView, int index) {
6611         addAction(new ViewGroupActionAdd(viewId, nestedView, index));
6612     }
6613 
6614     /**
6615      * Equivalent to calling {@link ViewGroup#removeAllViews()}.
6616      *
6617      * @param viewId The id of the parent {@link ViewGroup} to remove all
6618      *            children from.
6619      */
6620     public void removeAllViews(@IdRes int viewId) {
6621         addAction(new ViewGroupActionRemove(viewId));
6622     }
6623 
6624     /**
6625      * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any
6626      * child that has the {@code viewIdToKeep} as its id.
6627      *
6628      * @param viewId The id of the parent {@link ViewGroup} to remove children from.
6629      * @param viewIdToKeep The id of a child that should not be removed.
6630      *
6631      * @hide
6632      */
6633     public void removeAllViewsExceptId(@IdRes int viewId, @IdRes int viewIdToKeep) {
6634         addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
6635     }
6636 
6637     /**
6638      * Removes the {@link View} specified by the {@code viewId} from its parent {@link ViewManager}.
6639      * This will do nothing if the viewId specifies the root view of this RemoteViews.
6640      *
6641      * @param viewId The id of the {@link View} to remove from its parent.
6642      *
6643      * @hide
6644      */
6645     public void removeFromParent(@IdRes int viewId) {
6646         addAction(new RemoveFromParentAction(viewId));
6647     }
6648 
6649     /**
6650      * Equivalent to calling {@link AdapterViewAnimator#showNext()}
6651      *
6652      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
6653      * @deprecated As RemoteViews may be reapplied frequently, it is preferable to call
6654      * {@link #setDisplayedChild(int, int)} to ensure that the adapter index does not change
6655      * unexpectedly.
6656      */
6657     @Deprecated
6658     public void showNext(@IdRes int viewId) {
6659         addAction(new ViewContentNavigation(viewId, true /* next */));
6660     }
6661 
6662     /**
6663      * Equivalent to calling {@link AdapterViewAnimator#showPrevious()}
6664      *
6665      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
6666      * @deprecated As RemoteViews may be reapplied frequently, it is preferable to call
6667      * {@link #setDisplayedChild(int, int)} to ensure that the adapter index does not change
6668      * unexpectedly.
6669      */
6670     @Deprecated
6671     public void showPrevious(@IdRes int viewId) {
6672         addAction(new ViewContentNavigation(viewId, false /* next */));
6673     }
6674 
6675     /**
6676      * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)}
6677      *
6678      * @param viewId The id of the view on which to call
6679      *               {@link AdapterViewAnimator#setDisplayedChild(int)}
6680      */
6681     public void setDisplayedChild(@IdRes int viewId, int childIndex) {
6682         setInt(viewId, "setDisplayedChild", childIndex);
6683     }
6684 
6685     /**
6686      * Equivalent to calling {@link View#setVisibility(int)}
6687      *
6688      * @param viewId The id of the view whose visibility should change
6689      * @param visibility The new visibility for the view
6690      */
6691     public void setViewVisibility(@IdRes int viewId, @View.Visibility int visibility) {
6692         setInt(viewId, "setVisibility", visibility);
6693     }
6694 
6695     /**
6696      * Equivalent to calling {@link TextView#setText(CharSequence)}
6697      *
6698      * @param viewId The id of the view whose text should change
6699      * @param text The new text for the view
6700      */
6701     public void setTextViewText(@IdRes int viewId, CharSequence text) {
6702         setCharSequence(viewId, "setText", text);
6703     }
6704 
6705     /**
6706      * Equivalent to calling {@link TextView#setTextSize(int, float)}
6707      *
6708      * @param viewId The id of the view whose text size should change
6709      * @param units The units of size (e.g. COMPLEX_UNIT_SP)
6710      * @param size The size of the text
6711      */
6712     public void setTextViewTextSize(@IdRes int viewId, int units, float size) {
6713         addAction(new TextViewSizeAction(viewId, units, size));
6714     }
6715 
6716     /**
6717      * Equivalent to calling
6718      * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}.
6719      *
6720      * @param viewId The id of the view whose text should change
6721      * @param left The id of a drawable to place to the left of the text, or 0
6722      * @param top The id of a drawable to place above the text, or 0
6723      * @param right The id of a drawable to place to the right of the text, or 0
6724      * @param bottom The id of a drawable to place below the text, or 0
6725      */
6726     public void setTextViewCompoundDrawables(@IdRes int viewId, @DrawableRes int left,
6727             @DrawableRes int top, @DrawableRes int right, @DrawableRes int bottom) {
6728         addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
6729     }
6730 
6731     /**
6732      * Equivalent to calling {@link
6733      * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}.
6734      *
6735      * @param viewId The id of the view whose text should change
6736      * @param start The id of a drawable to place before the text (relative to the
6737      * layout direction), or 0
6738      * @param top The id of a drawable to place above the text, or 0
6739      * @param end The id of a drawable to place after the text, or 0
6740      * @param bottom The id of a drawable to place below the text, or 0
6741      */
6742     public void setTextViewCompoundDrawablesRelative(@IdRes int viewId, @DrawableRes int start,
6743             @DrawableRes int top, @DrawableRes int end, @DrawableRes int bottom) {
6744         addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
6745     }
6746 
6747     /**
6748      * Equivalent to calling {@link
6749      * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
6750      * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
6751      *
6752      * @param viewId The id of the view whose text should change
6753      * @param left an Icon to place to the left of the text, or 0
6754      * @param top an Icon to place above the text, or 0
6755      * @param right an Icon to place to the right of the text, or 0
6756      * @param bottom an Icon to place below the text, or 0
6757      *
6758      * @hide
6759      */
6760     public void setTextViewCompoundDrawables(@IdRes int viewId,
6761             Icon left, Icon top, Icon right, Icon bottom) {
6762         addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom));
6763     }
6764 
6765     /**
6766      * Equivalent to calling {@link
6767      * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
6768      * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
6769      *
6770      * @param viewId The id of the view whose text should change
6771      * @param start an Icon to place before the text (relative to the
6772      * layout direction), or 0
6773      * @param top an Icon to place above the text, or 0
6774      * @param end an Icon to place after the text, or 0
6775      * @param bottom an Icon to place below the text, or 0
6776      *
6777      * @hide
6778      */
6779     public void setTextViewCompoundDrawablesRelative(@IdRes int viewId,
6780             Icon start, Icon top, Icon end, Icon bottom) {
6781         addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom));
6782     }
6783 
6784     /**
6785      * Equivalent to calling {@link ImageView#setImageResource(int)}
6786      *
6787      * @param viewId The id of the view whose drawable should change
6788      * @param srcId The new resource id for the drawable
6789      */
6790     public void setImageViewResource(@IdRes int viewId, @DrawableRes int srcId) {
6791         setInt(viewId, "setImageResource", srcId);
6792     }
6793 
6794     /**
6795      * Equivalent to calling {@link ImageView#setImageURI(Uri)}
6796      *
6797      * @param viewId The id of the view whose drawable should change
6798      * @param uri The Uri for the image
6799      */
6800     public void setImageViewUri(@IdRes int viewId, Uri uri) {
6801         setUri(viewId, "setImageURI", uri);
6802     }
6803 
6804     /**
6805      * Equivalent to calling {@link ImageView#setImageBitmap(Bitmap)}
6806      *
6807      * @param viewId The id of the view whose bitmap should change
6808      * @param bitmap The new Bitmap for the drawable
6809      */
6810     public void setImageViewBitmap(@IdRes int viewId, Bitmap bitmap) {
6811         setBitmap(viewId, "setImageBitmap", bitmap);
6812     }
6813 
6814     /**
6815      * Equivalent to calling {@link ImageView#setImageIcon(Icon)}
6816      *
6817      * @param viewId The id of the view whose bitmap should change
6818      * @param icon The new Icon for the ImageView
6819      */
6820     public void setImageViewIcon(@IdRes int viewId, Icon icon) {
6821         setIcon(viewId, "setImageIcon", icon);
6822     }
6823 
6824     /**
6825      * Equivalent to calling {@link AdapterView#setEmptyView(View)}
6826      *
6827      * @param viewId The id of the view on which to set the empty view
6828      * @param emptyViewId The view id of the empty view
6829      */
6830     public void setEmptyView(@IdRes int viewId, @IdRes int emptyViewId) {
6831         addAction(new SetEmptyView(viewId, emptyViewId));
6832     }
6833 
6834     /**
6835      * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
6836      * {@link Chronometer#setFormat Chronometer.setFormat},
6837      * and {@link Chronometer#start Chronometer.start()} or
6838      * {@link Chronometer#stop Chronometer.stop()}.
6839      *
6840      * @param viewId The id of the {@link Chronometer} to change
6841      * @param base The time at which the timer would have read 0:00.  This
6842      *             time should be based off of
6843      *             {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
6844      * @param format The Chronometer format string, or null to
6845      *               simply display the timer value.
6846      * @param started True if you want the clock to be started, false if not.
6847      *
6848      * @see #setChronometerCountDown(int, boolean)
6849      */
6850     public void setChronometer(@IdRes int viewId, long base, String format, boolean started) {
6851         setLong(viewId, "setBase", base);
6852         setString(viewId, "setFormat", format);
6853         setBoolean(viewId, "setStarted", started);
6854     }
6855 
6856     /**
6857      * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
6858      * the chronometer with the given viewId.
6859      *
6860      * @param viewId The id of the {@link Chronometer} to change
6861      * @param isCountDown True if you want the chronometer to count down to base instead of
6862      *                    counting up.
6863      */
6864     public void setChronometerCountDown(@IdRes int viewId, boolean isCountDown) {
6865         setBoolean(viewId, "setCountDown", isCountDown);
6866     }
6867 
6868     /**
6869      * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
6870      * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
6871      * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
6872      *
6873      * If indeterminate is true, then the values for max and progress are ignored.
6874      *
6875      * @param viewId The id of the {@link ProgressBar} to change
6876      * @param max The 100% value for the progress bar
6877      * @param progress The current value of the progress bar.
6878      * @param indeterminate True if the progress bar is indeterminate,
6879      *                false if not.
6880      */
6881     public void setProgressBar(@IdRes int viewId, int max, int progress,
6882             boolean indeterminate) {
6883         setBoolean(viewId, "setIndeterminate", indeterminate);
6884         if (!indeterminate) {
6885             setInt(viewId, "setMax", max);
6886             setInt(viewId, "setProgress", progress);
6887         }
6888     }
6889 
6890     /**
6891      * Equivalent to calling
6892      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
6893      * to launch the provided {@link PendingIntent}. The source bounds
6894      * ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the clicked
6895      * view in screen space.
6896      * Note that any activity options associated with the mPendingIntent may get overridden
6897      * before starting the intent.
6898      *
6899      * When setting the on-click action of items within collections (eg. {@link ListView},
6900      * {@link StackView} etc.), this method will not work. Instead, use {@link
6901      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)} in conjunction with
6902      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
6903      *
6904      * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
6905      * @param pendingIntent The {@link PendingIntent} to send when user clicks
6906      */
6907     public void setOnClickPendingIntent(@IdRes int viewId, PendingIntent pendingIntent) {
6908         setOnClickResponse(viewId, RemoteResponse.fromPendingIntent(pendingIntent));
6909     }
6910 
6911     /**
6912      * Equivalent of calling
6913      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
6914      * to launch the provided {@link RemoteResponse}.
6915      *
6916      * @param viewId The id of the view that will trigger the {@link RemoteResponse} when clicked
6917      * @param response The {@link RemoteResponse} to send when user clicks
6918      */
6919     public void setOnClickResponse(@IdRes int viewId, @NonNull RemoteResponse response) {
6920         addAction(new SetOnClickResponse(viewId, response));
6921     }
6922 
6923     /**
6924      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
6925      * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
6926      * this method should be used to set a single PendingIntent template on the collection, and
6927      * individual items can differentiate their on-click behavior using
6928      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
6929      *
6930      * @param viewId The id of the collection who's children will use this PendingIntent template
6931      *          when clicked
6932      * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
6933      *          by a child of viewId and executed when that child is clicked
6934      */
6935     public void setPendingIntentTemplate(@IdRes int viewId, PendingIntent pendingIntentTemplate) {
6936         if (hasDrawInstructions()) {
6937             getPendingIntentTemplate().set(viewId, pendingIntentTemplate);
6938             tryAddRemoteResponse(viewId);
6939         } else {
6940             addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
6941         }
6942     }
6943 
6944     /**
6945      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
6946      * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
6947      * a single PendingIntent template can be set on the collection, see {@link
6948      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
6949      * action of a given item can be distinguished by setting a fillInIntent on that item. The
6950      * fillInIntent is then combined with the PendingIntent template in order to determine the final
6951      * intent which will be executed when the item is clicked. This works as follows: any fields
6952      * which are left blank in the PendingIntent template, but are provided by the fillInIntent
6953      * will be overwritten, and the resulting PendingIntent will be used. The rest
6954      * of the PendingIntent template will then be filled in with the associated fields that are
6955      * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
6956      *
6957      * @param viewId The id of the view on which to set the fillInIntent
6958      * @param fillInIntent The intent which will be combined with the parent's PendingIntent
6959      *        in order to determine the on-click behavior of the view specified by viewId
6960      */
6961     public void setOnClickFillInIntent(@IdRes int viewId, Intent fillInIntent) {
6962         if (hasDrawInstructions()) {
6963             getFillInIntent().set(viewId, fillInIntent);
6964             tryAddRemoteResponse(viewId);
6965         } else {
6966             setOnClickResponse(viewId, RemoteResponse.fromFillInIntent(fillInIntent));
6967         }
6968     }
6969 
6970     /**
6971      * Equivalent to calling
6972      * {@link android.widget.CompoundButton#setOnCheckedChangeListener(
6973      * android.widget.CompoundButton.OnCheckedChangeListener)}
6974      * to launch the provided {@link RemoteResponse}.
6975      *
6976      * The intent will be filled with the current checked state of the view at the key
6977      * {@link #EXTRA_CHECKED}.
6978      *
6979      * The {@link RemoteResponse} will not be launched in response to check changes arising from
6980      * {@link #setCompoundButtonChecked(int, boolean)} or {@link #setRadioGroupChecked(int, int)}
6981      * usages.
6982      *
6983      * The {@link RemoteResponse} must be created using
6984      * {@link RemoteResponse#fromFillInIntent(Intent)} in conjunction with
6985      * {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)} for items inside
6986      * collections (eg. {@link ListView}, {@link StackView} etc.).
6987      *
6988      * Otherwise, create the {@link RemoteResponse} using
6989      * {@link RemoteResponse#fromPendingIntent(PendingIntent)}.
6990      *
6991      * @param viewId The id of the view that will trigger the {@link PendingIntent} when checked
6992      *               state changes.
6993      * @param response The {@link RemoteResponse} to send when the checked state changes.
6994      */
6995     public void setOnCheckedChangeResponse(
6996             @IdRes int viewId,
6997             @NonNull RemoteResponse response) {
6998         addAction(
6999                 new SetOnCheckedChangeResponse(
7000                         viewId,
7001                         response.setInteractionType(
7002                                 RemoteResponse.INTERACTION_TYPE_CHECKED_CHANGE)));
7003     }
7004 
7005     /**
7006      * Equivalent to calling {@link View#setHandwritingDelegatorCallback(Runnable)} to send the
7007      * provided {@link PendingIntent}.
7008      *
7009      * <p>A common use case is a remote view which looks like a text editor but does not actually
7010      * support text editing itself, and clicking on the remote view launches an activity containing
7011      * an EditText. To support handwriting initiation in this case, this method can be called on the
7012      * remote view to configure it as a handwriting delegator, meaning that stylus movement on the
7013      * remote view triggers a {@link PendingIntent} and starts handwriting mode for the delegate
7014      * EditText. The {@link PendingIntent} is typically the same as the one passed to {@link
7015      * #setOnClickPendingIntent} which launches the activity containing the EditText. The EditText
7016      * should call {@link View#setIsHandwritingDelegate} to set it as a delegate, and also use
7017      * {@link View#setAllowedHandwritingDelegatorPackage} or {@link
7018      * android.view.inputmethod.InputMethodManager#HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED}
7019      * if necessary to support delegators from the package displaying the remote view.
7020      *
7021      * @param viewId identifier of the view that will trigger the {@link PendingIntent} when a
7022      *     stylus {@link MotionEvent} occurs within the view's bounds
7023      * @param pendingIntent the {@link PendingIntent} to send, or {@code null} to clear the
7024      *     handwriting delegation
7025      */
7026     @FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
7027     public void setOnStylusHandwritingPendingIntent(
7028             @IdRes int viewId, @Nullable PendingIntent pendingIntent) {
7029         addAction(new SetOnStylusHandwritingResponse(viewId, pendingIntent));
7030     }
7031 
7032     /**
7033      * @hide
7034      * Equivalent to calling
7035      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
7036      * on the {@link Drawable} of a given view.
7037      * <p>
7038      *
7039      * @param viewId The id of the view that contains the target
7040      *            {@link Drawable}
7041      * @param targetBackground If true, apply these parameters to the
7042      *            {@link Drawable} returned by
7043      *            {@link android.view.View#getBackground()}. Otherwise, assume
7044      *            the target view is an {@link ImageView} and apply them to
7045      *            {@link ImageView#getDrawable()}.
7046      * @param colorFilter Specify a color for a
7047      *            {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
7048      *            {@code mode} is {@code null}.
7049      * @param mode Specify a PorterDuff mode for this drawable, or null to leave
7050      *            unchanged.
7051      */
7052     public void setDrawableTint(@IdRes int viewId, boolean targetBackground,
7053             @ColorInt int colorFilter, @NonNull PorterDuff.Mode mode) {
7054         addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
7055     }
7056 
7057     /**
7058      * @hide
7059      * Equivalent to calling
7060      * {@link RippleDrawable#setColor(ColorStateList)} on the {@link Drawable} of a given view,
7061      * assuming it's a {@link RippleDrawable}.
7062      * <p>
7063      *
7064      * @param viewId The id of the view that contains the target
7065      *            {@link RippleDrawable}
7066      * @param colorStateList Specify a color for a
7067      *            {@link ColorStateList} for this drawable.
7068      */
7069     public void setRippleDrawableColor(@IdRes int viewId, ColorStateList colorStateList) {
7070         addAction(new SetRippleDrawableColor(viewId, colorStateList));
7071     }
7072 
7073     /**
7074      * @hide
7075      * Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
7076      *
7077      * @param viewId The id of the view whose tint should change
7078      * @param tint the tint to apply, may be {@code null} to clear tint
7079      */
7080     public void setProgressTintList(@IdRes int viewId, ColorStateList tint) {
7081         addAction(new ReflectionAction(viewId, "setProgressTintList",
7082                 BaseReflectionAction.COLOR_STATE_LIST, tint));
7083     }
7084 
7085     /**
7086      * @hide
7087      * Equivalent to calling {@link android.widget.ProgressBar#setProgressBackgroundTintList}.
7088      *
7089      * @param viewId The id of the view whose tint should change
7090      * @param tint the tint to apply, may be {@code null} to clear tint
7091      */
7092     public void setProgressBackgroundTintList(@IdRes int viewId, ColorStateList tint) {
7093         addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
7094                 BaseReflectionAction.COLOR_STATE_LIST, tint));
7095     }
7096 
7097     /**
7098      * @hide
7099      * Equivalent to calling {@link android.widget.ProgressBar#setIndeterminateTintList}.
7100      *
7101      * @param viewId The id of the view whose tint should change
7102      * @param tint the tint to apply, may be {@code null} to clear tint
7103      */
7104     public void setProgressIndeterminateTintList(@IdRes int viewId, ColorStateList tint) {
7105         addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
7106                 BaseReflectionAction.COLOR_STATE_LIST, tint));
7107     }
7108 
7109     /**
7110      * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}.
7111      *
7112      * @param viewId The id of the view whose text color should change
7113      * @param color Sets the text color for all the states (normal, selected,
7114      *            focused) to be this color.
7115      */
7116     public void setTextColor(@IdRes int viewId, @ColorInt int color) {
7117         setInt(viewId, "setTextColor", color);
7118     }
7119 
7120     /**
7121      * @hide
7122      * Equivalent to calling {@link android.widget.TextView#setTextColor(ColorStateList)}.
7123      *
7124      * @param viewId The id of the view whose text color should change
7125      * @param colors the text colors to set
7126      */
7127     public void setTextColor(@IdRes int viewId, ColorStateList colors) {
7128         addAction(new ReflectionAction(viewId, "setTextColor",
7129                 BaseReflectionAction.COLOR_STATE_LIST, colors));
7130     }
7131 
7132     /**
7133      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
7134      *
7135      * @param appWidgetId The id of the app widget which contains the specified view. (This
7136      *      parameter is ignored in this deprecated method)
7137      * @param viewId The id of the {@link AdapterView}
7138      * @param intent The intent of the service which will be
7139      *            providing data to the RemoteViewsAdapter
7140      * @deprecated This method has been deprecated. See
7141      *      {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
7142      */
7143     @Deprecated
7144     public void setRemoteAdapter(int appWidgetId, @IdRes int viewId, Intent intent) {
7145         setRemoteAdapter(viewId, intent);
7146     }
7147 
7148     /**
7149      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
7150      * Can only be used for App Widgets.
7151      *
7152      * @param viewId The id of the {@link AdapterView}
7153      * @param intent The intent of the service which will be
7154      *            providing data to the RemoteViewsAdapter
7155      * @deprecated use
7156      * {@link #setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)} instead
7157      */
7158     @Deprecated
7159     public void setRemoteAdapter(@IdRes int viewId, Intent intent) {
7160         if (remoteAdapterConversion()) {
7161             addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent));
7162         } else {
7163             addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
7164         }
7165     }
7166 
7167     /**
7168      * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
7169      * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
7170      * This is a simpler but less flexible approach to populating collection widgets. Its use is
7171      * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
7172      * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
7173      * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
7174      * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
7175      *
7176      * This API is supported in the compatibility library for previous API levels, see
7177      * RemoteViewsCompat.
7178      *
7179      * @param viewId The id of the {@link AdapterView}
7180      * @param list The list of RemoteViews which will populate the view specified by viewId.
7181      * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
7182      *      RemoteViews. This count cannot change during the life-cycle of a given widget, so this
7183      *      parameter should account for the maximum possible number of types that may appear in the
7184      *      See {@link Adapter#getViewTypeCount()}.
7185      *
7186      * @hide
7187      * @deprecated this appears to have no users outside of UnsupportedAppUsage?
7188      */
7189     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
7190     @Deprecated
7191     public void setRemoteAdapter(@IdRes int viewId, ArrayList<RemoteViews> list,
7192             int viewTypeCount) {
7193         RemoteCollectionItems.Builder b = new RemoteCollectionItems.Builder();
7194         for (int i = 0; i < list.size(); i++) {
7195             b.addItem(i, list.get(i));
7196         }
7197         setRemoteAdapter(viewId, b.setViewTypeCount(viewTypeCount).build());
7198     }
7199 
7200     /**
7201      * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
7202      * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
7203      * This is a simpler but less flexible approach to populating collection widgets. Its use is
7204      * encouraged for most scenarios, as long as the total memory within the list of RemoteViews
7205      * is relatively small (ie. doesn't contain large or numerous Bitmaps, see {@link
7206      * RemoteViews#setImageViewBitmap}). In the case of numerous images, the use of API is still
7207      * possible by setting image URIs instead of Bitmaps, see {@link RemoteViews#setImageViewUri}.
7208      *
7209      * This API is supported in the compatibility library for previous API levels, see
7210      * RemoteViewsCompat.
7211      *
7212      * @param viewId The id of the {@link AdapterView}.
7213      * @param items The items to display in the {@link AdapterView}.
7214      */
7215     public void setRemoteAdapter(@IdRes int viewId, @NonNull RemoteCollectionItems items) {
7216         addAction(new SetRemoteCollectionItemListAdapterAction(viewId, items));
7217     }
7218 
7219     /**
7220      * Equivalent to calling {@link ListView#smoothScrollToPosition(int)}.
7221      *
7222      * @param viewId The id of the view to change
7223      * @param position Scroll to this adapter position
7224      */
7225     public void setScrollPosition(@IdRes int viewId, int position) {
7226         setInt(viewId, "smoothScrollToPosition", position);
7227     }
7228 
7229     /**
7230      * Equivalent to calling {@link ListView#smoothScrollByOffset(int)}.
7231      *
7232      * @param viewId The id of the view to change
7233      * @param offset Scroll by this adapter position offset
7234      */
7235     public void setRelativeScrollPosition(@IdRes int viewId, int offset) {
7236         setInt(viewId, "smoothScrollByOffset", offset);
7237     }
7238 
7239     /**
7240      * Equivalent to calling {@link android.view.View#setPadding(int, int, int, int)}.
7241      *
7242      * @param viewId The id of the view to change
7243      * @param left the left padding in pixels
7244      * @param top the top padding in pixels
7245      * @param right the right padding in pixels
7246      * @param bottom the bottom padding in pixels
7247      */
7248     public void setViewPadding(@IdRes int viewId,
7249             @Px int left, @Px int top, @Px int right, @Px int bottom) {
7250         addAction(new ViewPaddingAction(viewId, left, top, right, bottom));
7251     }
7252 
7253     /**
7254      * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
7255      * Only works if the {@link View#getLayoutParams()} supports margins.
7256      *
7257      * @param viewId The id of the view to change
7258      * @param type The margin being set e.g. {@link #MARGIN_END}
7259      * @param dimen a dimension resource to apply to the margin, or 0 to clear the margin.
7260      */
7261     public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type,
7262             @DimenRes int dimen) {
7263         addAction(new LayoutParamAction(viewId, type, dimen, VALUE_TYPE_RESOURCE));
7264     }
7265 
7266     /**
7267      * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
7268      * Only works if the {@link View#getLayoutParams()} supports margins.
7269      *
7270      * @param viewId The id of the view to change
7271      * @param type The margin being set e.g. {@link #MARGIN_END}
7272      * @param attr a dimension attribute to apply to the margin, or 0 to clear the margin.
7273      */
7274     public void setViewLayoutMarginAttr(@IdRes int viewId, @MarginType int type,
7275             @AttrRes int attr) {
7276         addAction(new LayoutParamAction(viewId, type, attr, VALUE_TYPE_ATTRIBUTE));
7277     }
7278 
7279     /**
7280      * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
7281      * Only works if the {@link View#getLayoutParams()} supports margins.
7282      *
7283      * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
7284      * Setting margins in pixels will behave poorly when the RemoteViews object is used on a
7285      * display with a different density.
7286      *
7287      * @param viewId The id of the view to change
7288      * @param type The margin being set e.g. {@link #MARGIN_END}
7289      * @param value a value for the margin the given units.
7290      * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
7291      */
7292     public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value,
7293             @ComplexDimensionUnit int units) {
7294         addAction(new LayoutParamAction(viewId, type, value, units));
7295     }
7296 
7297     /**
7298      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} except that you may
7299      * provide the value in any dimension units.
7300      *
7301      * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0,
7302      * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
7303      * Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a
7304      * display with a different density.
7305      *
7306      * @param width Width of the view in the given units
7307      * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
7308      */
7309     public void setViewLayoutWidth(@IdRes int viewId, float width,
7310             @ComplexDimensionUnit int units) {
7311         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, width, units));
7312     }
7313 
7314     /**
7315      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} with
7316      * the result of {@link Resources#getDimensionPixelSize(int)}.
7317      *
7318      * @param widthDimen the dimension resource for the view's width
7319      */
7320     public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) {
7321         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen,
7322                 VALUE_TYPE_RESOURCE));
7323     }
7324 
7325     /**
7326      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} with
7327      * the value of the given attribute in the current theme.
7328      *
7329      * @param widthAttr the dimension attribute for the view's width
7330      */
7331     public void setViewLayoutWidthAttr(@IdRes int viewId, @AttrRes int widthAttr) {
7332         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthAttr,
7333                 VALUE_TYPE_ATTRIBUTE));
7334     }
7335 
7336     /**
7337      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} except that you may
7338      * provide the value in any dimension units.
7339      *
7340      * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0,
7341      * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
7342      * Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a
7343      * display with a different density.
7344      *
7345      * @param height height of the view in the given units
7346      * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
7347      */
7348     public void setViewLayoutHeight(@IdRes int viewId, float height,
7349             @ComplexDimensionUnit int units) {
7350         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, height, units));
7351     }
7352 
7353     /**
7354      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} with
7355      * the result of {@link Resources#getDimensionPixelSize(int)}.
7356      *
7357      * @param heightDimen a dimen resource to read the height from.
7358      */
7359     public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) {
7360         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen,
7361                 VALUE_TYPE_RESOURCE));
7362     }
7363 
7364     /**
7365      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} with
7366      * the value of the given attribute in the current theme.
7367      *
7368      * @param heightAttr a dimen attribute to read the height from.
7369      */
7370     public void setViewLayoutHeightAttr(@IdRes int viewId, @AttrRes int heightAttr) {
7371         addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightAttr,
7372                 VALUE_TYPE_ATTRIBUTE));
7373     }
7374 
7375     /**
7376      * Sets an OutlineProvider on the view whose corner radius is a dimension calculated using
7377      * {@link TypedValue#applyDimension(int, float, DisplayMetrics)}.
7378      *
7379      * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
7380      * Setting margins in pixels will behave poorly when the RemoteViews object is used on a
7381      * display with a different density.
7382      */
7383     public void setViewOutlinePreferredRadius(
7384             @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
7385         addAction(new SetViewOutlinePreferredRadiusAction(viewId, radius, units));
7386     }
7387 
7388     /**
7389      * Sets an OutlineProvider on the view whose corner radius is a dimension resource with
7390      * {@code resId}.
7391      */
7392     public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) {
7393         addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId, VALUE_TYPE_RESOURCE));
7394     }
7395 
7396     /**
7397      * Sets an OutlineProvider on the view whose corner radius is a dimension attribute with
7398      * {@code attrId}.
7399      */
7400     public void setViewOutlinePreferredRadiusAttr(@IdRes int viewId, @AttrRes int attrId) {
7401         addAction(new SetViewOutlinePreferredRadiusAction(viewId, attrId, VALUE_TYPE_ATTRIBUTE));
7402     }
7403 
7404     /**
7405      * Call a method taking one boolean on a view in the layout for this RemoteViews.
7406      *
7407      * @param viewId The id of the view on which to call the method.
7408      * @param methodName The name of the method to call.
7409      * @param value The value to pass to the method.
7410      */
7411     public void setBoolean(@IdRes int viewId, String methodName, boolean value) {
7412         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BOOLEAN, value));
7413     }
7414 
7415     /**
7416      * Call a method taking one byte on a view in the layout for this RemoteViews.
7417      *
7418      * @param viewId The id of the view on which to call the method.
7419      * @param methodName The name of the method to call.
7420      * @param value The value to pass to the method.
7421      */
7422     public void setByte(@IdRes int viewId, String methodName, byte value) {
7423         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BYTE, value));
7424     }
7425 
7426     /**
7427      * Call a method taking one short on a view in the layout for this RemoteViews.
7428      *
7429      * @param viewId The id of the view on which to call the method.
7430      * @param methodName The name of the method to call.
7431      * @param value The value to pass to the method.
7432      */
7433     public void setShort(@IdRes int viewId, String methodName, short value) {
7434         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.SHORT, value));
7435     }
7436 
7437     /**
7438      * Call a method taking one int on a view in the layout for this RemoteViews.
7439      *
7440      * @param viewId The id of the view on which to call the method.
7441      * @param methodName The name of the method to call.
7442      * @param value The value to pass to the method.
7443      */
7444     public void setInt(@IdRes int viewId, String methodName, int value) {
7445         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INT, value));
7446     }
7447 
7448     /**
7449      * Call a method taking one int, a size in pixels, on a view in the layout for this
7450      * RemoteViews.
7451      *
7452      * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
7453      * (re-)applied.
7454      *
7455      * Undefined resources will result in an exception, except 0 which will resolve to 0.
7456      *
7457      * @param viewId The id of the view on which to call the method.
7458      * @param methodName The name of the method to call.
7459      * @param dimenResource The resource to resolve and pass as argument to the method.
7460      */
7461     public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
7462             @DimenRes int dimenResource) {
7463         addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
7464                 ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
7465     }
7466 
7467     /**
7468      * Call a method taking one int, a size in pixels, on a view in the layout for this
7469      * RemoteViews.
7470      *
7471      * The dimension will be resolved from the specified dimension at the time of inflation.
7472      *
7473      * @param viewId The id of the view on which to call the method.
7474      * @param methodName The name of the method to call.
7475      * @param value The value of the dimension.
7476      * @param unit The unit in which the value is specified.
7477      */
7478     public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
7479             float value, @ComplexDimensionUnit int unit) {
7480         addAction(new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.INT,
7481                 value, unit));
7482     }
7483 
7484     /**
7485      * Call a method taking one int, a size in pixels, on a view in the layout for this
7486      * RemoteViews.
7487      *
7488      * The dimension will be resolved from the theme attribute at the time the
7489      * {@link RemoteViews} is (re-)applied.
7490      *
7491      * Unresolvable attributes will result in an exception, except 0 which will resolve to 0.
7492      *
7493      * @param viewId The id of the view on which to call the method.
7494      * @param methodName The name of the method to call.
7495      * @param dimenAttr The attribute to resolve and pass as argument to the method.
7496      */
7497     public void setIntDimenAttr(@IdRes int viewId, @NonNull String methodName,
7498             @AttrRes int dimenAttr) {
7499         addAction(new AttributeReflectionAction(viewId, methodName, BaseReflectionAction.INT,
7500                 ResourceReflectionAction.DIMEN_RESOURCE, dimenAttr));
7501     }
7502 
7503     /**
7504      * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
7505      *
7506      * The Color will be resolved from the resources at the time the {@link RemoteViews} is (re-)
7507      * applied.
7508      *
7509      * Undefined resources will result in an exception, except 0 which will resolve to 0.
7510      *
7511      * @param viewId The id of the view on which to call the method.
7512      * @param methodName The name of the method to call.
7513      * @param colorResource The resource to resolve and pass as argument to the method.
7514      */
7515     public void setColor(@IdRes int viewId, @NonNull String methodName,
7516             @ColorRes int colorResource) {
7517         addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
7518                 ResourceReflectionAction.COLOR_RESOURCE, colorResource));
7519     }
7520 
7521     /**
7522      * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
7523      *
7524      * The Color will be resolved from the theme attribute at the time the {@link RemoteViews} is
7525      * (re-)applied.
7526      *
7527      * Unresolvable attributes will result in an exception, except 0 which will resolve to 0.
7528      *
7529      * @param viewId The id of the view on which to call the method.
7530      * @param methodName The name of the method to call.
7531      * @param colorAttribute The theme attribute to resolve and pass as argument to the method.
7532      */
7533     public void setColorAttr(@IdRes int viewId, @NonNull String methodName,
7534             @AttrRes int colorAttribute) {
7535         addAction(new AttributeReflectionAction(viewId, methodName, BaseReflectionAction.INT,
7536                 AttributeReflectionAction.COLOR_RESOURCE, colorAttribute));
7537     }
7538 
7539     /**
7540      * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
7541      *
7542      * @param viewId The id of the view on which to call the method.
7543      * @param methodName The name of the method to call.
7544      * @param notNight The value to pass to the method when the view's configuration is set to
7545      *                 {@link Configuration#UI_MODE_NIGHT_NO}
7546      * @param night The value to pass to the method when the view's configuration is set to
7547      *                 {@link Configuration#UI_MODE_NIGHT_YES}
7548      */
7549     public void setColorInt(
7550             @IdRes int viewId,
7551             @NonNull String methodName,
7552             @ColorInt int notNight,
7553             @ColorInt int night) {
7554         addAction(
7555                 new NightModeReflectionAction(
7556                         viewId,
7557                         methodName,
7558                         BaseReflectionAction.INT,
7559                         notNight,
7560                         night));
7561     }
7562 
7563 
7564     /**
7565      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
7566      *
7567      * @param viewId The id of the view on which to call the method.
7568      * @param methodName The name of the method to call.
7569      * @param value The value to pass to the method.
7570      */
7571     public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
7572             @Nullable ColorStateList value) {
7573         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST,
7574                 value));
7575     }
7576 
7577     /**
7578      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
7579      *
7580      * @param viewId The id of the view on which to call the method.
7581      * @param methodName The name of the method to call.
7582      * @param notNight The value to pass to the method when the view's configuration is set to
7583      *                 {@link Configuration#UI_MODE_NIGHT_NO}
7584      * @param night The value to pass to the method when the view's configuration is set to
7585      *                 {@link Configuration#UI_MODE_NIGHT_YES}
7586      */
7587     public void setColorStateList(
7588             @IdRes int viewId,
7589             @NonNull String methodName,
7590             @Nullable ColorStateList notNight,
7591             @Nullable ColorStateList night) {
7592         addAction(
7593                 new NightModeReflectionAction(
7594                         viewId,
7595                         methodName,
7596                         BaseReflectionAction.COLOR_STATE_LIST,
7597                         notNight,
7598                         night));
7599     }
7600 
7601     /**
7602      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
7603      *
7604      * The ColorStateList will be resolved from the resources at the time the {@link RemoteViews} is
7605      * (re-)applied.
7606      *
7607      * Undefined resources will result in an exception, except 0 which will resolve to null.
7608      *
7609      * @param viewId The id of the view on which to call the method.
7610      * @param methodName The name of the method to call.
7611      * @param colorResource The resource to resolve and pass as argument to the method.
7612      */
7613     public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
7614             @ColorRes int colorResource) {
7615         addAction(new ResourceReflectionAction(viewId, methodName,
7616                 BaseReflectionAction.COLOR_STATE_LIST, ResourceReflectionAction.COLOR_RESOURCE,
7617                 colorResource));
7618     }
7619 
7620     /**
7621      * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
7622      *
7623      * The ColorStateList will be resolved from the theme attribute at the time the
7624      * {@link RemoteViews} is (re-)applied.
7625      *
7626      * Unresolvable attributes will result in an exception, except 0 which will resolve to null.
7627      *
7628      * @param viewId The id of the view on which to call the method.
7629      * @param methodName The name of the method to call.
7630      * @param colorAttr The theme attribute to resolve and pass as argument to the method.
7631      */
7632     public void setColorStateListAttr(@IdRes int viewId, @NonNull String methodName,
7633             @AttrRes int colorAttr) {
7634         addAction(new AttributeReflectionAction(viewId, methodName,
7635                 BaseReflectionAction.COLOR_STATE_LIST, ResourceReflectionAction.COLOR_RESOURCE,
7636                 colorAttr));
7637     }
7638 
7639     /**
7640      * Call a method taking one long on a view in the layout for this RemoteViews.
7641      *
7642      * @param viewId The id of the view on which to call the method.
7643      * @param methodName The name of the method to call.
7644      * @param value The value to pass to the method.
7645      */
7646     public void setLong(@IdRes int viewId, String methodName, long value) {
7647         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.LONG, value));
7648     }
7649 
7650     /**
7651      * Call a method taking one float on a view in the layout for this RemoteViews.
7652      *
7653      * @param viewId The id of the view on which to call the method.
7654      * @param methodName The name of the method to call.
7655      * @param value The value to pass to the method.
7656      */
7657     public void setFloat(@IdRes int viewId, String methodName, float value) {
7658         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT, value));
7659     }
7660 
7661     /**
7662      * Call a method taking one float, a size in pixels, on a view in the layout for this
7663      * RemoteViews.
7664      *
7665      * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
7666      * (re-)applied.
7667      *
7668      * Undefined resources will result in an exception, except 0 which will resolve to 0f.
7669      *
7670      * @param viewId The id of the view on which to call the method.
7671      * @param methodName The name of the method to call.
7672      * @param dimenResource The resource to resolve and pass as argument to the method.
7673      */
7674     public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
7675             @DimenRes int dimenResource) {
7676         addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT,
7677                 ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
7678     }
7679 
7680     /**
7681      * Call a method taking one float, a size in pixels, on a view in the layout for this
7682      * RemoteViews.
7683      *
7684      * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
7685      * (re-)applied.
7686      *
7687      * @param viewId The id of the view on which to call the method.
7688      * @param methodName The name of the method to call.
7689      * @param value The value of the dimension.
7690      * @param unit The unit in which the value is specified.
7691      */
7692     public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
7693             float value, @ComplexDimensionUnit int unit) {
7694         addAction(
7695                 new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.FLOAT,
7696                         value, unit));
7697     }
7698 
7699     /**
7700      * Call a method taking one float, a size in pixels, on a view in the layout for this
7701      * RemoteViews.
7702      *
7703      * The dimension will be resolved from the theme attribute at the time the {@link RemoteViews}
7704      * is (re-)applied.
7705      *
7706      * Unresolvable attributes will result in an exception, except 0 which will resolve to 0f.
7707      *
7708      * @param viewId The id of the view on which to call the method.
7709      * @param methodName The name of the method to call.
7710      * @param dimenAttr The attribute to resolve and pass as argument to the method.
7711      */
7712     public void setFloatDimenAttr(@IdRes int viewId, @NonNull String methodName,
7713             @AttrRes int dimenAttr) {
7714         addAction(new AttributeReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT,
7715                 ResourceReflectionAction.DIMEN_RESOURCE, dimenAttr));
7716     }
7717 
7718     /**
7719      * Call a method taking one double on a view in the layout for this RemoteViews.
7720      *
7721      * @param viewId The id of the view on which to call the method.
7722      * @param methodName The name of the method to call.
7723      * @param value The value to pass to the method.
7724      */
7725     public void setDouble(@IdRes int viewId, String methodName, double value) {
7726         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.DOUBLE, value));
7727     }
7728 
7729     /**
7730      * Call a method taking one char on a view in the layout for this RemoteViews.
7731      *
7732      * @param viewId The id of the view on which to call the method.
7733      * @param methodName The name of the method to call.
7734      * @param value The value to pass to the method.
7735      */
7736     public void setChar(@IdRes int viewId, String methodName, char value) {
7737         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR, value));
7738     }
7739 
7740     /**
7741      * Call a method taking one String on a view in the layout for this RemoteViews.
7742      *
7743      * @param viewId The id of the view on which to call the method.
7744      * @param methodName The name of the method to call.
7745      * @param value The value to pass to the method.
7746      */
7747     public void setString(@IdRes int viewId, String methodName, String value) {
7748         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.STRING, value));
7749     }
7750 
7751     /**
7752      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
7753      *
7754      * @param viewId The id of the view on which to call the method.
7755      * @param methodName The name of the method to call.
7756      * @param value The value to pass to the method.
7757      */
7758     public void setCharSequence(@IdRes int viewId, String methodName, CharSequence value) {
7759         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
7760                 value));
7761     }
7762 
7763     /**
7764      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
7765      *
7766      * The CharSequence will be resolved from the resources at the time the {@link RemoteViews} is
7767      * (re-)applied.
7768      *
7769      * Undefined resources will result in an exception, except 0 which will resolve to null.
7770      *
7771      * @param viewId The id of the view on which to call the method.
7772      * @param methodName The name of the method to call.
7773      * @param stringResource The resource to resolve and pass as argument to the method.
7774      */
7775     public void setCharSequence(@IdRes int viewId, @NonNull String methodName,
7776             @StringRes int stringResource) {
7777         addAction(
7778                 new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
7779                         ResourceReflectionAction.STRING_RESOURCE, stringResource));
7780     }
7781 
7782     /**
7783      * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
7784      *
7785      * The CharSequence will be resolved from the theme attribute at the time the
7786      * {@link RemoteViews} is (re-)applied.
7787      *
7788      * Unresolvable attributes will result in an exception, except 0 which will resolve to null.
7789      *
7790      * @param viewId The id of the view on which to call the method.
7791      * @param methodName The name of the method to call.
7792      * @param stringAttribute The attribute to resolve and pass as argument to the method.
7793      */
7794     public void setCharSequenceAttr(@IdRes int viewId, @NonNull String methodName,
7795             @AttrRes int stringAttribute) {
7796         addAction(
7797                 new AttributeReflectionAction(viewId, methodName,
7798                         BaseReflectionAction.CHAR_SEQUENCE,
7799                         AttributeReflectionAction.STRING_RESOURCE, stringAttribute));
7800     }
7801 
7802     /**
7803      * Call a method taking one Uri on a view in the layout for this RemoteViews.
7804      *
7805      * @param viewId The id of the view on which to call the method.
7806      * @param methodName The name of the method to call.
7807      * @param value The value to pass to the method.
7808      */
7809     public void setUri(@IdRes int viewId, String methodName, Uri value) {
7810         if (value != null) {
7811             // Resolve any filesystem path before sending remotely
7812             value = value.getCanonicalUri();
7813             if (StrictMode.vmFileUriExposureEnabled()) {
7814                 value.checkFileUriExposed("RemoteViews.setUri()");
7815             }
7816         }
7817         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.URI, value));
7818     }
7819 
7820     /**
7821      * Call a method taking one Bitmap on a view in the layout for this RemoteViews.
7822      * @more
7823      * <p class="note">The bitmap will be flattened into the parcel if this object is
7824      * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p>
7825      *
7826      * @param viewId The id of the view on which to call the method.
7827      * @param methodName The name of the method to call.
7828      * @param value The value to pass to the method.
7829      */
7830     public void setBitmap(@IdRes int viewId, String methodName, Bitmap value) {
7831         addAction(new BitmapReflectionAction(viewId, methodName, value));
7832     }
7833 
7834     /**
7835      * Call a method taking one BlendMode on a view in the layout for this RemoteViews.
7836      *
7837      * @param viewId The id of the view on which to call the method.
7838      * @param methodName The name of the method to call.
7839      * @param value The value to pass to the method.
7840      */
7841     public void setBlendMode(@IdRes int viewId, @NonNull String methodName,
7842             @Nullable BlendMode value) {
7843         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BLEND_MODE, value));
7844     }
7845 
7846     /**
7847      * Call a method taking one Bundle on a view in the layout for this RemoteViews.
7848      *
7849      * @param viewId The id of the view on which to call the method.
7850      * @param methodName The name of the method to call.
7851      * @param value The value to pass to the method.
7852      */
7853     public void setBundle(@IdRes int viewId, String methodName, Bundle value) {
7854         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BUNDLE, value));
7855     }
7856 
7857     /**
7858      * Call a method taking one Intent on a view in the layout for this RemoteViews.
7859      *
7860      * @param viewId The id of the view on which to call the method.
7861      * @param methodName The name of the method to call.
7862      * @param value The {@link android.content.Intent} to pass the method.
7863      */
7864     public void setIntent(@IdRes int viewId, String methodName, Intent value) {
7865         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INTENT, value));
7866     }
7867 
7868     /**
7869      * Call a method taking one Icon on a view in the layout for this RemoteViews.
7870      *
7871      * @param viewId The id of the view on which to call the method.
7872      * @param methodName The name of the method to call.
7873      * @param value The {@link android.graphics.drawable.Icon} to pass the method.
7874      */
7875     public void setIcon(@IdRes int viewId, String methodName, Icon value) {
7876         addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.ICON, value));
7877     }
7878 
7879     /**
7880      * Call a method taking one Icon on a view in the layout for this RemoteViews.
7881      *
7882      * @param viewId The id of the view on which to call the method.
7883      * @param methodName The name of the method to call.
7884      * @param notNight The value to pass to the method when the view's configuration is set to
7885      *                 {@link Configuration#UI_MODE_NIGHT_NO}
7886      * @param night The value to pass to the method when the view's configuration is set to
7887      *                 {@link Configuration#UI_MODE_NIGHT_YES}
7888      */
7889     public void setIcon(
7890             @IdRes int viewId,
7891             @NonNull String methodName,
7892             @Nullable Icon notNight,
7893             @Nullable Icon night) {
7894         addAction(
7895                 new NightModeReflectionAction(
7896                         viewId,
7897                         methodName,
7898                         BaseReflectionAction.ICON,
7899                         notNight,
7900                         night));
7901     }
7902 
7903     /**
7904      * Equivalent to calling View.setContentDescription(CharSequence).
7905      *
7906      * @param viewId The id of the view whose content description should change.
7907      * @param contentDescription The new content description for the view.
7908      */
7909     public void setContentDescription(@IdRes int viewId, CharSequence contentDescription) {
7910         setCharSequence(viewId, "setContentDescription", contentDescription);
7911     }
7912 
7913     /**
7914      * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}.
7915      *
7916      * @param viewId The id of the view whose before view in accessibility traversal to set.
7917      * @param nextId The id of the next in the accessibility traversal.
7918      **/
7919     public void setAccessibilityTraversalBefore(@IdRes int viewId, @IdRes int nextId) {
7920         setInt(viewId, "setAccessibilityTraversalBefore", nextId);
7921     }
7922 
7923     /**
7924      * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}.
7925      *
7926      * @param viewId The id of the view whose after view in accessibility traversal to set.
7927      * @param nextId The id of the next in the accessibility traversal.
7928      **/
7929     public void setAccessibilityTraversalAfter(@IdRes int viewId, @IdRes int nextId) {
7930         setInt(viewId, "setAccessibilityTraversalAfter", nextId);
7931     }
7932 
7933     /**
7934      * Equivalent to calling {@link View#setLabelFor(int)}.
7935      *
7936      * @param viewId The id of the view whose property to set.
7937      * @param labeledId The id of a view for which this view serves as a label.
7938      */
7939     public void setLabelFor(@IdRes int viewId, @IdRes int labeledId) {
7940         setInt(viewId, "setLabelFor", labeledId);
7941     }
7942 
7943     /**
7944      * Equivalent to calling {@link android.widget.CompoundButton#setChecked(boolean)}.
7945      *
7946      * @param viewId The id of the view whose property to set.
7947      * @param checked true to check the button, false to uncheck it.
7948      */
7949     public void setCompoundButtonChecked(@IdRes int viewId, boolean checked) {
7950         addAction(new SetCompoundButtonCheckedAction(viewId, checked));
7951     }
7952 
7953     /**
7954      * Equivalent to calling {@link android.widget.RadioGroup#check(int)}.
7955      *
7956      * @param viewId The id of the view whose property to set.
7957      * @param checkedId The unique id of the radio button to select in the group.
7958      */
7959     public void setRadioGroupChecked(@IdRes int viewId, @IdRes int checkedId) {
7960         addAction(new SetRadioGroupCheckedAction(viewId, checkedId));
7961     }
7962 
7963     /**
7964      * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
7965      * used by the host when the widgets displayed on a light-background where foreground elements
7966      * and text can safely draw using a dark color without any additional background protection.
7967      */
7968     public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
7969         mLightBackgroundLayoutId = layoutId;
7970     }
7971 
7972     /**
7973      * If this view supports dark text versions, creates a copy representing that version,
7974      * otherwise returns itself.
7975      * @hide
7976      */
7977     public RemoteViews getDarkTextViews() {
7978         if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
7979             return this;
7980         }
7981 
7982         try {
7983             addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
7984             return new RemoteViews(this);
7985         } finally {
7986             mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
7987         }
7988     }
7989 
7990     private boolean hasDrawInstructions() {
7991         return mHasDrawInstructions;
7992     }
7993 
7994     private RemoteViews getRemoteViewsToApply(Context context) {
7995         if (hasLandscapeAndPortraitLayouts()) {
7996             int orientation = context.getResources().getConfiguration().orientation;
7997             if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
7998                 return mLandscape;
7999             }
8000             return mPortrait;
8001         }
8002         if (hasSizedRemoteViews()) {
8003             return findSmallestRemoteView();
8004         }
8005         return this;
8006     }
8007 
8008     /**
8009      * Returns the square distance between two points.
8010      *
8011      * This is particularly useful when we only care about the ordering of the distances.
8012      */
8013     private static float squareDistance(SizeF p1, SizeF p2) {
8014         float dx = p1.getWidth() - p2.getWidth();
8015         float dy = p1.getHeight() - p2.getHeight();
8016         return dx * dx + dy * dy;
8017     }
8018 
8019     /**
8020      * Returns whether the layout fits in the space available to the widget.
8021      *
8022      * A layout fits on a widget if the widget size is known (i.e. not null) and both dimensions
8023      * are smaller than the ones of the widget, adding some padding to account for rounding errors.
8024      */
8025     private static boolean fitsIn(SizeF sizeLayout, @Nullable SizeF sizeWidget) {
8026         return sizeWidget != null && (Math.ceil(sizeWidget.getWidth()) + 1 > sizeLayout.getWidth())
8027                 && (Math.ceil(sizeWidget.getHeight()) + 1 > sizeLayout.getHeight());
8028     }
8029 
8030     private RemoteViews findBestFitLayout(@NonNull SizeF widgetSize) {
8031         // Find the better remote view
8032         RemoteViews bestFit = null;
8033         float bestSqDist = Float.MAX_VALUE;
8034         for (RemoteViews layout : mSizedRemoteViews) {
8035             SizeF layoutSize = layout.getIdealSize();
8036             if (layoutSize == null) {
8037                 throw new IllegalStateException("Expected RemoteViews to have ideal size");
8038             }
8039 
8040             if (fitsIn(layoutSize, widgetSize)) {
8041                 if (bestFit == null) {
8042                     bestFit = layout;
8043                     bestSqDist = squareDistance(layoutSize, widgetSize);
8044                 } else {
8045                     float newSqDist = squareDistance(layoutSize, widgetSize);
8046                     if (newSqDist < bestSqDist) {
8047                         bestFit = layout;
8048                         bestSqDist = newSqDist;
8049                     }
8050                 }
8051             }
8052         }
8053         if (bestFit == null) {
8054             Log.w(LOG_TAG, "Could not find a RemoteViews fitting the current size: " + widgetSize);
8055             return findSmallestRemoteView();
8056         }
8057         return bestFit;
8058     }
8059 
8060     /**
8061      * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
8062      * size of the widget.
8063      *
8064      * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
8065      * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
8066      * diagonal the most similar to the widget. If no layout fits or the size of the widget is
8067      * not specified, the one with the smallest area will be chosen.
8068      *
8069      * @hide
8070      */
8071     public RemoteViews getRemoteViewsToApply(@NonNull Context context,
8072             @Nullable SizeF widgetSize) {
8073         if (!hasSizedRemoteViews() || widgetSize == null) {
8074             // If there isn't multiple remote views, fall back on the previous methods.
8075             return getRemoteViewsToApply(context);
8076         }
8077         return findBestFitLayout(widgetSize);
8078     }
8079 
8080     /**
8081      * Checks whether the change of size will lead to using a different {@link RemoteViews}.
8082      *
8083      * @hide
8084      */
8085     @Nullable
8086     public RemoteViews getRemoteViewsToApplyIfDifferent(@Nullable SizeF oldSize,
8087             @NonNull SizeF newSize) {
8088         if (!hasSizedRemoteViews()) {
8089             return null;
8090         }
8091         RemoteViews oldBestFit = oldSize == null ? findSmallestRemoteView() : findBestFitLayout(
8092                 oldSize);
8093         RemoteViews newBestFit = findBestFitLayout(newSize);
8094         if (oldBestFit != newBestFit) {
8095             return newBestFit;
8096         }
8097         return null;
8098     }
8099 
8100 
8101     /**
8102      * Inflates the view hierarchy represented by this object and applies
8103      * all of the actions.
8104      *
8105      * <p><strong>Caller beware: this may throw</strong>
8106      *
8107      * @param context Default context to use
8108      * @param parent Parent that the resulting view hierarchy will be attached to. This method
8109      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
8110      * @return The inflated view hierarchy
8111      */
8112     public View apply(Context context, ViewGroup parent) {
8113         return apply(context, parent, null);
8114     }
8115 
8116     /** @hide */
8117     public View apply(Context context, ViewGroup parent, InteractionHandler handler) {
8118         return apply(context, parent, handler, null);
8119     }
8120 
8121     /** @hide */
8122     public View apply(@NonNull Context context, @NonNull ViewGroup parent,
8123             @Nullable InteractionHandler handler, @Nullable SizeF size) {
8124         return apply(context, parent, size, new ActionApplyParams()
8125                 .withInteractionHandler(handler));
8126     }
8127 
8128     /** @hide */
8129     public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
8130             @Nullable InteractionHandler handler, @StyleRes int applyThemeResId) {
8131         return apply(context, parent, null, new ActionApplyParams()
8132                 .withInteractionHandler(handler)
8133                 .withThemeResId(applyThemeResId));
8134     }
8135 
8136     /** @hide */
8137     public View apply(Context context, ViewGroup parent, InteractionHandler handler,
8138             @Nullable SizeF size, @Nullable ColorResources colorResources) {
8139         return apply(context, parent, size, new ActionApplyParams()
8140                 .withInteractionHandler(handler)
8141                 .withColorResources(colorResources));
8142     }
8143 
8144     /** @hide **/
8145     public View apply(Context context, ViewGroup parent, @Nullable SizeF size,
8146             ActionApplyParams params) {
8147         return apply(context, parent, parent, size, params);
8148     }
8149 
8150     private View apply(Context context, ViewGroup directParent, ViewGroup rootParent,
8151             @Nullable SizeF size, ActionApplyParams params) {
8152         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
8153         View result = inflateView(context, rvToApply, directParent,
8154                 params.applyThemeResId, params.colorResources);
8155         rvToApply.performApply(result, rootParent, params);
8156         return result;
8157     }
8158 
8159     private View inflateView(Context context, RemoteViews rv, @Nullable ViewGroup parent,
8160             @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) {
8161         try {
8162             Trace.beginSection(rv.hasDrawInstructions()
8163                     ? "RemoteViews#inflateViewWithDrawInstructions"
8164                     : "RemoteViews#inflateView");
8165             return inflateViewInternal(context, rv, parent, applyThemeResId, colorResources);
8166         } finally {
8167             Trace.endSection();
8168         }
8169     }
8170 
8171     private View inflateViewInternal(Context context, RemoteViews rv, @Nullable ViewGroup parent,
8172             @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) {
8173         // RemoteViews may be built by an application installed in another
8174         // user. So build a context that loads resources from that user but
8175         // still returns the current users userId so settings like data / time formats
8176         // are loaded without requiring cross user persmissions.
8177         final Context contextForResources =
8178                 getContextForResourcesEnsuringCorrectCachedApkPaths(context);
8179         if (colorResources != null) {
8180             colorResources.apply(contextForResources);
8181         }
8182         Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
8183 
8184         // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
8185         if (applyThemeResId != 0) {
8186             inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
8187         }
8188         View v;
8189         // If the RemoteViews contains draw instructions, just use it instead.
8190         if (rv.hasDrawInstructions()) {
8191             final RemoteComposePlayer player = new RemoteComposePlayer(inflationContext);
8192             player.setDebug(Build.IS_USERDEBUG || Build.IS_ENG ? 1 : 0);
8193             v = player;
8194         } else {
8195             LayoutInflater inflater = LayoutInflater.from(context);
8196 
8197             // Clone inflater so we load resources from correct context and
8198             // we don't add a filter to the static version returned by getSystemService.
8199             inflater = inflater.cloneInContext(inflationContext);
8200             inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
8201             if (mLayoutInflaterFactory2 != null) {
8202                 inflater.setFactory2(mLayoutInflaterFactory2);
8203             }
8204             v = inflater.inflate(rv.getLayoutId(), parent, false);
8205         }
8206         if (mViewId != View.NO_ID) {
8207             v.setId(mViewId);
8208             v.setTagInternal(R.id.remote_views_override_id, mViewId);
8209         }
8210         v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
8211         return v;
8212     }
8213 
8214     /**
8215      * A static filter is much lighter than RemoteViews itself. It's optimized here only for
8216      * RemoteVies class. Subclasses should always override this and return true if not overriding
8217      * {@link this#onLoadClass(Class)}.
8218      *
8219      * @hide
8220      */
8221     protected boolean shouldUseStaticFilter() {
8222         return this.getClass().equals(RemoteViews.class);
8223     }
8224 
8225     /**
8226      * Implement this interface to receive a callback when
8227      * {@link #applyAsync} or {@link #reapplyAsync} is finished.
8228      * @hide
8229      */
8230     public interface OnViewAppliedListener {
8231         /**
8232          * Callback when the RemoteView has finished inflating,
8233          * but no actions have been applied yet.
8234          */
8235         default void onViewInflated(View v) {}
8236 
8237         void onViewApplied(View v);
8238 
8239         void onError(Exception e);
8240     }
8241 
8242     /**
8243      * Applies the views asynchronously, moving as much of the task on the background
8244      * thread as possible.
8245      *
8246      * @see #apply(Context, ViewGroup)
8247      * @param context Default context to use
8248      * @param parent Parent that the resulting view hierarchy will be attached to. This method
8249      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
8250      * @param listener the callback to run when all actions have been applied. May be null.
8251      * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used.
8252      * @return CancellationSignal
8253      * @hide
8254      */
8255     public CancellationSignal applyAsync(
8256             Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) {
8257         return applyAsync(context, parent, executor, listener, null /* handler */);
8258     }
8259 
8260     /** @hide */
8261     public CancellationSignal applyAsync(Context context, ViewGroup parent,
8262             Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
8263         return applyAsync(context, parent, executor, listener, handler, null /* size */);
8264     }
8265 
8266     /** @hide */
8267     public CancellationSignal applyAsync(Context context, ViewGroup parent,
8268             Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
8269             SizeF size) {
8270         return applyAsync(context, parent, executor, listener, handler, size,
8271                 null /* themeColors */);
8272     }
8273 
8274     /** @hide */
8275     public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
8276             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
8277             ColorResources colorResources) {
8278 
8279         ActionApplyParams params = new ActionApplyParams()
8280                 .withInteractionHandler(handler)
8281                 .withColorResources(colorResources)
8282                 .withExecutor(executor);
8283         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
8284                 params, null /* result */, true /* topLevel */).startTaskOnExecutor(executor);
8285     }
8286 
8287     private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
8288             OnViewAppliedListener listener, ActionApplyParams params, SizeF size, View result) {
8289         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
8290                 params, result, false /* topLevel */);
8291     }
8292 
8293     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
8294             implements CancellationSignal.OnCancelListener {
8295         final CancellationSignal mCancelSignal = new CancellationSignal();
8296         final RemoteViews mRV;
8297         final ViewGroup mParent;
8298         final Context mContext;
8299         final OnViewAppliedListener mListener;
8300         final ActionApplyParams mApplyParams;
8301 
8302         /**
8303          * Whether the remote view is the top-level one (i.e. not within an action).
8304          *
8305          * This is only used if the result is specified (i.e. the view is being recycled).
8306          */
8307         final boolean mTopLevel;
8308 
8309         private View mResult;
8310         private ViewTree mTree;
8311         private Action[] mActions;
8312         private Exception mError;
8313 
8314         private AsyncApplyTask(
8315                 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
8316                 ActionApplyParams applyParams, View result, boolean topLevel) {
8317             mRV = rv;
8318             mParent = parent;
8319             mContext = context;
8320             mListener = listener;
8321             mTopLevel = topLevel;
8322             mApplyParams = applyParams;
8323             mResult = result;
8324         }
8325 
8326         @Nullable
8327         @Override
8328         protected ViewTree doInBackground(Void... params) {
8329             try {
8330                 if (mResult == null) {
8331                     mResult = inflateView(mContext, mRV, mParent, 0, mApplyParams.colorResources);
8332                 }
8333 
8334                 mTree = new ViewTree(mResult);
8335 
8336                 if (mRV.mActions != null) {
8337                     int count = mRV.mActions.size();
8338                     mActions = new Action[count];
8339                     try {
8340                         Trace.beginSection(hasDrawInstructions()
8341                                 ? "RemoteViews#initActionAsyncWithDrawInstructions"
8342                                 : "RemoteViews#initActionAsync");
8343                         for (int i = 0; i < count && !isCancelled(); i++) {
8344                             // TODO: check if isCancelled in nested views.
8345                             mActions[i] = mRV.mActions.get(i)
8346                                     .initActionAsync(mTree, mParent, mApplyParams);
8347                         }
8348                     } finally {
8349                         Trace.endSection();
8350                     }
8351                 } else {
8352                     mActions = null;
8353                 }
8354                 return mTree;
8355             } catch (Exception e) {
8356                 mError = e;
8357                 return null;
8358             }
8359         }
8360 
8361         @Override
8362         protected void onPostExecute(ViewTree viewTree) {
8363             mCancelSignal.setOnCancelListener(null);
8364             if (mError == null) {
8365                 if (mListener != null) {
8366                     mListener.onViewInflated(viewTree.mRoot);
8367                 }
8368 
8369                 try {
8370                     if (mActions != null) {
8371                         ActionApplyParams applyParams = mApplyParams.clone();
8372                         if (applyParams.handler == null) {
8373                             applyParams.handler = DEFAULT_INTERACTION_HANDLER;
8374                         }
8375                         try {
8376                             Trace.beginSection(hasDrawInstructions()
8377                                     ? "RemoteViews#applyActionsAsyncWithDrawInstructions"
8378                                     : "RemoteViews#applyActionsAsync");
8379                             for (Action a : mActions) {
8380                                 a.apply(viewTree.mRoot, mParent, applyParams);
8381                             }
8382                         } finally {
8383                             Trace.endSection();
8384                         }
8385                     }
8386                     // If the parent of the view is has is a root, resolve the recycling.
8387                     if (mTopLevel && mResult instanceof ViewGroup) {
8388                         finalizeViewRecycling((ViewGroup) mResult);
8389                     }
8390                 } catch (Exception e) {
8391                     mError = e;
8392                 }
8393             }
8394 
8395             if (mListener != null) {
8396                 if (mError != null) {
8397                     mListener.onError(mError);
8398                 } else {
8399                     mListener.onViewApplied(viewTree.mRoot);
8400                 }
8401             } else if (mError != null) {
8402                 if (mError instanceof ActionException) {
8403                     throw (ActionException) mError;
8404                 } else {
8405                     throw new ActionException(mError);
8406                 }
8407             }
8408         }
8409 
8410         @Override
8411         public void onCancel() {
8412             cancel(true);
8413         }
8414 
8415         private CancellationSignal startTaskOnExecutor(Executor executor) {
8416             mCancelSignal.setOnCancelListener(this);
8417             executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
8418             return mCancelSignal;
8419         }
8420     }
8421 
8422     /**
8423      * Applies all of the actions to the provided view.
8424      *
8425      * <p><strong>Caller beware: this may throw</strong>
8426      *
8427      * @param v The view to apply the actions to.  This should be the result of
8428      * the {@link #apply(Context,ViewGroup)} call.
8429      */
8430     public void reapply(Context context, View v) {
8431         reapply(context, v, null /* size */, new ActionApplyParams());
8432     }
8433 
8434     /** @hide */
8435     public void reapply(Context context, View v, InteractionHandler handler) {
8436         reapply(context, v, null /* size */,
8437                 new ActionApplyParams().withInteractionHandler(handler));
8438     }
8439 
8440     /** @hide */
8441     public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
8442             ColorResources colorResources) {
8443         reapply(context, v, size, new ActionApplyParams()
8444                 .withInteractionHandler(handler).withColorResources(colorResources));
8445     }
8446 
8447     /** @hide */
8448     public void reapply(Context context, View v, @Nullable SizeF size, ActionApplyParams params) {
8449         reapply(context, v, (ViewGroup) v.getParent(), size, params, true);
8450     }
8451 
8452     private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
8453             ActionApplyParams params) {
8454         reapply(context, v, rootParent, null, params, false);
8455     }
8456 
8457     // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
8458     // should set it to false.
8459     private void reapply(Context context, View v, ViewGroup rootParent,
8460             @Nullable SizeF size, ActionApplyParams params, boolean topLevel) {
8461         RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
8462         rvToApply.performApply(v, rootParent, params);
8463 
8464         // If the parent of the view is has is a root, resolve the recycling.
8465         if (topLevel && v instanceof ViewGroup) {
8466             finalizeViewRecycling((ViewGroup) v);
8467         }
8468     }
8469 
8470     /** @hide */
8471     public boolean canRecycleView(@Nullable View v) {
8472         if (v == null || hasDrawInstructions()) {
8473             return false;
8474         }
8475         Integer previousLayoutId = (Integer) v.getTag(R.id.widget_frame);
8476         if (previousLayoutId == null) {
8477             return false;
8478         }
8479         Integer overrideIdTag = (Integer) v.getTag(R.id.remote_views_override_id);
8480         int overrideId = overrideIdTag == null ? View.NO_ID : overrideIdTag;
8481         // If mViewId is View.NO_ID, we only recycle if overrideId is also View.NO_ID.
8482         // Otherwise, it might be that, on a previous iteration, the view's ID was set to
8483         // something else, and it should now be reset to the ID defined in the XML layout file,
8484         // whatever it is.
8485         return previousLayoutId == getLayoutId() && mViewId == overrideId;
8486     }
8487 
8488     /**
8489      * Returns the RemoteViews that should be used in the reapply operation.
8490      *
8491      * If the current RemoteViews has multiple layout, this will select the correct one.
8492      *
8493      * @throws RuntimeException If the current RemoteViews should not be reapplied onto the provided
8494      * View.
8495      */
8496     private RemoteViews getRemoteViewsToReapply(Context context, View v, @Nullable SizeF size) {
8497         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
8498 
8499         // In the case that a view has this RemoteViews applied in one orientation or size, is
8500         // persisted across change, and has the RemoteViews re-applied in a different situation
8501         // (orientation or size), we throw an exception, since the layouts may be completely
8502         // unrelated.
8503         // If the ViewID has been changed on the view, or is changed by the RemoteViews, we also
8504         // may throw an exception, as the RemoteViews will probably not apply properly.
8505         // However, we need to let potentially unrelated RemoteViews apply, as this lack of testing
8506         // is already used in production code in some apps.
8507         if (hasMultipleLayouts()
8508                 || rvToApply.mViewId != View.NO_ID
8509                 || v.getTag(R.id.remote_views_override_id) != null) {
8510             if (!rvToApply.canRecycleView(v)) {
8511                 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
8512                         " that does not share the same root layout id.");
8513             }
8514         }
8515 
8516         return rvToApply;
8517     }
8518 
8519     /**
8520      * Applies all the actions to the provided view, moving as much of the task on the background
8521      * thread as possible.
8522      *
8523      * @see #reapply(Context, View)
8524      * @param context Default context to use
8525      * @param v The view to apply the actions to.  This should be the result of
8526      * the {@link #apply(Context,ViewGroup)} call.
8527      * @param listener the callback to run when all actions have been applied. May be null.
8528      * @param executor The executor to use. If null {@link AsyncTask#THREAD_POOL_EXECUTOR} is used
8529      * @return CancellationSignal
8530      * @hide
8531      */
8532     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
8533             OnViewAppliedListener listener) {
8534         return reapplyAsync(context, v, executor, listener, null);
8535     }
8536 
8537     /** @hide */
8538     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
8539             OnViewAppliedListener listener, InteractionHandler handler) {
8540         return reapplyAsync(context, v, executor, listener, handler, null, null);
8541     }
8542 
8543     /** @hide */
8544     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
8545             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
8546             ColorResources colorResources) {
8547         RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
8548 
8549         ActionApplyParams params = new ActionApplyParams()
8550                 .withColorResources(colorResources)
8551                 .withInteractionHandler(handler)
8552                 .withExecutor(executor);
8553 
8554         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
8555                 context, listener, params, v, true /* topLevel */)
8556                 .startTaskOnExecutor(executor);
8557     }
8558 
8559     private void performApply(View v, ViewGroup parent, ActionApplyParams params) {
8560         params = params.clone();
8561         if (params.handler == null) {
8562             params.handler = DEFAULT_INTERACTION_HANDLER;
8563         }
8564         if (v instanceof RemoteComposePlayer player) {
8565             player.setTheme(v.getResources().getConfiguration().isNightModeActive()
8566                     ? Theme.DARK : Theme.LIGHT);
8567         }
8568         if (mActions != null) {
8569             final int count = mActions.size();
8570             try {
8571                 Trace.beginSection(hasDrawInstructions()
8572                         ? "RemoteViews#applyActionsWithDrawInstructions"
8573                         : "RemoteViews#applyActions");
8574                 for (int i = 0; i < count; i++) {
8575                     mActions.get(i).apply(v, parent, params);
8576                 }
8577             } finally {
8578                 Trace.endSection();
8579             }
8580         }
8581     }
8582 
8583     /**
8584      * Returns true if the RemoteViews contains potentially costly operations and should be
8585      * applied asynchronously.
8586      *
8587      * @hide
8588      */
8589     public boolean prefersAsyncApply() {
8590         if (mActions != null) {
8591             final int count = mActions.size();
8592             for (int i = 0; i < count; i++) {
8593                 if (mActions.get(i).prefersAsyncApply()) {
8594                     return true;
8595                 }
8596             }
8597         }
8598         return false;
8599     }
8600 
8601     /** @hide */
8602     public void updateAppInfo(@NonNull ApplicationInfo info) {
8603         ApplicationInfo existing = mApplicationInfoCache.get(info);
8604         if (existing != null && !existing.sourceDir.equals(info.sourceDir)) {
8605             // Overlay paths are generated against a particular version of an application.
8606             // The overlays paths of a newly upgraded application are incompatible with the
8607             // old version of the application.
8608             return;
8609         }
8610 
8611         // If we can update to the new AppInfo, put it in the cache and propagate the change
8612         // throughout the hierarchy.
8613         mApplicationInfoCache.put(info);
8614         configureDescendantsAsChildren();
8615     }
8616 
8617     private Context getContextForResourcesEnsuringCorrectCachedApkPaths(Context context) {
8618         if (mApplication != null) {
8619             if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
8620                     && context.getPackageName().equals(mApplication.packageName)) {
8621                 return context;
8622             }
8623             try {
8624                 ApplicationInfo sanitizedApplication = mApplication;
8625                 try {
8626                     // Use PackageManager as the source of truth for application information, rather
8627                     // than the parceled ApplicationInfo provided by the app.
8628                     sanitizedApplication = context.getPackageManager().getApplicationInfoAsUser(
8629                         mApplication.packageName, 0, UserHandle.getUserId(mApplication.uid));
8630                 } catch(SecurityException se) {
8631                     Log.d(LOG_TAG, "Unable to fetch appInfo for " + mApplication.packageName);
8632                 }
8633 
8634                 Context applicationContext = context.createApplicationContext(
8635                         sanitizedApplication,
8636                         Context.CONTEXT_RESTRICTED);
8637                 // Get the correct apk paths while maintaining the current context's configuration.
8638                 return applicationContext.createConfigurationContext(
8639                         context.getResources().getConfiguration());
8640             } catch (NameNotFoundException e) {
8641                 Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
8642             }
8643         }
8644 
8645         return context;
8646     }
8647 
8648     @NonNull
8649     private SparseArray<PendingIntent> getPendingIntentTemplate() {
8650         if (mPendingIntentTemplate == null) {
8651             mPendingIntentTemplate = new SparseArray<>();
8652         }
8653         return mPendingIntentTemplate;
8654     }
8655 
8656     @NonNull
8657     private SparseArray<Intent> getFillInIntent() {
8658         if (mFillInIntent == null) {
8659             mFillInIntent = new SparseArray<>();
8660         }
8661         return mFillInIntent;
8662     }
8663 
8664     private void tryAddRemoteResponse(final int viewId) {
8665         final PendingIntent pendingIntent = getPendingIntentTemplate().get(viewId);
8666         final Intent intent = getFillInIntent().get(viewId);
8667         if (pendingIntent != null && intent != null) {
8668             addAction(new SetOnClickResponse(viewId,
8669                     RemoteResponse.fromPendingIntentTemplateAndFillInIntent(
8670                             pendingIntent, intent)));
8671         }
8672     }
8673 
8674     /**
8675      * Utility class to hold all the options when applying the remote views
8676      * @hide
8677      */
8678     public class ActionApplyParams {
8679         public InteractionHandler handler;
8680         public ColorResources colorResources;
8681         public Executor executor;
8682         @StyleRes public int applyThemeResId;
8683 
8684         @Override
8685         public ActionApplyParams clone() {
8686             return new ActionApplyParams()
8687                     .withInteractionHandler(handler)
8688                     .withColorResources(colorResources)
8689                     .withExecutor(executor)
8690                     .withThemeResId(applyThemeResId);
8691         }
8692 
8693         public ActionApplyParams withInteractionHandler(InteractionHandler handler) {
8694             this.handler = handler;
8695             return this;
8696         }
8697 
8698         public ActionApplyParams withColorResources(ColorResources colorResources) {
8699             this.colorResources = colorResources;
8700             return this;
8701         }
8702 
8703         public ActionApplyParams withThemeResId(@StyleRes int themeResId) {
8704             this.applyThemeResId = themeResId;
8705             return this;
8706         }
8707 
8708         public ActionApplyParams withExecutor(Executor executor) {
8709             this.executor = executor;
8710             return this;
8711         }
8712     }
8713 
8714     /**
8715      * Object allowing the modification of a context to overload the system's dynamic colors.
8716      *
8717      * @hide
8718      */
8719     public static final class ColorResources {
8720         // Set of valid colors resources.
8721         private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0;
8722         private static final int LAST_RESOURCE_COLOR_ID =
8723             android.R.color.system_error_1000;
8724         // Size, in bytes, of an entry in the array of colors in an ARSC file.
8725         private static final int ARSC_ENTRY_SIZE = 16;
8726 
8727         private static final String OVERLAY_NAME = "remote_views_color_resources";
8728         private static final String OVERLAY_TARGET_PACKAGE_NAME = "android";
8729 
8730         private final ResourcesLoader mLoader;
8731         private final SparseIntArray mColorMapping;
8732 
8733         private ColorResources(ResourcesLoader loader, SparseIntArray colorMapping) {
8734             mLoader = loader;
8735             mColorMapping = colorMapping;
8736         }
8737 
8738         /**
8739          * Apply the color resources to the given context.
8740          *
8741          * No resource resolution must have be done on the context given to that method.
8742          */
8743         public void apply(Context context) {
8744             context.getResources().addLoaders(mLoader);
8745         }
8746 
8747         public SparseIntArray getColorMapping() {
8748             return mColorMapping;
8749         }
8750 
8751         private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
8752             ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
8753             byte[] buffer = new byte[4096];
8754             while (input.available() > 0) {
8755                 int read = input.read(buffer);
8756                 content.write(buffer, 0, read);
8757             }
8758             return content;
8759         }
8760 
8761         /**
8762          * Creates the compiled resources content from the asset stored in the APK.
8763          *
8764          * The asset is a compiled resource with the correct resources name and correct ids, only
8765          * the values are incorrect. The last value is at the very end of the file. The resources
8766          * are in an array, the array's entries are 16 bytes each. We use this to work out the
8767          * location of all the positions of the various resources.
8768          */
8769         @Nullable
8770         private static byte[] createCompiledResourcesContent(Context context,
8771                 SparseIntArray colorResources) throws IOException {
8772             byte[] content;
8773             try (InputStream input = context.getResources().openRawResource(
8774                     com.android.internal.R.raw.remote_views_color_resources)) {
8775                 ByteArrayOutputStream rawContent = readFileContent(input);
8776                 content = rawContent.toByteArray();
8777             }
8778             int valuesOffset =
8779                     content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4;
8780             if (valuesOffset < 0) {
8781                 Log.e(LOG_TAG, "ARSC file for theme colors is invalid.");
8782                 return null;
8783             }
8784             for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID;
8785                     colorRes++) {
8786                 // The last 2 bytes are the index in the color array.
8787                 int index = colorRes & 0xffff;
8788                 int offset = valuesOffset + index * ARSC_ENTRY_SIZE;
8789                 int value = colorResources.get(colorRes, context.getColor(colorRes));
8790                 // Write the 32 bit integer in little endian
8791                 for (int b = 0; b < 4; b++) {
8792                     content[offset + b] = (byte) (value & 0xff);
8793                     value >>= 8;
8794                 }
8795             }
8796             return content;
8797         }
8798 
8799         /**
8800          *  Adds a resource loader for theme colors to the given context. The loader is created
8801          *  based on resource files created at build time.
8802          *
8803          * <p>Only colors from {@link android.R.color#system_accent1_0} to
8804          * {@link android.R.color#system_error_1000} can be overloaded.</p>
8805          *
8806          * @param context Context of the view hosting the widget.
8807          * @param colorMapping Mapping of resources to color values.
8808          *
8809          * @hide
8810          */
8811         @Nullable
8812         public static ColorResources create(Context context, SparseIntArray colorMapping) {
8813             try {
8814                 byte[] contentBytes = createCompiledResourcesContent(context, colorMapping);
8815                 if (contentBytes == null) {
8816                     return null;
8817                 }
8818                 FileDescriptor arscFile = null;
8819                 try {
8820                     arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */);
8821                     // Note: This must not be closed through the OutputStream.
8822                     try (OutputStream pipeWriter = new FileOutputStream(arscFile)) {
8823                         pipeWriter.write(contentBytes);
8824 
8825                         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) {
8826                             ResourcesLoader colorsLoader = new ResourcesLoader();
8827                             colorsLoader.addProvider(ResourcesProvider
8828                                     .loadFromTable(pfd, null /* assetsProvider */));
8829                             return new ColorResources(colorsLoader, colorMapping.clone());
8830                         }
8831                     }
8832                 } finally {
8833                     if (arscFile != null) {
8834                         Os.close(arscFile);
8835                     }
8836                 }
8837             } catch (Exception ex) {
8838                 Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex);
8839             }
8840             return null;
8841         }
8842 
8843         /**
8844          *  Adds a resource loader for theme colors to the given context. The loader is created
8845          *  using fabricated runtime resource overlay (FRRO).
8846          *
8847          *  <p>The created class can overlay any color resources, private or public, at runtime.</p>
8848          *
8849          * @param context Context of the view hosting the widget.
8850          * @param colorMapping Mapping of resources to color values.
8851          *
8852          * @hide
8853          */
8854         @FlaggedApi(FLAG_SELF_TARGETING_ANDROID_RESOURCE_FRRO)
8855         @Nullable
8856         public static ColorResources createWithOverlay(Context context,
8857                 SparseIntArray colorMapping) {
8858             try {
8859                 String owningPackage = context.getPackageName();
8860                 FabricatedOverlay overlay = new FabricatedOverlay.Builder(owningPackage,
8861                         OVERLAY_NAME, OVERLAY_TARGET_PACKAGE_NAME).build();
8862 
8863                 for (int i = 0; i < colorMapping.size(); i++) {
8864                     overlay.setResourceValue(
8865                             context.getResources().getResourceName(colorMapping.keyAt(i)),
8866                             TYPE_INT_COLOR_ARGB8, colorMapping.valueAt(i), null);
8867                 }
8868                 OverlayManager overlayManager = context.getSystemService(OverlayManager.class);
8869                 OverlayManagerTransaction.Builder transaction =
8870                         new OverlayManagerTransaction.Builder()
8871                                 .registerFabricatedOverlay(overlay)
8872                                 .setSelfTargeting(true);
8873                 overlayManager.commit(transaction.build());
8874 
8875                 OverlayInfo overlayInfo =
8876                         overlayManager.getOverlayInfosForTarget(OVERLAY_TARGET_PACKAGE_NAME)
8877                                 .stream()
8878                                 .filter(info -> TextUtils.equals(info.overlayName, OVERLAY_NAME)
8879                                         && TextUtils.equals(info.packageName, owningPackage))
8880                                 .findFirst()
8881                                 .orElse(null);
8882                 if (overlayInfo == null) {
8883                     Log.e(LOG_TAG, "Failed to get overlay info ", new Throwable());
8884                     return null;
8885                 }
8886                 ResourcesLoader colorsLoader = new ResourcesLoader();
8887                 colorsLoader.addProvider(ResourcesProvider.loadOverlay(overlayInfo));
8888                 return new ColorResources(colorsLoader, colorMapping.clone());
8889             } catch (Exception e) {
8890                 Log.e(LOG_TAG, "Failed to add theme color overlay into loader", e);
8891             }
8892             return null;
8893         }
8894     }
8895 
8896     /**
8897      * Returns the number of actions in this RemoteViews. Can be used as a sequence number.
8898      *
8899      * @hide
8900      */
8901     public int getSequenceNumber() {
8902         return (mActions == null) ? 0 : mActions.size();
8903     }
8904 
8905     /**
8906      * Used to restrict the views which can be inflated
8907      *
8908      * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class)
8909      * @deprecated Used by system to enforce safe inflation of {@link RemoteViews}. Apps should not
8910      * override this method. Changing of this method will NOT affect the process where RemoteViews
8911      * is rendered.
8912      */
8913     @Deprecated
8914     public boolean onLoadClass(Class clazz) {
8915         return clazz.isAnnotationPresent(RemoteView.class);
8916     }
8917 
8918     public int describeContents() {
8919         return 0;
8920     }
8921 
8922     @Override
8923     public void writeToParcel(Parcel dest, int flags) {
8924         writeToParcel(dest, flags, /* intentsToIgnore= */ null);
8925     }
8926 
8927     private void writeToParcel(Parcel dest, int flags,
8928             @Nullable SparseArray<Intent> intentsToIgnore) {
8929         boolean prevSquashingAllowed = dest.allowSquashing();
8930 
8931         if (!hasMultipleLayouts()) {
8932             dest.writeInt(MODE_NORMAL);
8933             // We only write the bitmap cache if we are the root RemoteViews, as this cache
8934             // is shared by all children.
8935             if (mIsRoot) {
8936                 mBitmapCache.writeBitmapsToParcel(dest, flags);
8937                 mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
8938             }
8939             dest.writeTypedObject(mApplication, flags);
8940             if (mIsRoot || mIdealSize == null) {
8941                 dest.writeInt(0);
8942             } else {
8943                 dest.writeInt(1);
8944                 mIdealSize.writeToParcel(dest, flags);
8945             }
8946             dest.writeInt(mLayoutId);
8947             dest.writeInt(mViewId);
8948             dest.writeInt(mLightBackgroundLayoutId);
8949             writeActionsToParcel(dest, flags);
8950         } else if (hasSizedRemoteViews()) {
8951             dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
8952             if (mIsRoot) {
8953                 mBitmapCache.writeBitmapsToParcel(dest, flags);
8954                 mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
8955             }
8956             dest.writeInt(mSizedRemoteViews.size());
8957             for (RemoteViews view : mSizedRemoteViews) {
8958                 view.writeToParcel(dest, flags);
8959             }
8960         } else {
8961             dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
8962             // We only write the bitmap cache if we are the root RemoteViews, as this cache
8963             // is shared by all children.
8964             if (mIsRoot) {
8965                 mBitmapCache.writeBitmapsToParcel(dest, flags);
8966                 mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
8967             }
8968             mLandscape.writeToParcel(dest, flags);
8969             // Both RemoteViews already share the same package and user
8970             mPortrait.writeToParcel(dest, flags);
8971         }
8972         dest.writeInt(mApplyFlags);
8973         dest.writeLong(mProviderInstanceId);
8974         dest.writeBoolean(mHasDrawInstructions);
8975 
8976         dest.restoreAllowSquashing(prevSquashingAllowed);
8977     }
8978 
8979     private void writeActionsToParcel(Parcel parcel, int flags) {
8980         int count;
8981         if (mActions != null) {
8982             count = mActions.size();
8983         } else {
8984             count = 0;
8985         }
8986         parcel.writeInt(count);
8987         for (int i = 0; i < count; i++) {
8988             Action a = mActions.get(i);
8989             parcel.writeInt(a.getActionTag());
8990             a.writeToParcel(parcel, flags);
8991         }
8992     }
8993 
8994     @Nullable
8995     private static ApplicationInfo getApplicationInfo(@Nullable String packageName, int userId) {
8996         if (packageName == null) {
8997             return null;
8998         }
8999 
9000         // Get the application for the passed in package and user.
9001         Application application = ActivityThread.currentApplication();
9002         if (application == null) {
9003             throw new IllegalStateException("Cannot create remote views out of an aplication.");
9004         }
9005 
9006         ApplicationInfo applicationInfo = application.getApplicationInfo();
9007         if (UserHandle.getUserId(applicationInfo.uid) != userId
9008                 || !applicationInfo.packageName.equals(packageName)) {
9009             try {
9010                 Context context = application.getBaseContext().createPackageContextAsUser(
9011                         packageName, 0, new UserHandle(userId));
9012                 applicationInfo = context.getApplicationInfo();
9013             } catch (NameNotFoundException nnfe) {
9014                 throw new IllegalArgumentException("No such package " + packageName);
9015             }
9016         }
9017 
9018         return applicationInfo;
9019     }
9020 
9021     /**
9022      * Returns true if the {@link #mApplication} is same as the provided info.
9023      *
9024      * @hide
9025      */
9026     public boolean hasSameAppInfo(ApplicationInfo info) {
9027         return mApplication == null || mApplication.packageName.equals(info.packageName)
9028                 && mApplication.uid == info.uid;
9029     }
9030 
9031     /**
9032      * Parcelable.Creator that instantiates RemoteViews objects
9033      */
9034     @NonNull
9035     public static final Parcelable.Creator<RemoteViews> CREATOR =
9036             new Parcelable.Creator<RemoteViews>() {
9037                 public RemoteViews createFromParcel(Parcel parcel) {
9038                     return new RemoteViews(parcel);
9039                 }
9040 
9041                 public RemoteViews[] newArray(int size) {
9042                     return new RemoteViews[size];
9043                 }
9044             };
9045 
9046     /**
9047      * A representation of the view hierarchy. Only views which have a valid ID are added
9048      * and can be searched.
9049      */
9050     private static class ViewTree {
9051         private static final int INSERT_AT_END_INDEX = -1;
9052         private View mRoot;
9053         private ArrayList<ViewTree> mChildren;
9054 
9055         private ViewTree(View root) {
9056             mRoot = root;
9057         }
9058 
9059         public void createTree() {
9060             if (mChildren != null) {
9061                 return;
9062             }
9063 
9064             mChildren = new ArrayList<>();
9065             if (mRoot instanceof ViewGroup) {
9066                 ViewGroup vg = (ViewGroup) mRoot;
9067                 int count = vg.getChildCount();
9068                 for (int i = 0; i < count; i++) {
9069                     addViewChild(vg.getChildAt(i));
9070                 }
9071             }
9072         }
9073 
9074         @Nullable
9075         public ViewTree findViewTreeById(@IdRes int id) {
9076             if (mRoot.getId() == id) {
9077                 return this;
9078             }
9079             if (mChildren == null) {
9080                 return null;
9081             }
9082             for (ViewTree tree : mChildren) {
9083                 ViewTree result = tree.findViewTreeById(id);
9084                 if (result != null) {
9085                     return result;
9086                 }
9087             }
9088             return null;
9089         }
9090 
9091         @Nullable
9092         public ViewTree findViewTreeParentOf(ViewTree child) {
9093             if (mChildren == null) {
9094                 return null;
9095             }
9096             for (ViewTree tree : mChildren) {
9097                 if (tree == child) {
9098                     return this;
9099                 }
9100                 ViewTree result = tree.findViewTreeParentOf(child);
9101                 if (result != null) {
9102                     return result;
9103                 }
9104             }
9105             return null;
9106         }
9107 
9108         public void replaceView(View v) {
9109             mRoot = v;
9110             mChildren = null;
9111             createTree();
9112         }
9113 
9114         @Nullable
9115         public <T extends View> T findViewById(@IdRes int id) {
9116             if (mChildren == null) {
9117                 return mRoot.findViewById(id);
9118             }
9119             ViewTree tree = findViewTreeById(id);
9120             return tree == null ? null : (T) tree.mRoot;
9121         }
9122 
9123         public void addChild(ViewTree child) {
9124             addChild(child, INSERT_AT_END_INDEX);
9125         }
9126 
9127         /**
9128          * Adds the given {@link ViewTree} as a child at the given index.
9129          *
9130          * @param index The position at which to add the child or -1 to add last.
9131          */
9132         public void addChild(ViewTree child, int index) {
9133             if (mChildren == null) {
9134                 mChildren = new ArrayList<>();
9135             }
9136             child.createTree();
9137 
9138             if (index == INSERT_AT_END_INDEX) {
9139                 mChildren.add(child);
9140                 return;
9141             }
9142 
9143             mChildren.add(index, child);
9144         }
9145 
9146         public void removeChildren(int start, int count) {
9147             if (mChildren != null) {
9148                 for (int i = 0; i < count; i++) {
9149                     mChildren.remove(start);
9150                 }
9151             }
9152         }
9153 
9154         private void addViewChild(View v) {
9155             // ViewTree only contains Views which can be found using findViewById.
9156             // If isRootNamespace is true, this view is skipped.
9157             // @see ViewGroup#findViewTraversal(int)
9158             if (v.isRootNamespace()) {
9159                 return;
9160             }
9161             final ViewTree target;
9162 
9163             // If the view has a valid id, i.e., if can be found using findViewById, add it to the
9164             // tree, otherwise skip this view and add its children instead.
9165             if (v.getId() != 0) {
9166                 ViewTree tree = new ViewTree(v);
9167                 mChildren.add(tree);
9168                 target = tree;
9169             } else {
9170                 target = this;
9171             }
9172 
9173             if (v instanceof ViewGroup) {
9174                 if (target.mChildren == null) {
9175                     target.mChildren = new ArrayList<>();
9176                     ViewGroup vg = (ViewGroup) v;
9177                     int count = vg.getChildCount();
9178                     for (int i = 0; i < count; i++) {
9179                         target.addViewChild(vg.getChildAt(i));
9180                     }
9181                 }
9182             }
9183         }
9184 
9185         /** Find the first child for which the condition is true and return its index. */
9186         public int findChildIndex(Predicate<View> condition) {
9187             return findChildIndex(0, condition);
9188         }
9189 
9190         /**
9191          * Find the first child, starting at {@code startIndex}, for which the condition is true and
9192          * return its index.
9193          */
9194         public int findChildIndex(int startIndex, Predicate<View> condition) {
9195             if (mChildren == null) {
9196                 return -1;
9197             }
9198 
9199             for (int i = startIndex; i < mChildren.size(); i++) {
9200                 if (condition.test(mChildren.get(i).mRoot)) {
9201                     return i;
9202                 }
9203             }
9204             return -1;
9205         }
9206     }
9207 
9208     /**
9209      * Class representing a response to an action performed on any element of a RemoteViews.
9210      */
9211     public static class RemoteResponse {
9212 
9213         /** @hide **/
9214         @IntDef(prefix = "INTERACTION_TYPE_", value = {
9215                 INTERACTION_TYPE_CLICK,
9216                 INTERACTION_TYPE_CHECKED_CHANGE,
9217         })
9218         @Retention(RetentionPolicy.SOURCE)
9219         @interface InteractionType {}
9220         /** @hide */
9221         public static final int INTERACTION_TYPE_CLICK = 0;
9222         /** @hide */
9223         public static final int INTERACTION_TYPE_CHECKED_CHANGE = 1;
9224 
9225         private PendingIntent mPendingIntent;
9226         private Intent mFillIntent;
9227 
9228         private int mInteractionType = INTERACTION_TYPE_CLICK;
9229         private IntArray mViewIds;
9230         private ArrayList<String> mElementNames;
9231 
9232         /**
9233          * Creates a response which sends a pending intent as part of the response. The source
9234          * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
9235          * target view in screen space.
9236          * Note that any activity options associated with the mPendingIntent may get overridden
9237          * before starting the intent.
9238          *
9239          * @param pendingIntent The {@link PendingIntent} to send as part of the response
9240          */
9241         @NonNull
9242         public static RemoteResponse fromPendingIntent(@NonNull PendingIntent pendingIntent) {
9243             RemoteResponse response = new RemoteResponse();
9244             response.mPendingIntent = pendingIntent;
9245             return response;
9246         }
9247 
9248         /**
9249          * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
9250          * very costly to set PendingIntents on the individual items, and is hence not recommended.
9251          * Instead a single PendingIntent template can be set on the collection, see {@link
9252          * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
9253          * action of a given item can be distinguished by setting a fillInIntent on that item. The
9254          * fillInIntent is then combined with the PendingIntent template in order to determine the
9255          * final intent which will be executed when the item is clicked. This works as follows: any
9256          * fields which are left blank in the PendingIntent template, but are provided by the
9257          * fillInIntent will be overwritten, and the resulting PendingIntent will be used. The rest
9258          * of the PendingIntent template will then be filled in with the associated fields that are
9259          * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
9260          * Creates a response which sends a pending intent as part of the response. The source
9261          * bounds ({@link Intent#getSourceBounds()}) of the intent will be set to the bounds of the
9262          * target view in screen space.
9263          * Note that any activity options associated with the mPendingIntent may get overridden
9264          * before starting the intent.
9265          *
9266          * @param fillIntent The intent which will be combined with the parent's PendingIntent in
9267          *                   order to determine the behavior of the response
9268          * @see RemoteViews#setPendingIntentTemplate(int, PendingIntent)
9269          * @see RemoteViews#setOnClickFillInIntent(int, Intent)
9270          */
9271         @NonNull
9272         public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
9273             RemoteResponse response = new RemoteResponse();
9274             response.mFillIntent = fillIntent;
9275             if (fillIntent != null) {
9276                 // Although the parameter is marked as @NonNull, it is nullable. The method that
9277                 // calls it (RemoteReviews.setOnClickFillInIntent()) passes its fillInIntent
9278                 // parameter to this method and it does not guarantee that the fillInIntent is
9279                 // non-null.
9280                 fillIntent.collectExtraIntentKeys();
9281             }
9282             return response;
9283         }
9284 
9285         private static RemoteResponse fromPendingIntentTemplateAndFillInIntent(
9286                 @NonNull final PendingIntent pendingIntent, @NonNull final Intent intent) {
9287             RemoteResponse response = new RemoteResponse();
9288             response.mPendingIntent = pendingIntent;
9289             response.mFillIntent = intent;
9290             intent.collectExtraIntentKeys();
9291             return response;
9292         }
9293 
9294         /**
9295          * Adds a shared element to be transferred as part of the transition between Activities
9296          * using cross-Activity scene animations. The position of the first element will be used as
9297          * the epicenter for the exit Transition. The position of the associated shared element in
9298          * the launched Activity will be the epicenter of its entering Transition.
9299          *
9300          * @param viewId            The id of the view to be shared as part of the transition
9301          * @param sharedElementName The shared element name for this view
9302          * @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
9303          */
9304         @NonNull
9305         public RemoteResponse addSharedElement(@IdRes int viewId,
9306                 @NonNull String sharedElementName) {
9307             if (mViewIds == null) {
9308                 mViewIds = new IntArray();
9309                 mElementNames = new ArrayList<>();
9310             }
9311             mViewIds.add(viewId);
9312             mElementNames.add(sharedElementName);
9313             return this;
9314         }
9315 
9316         /**
9317          * Sets the interaction type for which this RemoteResponse responds.
9318          *
9319          * @param type the type of interaction for which this is a response, such as clicking or
9320          *             checked state changing
9321          *
9322          * @hide
9323          */
9324         @NonNull
9325         public RemoteResponse setInteractionType(@InteractionType int type) {
9326             mInteractionType = type;
9327             return this;
9328         }
9329 
9330         private void writeToParcel(Parcel dest, int flags) {
9331             PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
9332             dest.writeBoolean((mFillIntent != null));
9333             if (mFillIntent != null) {
9334                 dest.writeTypedObject(mFillIntent, flags);
9335             }
9336             dest.writeInt(mInteractionType);
9337             dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray());
9338             dest.writeStringList(mElementNames);
9339         }
9340 
9341         private void readFromParcel(Parcel parcel) {
9342             mPendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
9343             mFillIntent = parcel.readBoolean() ? parcel.readTypedObject(Intent.CREATOR) : null;
9344             mInteractionType = parcel.readInt();
9345             int[] viewIds = parcel.createIntArray();
9346             mViewIds = viewIds == null ? null : IntArray.wrap(viewIds);
9347             mElementNames = parcel.createStringArrayList();
9348         }
9349 
9350         private void handleViewInteraction(
9351                 View v,
9352                 InteractionHandler handler) {
9353             final PendingIntent pi;
9354             if (mPendingIntent != null) {
9355                 pi = mPendingIntent;
9356             } else if (mFillIntent != null) {
9357                 AdapterView<?> ancestor = getAdapterViewAncestor(v);
9358                 if (ancestor == null) {
9359                     Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
9360                     return;
9361                 }
9362 
9363                 // Ensure that a template pending intent has been set on the ancestor
9364                 if (!(ancestor.getTag() instanceof PendingIntent)) {
9365                     Log.e(LOG_TAG, "Attempting setOnClickFillInIntent or "
9366                             + "setOnCheckedChangeFillInIntent without calling "
9367                             + "setPendingIntentTemplate on parent.");
9368                     return;
9369                 }
9370 
9371                 pi = (PendingIntent) ancestor.getTag();
9372             } else {
9373                 Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent");
9374                 return;
9375             }
9376 
9377             handler.onInteraction(v, pi, this);
9378         }
9379 
9380         /**
9381          * Returns the closest ancestor of the view that is an AdapterView or null if none could be
9382          * found.
9383          */
9384         @Nullable
9385         private static AdapterView<?> getAdapterViewAncestor(@Nullable View view) {
9386             if (view == null) return null;
9387 
9388             View parent = (View) view.getParent();
9389             // Break the for loop on the first encounter of:
9390             //    1) an AdapterView,
9391             //    2) an AppWidgetHostView that is not a child of an adapter view, or
9392             //    3) a null parent.
9393             // 2) and 3) are unexpected and catch the case where a child is not
9394             // correctly parented in an AdapterView.
9395             while (parent != null && !(parent instanceof AdapterView<?>)
9396                     && !((parent instanceof AppWidgetHostView)
9397                             && !(parent instanceof AppWidgetHostView.AdapterChildHostView))) {
9398                 parent = (View) parent.getParent();
9399             }
9400 
9401             return parent instanceof AdapterView<?> ? (AdapterView<?>) parent : null;
9402         }
9403 
9404         /** @hide */
9405         public Pair<Intent, ActivityOptions> getLaunchOptions(View view) {
9406             Intent intent = mFillIntent == null ? new Intent() : new Intent(mFillIntent);
9407             intent.setSourceBounds(getSourceBounds(view));
9408 
9409             if (view instanceof CompoundButton
9410                     && mInteractionType == INTERACTION_TYPE_CHECKED_CHANGE) {
9411                 intent.putExtra(EXTRA_CHECKED, ((CompoundButton) view).isChecked());
9412             }
9413 
9414             ActivityOptions opts = null;
9415 
9416             Context context = view.getContext();
9417             if (context.getResources().getBoolean(
9418                     com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
9419                 TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
9420                         com.android.internal.R.styleable.Window);
9421                 int windowAnimations = windowStyle.getResourceId(
9422                         com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
9423                 TypedArray windowAnimationStyle = context.obtainStyledAttributes(
9424                         windowAnimations, com.android.internal.R.styleable.WindowAnimation);
9425                 int enterAnimationId = windowAnimationStyle.getResourceId(com.android.internal.R
9426                         .styleable.WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0);
9427                 windowStyle.recycle();
9428                 windowAnimationStyle.recycle();
9429 
9430                 if (enterAnimationId != 0) {
9431                     opts = ActivityOptions.makeCustomAnimation(context,
9432                             enterAnimationId, 0);
9433                     opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9434                 }
9435             }
9436 
9437             if (opts == null && mViewIds != null && mElementNames != null) {
9438                 View parent = (View) view.getParent();
9439                 while (parent != null && !(parent instanceof AppWidgetHostView)) {
9440                     parent = (View) parent.getParent();
9441                 }
9442                 if (parent instanceof AppWidgetHostView) {
9443                     opts = ((AppWidgetHostView) parent).createSharedElementActivityOptions(
9444                             mViewIds.toArray(),
9445                             mElementNames.toArray(new String[mElementNames.size()]), intent);
9446                 }
9447             }
9448 
9449             if (opts == null) {
9450                 opts = ActivityOptions.makeBasic();
9451                 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9452             }
9453             if (view.getDisplay() != null) {
9454                 opts.setLaunchDisplayId(view.getDisplay().getDisplayId());
9455             } else {
9456                 // TODO(b/218409359): Remove once bug is fixed.
9457                 Log.w(LOG_TAG, "getLaunchOptions: view.getDisplay() is null!",
9458                         new Exception());
9459             }
9460             // If the user interacts with a visible element it is safe to assume they consent that
9461             // something is going to start.
9462             opts.setPendingIntentBackgroundActivityStartMode(
9463                     ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
9464             return Pair.create(intent, opts);
9465         }
9466     }
9467 
9468     /** @hide */
9469     public static boolean startPendingIntent(View view, PendingIntent pendingIntent,
9470             Pair<Intent, ActivityOptions> options) {
9471         try {
9472             // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
9473             Context context = view.getContext();
9474             // The NEW_TASK flags are applied through the activity options and not as a part of
9475             // the call to startIntentSender() to ensure that they are consistently applied to
9476             // both mutable and immutable PendingIntents.
9477             context.startIntentSender(
9478                     pendingIntent.getIntentSender(), options.first,
9479                     0, 0, 0, options.second.toBundle());
9480         } catch (IntentSender.SendIntentException e) {
9481             Log.e(LOG_TAG, "Cannot send pending intent: ", e);
9482             return false;
9483         } catch (Exception e) {
9484             Log.e(LOG_TAG, "Cannot send pending intent due to unknown exception: ", e);
9485             return false;
9486         }
9487         return true;
9488     }
9489 
9490     private int getBitmapMemoryUsedByActions() {
9491         Set<Integer> bitmapIdSet = getBitmapIdsUsedByActions(new HashSet<>());
9492         int result = 0;
9493         for (int bitmapId: bitmapIdSet) {
9494             Bitmap currentBitmap = mBitmapCache.getBitmapForId(bitmapId);
9495             if (currentBitmap == null) {
9496                 continue;
9497             }
9498             result += currentBitmap.getAllocationByteCount();
9499         }
9500 
9501         return result;
9502     }
9503 
9504     private Set<Integer> getBitmapIdsUsedByActions(@NonNull Set<Integer> intSet) {
9505         if (hasSizedRemoteViews()) {
9506             for (RemoteViews views: mSizedRemoteViews) {
9507                 views.getBitmapIdsUsedByActions(intSet);
9508             }
9509         } else if (hasLandscapeAndPortraitLayouts()) {
9510             mLandscape.getBitmapIdsUsedByActions(intSet);
9511             mPortrait.getBitmapIdsUsedByActions(intSet);
9512         } else if (mActions != null) {
9513             for (Action action: mActions) {
9514                 if (action instanceof ViewGroupActionAdd vgaa
9515                         && vgaa.mNestedViews != null) {
9516                     vgaa.mNestedViews.getBitmapIdsUsedByActions(intSet);
9517                 } else if (action instanceof BitmapReflectionAction bitmapAction) {
9518                     intSet.add(bitmapAction.mBitmapId);
9519                 }
9520             }
9521         }
9522 
9523         return intSet;
9524     }
9525 
9526     /** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
9527     public static final class RemoteCollectionItems implements Parcelable {
9528         private final long[] mIds;
9529         private final RemoteViews[] mViews;
9530         private final boolean mHasStableIds;
9531         private final int mViewTypeCount;
9532 
9533         private HierarchyRootData mHierarchyRootData;
9534 
9535         RemoteCollectionItems(
9536                 long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount) {
9537             mIds = ids;
9538             mViews = views;
9539             mHasStableIds = hasStableIds;
9540             mViewTypeCount = viewTypeCount;
9541             if (ids.length != views.length) {
9542                 throw new IllegalArgumentException(
9543                         "RemoteCollectionItems has different number of ids and views");
9544             }
9545             if (viewTypeCount < 1) {
9546                 throw new IllegalArgumentException("View type count must be >= 1");
9547             }
9548             int layoutIdCount = (int) Arrays.stream(views)
9549                     .mapToInt(RemoteViews::getLayoutId)
9550                     .distinct()
9551                     .count();
9552             if (layoutIdCount > viewTypeCount) {
9553                 throw new IllegalArgumentException(
9554                         "View type count is set to " + viewTypeCount + ", but the collection "
9555                                 + "contains " + layoutIdCount + " different layout ids");
9556             }
9557 
9558             // Until the collection items are attached to a parent, we configure the first item
9559             // to be the root of the others to share caches and save space during serialization.
9560             if (views.length > 0) {
9561                 setHierarchyRootData(views[0].getHierarchyRootData());
9562                 views[0].mIsRoot = true;
9563             }
9564         }
9565 
9566         RemoteCollectionItems(@NonNull Parcel in, @Nullable HierarchyRootData hierarchyRootData) {
9567             mHasStableIds = in.readBoolean();
9568             mViewTypeCount = in.readInt();
9569             int length = in.readInt();
9570             mIds = new long[length];
9571             in.readLongArray(mIds);
9572 
9573             boolean attached = in.readBoolean();
9574             mViews = new RemoteViews[length];
9575             int firstChildIndex;
9576             if (attached) {
9577                 if (hierarchyRootData == null) {
9578                     throw new IllegalStateException("Cannot unparcel a RemoteCollectionItems that "
9579                             + "was parceled as attached without providing data for a root "
9580                             + "RemoteViews");
9581                 }
9582                 mHierarchyRootData = hierarchyRootData;
9583                 firstChildIndex = 0;
9584             } else {
9585                 mViews[0] = new RemoteViews(in);
9586                 mHierarchyRootData = mViews[0].getHierarchyRootData();
9587                 firstChildIndex = 1;
9588             }
9589 
9590             for (int i = firstChildIndex; i < length; i++) {
9591                 mViews[i] = new RemoteViews(
9592                         in,
9593                         mHierarchyRootData,
9594                         /* info= */ null,
9595                         /* depth= */ 0);
9596             }
9597         }
9598 
9599         void setHierarchyRootData(@NonNull HierarchyRootData rootData) {
9600             mHierarchyRootData = rootData;
9601             for (RemoteViews view : mViews) {
9602                 view.configureAsChild(rootData);
9603             }
9604         }
9605 
9606         @Override
9607         public int describeContents() {
9608             return 0;
9609         }
9610 
9611         @Override
9612         public void writeToParcel(@NonNull Parcel dest, int flags) {
9613             writeToParcel(dest, flags, /* attached= */ false);
9614         }
9615 
9616         private void writeToParcel(@NonNull Parcel dest, int flags, boolean attached) {
9617             boolean prevAllowSquashing = dest.allowSquashing();
9618 
9619             dest.writeBoolean(mHasStableIds);
9620             dest.writeInt(mViewTypeCount);
9621             dest.writeInt(mIds.length);
9622             dest.writeLongArray(mIds);
9623 
9624             if (attached && mHierarchyRootData == null) {
9625                 throw new IllegalStateException("Cannot call writeToParcelAttached for a "
9626                         + "RemoteCollectionItems without first calling setHierarchyRootData()");
9627             }
9628 
9629             // Write whether we parceled as attached or not. This allows cleaner validation and
9630             // proper error messaging when unparceling later.
9631             dest.writeBoolean(attached);
9632             boolean restoreRoot = false;
9633             if (!attached && mViews.length > 0 && !mViews[0].mIsRoot) {
9634                 // If we're writing unattached, temporarily set the first item as the root so that
9635                 // the bitmap cache is written to the parcel.
9636                 restoreRoot = true;
9637                 mViews[0].mIsRoot = true;
9638             }
9639 
9640             for (RemoteViews view : mViews) {
9641                 view.writeToParcel(dest, flags);
9642             }
9643 
9644             if (restoreRoot) mViews[0].mIsRoot = false;
9645             dest.restoreAllowSquashing(prevAllowSquashing);
9646         }
9647 
9648         /** @hide */
9649         public void writeToProto(Context context, ProtoOutputStream out) {
9650             writeToProto(context, out, /* attached= */ false);
9651         }
9652 
9653         private void writeToProto(Context context, ProtoOutputStream out, boolean attached) {
9654             for (long id : mIds) {
9655                 out.write(RemoteViewsProto.RemoteCollectionItems.IDS, id);
9656             }
9657 
9658             boolean restoreRoot = false;
9659             out.write(RemoteViewsProto.RemoteCollectionItems.ATTACHED, attached);
9660             if (!attached && mViews.length > 0 && !mViews[0].mIsRoot) {
9661                 restoreRoot = true;
9662                 mViews[0].mIsRoot = true;
9663             }
9664             for (RemoteViews view : mViews) {
9665                 final long viewsToken = out.start(RemoteViewsProto.RemoteCollectionItems.VIEWS);
9666                 view.writePreviewToProto(context, out);
9667                 out.end(viewsToken);
9668             }
9669             if (restoreRoot) mViews[0].mIsRoot = false;
9670             out.write(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS, mHasStableIds);
9671             out.write(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT, mViewTypeCount);
9672         }
9673 
9674         /**
9675          * Overload used for testing unattached RemoteCollectionItems serialization.
9676          *
9677          * @hide
9678          */
9679         public static RemoteCollectionItems createFromProto(Context context, ProtoInputStream in)
9680                 throws Exception {
9681             return createFromProto(in).create(context, context.getResources(), /* rootData= */
9682                     null, 0);
9683         }
9684 
9685         /** @hide */
9686         public static PendingResources<RemoteCollectionItems> createFromProto(ProtoInputStream in)
9687                 throws Exception {
9688             final LongSparseArray<Object> values = new LongSparseArray<>();
9689 
9690             values.put(RemoteViewsProto.RemoteCollectionItems.IDS, new ArrayList<Long>());
9691             values.put(RemoteViewsProto.RemoteCollectionItems.VIEWS,
9692                     new ArrayList<PendingResources<RemoteViews>>());
9693 
9694             while (in.nextField() != NO_MORE_FIELDS) {
9695                 switch (in.getFieldNumber()) {
9696                     case (int) RemoteViewsProto.RemoteCollectionItems.IDS:
9697                         ((ArrayList<Long>) values.get(
9698                                 RemoteViewsProto.RemoteCollectionItems.IDS)).add(
9699                                 in.readLong(RemoteViewsProto.RemoteCollectionItems.IDS));
9700                         break;
9701                     case (int) RemoteViewsProto.RemoteCollectionItems.VIEWS:
9702                         final long viewsToken = in.start(
9703                                 RemoteViewsProto.RemoteCollectionItems.VIEWS);
9704                         ((ArrayList<PendingResources<RemoteViews>>) values.get(
9705                                 RemoteViewsProto.RemoteCollectionItems.VIEWS)).add(
9706                                 RemoteViews.createFromProto(in));
9707                         in.end(viewsToken);
9708                         break;
9709                     case (int) RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS:
9710                         values.put(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS,
9711                                 in.readBoolean(
9712                                         RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS));
9713                         break;
9714                     case (int) RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT:
9715                         values.put(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT,
9716                                 in.readInt(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT));
9717                         break;
9718                     case (int) RemoteViewsProto.RemoteCollectionItems.ATTACHED:
9719                         values.put(RemoteViewsProto.RemoteCollectionItems.ATTACHED,
9720                                 in.readBoolean(RemoteViewsProto.RemoteCollectionItems.ATTACHED));
9721                         break;
9722                     default:
9723                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
9724                                 + ProtoUtils.currentFieldToString(in));
9725                 }
9726             }
9727 
9728             checkContainsKeys(values,
9729                     new long[]{RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT});
9730 
9731             return (context, resources, rootData, depth) -> {
9732                 List<Long> idList = (List<Long>) values.get(
9733                         RemoteViewsProto.RemoteCollectionItems.IDS);
9734                 long[] ids = new long[idList.size()];
9735                 for (int i = 0; i < idList.size(); i++) {
9736                     ids[i] = idList.get(i);
9737                 }
9738                 boolean attached = (boolean) values.get(
9739                         RemoteViewsProto.RemoteCollectionItems.ATTACHED, false);
9740                 List<PendingResources<RemoteViews>> pendingViews =
9741                         (List<PendingResources<RemoteViews>>) values.get(
9742                                 RemoteViewsProto.RemoteCollectionItems.VIEWS);
9743                 RemoteViews[] views = new RemoteViews[pendingViews.size()];
9744 
9745                 if (attached && rootData == null) {
9746                     throw new IllegalStateException("Cannot create a RemoteCollectionItems from "
9747                             + "proto that was attached without providing HierarchyRootData");
9748                 }
9749 
9750                 int firstChildIndex = 0;
9751                 if (!attached && pendingViews.size() > 0) {
9752                     // If written as unattached, get HierarchyRootData from first view
9753                     views[0] = pendingViews.get(0).create(context, resources, /* rootData= */ null,
9754                             /* depth= */ 0);
9755                     rootData = views[0].getHierarchyRootData();
9756                     firstChildIndex = 1;
9757                 }
9758                 for (int i = firstChildIndex; i < views.length; i++) {
9759                     // Depth is reset to 0 for RemoteCollectionItems item views, see Parcel
9760                     // constructor.
9761                     views[i] = pendingViews.get(i).create(context, resources, rootData,
9762                             /* depth= */ 0);
9763                 }
9764                 return new RemoteCollectionItems(ids, views,
9765                         (boolean) values.get(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS,
9766                                 false),
9767                         (int) values.get(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT,
9768                                 0));
9769             };
9770         }
9771 
9772         /**
9773          * Returns the id for {@code position}. See {@link #hasStableIds()} for whether this id
9774          * should be considered meaningful across collection updates.
9775          *
9776          * @return Id for the position.
9777          */
9778         public long getItemId(int position) {
9779             return mIds[position];
9780         }
9781 
9782         /**
9783          * Returns the {@link RemoteViews} to display at {@code position}.
9784          *
9785          * @return RemoteViews for the position.
9786          */
9787         @NonNull
9788         public RemoteViews getItemView(int position) {
9789             return mViews[position];
9790         }
9791 
9792         /**
9793          * Returns the number of elements in the collection.
9794          *
9795          * @return Count of items.
9796          */
9797         public int getItemCount() {
9798             return mIds.length;
9799         }
9800 
9801         /**
9802          * Returns the view type count for the collection when used in an adapter
9803          *
9804          * @return Count of view types for the collection when used in an adapter.
9805          * @see android.widget.Adapter#getViewTypeCount()
9806          */
9807         public int getViewTypeCount() {
9808             return mViewTypeCount;
9809         }
9810 
9811         /**
9812          * Indicates whether the item ids are stable across changes to the underlying data.
9813          *
9814          * @return True if the same id always refers to the same object.
9815          * @see android.widget.Adapter#hasStableIds()
9816          */
9817         public boolean hasStableIds() {
9818             return mHasStableIds;
9819         }
9820 
9821         @NonNull
9822         public static final Creator<RemoteCollectionItems> CREATOR =
9823                 new Creator<RemoteCollectionItems>() {
9824             @NonNull
9825             @Override
9826             public RemoteCollectionItems createFromParcel(@NonNull Parcel source) {
9827                 return new RemoteCollectionItems(source, /* hierarchyRoot= */ null);
9828             }
9829 
9830             @NonNull
9831             @Override
9832             public RemoteCollectionItems[] newArray(int size) {
9833                 return new RemoteCollectionItems[size];
9834             }
9835         };
9836 
9837         /** Builder class for {@link RemoteCollectionItems} objects.*/
9838         public static final class Builder {
9839             private final LongArray mIds = new LongArray();
9840             private final List<RemoteViews> mViews = new ArrayList<>();
9841             private boolean mHasStableIds;
9842             private int mViewTypeCount;
9843 
9844             /**
9845              * Adds a {@link RemoteViews} to the collection.
9846              *
9847              * @param id Id to associate with the row. Use {@link #setHasStableIds(boolean)} to
9848              *           indicate that ids are stable across changes to the collection.
9849              * @param view RemoteViews to display for the row.
9850              */
9851             @NonNull
9852             // Covered by getItemId, getItemView, getItemCount.
9853             @SuppressLint("MissingGetterMatchingBuilder")
9854             public Builder addItem(long id, @NonNull RemoteViews view) {
9855                 if (view == null) throw new NullPointerException();
9856                 if (view.hasMultipleLayouts()) {
9857                     throw new IllegalArgumentException(
9858                             "RemoteViews used in a RemoteCollectionItems cannot specify separate "
9859                                     + "layouts for orientations or sizes.");
9860                 }
9861                 mIds.add(id);
9862                 mViews.add(view);
9863                 return this;
9864             }
9865 
9866             /**
9867              * Sets whether the item ids are stable across changes to the underlying data.
9868              *
9869              * @see android.widget.Adapter#hasStableIds()
9870              */
9871             @NonNull
9872             public Builder setHasStableIds(boolean hasStableIds) {
9873                 mHasStableIds = hasStableIds;
9874                 return this;
9875             }
9876 
9877             /**
9878              * Sets the view type count for the collection when used in an adapter. This can be set
9879              * to the maximum number of different layout ids that will be used by RemoteViews in
9880              * this collection.
9881              *
9882              * If this value is not set, then a value will be inferred from the provided items. As
9883              * a result, the adapter may need to be recreated when the list is updated with
9884              * previously unseen RemoteViews layouts for new items.
9885              *
9886              * @see android.widget.Adapter#getViewTypeCount()
9887              */
9888             @NonNull
9889             public Builder setViewTypeCount(int viewTypeCount) {
9890                 mViewTypeCount = viewTypeCount;
9891                 return this;
9892             }
9893 
9894             /** Creates the {@link RemoteCollectionItems} defined by this builder. */
9895             @NonNull
9896             public RemoteCollectionItems build() {
9897                 if (mViewTypeCount < 1) {
9898                     // If a view type count wasn't specified, set it to be the number of distinct
9899                     // layout ids used in the items.
9900                     mViewTypeCount = (int) mViews.stream()
9901                             .mapToInt(RemoteViews::getLayoutId)
9902                             .distinct()
9903                             .count();
9904                 }
9905                 return new RemoteCollectionItems(
9906                         mIds.toArray(),
9907                         mViews.toArray(new RemoteViews[0]),
9908                         mHasStableIds,
9909                         Math.max(mViewTypeCount, 1));
9910             }
9911         }
9912 
9913         /**
9914          * See {@link RemoteViews#visitUris(Consumer)}.
9915          */
9916         private void visitUris(@NonNull Consumer<Uri> visitor) {
9917             for (RemoteViews view : mViews) {
9918                 view.visitUris(visitor);
9919             }
9920         }
9921 
9922         /**
9923          * See {@link RemoteViews#visitIcons(Consumer)}.
9924          */
9925         private void visitIcons(@NonNull Consumer<Icon> visitor) {
9926             for (RemoteViews view : mViews) {
9927                 view.visitIcons(visitor);
9928             }
9929         }
9930     }
9931 
9932     /**
9933      * A data parcel that carries the instructions to draw the RemoteViews, as an alternative to
9934      * XML layout.
9935      */
9936     @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
9937     public static final class DrawInstructions {
9938 
9939         private static final long VERSION = 1L;
9940 
9941         @NonNull
9942         final List<byte[]> mInstructions;
9943 
9944         private DrawInstructions() {
9945             throw new UnsupportedOperationException(
9946                     "DrawInstructions cannot be instantiate without instructions");
9947         }
9948 
9949         private DrawInstructions(@NonNull List<byte[]> instructions) {
9950             // Create and retain an immutable copy of given instructions.
9951             mInstructions = new ArrayList<>(instructions.size());
9952             for (byte[] instruction : instructions) {
9953                 final int len = instruction.length;
9954                 final byte[] target = new byte[len];
9955                 System.arraycopy(instruction, 0, target, 0, len);
9956                 mInstructions.add(target);
9957             }
9958         }
9959 
9960         @Nullable
9961         private static DrawInstructions readFromParcel(@NonNull final Parcel in) {
9962             int size = in.readInt();
9963             if (size == -1) {
9964                 return null;
9965             }
9966             byte[] instruction;
9967             final List<byte[]> instructions = new ArrayList<>(size);
9968             for (int i = 0; i < size; i++) {
9969                 instruction = in.readBlob();
9970                 instructions.add(instruction);
9971             }
9972             return new DrawInstructions(instructions);
9973         }
9974 
9975         private static void writeToParcel(@Nullable final DrawInstructions drawInstructions,
9976                 @NonNull final Parcel dest, final int flags) {
9977             if (drawInstructions == null) {
9978                 dest.writeInt(-1);
9979                 return;
9980             }
9981             final List<byte[]> instructions = drawInstructions.mInstructions;
9982             dest.writeInt(instructions.size());
9983             for (byte[] instruction : instructions) {
9984                 dest.writeBlob(instruction);
9985             }
9986         }
9987 
9988         /**
9989          * Version number of {@link DrawInstructions} currently supported.
9990          */
9991         @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
9992         public static long getSupportedVersion() {
9993             return (long) CoreDocument.getDocumentApiLevel();
9994         }
9995 
9996         /**
9997          * Builder class for {@link DrawInstructions} objects.
9998          */
9999         @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
10000         public static final class Builder {
10001 
10002             private final List<byte[]> mInstructions;
10003 
10004             /**
10005              * Constructor.
10006              *
10007              * @param instructions Information to draw the RemoteViews.
10008              */
10009             @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
10010             public Builder(@NonNull final List<byte[]> instructions) {
10011                 mInstructions = new ArrayList<>(instructions);
10012             }
10013 
10014             /**
10015              * Creates a {@link DrawInstructions} instance.
10016              */
10017             @NonNull
10018             @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
10019             public DrawInstructions build() {
10020                 return new DrawInstructions(mInstructions);
10021             }
10022         }
10023     }
10024 
10025     /**
10026      * Get the ID of the top-level view of the XML layout, if set using
10027      * {@link RemoteViews#RemoteViews(String, int, int)}.
10028      */
10029     @IdRes
10030     public int getViewId() {
10031         return mViewId;
10032     }
10033 
10034     /**
10035      * Set the provider instance ID.
10036      *
10037      * This should only be used by {@link com.android.server.appwidget.AppWidgetService}.
10038      * @hide
10039      */
10040     public void setProviderInstanceId(long id) {
10041         mProviderInstanceId = id;
10042     }
10043 
10044     /**
10045      * Get the provider instance id.
10046      *
10047      * This should uniquely identifies {@link RemoteViews} coming from a given App Widget
10048      * Provider. This changes each time the App Widget provider update the {@link RemoteViews} of
10049      * its widget. Returns -1 if the {@link RemoteViews} doesn't come from an App Widget provider.
10050      * @hide
10051      */
10052     public long getProviderInstanceId() {
10053         return mProviderInstanceId;
10054     }
10055 
10056     /**
10057      * Identify the child of this {@link RemoteViews}, or 0 if this is not a child.
10058      *
10059      * The returned value is always a small integer, currently between 0 and 17.
10060      */
10061     private int getChildId(@NonNull RemoteViews child) {
10062         if (child == this) {
10063             return 0;
10064         }
10065         if (hasSizedRemoteViews()) {
10066             for (int i = 0; i < mSizedRemoteViews.size(); i++) {
10067                 if (mSizedRemoteViews.get(i) == child) {
10068                     return i + 1;
10069                 }
10070             }
10071         }
10072         if (hasLandscapeAndPortraitLayouts()) {
10073             if (mLandscape == child) {
10074                 return 1;
10075             } else if (mPortrait == child) {
10076                 return 2;
10077             }
10078         }
10079         // This is not a child of this RemoteViews.
10080         return 0;
10081     }
10082 
10083     /**
10084      * Identify uniquely this RemoteViews, or returns -1 if not possible.
10085      *
10086      * @param parent If the {@link RemoteViews} is not a root {@link RemoteViews}, this should be
10087      *              the parent that contains it.
10088      *
10089      * @hide
10090      */
10091     public long computeUniqueId(@Nullable RemoteViews parent) {
10092         if (mIsRoot) {
10093             long viewId = getProviderInstanceId();
10094             if (viewId != -1) {
10095                 viewId <<= 8;
10096             }
10097             return viewId;
10098         }
10099         if (parent == null) {
10100             return -1;
10101         }
10102         long viewId = parent.getProviderInstanceId();
10103         if (viewId == -1) {
10104             return -1;
10105         }
10106         int childId = parent.getChildId(this);
10107         if (childId == -1) {
10108             return -1;
10109         }
10110         viewId <<= 8;
10111         viewId |= childId;
10112         return viewId;
10113     }
10114 
10115     @Nullable
10116     private static Pair<String, Integer> getPackageUserKey(@Nullable ApplicationInfo info) {
10117         if (info == null || info.packageName ==  null) return null;
10118         return Pair.create(info.packageName, info.uid);
10119     }
10120 
10121     private HierarchyRootData getHierarchyRootData() {
10122         return new HierarchyRootData(mBitmapCache, mCollectionCache,
10123                 mApplicationInfoCache, mClassCookies);
10124     }
10125 
10126     private static final class HierarchyRootData {
10127         final BitmapCache mBitmapCache;
10128         final RemoteCollectionCache mRemoteCollectionCache;
10129         final ApplicationInfoCache mApplicationInfoCache;
10130         final Map<Class, Object> mClassCookies;
10131 
10132         HierarchyRootData(
10133                 BitmapCache bitmapCache,
10134                 RemoteCollectionCache remoteCollectionCache,
10135                 ApplicationInfoCache applicationInfoCache,
10136                 Map<Class, Object> classCookies) {
10137             mBitmapCache = bitmapCache;
10138             mRemoteCollectionCache = remoteCollectionCache;
10139             mApplicationInfoCache = applicationInfoCache;
10140             mClassCookies = classCookies;
10141         }
10142     }
10143 
10144     /**
10145      * Write this RemoteViews to proto.
10146      * @hide
10147      */
10148     @FlaggedApi(FLAG_REMOTE_VIEWS_PROTO)
10149     public void writePreviewToProto(@NonNull Context context, @NonNull ProtoOutputStream out) {
10150         if (mApplication != null) {
10151             // mApplication may be null if this was created with DrawInstructions constructor.
10152             out.write(RemoteViewsProto.PACKAGE_NAME, mApplication.packageName);
10153             out.write(RemoteViewsProto.UID, mApplication.uid);
10154         }
10155         Resources appResources = getContextForResourcesEnsuringCorrectCachedApkPaths(
10156                 context).getResources();
10157         if (mLayoutId != 0) {
10158             out.write(RemoteViewsProto.LAYOUT_ID, appResources.getResourceName(mLayoutId));
10159         }
10160         if (mLightBackgroundLayoutId != 0) {
10161             out.write(RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID,
10162                     appResources.getResourceName(mLightBackgroundLayoutId));
10163         }
10164         if (mViewId != 0 && mViewId != -1) {
10165             out.write(RemoteViewsProto.VIEW_ID, appResources.getResourceName(mViewId));
10166         }
10167         if (mIsRoot) {
10168             mBitmapCache.writeBitmapsToProto(out);
10169             mCollectionCache.writeToProto(context, out);
10170         }
10171         out.write(RemoteViewsProto.IS_ROOT, mIsRoot);
10172         out.write(RemoteViewsProto.APPLY_FLAGS, mApplyFlags);
10173         out.write(RemoteViewsProto.HAS_DRAW_INSTRUCTIONS, mHasDrawInstructions);
10174         if (mProviderInstanceId != -1) {
10175             out.write(RemoteViewsProto.PROVIDER_INSTANCE_ID, mProviderInstanceId);
10176         }
10177 
10178         if (!hasMultipleLayouts()) {
10179             out.write(RemoteViewsProto.MODE, MODE_NORMAL);
10180             if (mIdealSize != null) {
10181                 final long token = out.start(RemoteViewsProto.IDEAL_SIZE);
10182                 out.write(SizeFProto.WIDTH, mIdealSize.getWidth());
10183                 out.write(SizeFProto.HEIGHT, mIdealSize.getHeight());
10184                 out.end(token);
10185             }
10186 
10187             if (mActions != null) {
10188                 for (Action action : mActions) {
10189                     if (action.canWriteToProto()) {
10190                         final long token = out.start(RemoteViewsProto.ACTIONS);
10191                         action.writeToProto(out, context, appResources);
10192                         out.end(token);
10193                     }
10194                 }
10195             }
10196         } else if (hasSizedRemoteViews()) {
10197             out.write(RemoteViewsProto.MODE, MODE_HAS_SIZED_REMOTEVIEWS);
10198             for (RemoteViews view : mSizedRemoteViews) {
10199                 final long sizedViewToken = out.start(RemoteViewsProto.SIZED_REMOTEVIEWS);
10200                 view.writePreviewToProto(context, out);
10201                 out.end(sizedViewToken);
10202             }
10203         } else {
10204             out.write(RemoteViewsProto.MODE, MODE_HAS_LANDSCAPE_AND_PORTRAIT);
10205             final long landscapeViewToken = out.start(RemoteViewsProto.LANDSCAPE_REMOTEVIEWS);
10206             mLandscape.writePreviewToProto(context, out);
10207             out.end(landscapeViewToken);
10208             final long portraitViewToken = out.start(RemoteViewsProto.PORTRAIT_REMOTEVIEWS);
10209             mPortrait.writePreviewToProto(context, out);
10210             out.end(portraitViewToken);
10211         }
10212     }
10213 
10214     /**
10215      * Create a RemoteViews from proto input.
10216      * @hide
10217      */
10218     @FlaggedApi(FLAG_REMOTE_VIEWS_PROTO)
10219     public static RemoteViews createPreviewFromProto(Context context, ProtoInputStream in)
10220             throws Exception {
10221         return createFromProto(in).create(context, context.getResources(), /* rootData= */ null,
10222                 /* depth= */ 0);
10223     }
10224 
10225     private static PendingResources<RemoteViews> createFromProto(ProtoInputStream in)
10226             throws Exception {
10227         // Grouping these variables into an anonymous object allows us to access them through `ref`
10228         // (which is final) later in the lambda.
10229         final var ref = new Object() {
10230             final RemoteViews mRv = new RemoteViews();
10231             int mMode = 0;
10232             int mApplyFlags = 0;
10233             long mProviderInstanceId = -1;
10234             String mPackageName = null;
10235             Integer mUid = null;
10236             SizeF mIdealSize = null;
10237             String mLayoutResName = null;
10238             String mLightBackgroundResName = null;
10239             String mViewResName = null;
10240             final List<PendingResources<Action>> mActions = new ArrayList<>();
10241             final List<PendingResources<RemoteViews>> mSizedRemoteViews = new ArrayList<>();
10242             PendingResources<RemoteViews> mLandscapeViews = null;
10243             PendingResources<RemoteViews> mPortraitViews = null;
10244             PendingResources<RemoteCollectionCache> mPopulateRemoteCollectionCache = null;
10245             boolean mIsRoot = false;
10246             boolean mHasDrawInstructions = false;
10247         };
10248 
10249         try {
10250             while (in.nextField() != NO_MORE_FIELDS) {
10251                 switch (in.getFieldNumber()) {
10252                     case (int) RemoteViewsProto.MODE:
10253                         ref.mMode = in.readInt(RemoteViewsProto.MODE);
10254                         break;
10255                     case (int) RemoteViewsProto.PACKAGE_NAME:
10256                         ref.mPackageName = in.readString(RemoteViewsProto.PACKAGE_NAME);
10257                         break;
10258                     case (int) RemoteViewsProto.UID:
10259                         ref.mUid = in.readInt(RemoteViewsProto.UID);
10260                         break;
10261                     case (int) RemoteViewsProto.IDEAL_SIZE:
10262                         final long idealSizeToken = in.start(RemoteViewsProto.IDEAL_SIZE);
10263                         ref.mIdealSize = createSizeFFromProto(in);
10264                         in.end(idealSizeToken);
10265                         break;
10266                     case (int) RemoteViewsProto.LAYOUT_ID:
10267                         ref.mLayoutResName = in.readString(RemoteViewsProto.LAYOUT_ID);
10268                         break;
10269                     case (int) RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID:
10270                         ref.mLightBackgroundResName = in.readString(
10271                                 RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID);
10272                         break;
10273                     case (int) RemoteViewsProto.VIEW_ID:
10274                         ref.mViewResName = in.readString(RemoteViewsProto.VIEW_ID);
10275                         break;
10276                     case (int) RemoteViewsProto.APPLY_FLAGS:
10277                         ref.mApplyFlags = in.readInt(RemoteViewsProto.APPLY_FLAGS);
10278                         break;
10279                     case (int) RemoteViewsProto.PROVIDER_INSTANCE_ID:
10280                         ref.mProviderInstanceId = in.readInt(RemoteViewsProto.PROVIDER_INSTANCE_ID);
10281                         break;
10282                     case (int) RemoteViewsProto.ACTIONS:
10283                         final long actionsToken = in.start(RemoteViewsProto.ACTIONS);
10284                         final PendingResources<Action> action = createActionFromProto(ref.mRv, in);
10285                         if (action != null) {
10286                             ref.mActions.add(action);
10287                         }
10288                         in.end(actionsToken);
10289                         break;
10290                     case (int) RemoteViewsProto.SIZED_REMOTEVIEWS:
10291                         final long sizedToken = in.start(RemoteViewsProto.SIZED_REMOTEVIEWS);
10292                         ref.mSizedRemoteViews.add(createFromProto(in));
10293                         in.end(sizedToken);
10294                         break;
10295                     case (int) RemoteViewsProto.LANDSCAPE_REMOTEVIEWS:
10296                         final long landscapeToken = in.start(
10297                                 RemoteViewsProto.LANDSCAPE_REMOTEVIEWS);
10298                         ref.mLandscapeViews = createFromProto(in);
10299                         in.end(landscapeToken);
10300                         break;
10301                     case (int) RemoteViewsProto.PORTRAIT_REMOTEVIEWS:
10302                         final long portraitToken = in.start(RemoteViewsProto.PORTRAIT_REMOTEVIEWS);
10303                         ref.mPortraitViews = createFromProto(in);
10304                         in.end(portraitToken);
10305                         break;
10306                     case (int) RemoteViewsProto.BITMAP_CACHE:
10307                         byte[] src = in.readBytes(RemoteViewsProto.BITMAP_CACHE);
10308                         Bitmap bitmap = BitmapFactory.decodeByteArray(src, 0, src.length);
10309                         ref.mRv.mBitmapCache.getBitmapId(bitmap);
10310                         break;
10311                     case (int) RemoteViewsProto.REMOTE_COLLECTION_CACHE:
10312                         final long collectionToken = in.start(
10313                                 RemoteViewsProto.REMOTE_COLLECTION_CACHE);
10314                         ref.mPopulateRemoteCollectionCache =
10315                                 ref.mRv.populateRemoteCollectionCacheFromProto(in);
10316                         in.end(collectionToken);
10317                         break;
10318                     case (int) RemoteViewsProto.IS_ROOT:
10319                         ref.mIsRoot = in.readBoolean(RemoteViewsProto.IS_ROOT);
10320                         break;
10321                     case (int) RemoteViewsProto.HAS_DRAW_INSTRUCTIONS:
10322                         ref.mHasDrawInstructions = in.readBoolean(
10323                                 RemoteViewsProto.HAS_DRAW_INSTRUCTIONS);
10324                         break;
10325                     default:
10326                         Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
10327                                 + ProtoUtils.currentFieldToString(in));
10328                 }
10329             }
10330         } catch (IOException e) {
10331             throw new RuntimeException(e);
10332         }
10333 
10334         return (context, resources, rootData, depth) -> {
10335             if (depth > MAX_NESTED_VIEWS && (UserHandle.getAppId(Binder.getCallingUid())
10336                     != Process.SYSTEM_UID)) {
10337                 throw new IllegalArgumentException("Too many nested views.");
10338             }
10339             depth++;
10340 
10341             RemoteViews rv = ref.mRv;
10342             rv.mApplyFlags = ref.mApplyFlags;
10343             rv.mIsRoot = ref.mIsRoot;
10344             rv.mHasDrawInstructions = ref.mHasDrawInstructions;
10345 
10346             // The root view will read its HierarchyRootData (bitmap cache, collection cache) from
10347             // proto; all nested views will instead get it through the rootData parameter.
10348             if (rootData == null) {
10349                 if (!rv.mIsRoot || depth != 1) {
10350                     throw new IllegalStateException(
10351                             "A nested view did not receive HierarchyRootData");
10352                 }
10353                 rootData = rv.getHierarchyRootData();
10354             } else {
10355                 rv.configureAsChild(rootData);
10356             }
10357 
10358             Context appContext = null;
10359             Resources appResources = null;
10360             if (!ref.mHasDrawInstructions) {
10361                 checkProtoResultNotNull(ref.mPackageName, "No application info");
10362                 checkProtoResultNotNull(ref.mUid, "No uid");
10363                 rv.mApplication = context.getPackageManager().getApplicationInfoAsUser(
10364                         ref.mPackageName, /* flags= */ 0, UserHandle.getUserId(ref.mUid));
10365                 appContext = rv.getContextForResourcesEnsuringCorrectCachedApkPaths(context);
10366                 appResources = appContext.getResources();
10367 
10368                 checkProtoResultNotNull(ref.mLayoutResName, "No layout id");
10369                 rv.mLayoutId = appResources.getIdentifier(ref.mLayoutResName, /* defType= */ null,
10370                         /* defPackage= */ null);
10371                 checkValidResource(rv.mLayoutId, "Invalid layout id", ref.mLayoutResName);
10372 
10373                 if (ref.mViewResName != null) {
10374                     rv.mViewId = appResources.getIdentifier(ref.mViewResName, /* defType= */ null,
10375                             /* defPackage= */ null);
10376                     checkValidResource(rv.mViewId, "Invalid view id", ref.mViewResName);
10377                 }
10378 
10379                 if (ref.mLightBackgroundResName != null) {
10380                     int lightBackgroundLayoutId = appResources.getIdentifier(
10381                             ref.mLightBackgroundResName,
10382                             /* defType= */ null, /* defPackage= */ null);
10383                     checkValidResource(lightBackgroundLayoutId,
10384                             "Invalid light background layout id", ref.mLightBackgroundResName);
10385                     rv.setLightBackgroundLayoutId(lightBackgroundLayoutId);
10386                 }
10387             }
10388             if (ref.mPopulateRemoteCollectionCache != null) {
10389                 ref.mPopulateRemoteCollectionCache.create(appContext, appResources, rootData,
10390                         depth);
10391             }
10392             if (ref.mProviderInstanceId != -1) {
10393                 rv.mProviderInstanceId = ref.mProviderInstanceId;
10394             }
10395             if (ref.mMode == MODE_NORMAL) {
10396                 rv.setIdealSize(ref.mIdealSize);
10397                 boolean hasDrawInstructionAction = false;
10398                 for (PendingResources<Action> pendingAction : ref.mActions) {
10399                     Action action = pendingAction.create(appContext, appResources, rootData, depth);
10400                     if (action != null) {
10401                         if (action instanceof SetDrawInstructionAction) {
10402                             hasDrawInstructionAction = true;
10403                         }
10404                         rv.addAction(action);
10405                     }
10406                 }
10407                 if (rv.mHasDrawInstructions && !hasDrawInstructionAction) {
10408                     throw new InvalidProtoException(
10409                             "RemoteViews proto is missing DrawInstructions");
10410                 }
10411                 return rv;
10412             } else if (ref.mMode == MODE_HAS_SIZED_REMOTEVIEWS) {
10413                 List<RemoteViews> sizedViews = new ArrayList<>();
10414                 for (RemoteViews.PendingResources<RemoteViews> pendingViews :
10415                         ref.mSizedRemoteViews) {
10416                     RemoteViews views = pendingViews.create(appContext, appResources, rootData,
10417                             depth);
10418                     sizedViews.add(views);
10419                 }
10420                 rv.initializeSizedRemoteViews(sizedViews.iterator());
10421                 return rv;
10422             } else if (ref.mMode == MODE_HAS_LANDSCAPE_AND_PORTRAIT) {
10423                 checkProtoResultNotNull(ref.mLandscapeViews, "Missing landscape views");
10424                 checkProtoResultNotNull(ref.mPortraitViews, "Missing portrait views");
10425                 RemoteViews parentRv = new RemoteViews(
10426                         ref.mLandscapeViews.create(appContext, appResources, rootData, depth),
10427                         ref.mPortraitViews.create(appContext, appResources, rootData, depth));
10428                 parentRv.initializeFrom(/* src= */ rv, /* hierarchyRoot= */ rv);
10429                 return parentRv;
10430             } else {
10431                 throw new InvalidProtoException(ref.mMode + " is not a valid mode.");
10432             }
10433         };
10434     }
10435 
10436     private static class InvalidProtoException extends Exception {
10437         InvalidProtoException(String message) {
10438             super(message);
10439         }
10440     }
10441 
10442     interface PendingResources<T> {
10443         T create(Context context, Resources appResources, HierarchyRootData rootData, int depth)
10444                 throws Exception;
10445     }
10446 
10447     @Nullable
10448     private static PendingResources<Action> createActionFromProto(RemoteViews rv,
10449             ProtoInputStream in) throws Exception {
10450         int actionFieldId = in.nextField();
10451         if (actionFieldId == NO_MORE_FIELDS) {
10452             // action was omitted
10453             return null;
10454         }
10455         switch (actionFieldId) {
10456             case (int) RemoteViewsProto.Action.ATTRIBUTE_REFLECTION_ACTION:
10457                 return AttributeReflectionAction.createFromProto(in);
10458             case (int) RemoteViewsProto.Action.BITMAP_REFLECTION_ACTION:
10459                 return rv.createFromBitmapReflectionActionFromProto(in);
10460             case (int) RemoteViewsProto.Action.COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION:
10461                 return ComplexUnitDimensionReflectionAction.createFromProto(in);
10462             case (int) RemoteViewsProto.Action.LAYOUT_PARAM_ACTION:
10463                 return LayoutParamAction.createFromProto(in);
10464             case (int) RemoteViewsProto.Action.NIGHT_MODE_REFLECTION_ACTION:
10465                 return NightModeReflectionAction.createFromProto(in);
10466             case (int) RemoteViewsProto.Action.REFLECTION_ACTION:
10467                 return ReflectionAction.createFromProto(in);
10468             case (int) RemoteViewsProto.Action.REMOVE_FROM_PARENT_ACTION:
10469                 return RemoveFromParentAction.createFromProto(in);
10470             case (int) RemoteViewsProto.Action.RESOURCE_REFLECTION_ACTION:
10471                 return ResourceReflectionAction.createFromProto(in);
10472             case (int) RemoteViewsProto.Action.SET_COMPOUND_BUTTON_CHECKED_ACTION:
10473                 return SetCompoundButtonCheckedAction.createFromProto(in);
10474             case (int) RemoteViewsProto.Action.SET_DRAWABLE_TINT_ACTION:
10475                 return SetDrawableTint.createFromProto(in);
10476             case (int) RemoteViewsProto.Action.SET_EMPTY_VIEW_ACTION:
10477                 return SetEmptyView.createFromProto(in);
10478             case (int) RemoteViewsProto.Action.SET_INT_TAG_ACTION:
10479                 return SetIntTagAction.createFromProto(in);
10480             case (int) RemoteViewsProto.Action.SET_RADIO_GROUP_CHECKED_ACTION:
10481                 return SetRadioGroupCheckedAction.createFromProto(in);
10482             case (int) RemoteViewsProto.Action.SET_REMOTE_COLLECTION_ITEM_LIST_ADAPTER_ACTION:
10483                 return rv.createSetRemoteCollectionItemListAdapterActionFromProto(in);
10484             case (int) RemoteViewsProto.Action.SET_RIPPLE_DRAWABLE_COLOR_ACTION:
10485                 return SetRippleDrawableColor.createFromProto(in);
10486             case (int) RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION:
10487                 return SetViewOutlinePreferredRadiusAction.createFromProto(in);
10488             case (int) RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION:
10489                 return TextViewDrawableAction.createFromProto(in);
10490             case (int) RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION:
10491                 return TextViewSizeAction.createFromProto(in);
10492             case (int) RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION:
10493                 return rv.createViewGroupActionAddFromProto(in);
10494             case (int) RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION:
10495                 return ViewGroupActionRemove.createFromProto(in);
10496             case (int) RemoteViewsProto.Action.VIEW_PADDING_ACTION:
10497                 return ViewPaddingAction.createFromProto(in);
10498             case (int) RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION:
10499                 if (!drawDataParcel()) {
10500                     return null;
10501                 }
10502                 return rv.createSetDrawInstructionActionFromProto(in);
10503             default:
10504                 throw new RuntimeException("Unhandled field while reading Action proto!\n"
10505                         + ProtoUtils.currentFieldToString(in));
10506         }
10507     }
10508 
10509     private static void checkValidResource(int id, String message, String resName)
10510             throws Exception {
10511         if (id == 0) throw new Exception(message + ": " + resName);
10512     }
10513 
10514     private static void checkProtoResultNotNull(Object o, String message)
10515             throws InvalidProtoException {
10516         if (o == null) {
10517             throw new InvalidProtoException(message);
10518         }
10519     }
10520 
10521     private static void checkContainsKeys(LongSparseArray<?> array, long[] requiredFields) {
10522         for (long requiredField : requiredFields) {
10523             if (array.indexOfKey(requiredField) < 0) {
10524                 throw new IllegalArgumentException(
10525                         "RemoteViews proto missing field: " + ProtoStream.getFieldIdString(
10526                                 requiredField));
10527             }
10528         }
10529     }
10530 
10531     private static int getAsIdentifier(Resources resources, LongSparseArray<?> array, long fieldId)
10532             throws Exception {
10533         String resName = (String) array.get(fieldId);
10534         int id = resources.getIdentifier(resName, /* defType= */ null, /* defPackage= */ null);
10535         checkValidResource(id, "Invalid id", resName);
10536         return id;
10537     }
10538 
10539     private static int getAsIdentifier(Resources resources, SparseArray<?> array, int key)
10540             throws Exception {
10541         String resName = (String) array.get(key);
10542         int id = resources.getIdentifier(resName, /* defType= */ null, /* defPackage= */ null);
10543         checkValidResource(id, "Invalid id", resName);
10544         return id;
10545     }
10546 
10547     private static SizeF createSizeFFromProto(ProtoInputStream in) throws Exception {
10548         float width = 0;
10549         float height = 0;
10550         while (in.nextField() != NO_MORE_FIELDS) {
10551             switch (in.getFieldNumber()) {
10552                 case (int) SizeFProto.WIDTH:
10553                     width = in.readFloat(SizeFProto.WIDTH);
10554                     break;
10555                 case (int) SizeFProto.HEIGHT:
10556                     height = in.readFloat(SizeFProto.HEIGHT);
10557                     break;
10558                 default:
10559                     Log.w(LOG_TAG, "Unhandled field while reading SizeF proto!\n"
10560                             + ProtoUtils.currentFieldToString(in));
10561             }
10562         }
10563 
10564         return new SizeF(width, height);
10565     }
10566 
10567     private static void writeIconToProto(ProtoOutputStream out, Resources appResources, Icon icon,
10568             long fieldId) {
10569         long token = out.start(fieldId);
10570         RemoteViewsSerializers.writeIconToProto(out, appResources, icon);
10571         out.end(token);
10572     }
10573 
10574     private static PendingResources<Icon> createIconFromProto(ProtoInputStream in, long fieldId)
10575             throws Exception {
10576         long token = in.start(fieldId);
10577         Function<Resources, Icon> icon = RemoteViewsSerializers.createIconFromProto(in);
10578         in.end(token);
10579         return (context, resources, rootData, depth) -> icon.apply(resources);
10580     }
10581 
10582     private static void writeColorStateListToProto(ProtoOutputStream out,
10583             ColorStateList colorStateList, long fieldId) {
10584         long token = out.start(fieldId);
10585         colorStateList.writeToProto(out);
10586         out.end(token);
10587     }
10588 
10589     private static ColorStateList createColorStateListFromProto(ProtoInputStream in, long fieldId)
10590             throws Exception {
10591         long token = in.start(fieldId);
10592         ColorStateList colorStateList = ColorStateList.createFromProto(in);
10593         in.end(token);
10594         return colorStateList;
10595     }
10596 
10597     private static CharSequence createCharSequenceFromProto(ProtoInputStream in, long fieldId)
10598             throws Exception {
10599         long token = in.start(fieldId);
10600         CharSequence cs = RemoteViewsSerializers.createCharSequenceFromProto(in);
10601         in.end(token);
10602         return cs;
10603     }
10604 
10605 }
10606