• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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.appwidget;
18 
19 import static android.appwidget.flags.Flags.remoteAdapterConversion;
20 
21 import android.annotation.BroadcastBehavior;
22 import android.annotation.FlaggedApi;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresFeature;
26 import android.annotation.SdkConstant;
27 import android.annotation.SdkConstant.SdkConstantType;
28 import android.annotation.SystemService;
29 import android.annotation.TestApi;
30 import android.annotation.UiThread;
31 import android.annotation.UserIdInt;
32 import android.app.IServiceConnection;
33 import android.app.PendingIntent;
34 import android.app.usage.UsageStatsManager;
35 import android.appwidget.flags.Flags;
36 import android.compat.annotation.UnsupportedAppUsage;
37 import android.content.ComponentName;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.content.Intent.FilterComparison;
41 import android.content.IntentSender;
42 import android.content.ServiceConnection;
43 import android.content.pm.PackageManager;
44 import android.content.pm.ParceledListSlice;
45 import android.content.pm.ShortcutInfo;
46 import android.graphics.Rect;
47 import android.os.Binder;
48 import android.os.Build;
49 import android.os.Bundle;
50 import android.os.Handler;
51 import android.os.HandlerExecutor;
52 import android.os.HandlerThread;
53 import android.os.IBinder;
54 import android.os.Looper;
55 import android.os.PersistableBundle;
56 import android.os.Process;
57 import android.os.RemoteException;
58 import android.os.UserHandle;
59 import android.util.ArrayMap;
60 import android.util.DisplayMetrics;
61 import android.util.Log;
62 import android.util.Pair;
63 import android.widget.RemoteViews;
64 
65 import com.android.internal.appwidget.IAppWidgetService;
66 import com.android.internal.os.BackgroundThread;
67 import com.android.internal.util.FunctionalUtils;
68 
69 import java.util.ArrayDeque;
70 import java.util.ArrayList;
71 import java.util.Collections;
72 import java.util.List;
73 import java.util.Map;
74 import java.util.Objects;
75 import java.util.concurrent.CompletableFuture;
76 import java.util.concurrent.Executor;
77 import java.util.function.Consumer;
78 
79 /**
80  * Updates AppWidget state; gets information about installed AppWidget providers and other
81  * AppWidget related state.
82  *
83  * <div class="special reference">
84  * <h3>Developer Guides</h3>
85  * <p>For more information about creating app widgets, read the
86  * <a href="{@docRoot}guide/topics/appwidgets/index.html">App Widgets</a> developer guide.</p>
87  * </div>
88  */
89 @SystemService(Context.APPWIDGET_SERVICE)
90 @RequiresFeature(PackageManager.FEATURE_APP_WIDGETS)
91 public class AppWidgetManager {
92 
93 
94     /**
95      * Activity action to launch from your {@link AppWidgetHost} activity when you want to
96      * pick an AppWidget to display.  The AppWidget picker activity will be launched.
97      * <p>
98      * You must supply the following extras:
99      * <table>
100      *   <tr>
101      *     <td>{@link #EXTRA_APPWIDGET_ID}</td>
102      *     <td>A newly allocated appWidgetId, which will be bound to the AppWidget provider
103      *         once the user has selected one.</td>
104      *  </tr>
105      * </table>
106      *
107      * <p>
108      * The system will respond with an onActivityResult call with the following extras in
109      * the intent:
110      * <table>
111      *   <tr>
112      *     <td>{@link #EXTRA_APPWIDGET_ID}</td>
113      *     <td>The appWidgetId that you supplied in the original intent.</td>
114      *  </tr>
115      * </table>
116      * <p>
117      * When you receive the result from the AppWidget pick activity, if the resultCode is
118      * {@link android.app.Activity#RESULT_OK}, an AppWidget has been selected.  You should then
119      * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its
120      * configuration activity.  If {@link android.app.Activity#RESULT_CANCELED} is returned, you
121      * should delete the appWidgetId.
122      *
123      * @see #ACTION_APPWIDGET_CONFIGURE
124      */
125     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
126     public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
127 
128     /**
129      * Similar to ACTION_APPWIDGET_PICK, but used from keyguard
130      * @hide
131      */
132     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
133     public static final String
134             ACTION_KEYGUARD_APPWIDGET_PICK = "android.appwidget.action.KEYGUARD_APPWIDGET_PICK";
135 
136     /**
137      * Activity action to launch from your {@link AppWidgetHost} activity when you want to bind
138      * an AppWidget to display and bindAppWidgetIdIfAllowed returns false.
139      * <p>
140      * You must supply the following extras:
141      * <table>
142      *   <tr>
143      *     <td>{@link #EXTRA_APPWIDGET_ID}</td>
144      *     <td>A newly allocated appWidgetId, which will be bound to the AppWidget provider
145      *         you provide.</td>
146      *  </tr>
147      *  <tr>
148      *     <td>{@link #EXTRA_APPWIDGET_PROVIDER}</td>
149      *     <td>The BroadcastReceiver that will be the AppWidget provider for this AppWidget.
150      *     </td>
151      *  </tr>
152      *  <tr>
153      *     <td>{@link #EXTRA_APPWIDGET_PROVIDER_PROFILE}</td>
154      *     <td>An optional handle to a user profile under which runs the provider
155      *     for this AppWidget.
156      *     </td>
157      *  </tr>
158      * </table>
159      *
160      * <p>
161      * The system will respond with an onActivityResult call with the following extras in
162      * the intent:
163      * <table>
164      *   <tr>
165      *     <td>{@link #EXTRA_APPWIDGET_ID}</td>
166      *     <td>The appWidgetId that you supplied in the original intent.</td>
167      *  </tr>
168      * </table>
169      * <p>
170      * When you receive the result from the AppWidget bind activity, if the resultCode is
171      * {@link android.app.Activity#RESULT_OK}, the AppWidget has been bound.  You should then
172      * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its
173      * configuration activity.  If {@link android.app.Activity#RESULT_CANCELED} is returned, you
174      * should delete the appWidgetId.
175      *
176      * @see #ACTION_APPWIDGET_CONFIGURE
177      *
178      */
179     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
180     public static final String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND";
181 
182     /**
183      * Sent when it is time to configure your AppWidget while it is being added to a host.
184      * This action is not sent as a broadcast to the AppWidget provider, but as a startActivity
185      * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo
186      * meta-data}.
187      *
188      * <p>
189      * The intent will contain the following extras:
190      * <table>
191      *   <tr>
192      *     <td>{@link #EXTRA_APPWIDGET_ID}</td>
193      *     <td>The appWidgetId to configure.</td>
194      *  </tr>
195      * </table>
196      *
197      * <p>If you return {@link android.app.Activity#RESULT_OK} using
198      * {@link android.app.Activity#setResult Activity.setResult()}, the AppWidget will be added,
199      * and you will receive an {@link #ACTION_APPWIDGET_UPDATE} broadcast for this AppWidget.
200      * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add
201      * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED}
202      * broadcast.
203      */
204     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
205     public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
206 
207     /**
208      * An intent extra (int) that contains one appWidgetId.
209      * <p>
210      * The value will be an int that can be retrieved like this:
211      * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/AppWidgetHostActivity.java getExtra_EXTRA_APPWIDGET_ID}
212      */
213     public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
214 
215     /**
216      * A bundle extra (boolean) that contains whether or not an app has finished restoring a widget.
217      * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its
218      * widgets followed by calling {@link #updateAppWidget} to update the views.
219      *
220      * @see #updateAppWidgetOptions(int, Bundle)
221      */
222     public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
223 
224 
225     /**
226      * A bundle extra (int) that contains the lower bound on the current width, in dips, of a
227      * widget instance.
228      */
229     public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
230 
231     /**
232      * A bundle extra (int) that contains the lower bound on the current height, in dips, of a
233      * widget instance.
234      */
235     public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
236 
237     /**
238      * A bundle extra (int) that contains the upper bound on the current width, in dips, of a
239      * widget instance.
240      */
241     public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
242 
243     /**
244      * A bundle extra (int) that contains the upper bound on the current width, in dips, of a
245      * widget instance.
246      */
247     public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
248 
249     /**
250      * A bundle extra ({@code List<SizeF>}) that contains the list of possible sizes, in dips, a
251      * widget instance can take.
252      */
253     public static final String OPTION_APPWIDGET_SIZES = "appWidgetSizes";
254 
255     /**
256      * A bundle extra that hints to the AppWidgetProvider the category of host that owns this
257      * this widget. Can have the value {@link
258      * AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN} or {@link
259      * AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD} or {@link
260      * AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX} or {@link
261      * AppWidgetProviderInfo#WIDGET_CATEGORY_NOT_KEYGUARD}.
262      */
263     public static final String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
264 
265     /**
266      * An intent extra which points to a bundle of extra information for a particular widget id.
267      * In particular this bundle can contain {@link #OPTION_APPWIDGET_MIN_WIDTH},
268      * {@link #OPTION_APPWIDGET_MIN_HEIGHT}, {@link #OPTION_APPWIDGET_MAX_WIDTH},
269      * {@link #OPTION_APPWIDGET_MAX_HEIGHT}.
270      */
271     public static final String EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions";
272 
273     /**
274      * An intent extra that contains multiple appWidgetIds.
275      * <p>
276      * The value will be an int array that can be retrieved like this:
277      * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS}
278      */
279     public static final String EXTRA_APPWIDGET_IDS = "appWidgetIds";
280 
281     /**
282      * An intent extra that contains the component name of a AppWidget provider.
283      * <p>
284      * The value will be an {@link android.content.ComponentName}.
285      */
286     public static final String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
287 
288     /**
289      * An intent extra that contains the user handle of the profile under
290      * which an AppWidget provider is registered.
291      * <p>
292      * The value will be a {@link android.os.UserHandle}.
293      */
294     public static final String EXTRA_APPWIDGET_PROVIDER_PROFILE = "appWidgetProviderProfile";
295 
296     /**
297      * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
298      * {@link AppWidgetProviderInfo} objects to mix in to the list of AppWidgets that are
299      * installed.  (This is how the launcher shows the search widget).
300      */
301     public static final String EXTRA_CUSTOM_INFO = "customInfo";
302 
303     /**
304      * An intent extra attached to the {@link #ACTION_APPWIDGET_HOST_RESTORED} broadcast,
305      * indicating the integer ID of the host whose widgets have just been restored.
306      */
307     public static final String EXTRA_HOST_ID = "hostId";
308 
309     /**
310      * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
311      * {@link android.os.Bundle} objects to mix in to the list of AppWidgets that are
312      * installed.  It will be added to the extras object on the {@link android.content.Intent}
313      * that is returned from the picker activity.
314      *
315      * {@more}
316      */
317     public static final String EXTRA_CUSTOM_EXTRAS = "customExtras";
318 
319     /**
320      * An intent extra to pass to the AppWidget picker which allows the picker to filter
321      * the list based on the {@link AppWidgetProviderInfo#widgetCategory}.
322      *
323      * @hide
324      */
325     public static final String EXTRA_CATEGORY_FILTER = "categoryFilter";
326 
327     /**
328      * An intent extra to pass to the AppWidget picker to specify whether or not to sort
329      * the list of caller-specified extra AppWidgets along with the rest of the AppWidgets
330      * @hide
331      */
332     public static final String EXTRA_CUSTOM_SORT = "customSort";
333 
334     /**
335      * A sentinel value that the AppWidget manager will never return as a appWidgetId.
336      */
337     public static final int INVALID_APPWIDGET_ID = 0;
338 
339     /**
340      * Sent when it is time to update your AppWidget.
341      *
342      * <p>This may be sent in response to a new instance for this AppWidget provider having
343      * been instantiated, the requested {@link AppWidgetProviderInfo#updatePeriodMillis update interval}
344      * having lapsed, or the system booting.
345      *
346      * <p>
347      * The intent will contain the following extras:
348      * <table>
349      *   <tr>
350      *     <td>{@link #EXTRA_APPWIDGET_IDS}</td>
351      *     <td>The appWidgetIds to update.  This may be all of the AppWidgets created for this
352      *     provider, or just a subset.  The system tries to send updates for as few AppWidget
353      *     instances as possible.</td>
354      *  </tr>
355      * </table>
356      *
357      * @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
358      */
359     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
360     @BroadcastBehavior(explicitOnly = true)
361     public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
362 
363     /**
364      * A combination broadcast of APPWIDGET_ENABLED and APPWIDGET_UPDATE.
365      * Sent during boot time and when the host is binding the widget for the very first time
366      *
367      * @hide
368      */
369     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
370     @BroadcastBehavior(explicitOnly = true)
371     public static final String ACTION_APPWIDGET_ENABLE_AND_UPDATE = "android.appwidget.action"
372             + ".APPWIDGET_ENABLE_AND_UPDATE";
373 
374     /**
375      * Sent when the custom extras for an AppWidget change.
376      *
377      * <p class="note">This is a protected intent that can only be sent
378      * by the system.
379      *
380      * @see AppWidgetProvider#onAppWidgetOptionsChanged
381      *      AppWidgetProvider.onAppWidgetOptionsChanged(Context context,
382      *      AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras)
383      */
384     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
385     @BroadcastBehavior(explicitOnly = true)
386     public static final String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
387 
388     /**
389      * Sent when an instance of an AppWidget is deleted from its host.
390      *
391      * <p class="note">This is a protected intent that can only be sent
392      * by the system.
393      *
394      * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds)
395      */
396     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
397     @BroadcastBehavior(explicitOnly = true)
398     public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
399 
400     /**
401      * Sent when the last AppWidget of this provider is removed from the last host.
402      *
403      * <p class="note">This is a protected intent that can only be sent
404      * by the system.
405      *
406      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onDisabled(Context context)
407      */
408     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
409     @BroadcastBehavior(explicitOnly = true)
410     public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
411 
412     /**
413      * Sent when an instance of an AppWidget is added to a host for the first time.
414      * This broadcast is sent at boot time if there is a AppWidgetHost installed with
415      * an instance for this provider.
416      *
417      * <p class="note">This is a protected intent that can only be sent
418      * by the system.
419      *
420      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
421      */
422     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
423     @BroadcastBehavior(explicitOnly = true)
424     public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
425 
426     /**
427      * Sent to an {@link AppWidgetProvider} after AppWidget state related to that provider has
428      * been restored from backup. The intent contains information about how to translate AppWidget
429      * ids from the restored data to their new equivalents.
430      *
431      * <p>The intent will contain the following extras:
432      *
433      * <table>
434      *   <tr>
435      *     <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td>
436      *     <td>The set of appWidgetIds represented in a restored backup that have been successfully
437      *     incorporated into the current environment.  This may be all of the AppWidgets known
438      *     to this application, or just a subset.  Each entry in this array of appWidgetIds has
439      *     a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td>
440      *  </tr>
441      *   <tr>
442      *     <td>{@link #EXTRA_APPWIDGET_IDS}</td>
443      *     <td>The set of appWidgetIds now valid for this application.  The app should look at
444      *     its restored widget configuration and translate each appWidgetId in the
445      *     {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding
446      *     index within this array.</td>
447      *  </tr>
448      * </table>
449      *
450      * <p class="note">This is a protected intent that can only be sent
451      * by the system.
452      *
453      * @see #ACTION_APPWIDGET_HOST_RESTORED
454      */
455     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
456     @BroadcastBehavior(explicitOnly = true)
457     public static final String ACTION_APPWIDGET_RESTORED
458             = "android.appwidget.action.APPWIDGET_RESTORED";
459 
460     /**
461      * Sent to widget hosts after AppWidget state related to the host has been restored from
462      * backup. The intent contains information about how to translate AppWidget ids from the
463      * restored data to their new equivalents.  If an application maintains multiple separate
464      * widget host instances, it will receive this broadcast separately for each one.
465      *
466      * <p>The intent will contain the following extras:
467      *
468      * <table>
469      *   <tr>
470      *     <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td>
471      *     <td>The set of appWidgetIds represented in a restored backup that have been successfully
472      *     incorporated into the current environment.  This may be all of the AppWidgets known
473      *     to this application, or just a subset.  Each entry in this array of appWidgetIds has
474      *     a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td>
475      *  </tr>
476      *   <tr>
477      *     <td>{@link #EXTRA_APPWIDGET_IDS}</td>
478      *     <td>The set of appWidgetIds now valid for this application.  The app should look at
479      *     its restored widget configuration and translate each appWidgetId in the
480      *     {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding
481      *     index within this array.</td>
482      *  </tr>
483      *  <tr>
484      *     <td>{@link #EXTRA_HOST_ID}</td>
485      *     <td>The integer ID of the widget host instance whose state has just been restored.</td>
486      *  </tr>
487      * </table>
488      *
489      * <p class="note">This is a protected intent that can only be sent
490      * by the system.
491      *
492      * @see #ACTION_APPWIDGET_RESTORED
493      */
494     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
495     @BroadcastBehavior(explicitOnly = true)
496     public static final String ACTION_APPWIDGET_HOST_RESTORED
497             = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
498 
499     /**
500      * This is the value of {@link UsageStatsManager.EXTRA_EVENT_ACTION} in the event bundle for
501      * widget user interaction events.
502      *
503      * A single widget interaction event describes what user interactions happened during a single
504      * impression of the widget.
505      */
506     @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
507     public static final String EVENT_TYPE_WIDGET_INTERACTION = "widget_interaction";
508 
509     /**
510      * This is the value of {@link UsageStatsManager.EXTRA_EVENT_CATEGORY} in the event bundle for
511      * widget user interaction events.
512      */
513     @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
514     public static final String EVENT_CATEGORY_APPWIDGET = "android.appwidget";
515 
516     /**
517      * This bundle extra describes which views have been clicked during a single impression of the
518      * widget. It is an integer array of view IDs of the clicked views. The array may contain up to
519      * 10 distinct IDs per event.
520      *
521      * Widget providers may set a different ID for event logging by setting the usage event tag on
522      * the view with {@link RemoteViews#setUsageEventTag}.
523      *
524      * @see android.widget.RemoteViews#setUsageEventTag
525      */
526     @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
527     public static final String EXTRA_EVENT_CLICKED_VIEWS =
528             "android.appwidget.extra.EVENT_CLICKED_VIEWS";
529 
530     /**
531      * This bundle extra describes which views have been scrolled during a single impression of the
532      * widget. It is an integer array of view IDs of the scrolled views. The array may contain up to
533      * 10 distinct IDs per event.
534      *
535      * Widget providers may set a different ID for event logging by setting the usage event tag on
536      * the view with {@link RemoteViews#setUsageEventTag}.
537      *
538      * @see android.widget.RemoteViews#setUsageEventTag
539      */
540     @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
541     public static final String EXTRA_EVENT_SCROLLED_VIEWS =
542             "android.appwidget.extra.EVENT_SCROLLED_VIEWS";
543 
544     /**
545      * This bundle extra contains a long that represents the duration of time in milliseconds
546      * during which the widget was visible.
547      */
548     @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
549     public static final String EXTRA_EVENT_DURATION_MS =
550             "android.appwidget.extra.EVENT_DURATION_MS";
551 
552     /**
553      * This bundle extra contains an integer array with 4 elements that describe the left, top,
554      * right, and bottom coordinates of the widget at the end of the interaction event.
555      *
556      * This Rect indicates the current position and size of the widget.
557      */
558     @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
559     public static final String EXTRA_EVENT_POSITION_RECT =
560             "android.appwidget.extra.EVENT_POSITION_RECT";
561 
562     private static final String TAG = "AppWidgetManager";
563 
564     private static Executor sUpdateExecutor;
565 
566     /**
567      * An intent extra that contains multiple appWidgetIds.  These are id values as
568      * they were provided to the application during a recent restore from backup.  It is
569      * attached to the {@link #ACTION_APPWIDGET_RESTORED} broadcast intent.
570      *
571      * <p>
572      * The value will be an int array that can be retrieved like this:
573      * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS}
574      */
575     public static final String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds";
576 
577     /**
578      * An extra that can be passed to
579      * {@link #requestPinAppWidget(ComponentName, Bundle, PendingIntent)}. This would allow the
580      * launcher app to present a custom preview to the user.
581      *
582      * <p>
583      * The value should be a {@link RemoteViews} similar to what is used with
584      * {@link #updateAppWidget} calls.
585      */
586     public static final String EXTRA_APPWIDGET_PREVIEW = "appWidgetPreview";
587 
588     /**
589      * Field for the manifest meta-data tag.
590      *
591      * @see AppWidgetProviderInfo
592      */
593     public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
594 
595     private final Context mContext;
596     private final String mPackageName;
597     @UnsupportedAppUsage
598     private final IAppWidgetService mService;
599     private final DisplayMetrics mDisplayMetrics;
600 
601     private int mMaxBitmapMemory = 0;
602 
603     private boolean mHasPostedLegacyLists = false;
604 
605     private @NonNull ServiceCollectionCache mServiceCollectionCache;
606 
607     /**
608      * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
609      * Context} object.
610      */
getInstance(Context context)611     public static AppWidgetManager getInstance(Context context) {
612         return (AppWidgetManager) context.getSystemService(Context.APPWIDGET_SERVICE);
613     }
614 
615     /**
616      * Creates a new instance.
617      *
618      * @param context The current context in which to operate.
619      * @param service The backing system service.
620      * @hide
621      */
AppWidgetManager(Context context, IAppWidgetService service)622     public AppWidgetManager(Context context, IAppWidgetService service) {
623         mContext = context;
624         mPackageName = context.getOpPackageName();
625         mService = service;
626         mDisplayMetrics = context.getResources().getDisplayMetrics();
627         mServiceCollectionCache = new ServiceCollectionCache(context, /* timeout= */ 5000L);
628         if (mService == null) {
629             return;
630         }
631         // Allowing some buffer when estimating the maximum bitmap cache size
632         try {
633             mMaxBitmapMemory = (int) (mService.getMaxBitmapMemory() * 0.9);
634         } catch (Exception e) {
635             Log.e(TAG, "Error setting the maximum bitmap memory", e);
636         }
637         BackgroundThread.getExecutor().execute(() -> {
638             try {
639                 mService.notifyProviderInheritance(getInstalledProvidersForPackage(mPackageName,
640                         null)
641                         .stream().filter(Objects::nonNull)
642                         .map(info -> info.provider).filter(p -> {
643                             try {
644                                 Class clazz = Class.forName(p.getClassName());
645                                 return AppWidgetProvider.class.isAssignableFrom(clazz);
646                             } catch (Exception e) {
647                                 return false;
648                             }
649                         }).toArray(ComponentName[]::new));
650             } catch (Exception e) {
651                 Log.e(TAG, "Notify service of inheritance info", e);
652             }
653         });
654     }
655 
tryAdapterConversion( FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action, RemoteViews original, String failureMsg)656     private void tryAdapterConversion(
657             FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action,
658             RemoteViews original, String failureMsg) {
659         if (remoteAdapterConversion()
660                 && (mHasPostedLegacyLists = mHasPostedLegacyLists
661                         || (original != null && original.hasLegacyLists()))) {
662             final RemoteViews viewsCopy = new RemoteViews(original);
663             Runnable updateWidgetWithTask = () -> {
664                 try {
665                     viewsCopy.collectAllIntents(mMaxBitmapMemory, mServiceCollectionCache).get();
666                     action.acceptOrThrow(viewsCopy);
667                 } catch (Exception e) {
668                     Log.e(TAG, failureMsg, e);
669                 }
670             };
671 
672             if (Looper.getMainLooper() == Looper.myLooper()) {
673                 createUpdateExecutorIfNull().execute(updateWidgetWithTask);
674                 return;
675             }
676 
677             updateWidgetWithTask.run();
678         } else {
679             try {
680                 action.acceptOrThrow(original);
681             } catch (RemoteException re) {
682                 throw re.rethrowFromSystemServer();
683             }
684         }
685     }
686 
687     /**
688      * Set the RemoteViews to use for the specified appWidgetIds.
689      * <p>
690      * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
691      * contain a complete representation of the widget. For performing partial widget updates, see
692      * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}.
693      *
694      * <p>
695      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
696      * and outside of the handler.
697      * This method will only work when called from the uid that owns the AppWidget provider.
698      *
699      * <p>
700      * The total Bitmap memory used by the RemoteViews object cannot exceed that required to
701      * fill the screen 1.5 times, ie. (screen width x screen height x 4 x 1.5) bytes.
702      *
703      * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
704      * @param views The RemoteViews object to show.
705      */
updateAppWidget(int[] appWidgetIds, RemoteViews views)706     public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
707         if (mService == null) {
708             return;
709         }
710 
711         tryAdapterConversion(view -> mService.updateAppWidgetIds(mPackageName, appWidgetIds,
712                 view), views, "Error updating app widget views in background");
713     }
714 
715     /**
716      * Update the extras for a given widget instance.
717      * <p>
718      * The extras can be used to embed additional information about this widget to be accessed
719      * by the associated widget's AppWidgetProvider.
720      *
721      * <p>
722      * The new options are merged into existing options using {@link Bundle#putAll} semantics.
723      *
724      * @see #getAppWidgetOptions(int)
725      *
726      * @param appWidgetId The AppWidget instances for which to set the RemoteViews.
727      * @param options The options to associate with this widget
728      */
updateAppWidgetOptions(int appWidgetId, Bundle options)729     public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
730         if (mService == null) {
731             return;
732         }
733         try {
734             mService.updateAppWidgetOptions(mPackageName, appWidgetId, options);
735         } catch (RemoteException e) {
736             throw e.rethrowFromSystemServer();
737         }
738     }
739 
740     /**
741      * Get the extras associated with a given widget instance.
742      * <p>
743      * The extras can be used to embed additional information about this widget to be accessed
744      * by the associated widget's AppWidgetProvider.
745      *
746      * @see #updateAppWidgetOptions(int, Bundle)
747      *
748      * @param appWidgetId The AppWidget instances for which to set the RemoteViews.
749      * @return The options associated with the given widget instance.
750      */
getAppWidgetOptions(int appWidgetId)751     public Bundle getAppWidgetOptions(int appWidgetId) {
752         if (mService == null) {
753             return Bundle.EMPTY;
754         }
755         try {
756             return mService.getAppWidgetOptions(mPackageName, appWidgetId);
757         } catch (RemoteException e) {
758             throw e.rethrowFromSystemServer();
759         }
760     }
761 
762     /**
763      * Set the RemoteViews to use for the specified appWidgetId.
764      * <p>
765      * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
766      * contain a complete representation of the widget. For performing partial widget updates, see
767      * {@link #partiallyUpdateAppWidget(int, RemoteViews)}.
768      *
769      * <p>
770      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
771      * and outside of the handler.
772      * This method will only work when called from the uid that owns the AppWidget provider.
773      *
774      * <p>
775      * The total Bitmap memory used by the RemoteViews object cannot exceed that required to
776      * fill the screen 1.5 times, ie. (screen width x screen height x 4 x 1.5) bytes.
777      *
778      * @param appWidgetId      The AppWidget instance for which to set the RemoteViews.
779      * @param views         The RemoteViews object to show.
780      */
updateAppWidget(int appWidgetId, RemoteViews views)781     public void updateAppWidget(int appWidgetId, RemoteViews views) {
782         if (mService == null) {
783             return;
784         }
785         updateAppWidget(new int[] { appWidgetId }, views);
786     }
787 
788     /**
789      * Perform an incremental update or command on the widget(s) specified by appWidgetIds.
790      * <p>
791      * This update  differs from {@link #updateAppWidget(int[], RemoteViews)} in that the
792      * RemoteViews object which is passed is understood to be an incomplete representation of the
793      * widget, and hence does not replace the cached representation of the widget. As of API
794      * level 17, the new properties set within the views objects will be appended to the cached
795      * representation of the widget, and hence will persist.
796      *
797      * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
798      * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
799      *
800      * <p>
801      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
802      * and outside of the handler.
803      * This method will only work when called from the uid that owns the AppWidget provider.
804      *
805      * <p>
806      * This method will be ignored if a widget has not received a full update via
807      * {@link #updateAppWidget(int[], RemoteViews)}.
808      *
809      * @param appWidgetIds     The AppWidget instances for which to set the RemoteViews.
810      * @param views            The RemoteViews object containing the incremental update / command.
811      */
partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views)812     public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) {
813         if (mService == null) {
814             return;
815         }
816 
817         tryAdapterConversion(view -> mService.partiallyUpdateAppWidgetIds(mPackageName,
818                 appWidgetIds, view), views,
819                 "Error partially updating app widget views in background");
820     }
821 
822     /**
823      * Perform an incremental update or command on the widget specified by appWidgetId.
824      * <p>
825      * This update  differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews
826      * object which is passed is understood to be an incomplete representation of the widget, and
827      * hence is not cached by the AppWidgetService. Note that because these updates are not cached,
828      * any state that they modify that is not restored by restoreInstanceState will not persist in
829      * the case that the widgets are restored using the cached version in AppWidgetService.
830      *
831      * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
832      * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
833      *
834      * <p>
835      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
836      * and outside of the handler.
837      * This method will only work when called from the uid that owns the AppWidget provider.
838      *
839      * <p>
840      * This method will be ignored if a widget has not received a full update via
841      * {@link #updateAppWidget(int[], RemoteViews)}.
842      *
843      * @param appWidgetId      The AppWidget instance for which to set the RemoteViews.
844      * @param views            The RemoteViews object containing the incremental update / command.
845      */
partiallyUpdateAppWidget(int appWidgetId, RemoteViews views)846     public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
847         if (mService == null) {
848             return;
849         }
850         partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
851     }
852 
853     /**
854      * Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider.
855      *
856      * <p>
857      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
858      * and outside of the handler.
859      * This method will only work when called from the uid that owns the AppWidget provider.
860      *
861      * @param provider      The {@link ComponentName} for the {@link
862      * android.content.BroadcastReceiver BroadcastReceiver} provider
863      *                      for your AppWidget.
864      * @param views         The RemoteViews object to show.
865      */
updateAppWidget(ComponentName provider, RemoteViews views)866     public void updateAppWidget(ComponentName provider, RemoteViews views) {
867         if (mService == null) {
868             return;
869         }
870 
871         tryAdapterConversion(view -> mService.updateAppWidgetProvider(provider, view), views,
872                 "Error updating app widget view using provider in background");
873     }
874 
875     /**
876      * Updates the info for the supplied AppWidget provider. Apps can use this to change the default
877      * behavior of the widget based on the state of the app (for e.g., if the user is logged in
878      * or not). Calling this API completely replaces the previous definition.
879      *
880      * <p>
881      * The manifest entry of the provider should contain an additional meta-data tag similar to
882      * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any alternative definitions for
883      * the provider.
884      *
885      * <p>
886      * This is persisted across device reboots and app updates. If this meta-data key is not
887      * present in the manifest entry, the info reverts to default.
888      *
889      * @param provider {@link ComponentName} for the {@link
890      *    android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
891      * @param metaDataKey key for the meta-data tag pointing to the new provider info. Use null
892      *    to reset any previously set info.
893      */
updateAppWidgetProviderInfo(ComponentName provider, @Nullable String metaDataKey)894     public void updateAppWidgetProviderInfo(ComponentName provider, @Nullable String metaDataKey) {
895         if (mService == null) {
896             return;
897         }
898         try {
899             mService.updateAppWidgetProviderInfo(provider, metaDataKey);
900         } catch (RemoteException e) {
901             throw e.rethrowFromSystemServer();
902         }
903     }
904 
905     /**
906      * Notifies the specified collection view in all the specified AppWidget instances
907      * to invalidate their data.
908      *
909      * @param appWidgetIds  The AppWidget instances to notify of view data changes.
910      * @param viewId        The collection view id.
911      * @deprecated The corresponding API
912      * {@link RemoteViews#setRemoteAdapter(int, Intent)} associated with this method has been
913      * deprecated. Moving forward please use
914      * {@link RemoteViews#setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)}
915      * instead to set {@link android.widget.RemoteViews.RemoteCollectionItems} for the remote
916      * adapter and update the widget views by calling {@link #updateAppWidget(int[], RemoteViews)},
917      * {@link #updateAppWidget(int, RemoteViews)},
918      * {@link #updateAppWidget(ComponentName, RemoteViews)},
919      * {@link #partiallyUpdateAppWidget(int[], RemoteViews)},
920      * or {@link #partiallyUpdateAppWidget(int, RemoteViews)}, whichever applicable.
921      */
922     @Deprecated
notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)923     public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
924         if (mService == null) {
925             return;
926         }
927 
928         if (remoteAdapterConversion()) {
929             if (Looper.myLooper() == Looper.getMainLooper()) {
930                 mHasPostedLegacyLists = true;
931                 createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(
932                         appWidgetIds, viewId));
933             } else {
934                 notifyCollectionWidgetChange(appWidgetIds, viewId);
935             }
936         } else {
937             try {
938                 mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
939             } catch (RemoteException re) {
940                 throw re.rethrowFromSystemServer();
941             }
942         }
943     }
944 
notifyCollectionWidgetChange(int[] appWidgetIds, int viewId)945     private void notifyCollectionWidgetChange(int[] appWidgetIds, int viewId) {
946         try {
947             List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
948             for (int i = 0; i < appWidgetIds.length; i++) {
949                 final int widgetId = appWidgetIds[i];
950                 updateFutures.add(CompletableFuture.runAsync(() -> {
951                     try {
952                         RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
953                         if (views.replaceRemoteCollections(viewId)) {
954                             updateAppWidget(widgetId, views);
955                         }
956                     } catch (Exception e) {
957                         Log.e(TAG, "Error notifying changes in RemoteViews", e);
958                     }
959                 }));
960             }
961             CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
962         } catch (Exception e) {
963             Log.e(TAG, "Error notifying changes for all widgets", e);
964         }
965     }
966 
967     /**
968      * Notifies the specified collection view in the specified AppWidget instance
969      * to invalidate its data.
970      *
971      * @param appWidgetId  The AppWidget instance to notify of view data changes.
972      * @param viewId       The collection view id.
973      * @deprecated The corresponding API
974      * {@link RemoteViews#setRemoteAdapter(int, Intent)} associated with this method has been
975      * deprecated. Moving forward please use
976      * {@link RemoteViews#setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)}
977      * instead to set {@link android.widget.RemoteViews.RemoteCollectionItems} for the remote
978      * adapter and update the widget views by calling {@link #updateAppWidget(int[], RemoteViews)},
979      * {@link #updateAppWidget(int, RemoteViews)},
980      * {@link #updateAppWidget(ComponentName, RemoteViews)},
981      * {@link #partiallyUpdateAppWidget(int[], RemoteViews)},
982      * or {@link #partiallyUpdateAppWidget(int, RemoteViews)}, whichever applicable.
983      */
984     @Deprecated
notifyAppWidgetViewDataChanged(int appWidgetId, int viewId)985     public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
986         if (mService == null) {
987             return;
988         }
989         notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId);
990     }
991 
992     /**
993      * Gets the AppWidget providers for the given user profile. User profile can only
994      * be the current user or a profile of the current user. For example, the current
995      * user may have a corporate profile. In this case the parent user profile has a
996      * child profile, the corporate one.
997      *
998      * @param profile The profile for which to get providers. Passing null is equivalent
999      *        to querying for only the calling user.
1000      * @return The installed providers, or an empty list if none are found for the given user.
1001      *
1002      * @see android.os.Process#myUserHandle()
1003      * @see android.os.UserManager#getUserProfiles()
1004      */
getInstalledProvidersForProfile( @ullable UserHandle profile)1005     public @NonNull List<AppWidgetProviderInfo> getInstalledProvidersForProfile(
1006             @Nullable UserHandle profile) {
1007         if (mService == null) {
1008             return Collections.emptyList();
1009         }
1010         return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
1011                 profile, null);
1012     }
1013 
1014     /**
1015      * Gets the AppWidget providers for the given package and user profile. User
1016      * profile can only be the current user or a profile of the current user. For
1017      * example, the current user may have a corporate profile. In this case the
1018      * parent user profile has a child profile, the corporate one.
1019      *
1020      * @param packageName The package for which to get providers. If null, this method is
1021      *        equivalent to {@link #getInstalledProvidersForProfile(UserHandle)}.
1022      * @param profile The profile for which to get providers. Passing null is equivalent
1023      *        to querying for only the calling user.
1024      * @return The installed providers, or an empty list if none are found for the given
1025      *         package and user.
1026      * @throws NullPointerException if the provided package name is null
1027      *
1028      * @see android.os.Process#myUserHandle()
1029      * @see android.os.UserManager#getUserProfiles()
1030      */
getInstalledProvidersForPackage( @onNull String packageName, @Nullable UserHandle profile)1031     public @NonNull List<AppWidgetProviderInfo> getInstalledProvidersForPackage(
1032             @NonNull String packageName, @Nullable UserHandle profile) {
1033         if (packageName == null) {
1034             throw new NullPointerException("A non-null package must be passed to this method. " +
1035                     "If you want all widgets regardless of package, see " +
1036                     "getInstalledProvidersForProfile(UserHandle)");
1037         }
1038         if (mService == null) {
1039             return Collections.emptyList();
1040         }
1041         return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
1042                 profile, packageName);
1043     }
1044 
1045     /**
1046      * Return a list of the AppWidget providers that are currently installed.
1047      */
getInstalledProviders()1048     public List<AppWidgetProviderInfo> getInstalledProviders() {
1049         if (mService == null) {
1050             return Collections.emptyList();
1051         }
1052         return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
1053                 null, null);
1054     }
1055 
1056     /**
1057      * Gets the AppWidget providers for the current user.
1058      *
1059      * @param categoryFilter Will only return providers which register as any of the specified
1060      *        specified categories. See {@link AppWidgetProviderInfo#widgetCategory}.
1061      * @return The intalled providers.
1062      *
1063      * @see android.os.Process#myUserHandle()
1064      * @see android.os.UserManager#getUserProfiles()
1065      *
1066      * @hide
1067      */
1068     @UnsupportedAppUsage
getInstalledProviders(int categoryFilter)1069     public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
1070         if (mService == null) {
1071             return Collections.emptyList();
1072         }
1073         return getInstalledProvidersForProfile(categoryFilter, null, null);
1074     }
1075 
1076     /**
1077      * Gets the AppWidget providers for the given user profile. User profile can only
1078      * be the current user or a profile of the current user. For example, the current
1079      * user may have a corporate profile. In this case the parent user profile has a
1080      * child profile, the corporate one.
1081      *
1082      * @param categoryFilter Will only return providers which register as any of the specified
1083      *        specified categories. See {@link AppWidgetProviderInfo#widgetCategory}.
1084      * @param profile A profile of the current user which to be queried. The user
1085      *        is itself also a profile. If null, the providers only for the current user
1086      *        are returned.
1087      * @param packageName If specified, will only return providers from the given package.
1088      * @return The intalled providers.
1089      *
1090      * @see android.os.Process#myUserHandle()
1091      * @see android.os.UserManager#getUserProfiles()
1092      *
1093      * @hide
1094      */
1095     @UnsupportedAppUsage
getInstalledProvidersForProfile(int categoryFilter, @Nullable UserHandle profile, @Nullable String packageName)1096     public List<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
1097             @Nullable UserHandle profile, @Nullable String packageName) {
1098         if (mService == null) {
1099             return Collections.emptyList();
1100         }
1101 
1102         if (profile == null) {
1103             profile = mContext.getUser();
1104         }
1105 
1106         try {
1107             ParceledListSlice<AppWidgetProviderInfo> providers = mService.getInstalledProvidersForProfile(
1108                     categoryFilter, profile.getIdentifier(), packageName);
1109             if (providers == null) {
1110                 return Collections.emptyList();
1111             }
1112             for (AppWidgetProviderInfo info : providers.getList()) {
1113                 // Converting complex to dp.
1114                 info.updateDimensions(mDisplayMetrics);
1115             }
1116             return providers.getList();
1117         } catch (RemoteException e) {
1118             throw e.rethrowFromSystemServer();
1119         }
1120     }
1121 
1122     /**
1123      * Returns the {@link AppWidgetProviderInfo} for the specified AppWidget.
1124      *
1125      * @return Information regarding the provider of speficied widget, returns null if the
1126      *         appWidgetId has not been bound to a provider yet, or you don't have access
1127      *         to that widget.
1128      */
getAppWidgetInfo(int appWidgetId)1129     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
1130         if (mService == null) {
1131             Log.e(TAG, "Service wasn't initialized, appWidgetId=" + appWidgetId);
1132             return null;
1133         }
1134         try {
1135             AppWidgetProviderInfo info = mService.getAppWidgetInfo(mPackageName, appWidgetId);
1136             if (info != null) {
1137                 // Converting complex to dp.
1138                 info.updateDimensions(mDisplayMetrics);
1139             } else {
1140                 Log.e(TAG, "App widget provider info is null. PackageName=" + mPackageName
1141                         + " appWidgetId-" + appWidgetId);
1142             }
1143             return info;
1144         } catch (RemoteException e) {
1145             throw e.rethrowFromSystemServer();
1146         }
1147     }
1148 
1149     /**
1150      * Set the component for a given appWidgetId.
1151      *
1152      * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding
1153      *         widgets always for your component. This method is used by the AppWidget picker and
1154      *         should not be used by other apps.
1155      *
1156      * @param appWidgetId     The AppWidget instance for which to set the RemoteViews.
1157      * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
1158      *                      provider for this AppWidget.
1159      * @hide
1160      */
1161     @UnsupportedAppUsage
bindAppWidgetId(int appWidgetId, ComponentName provider)1162     public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
1163         if (mService == null) {
1164             return;
1165         }
1166         bindAppWidgetId(appWidgetId, provider, null);
1167     }
1168 
1169     /**
1170      * Set the component for a given appWidgetId.
1171      *
1172      * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding
1173      *         widgets always for your component. This method is used by the AppWidget picker and
1174      *         should not be used by other apps.
1175      *
1176      * @param appWidgetId     The AppWidget instance for which to set the RemoteViews.
1177      * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
1178      *                      provider for this AppWidget.
1179      * @param options       Bundle containing options for the AppWidget. See also
1180      *                      {@link #updateAppWidgetOptions(int, Bundle)}
1181      *
1182      * @hide
1183      */
1184     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options)1185     public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
1186         if (mService == null) {
1187             return;
1188         }
1189         bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUser(), provider, options);
1190     }
1191 
1192     /**
1193      * Set the component for a given appWidgetId.
1194      *
1195      * If successful, the app widget provider will receive a {@link #ACTION_APPWIDGET_UPDATE}
1196      * broadcast.
1197      *
1198      * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding
1199      *         widgets always for your component. Should be used by apps that host widgets; if this
1200      *         method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to
1201      *         bind
1202      *
1203      * @param appWidgetId   The AppWidget id under which to bind the provider.
1204      * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
1205      *                      provider for this AppWidget.
1206      * @return true if this component has permission to bind the AppWidget
1207      */
bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider)1208     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider) {
1209         if (mService == null) {
1210             return false;
1211         }
1212         return bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUserId(), provider, null);
1213     }
1214 
1215     /**
1216      * Set the component for a given appWidgetId.
1217      *
1218      * If successful, the app widget provider will receive a {@link #ACTION_APPWIDGET_UPDATE}
1219      * broadcast.
1220      *
1221      * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding
1222      *         widgets always for your component. Should be used by apps that host widgets; if this
1223      *         method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to
1224      *         bind
1225      *
1226      * @param appWidgetId The AppWidget id under which to bind the provider.
1227      * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
1228      *                      provider for this AppWidget.
1229      * @param options       Bundle containing options for the AppWidget. See also
1230      *                      {@link #updateAppWidgetOptions(int, Bundle)}
1231      *
1232      * @return true if this component has permission to bind the AppWidget
1233      */
bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider, Bundle options)1234     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider,
1235             Bundle options) {
1236         if (mService == null) {
1237             return false;
1238         }
1239         return bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUserId(), provider, options);
1240     }
1241 
1242     /**
1243      * Set the provider for a given appWidgetId if the caller has a permission.
1244      *
1245      * If successful, the app widget provider will receive a {@link #ACTION_APPWIDGET_UPDATE}
1246      * broadcast.
1247      *
1248      * <p>
1249      * <strong>Note:</strong> You need the {@link android.Manifest.permission#BIND_APPWIDGET}
1250      * permission or the user must have enabled binding widgets always for your component.
1251      * Should be used by apps that host widgets. If this method returns false, call {@link
1252      * #ACTION_APPWIDGET_BIND} to request permission to bind.
1253      * </p>
1254      *
1255      * @param appWidgetId The AppWidget id under which to bind the provider.
1256      * @param user The user id in which the provider resides.
1257      * @param provider The component name of the provider.
1258      * @param options An optional Bundle containing options for the AppWidget.
1259      *
1260      * @return true if this component has permission to bind the AppWidget
1261      */
bindAppWidgetIdIfAllowed(int appWidgetId, UserHandle user, ComponentName provider, Bundle options)1262     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, UserHandle user,
1263             ComponentName provider, Bundle options) {
1264         if (mService == null) {
1265             return false;
1266         }
1267         return bindAppWidgetIdIfAllowed(appWidgetId, user.getIdentifier(), provider, options);
1268     }
1269 
1270     /**
1271      * Query if a given package was granted permission by the user to bind app widgets
1272      *
1273      * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
1274      *
1275      * @param packageName The package for which the permission is being queried
1276      * @param userId The user id of the user under which the package runs.
1277      * @return true if the package was granted permission by the user to bind app widgets
1278      * @hide
1279      */
hasBindAppWidgetPermission(String packageName, int userId)1280     public boolean hasBindAppWidgetPermission(String packageName, int userId) {
1281         if (mService == null) {
1282             return false;
1283         }
1284         try {
1285             return mService.hasBindAppWidgetPermission(packageName, userId);
1286         } catch (RemoteException e) {
1287             throw e.rethrowFromSystemServer();
1288         }
1289     }
1290 
1291     /**
1292      * Query if a given package was granted permission by the user to bind app widgets
1293      *
1294      * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
1295      *
1296      * @param packageName        The package for which the permission is being queried
1297      * @return true if the package was granted permission by the user to bind app widgets
1298      * @hide
1299      */
hasBindAppWidgetPermission(String packageName)1300     public boolean hasBindAppWidgetPermission(String packageName) {
1301         if (mService == null) {
1302             return false;
1303         }
1304         try {
1305             return mService.hasBindAppWidgetPermission(packageName, mContext.getUserId());
1306         } catch (RemoteException e) {
1307             throw e.rethrowFromSystemServer();
1308         }
1309     }
1310 
1311     /**
1312      * Changes any user-granted permission for the given package to bind app widgets
1313      *
1314      * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
1315      *
1316      * @param packageName The package whose permission is being changed
1317      * @param permission Whether to give the package permission to bind widgets
1318      *
1319      * @hide
1320      */
setBindAppWidgetPermission(String packageName, boolean permission)1321     public void setBindAppWidgetPermission(String packageName, boolean permission) {
1322         if (mService == null) {
1323             return;
1324         }
1325         setBindAppWidgetPermission(packageName, mContext.getUserId(), permission);
1326     }
1327 
1328     /**
1329      * Changes any user-granted permission for the given package to bind app widgets
1330      *
1331      * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
1332      *
1333      * @param packageName The package whose permission is being changed
1334      * @param userId The user under which the package is running.
1335      * @param permission Whether to give the package permission to bind widgets
1336      *
1337      * @hide
1338      */
1339     @TestApi
setBindAppWidgetPermission( @onNull String packageName, @UserIdInt int userId, boolean permission)1340     public void setBindAppWidgetPermission(
1341             @NonNull String packageName, @UserIdInt int userId, boolean permission) {
1342         if (mService == null) {
1343             return;
1344         }
1345         try {
1346             mService.setBindAppWidgetPermission(packageName, userId, permission);
1347         } catch (RemoteException e) {
1348             throw e.rethrowFromSystemServer();
1349         }
1350     }
1351 
1352     /**
1353      * Binds the RemoteViewsService for a given appWidgetId and intent.
1354      *
1355      * The appWidgetId specified must already be bound to the calling AppWidgetHost via
1356      * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
1357      *
1358      * @param appWidgetId   The AppWidget instance for which to bind the RemoteViewsService.
1359      * @param intent        The intent of the service which will be providing the data to the
1360      *                      RemoteViewsAdapter.
1361      * @param connection    The callback interface to be notified when a connection is made or lost.
1362      * @param flags         Flags used for binding to the service. Currently only
1363      *                     {@link Context#BIND_AUTO_CREATE} and
1364      *                     {@link Context#BIND_FOREGROUND_SERVICE_WHILE_AWAKE} are supported.
1365      *
1366      * @see Context#getServiceDispatcher(ServiceConnection, Handler, long)
1367      * @hide
1368      */
1369     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
bindRemoteViewsService(Context context, int appWidgetId, Intent intent, IServiceConnection connection, @Context.BindServiceFlagsBits int flags)1370     public boolean bindRemoteViewsService(Context context, int appWidgetId, Intent intent,
1371             IServiceConnection connection, @Context.BindServiceFlagsBits int flags) {
1372         if (mService == null) {
1373             return false;
1374         }
1375         try {
1376             return mService.bindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent,
1377                     context.getIApplicationThread(), context.getActivityToken(), connection,
1378                     Integer.toUnsignedLong(flags));
1379         } catch (RemoteException e) {
1380             throw e.rethrowFromSystemServer();
1381         }
1382     }
1383 
1384     /**
1385      * Get the list of appWidgetIds that have been bound to the given AppWidget
1386      * provider.
1387      *
1388      * @param provider The {@link android.content.BroadcastReceiver} that is the
1389      *            AppWidget provider to find appWidgetIds for.
1390      */
getAppWidgetIds(ComponentName provider)1391     public int[] getAppWidgetIds(ComponentName provider) {
1392         if (mService == null) {
1393             return new int[0];
1394         }
1395         try {
1396             return mService.getAppWidgetIds(provider);
1397         } catch (RemoteException e) {
1398             throw e.rethrowFromSystemServer();
1399         }
1400     }
1401 
1402     /**
1403      * @hide
1404      */
isBoundWidgetPackage(String packageName, int userId)1405     public boolean isBoundWidgetPackage(String packageName, int userId) {
1406         if (mService == null) {
1407             return false;
1408         }
1409         try {
1410             return mService.isBoundWidgetPackage(packageName, userId);
1411         } catch (RemoteException e) {
1412             throw e.rethrowFromSystemServer();
1413         }
1414     }
1415 
1416     @UnsupportedAppUsage
bindAppWidgetIdIfAllowed(int appWidgetId, int profileId, ComponentName provider, Bundle options)1417     private boolean bindAppWidgetIdIfAllowed(int appWidgetId, int profileId,
1418             ComponentName provider, Bundle options) {
1419         if (mService == null) {
1420             return false;
1421         }
1422         try {
1423             return mService.bindAppWidgetId(mPackageName, appWidgetId,
1424                     profileId, provider, options);
1425         } catch (RemoteException e) {
1426             throw e.rethrowFromSystemServer();
1427         }
1428     }
1429 
1430     /**
1431      * Return {@code TRUE} if the default launcher supports
1432      * {@link #requestPinAppWidget(ComponentName, Bundle, PendingIntent)}
1433      */
isRequestPinAppWidgetSupported()1434     public boolean isRequestPinAppWidgetSupported() {
1435         try {
1436             return mService.isRequestPinAppWidgetSupported();
1437         } catch (RemoteException e) {
1438             throw e.rethrowFromSystemServer();
1439         }
1440     }
1441 
1442     /**
1443      * Only used during development. Can be deleted before release.
1444      * @hide
1445      */
requestPinAppWidget(@onNull ComponentName provider, @Nullable PendingIntent successCallback)1446     public boolean requestPinAppWidget(@NonNull ComponentName provider,
1447             @Nullable PendingIntent successCallback) {
1448         return requestPinAppWidget(provider, null, successCallback);
1449     }
1450 
1451     /**
1452      * Request to pin an app widget on the current launcher. It's up to the launcher to accept this
1453      * request (optionally showing a user confirmation). If the request is accepted, the caller will
1454      * get a confirmation with extra {@link #EXTRA_APPWIDGET_ID}.
1455      *
1456      * <p>When a request is denied by the user, the caller app will not get any response.
1457      *
1458      * <p>Only apps with a foreground activity or a foreground service can call it.  Otherwise
1459      * it'll throw {@link IllegalStateException}.
1460      *
1461      * <p>It's up to the launcher how to handle previous pending requests when the same package
1462      * calls this API multiple times in a row.  It may ignore the previous requests,
1463      * for example.
1464      *
1465      * <p>Launcher will not show the configuration activity associated with the provider in this
1466      * case. The app could either show the configuration activity as a response to the callback,
1467      * or show if before calling the API (various configurations can be encapsulated in
1468      * {@code successCallback} to avoid persisting them before the widgetId is known).
1469      *
1470      * @param provider The {@link ComponentName} for the {@link
1471      *    android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
1472      * @param extras IF not null, this is passed to the launcher app. e.g. {@link
1473      *    #EXTRA_APPWIDGET_PREVIEW} can be used for a custom preview.
1474      * @param successCallback If not null, this intent will be sent when the widget is created.
1475      *
1476      * @return {@code TRUE} if the launcher supports this feature. Note the API will return without
1477      *    waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean
1478      *    the shortcut is pinned. {@code FALSE} if the launcher doesn't support this feature or if
1479      *    calling app belongs to a user-profile with items restricted on home screen.
1480      *
1481      * @see android.content.pm.ShortcutManager#isRequestPinShortcutSupported()
1482      * @see android.content.pm.ShortcutManager#requestPinShortcut(ShortcutInfo, IntentSender)
1483      * @see #isRequestPinAppWidgetSupported()
1484      *
1485      * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
1486      * service or when the user is locked.
1487      */
requestPinAppWidget(@onNull ComponentName provider, @Nullable Bundle extras, @Nullable PendingIntent successCallback)1488     public boolean requestPinAppWidget(@NonNull ComponentName provider,
1489             @Nullable Bundle extras, @Nullable PendingIntent successCallback) {
1490         try {
1491             return mService.requestPinAppWidget(mPackageName, provider, extras,
1492                     successCallback == null ? null : successCallback.getIntentSender());
1493         } catch (RemoteException e) {
1494             throw e.rethrowFromSystemServer();
1495         }
1496     }
1497 
1498     /**
1499      * Note an app widget is tapped on.
1500      *
1501      * @param appWidgetId App widget id.
1502      * @hide
1503      */
noteAppWidgetTapped(int appWidgetId)1504     public void noteAppWidgetTapped(int appWidgetId) {
1505         try {
1506             mService.noteAppWidgetTapped(mPackageName, appWidgetId);
1507         } catch (RemoteException e) {
1508             throw e.rethrowFromSystemServer();
1509         }
1510     }
1511 
1512     /**
1513      * Set a preview for this widget. This preview will be used instead of the provider's {@link
1514      * AppWidgetProviderInfo#previewLayout previewLayout} or {@link
1515      * AppWidgetProviderInfo#previewImage previewImage} for previewing the widget in the widget
1516      * picker and pin app widget flow.
1517      *
1518      * @param provider The {@link ComponentName} for the {@link android.content.BroadcastReceiver
1519      *    BroadcastReceiver} provider for the AppWidget you intend to provide a preview for.
1520      * @param widgetCategories The categories that this preview should be used for. This can be a
1521      *    single category or combination of categories. If multiple categories are specified,
1522      *    then this preview will be used for each of those categories. For example, if you
1523      *    set a preview for WIDGET_CATEGORY_HOME_SCREEN | WIDGET_CATEGORY_KEYGUARD, the preview will
1524      *    be used when picking widgets for the home screen and keyguard.
1525      *
1526      *    <p>Note: You should only use the widget categories that the provider supports, as defined
1527      *    in {@link AppWidgetProviderInfo#widgetCategory}.
1528      * @param preview This preview will be used for previewing the provider when picking widgets for
1529      *    the selected categories.
1530      *
1531      * @see AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN
1532      * @see AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD
1533      * @see AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX
1534      *
1535      * @return true if the call was successful, false if it was rate-limited.
1536      */
1537     @FlaggedApi(Flags.FLAG_GENERATED_PREVIEWS)
setWidgetPreview(@onNull ComponentName provider, @AppWidgetProviderInfo.CategoryFlags int widgetCategories, @NonNull RemoteViews preview)1538     public boolean setWidgetPreview(@NonNull ComponentName provider,
1539             @AppWidgetProviderInfo.CategoryFlags int widgetCategories,
1540             @NonNull RemoteViews preview) {
1541         try {
1542             return mService.setWidgetPreview(provider, widgetCategories, preview);
1543         } catch (RemoteException e) {
1544             throw e.rethrowFromSystemServer();
1545         }
1546     }
1547 
1548     /**
1549      * Get the RemoteViews previews for this widget.
1550      *
1551      * @param provider The {@link ComponentName} for the {@link android.content.BroadcastReceiver
1552      *    BroadcastReceiver} provider for the AppWidget you intend to get a preview for.
1553      * @param profile The profile in which the provider resides. Passing null is equivalent
1554      *        to querying for only the calling user.
1555      * @param widgetCategory The widget category for which you want to display previews. This should
1556      *    be a single category. If a combination of categories is provided, this function will
1557      *    return a preview that matches at least one of the categories.
1558      *
1559      * @return The widget preview for the selected category, if available.
1560      * @see AppWidgetProviderInfo#generatedPreviewCategories
1561      */
1562     @Nullable
1563     @FlaggedApi(Flags.FLAG_GENERATED_PREVIEWS)
getWidgetPreview(@onNull ComponentName provider, @Nullable UserHandle profile, @AppWidgetProviderInfo.CategoryFlags int widgetCategory)1564     public RemoteViews getWidgetPreview(@NonNull ComponentName provider,
1565             @Nullable UserHandle profile, @AppWidgetProviderInfo.CategoryFlags int widgetCategory) {
1566         try {
1567             if (profile == null) {
1568                 profile = mContext.getUser();
1569             }
1570             return mService.getWidgetPreview(mPackageName, provider, profile.getIdentifier(),
1571                     widgetCategory);
1572         } catch (RemoteException e) {
1573             throw e.rethrowFromSystemServer();
1574         }
1575     }
1576 
1577     /**
1578      * Remove this provider's preview for the specified widget categories. If the provider does not
1579      * have a preview for the specified widget category, this is a no-op.
1580      *
1581      * @param provider The AppWidgetProvider to remove previews for.
1582      * @param widgetCategories The categories of the preview to remove. For example, removing the
1583      *    preview for WIDGET_CATEGORY_HOME_SCREEN | WIDGET_CATEGORY_KEYGUARD will remove the
1584      *    previews for both categories.
1585      */
1586     @FlaggedApi(Flags.FLAG_GENERATED_PREVIEWS)
removeWidgetPreview(@onNull ComponentName provider, @AppWidgetProviderInfo.CategoryFlags int widgetCategories)1587     public void removeWidgetPreview(@NonNull ComponentName provider,
1588             @AppWidgetProviderInfo.CategoryFlags int widgetCategories) {
1589         try {
1590             mService.removeWidgetPreview(provider, widgetCategories);
1591         } catch (RemoteException e) {
1592             throw e.rethrowFromSystemServer();
1593         }
1594     }
1595 
1596     /**
1597      * Create a {@link PersistableBundle} that represents a single widget interaction event.
1598      *
1599      * @param appWidgetId App Widget ID of the widget.
1600      * @param durationMs Duration of the impression in milliseconds
1601      * @param position Current position of the widget.
1602      * @param clickedIds IDs of views clicked during this event.
1603      * @param scrolledIds IDs of views scrolled during this event.
1604      *
1605      * @hide
1606      */
1607     @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS)
1608     @NonNull
createWidgetInteractionEvent(int appWidgetId, long durationMs, @Nullable Rect position, @Nullable int[] clickedIds, @Nullable int[] scrolledIds)1609     public static PersistableBundle createWidgetInteractionEvent(int appWidgetId, long durationMs,
1610             @Nullable Rect position, @Nullable int[] clickedIds, @Nullable int[] scrolledIds) {
1611         PersistableBundle extras = new PersistableBundle();
1612         extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, EVENT_TYPE_WIDGET_INTERACTION);
1613         extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, EVENT_CATEGORY_APPWIDGET);
1614         extras.putInt(EXTRA_APPWIDGET_ID, appWidgetId);
1615         extras.putLong(EXTRA_EVENT_DURATION_MS, durationMs);
1616         if (position != null) {
1617             extras.putIntArray(EXTRA_EVENT_POSITION_RECT,
1618                     new int[]{position.left, position.top, position.right, position.bottom});
1619         }
1620         if (clickedIds != null && clickedIds.length > 0) {
1621             extras.putIntArray(EXTRA_EVENT_CLICKED_VIEWS, clickedIds);
1622         }
1623         if (scrolledIds != null && scrolledIds.length > 0) {
1624             extras.putIntArray(EXTRA_EVENT_SCROLLED_VIEWS, scrolledIds);
1625         }
1626         return extras;
1627     }
1628 
1629 
1630     @UiThread
createUpdateExecutorIfNull()1631     private static @NonNull Executor createUpdateExecutorIfNull() {
1632         if (sUpdateExecutor == null) {
1633             sUpdateExecutor = new HandlerExecutor(createAndStartNewHandler(
1634                     "widget_manager_update_helper_thread", Process.THREAD_PRIORITY_FOREGROUND));
1635         }
1636 
1637         return sUpdateExecutor;
1638     }
1639 
createAndStartNewHandler(@onNull String name, int priority)1640     private static @NonNull Handler createAndStartNewHandler(@NonNull String name, int priority) {
1641         HandlerThread thread = new HandlerThread(name, priority);
1642         thread.start();
1643         return thread.getThreadHandler();
1644     }
1645 
1646     /**
1647      * @hide
1648      */
1649     public static class ServiceCollectionCache {
1650 
1651         private final Context mContext;
1652         private final Handler mHandler;
1653         private final long mTimeOut;
1654 
1655         private final Map<FilterComparison, ConnectionTask> mActiveConnections =
1656                 new ArrayMap<>();
1657 
ServiceCollectionCache(Context context, long timeOut)1658         public ServiceCollectionCache(Context context, long timeOut) {
1659             mContext = context;
1660             mHandler = new Handler(BackgroundThread.getHandler().getLooper());
1661             mTimeOut = timeOut;
1662         }
1663 
1664         /**
1665          * Connect to the service indicated by the {@code Intent}, and consume the binder on the
1666          * specified executor
1667          */
connectAndConsume(Intent intent, Consumer<IBinder> task, Executor executor)1668         public void connectAndConsume(Intent intent, Consumer<IBinder> task, Executor executor) {
1669             mHandler.post(() -> connectAndConsumeInner(intent, task, executor));
1670         }
1671 
connectAndConsumeInner(Intent intent, Consumer<IBinder> task, Executor executor)1672         private void connectAndConsumeInner(Intent intent, Consumer<IBinder> task,
1673                 Executor executor) {
1674             ConnectionTask activeConnection = mActiveConnections.computeIfAbsent(
1675                     new FilterComparison(intent), ConnectionTask::new);
1676             activeConnection.add(task, executor);
1677         }
1678 
1679         private class ConnectionTask implements ServiceConnection {
1680 
1681             private final Runnable mDestroyAfterTimeout = this::onDestroyTimeout;
1682             private final ArrayDeque<Pair<Consumer<IBinder>, Executor>> mTaskQueue =
1683                     new ArrayDeque<>();
1684 
1685             private boolean mOnDestroyTimeout = false;
1686             private IBinder mIBinder;
1687 
ConnectionTask(@onNull FilterComparison filter)1688             ConnectionTask(@NonNull FilterComparison filter) {
1689                 try {
1690                     mContext.bindService(filter.getIntent(),
1691                             Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
1692                             mHandler::post,
1693                             this);
1694                 } catch (Exception e) {
1695                     Log.e(TAG, "Error connecting to service in connection cache", e);
1696                 }
1697             }
1698 
1699             @Override
onServiceConnected(ComponentName componentName, IBinder iBinder)1700             public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
1701                 mIBinder = iBinder;
1702                 mHandler.post(this::handleNext);
1703             }
1704 
1705             @Override
onNullBinding(ComponentName name)1706             public void onNullBinding(ComponentName name) {
1707                 // Use an empty binder, follow up tasks will handle the failure
1708                 onServiceConnected(name, new Binder());
1709             }
1710 
1711             @Override
onServiceDisconnected(ComponentName componentName)1712             public void onServiceDisconnected(ComponentName componentName) { }
1713 
add(Consumer<IBinder> task, Executor executor)1714             void add(Consumer<IBinder> task, Executor executor) {
1715                 mTaskQueue.add(Pair.create(task, executor));
1716                 if (mOnDestroyTimeout) {
1717                     // If we are waiting for timeout, cancel it and execute the next task
1718                     handleNext();
1719                 }
1720             }
1721 
handleNext()1722             private void handleNext() {
1723                 mHandler.removeCallbacks(mDestroyAfterTimeout);
1724                 Pair<Consumer<IBinder>, Executor> next = mTaskQueue.pollFirst();
1725                 if (next != null) {
1726                     mOnDestroyTimeout = false;
1727                     next.second.execute(() -> {
1728                         next.first.accept(mIBinder);
1729                         mHandler.post(this::handleNext);
1730                     });
1731                 } else {
1732                     // Finished all tasks, start a timeout to unbind this service
1733                     mOnDestroyTimeout = true;
1734                     mHandler.postDelayed(mDestroyAfterTimeout, mTimeOut);
1735                 }
1736             }
1737 
1738             /**
1739              * Called after we have waited for {@link #mTimeOut} after the last task is finished
1740              */
onDestroyTimeout()1741             private void onDestroyTimeout() {
1742                 if (!mTaskQueue.isEmpty()) {
1743                     handleNext();
1744                     return;
1745                 }
1746                 try {
1747                     mContext.unbindService(this);
1748                 } catch (Exception e) {
1749                     Log.e(TAG, "Error unbinding the cached connection", e);
1750                 }
1751                 mActiveConnections.values().remove(this);
1752             }
1753         }
1754     }
1755 }
1756