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