AppWidgetServiceImpl.java revision e95057ade126e9e159fe05b69c32f85f7891490f
1/* 2 * Copyright (C) 2011 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 17package com.android.server.appwidget; 18 19import static android.content.Context.KEYGUARD_SERVICE; 20import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 21import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 22 23import android.app.AlarmManager; 24import android.app.AppGlobals; 25import android.app.AppOpsManager; 26import android.app.KeyguardManager; 27import android.app.PendingIntent; 28import android.app.admin.DevicePolicyManagerInternal; 29import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener; 30import android.appwidget.AppWidgetManager; 31import android.appwidget.AppWidgetProviderInfo; 32import android.content.BroadcastReceiver; 33import android.content.ComponentName; 34import android.content.Context; 35import android.content.Intent; 36import android.content.Intent.FilterComparison; 37import android.content.IntentFilter; 38import android.content.IntentSender; 39import android.content.ServiceConnection; 40import android.content.pm.ActivityInfo; 41import android.content.pm.ApplicationInfo; 42import android.content.pm.IPackageManager; 43import android.content.pm.PackageInfo; 44import android.content.pm.PackageManager; 45import android.content.pm.PackageManager.NameNotFoundException; 46import android.content.pm.ParceledListSlice; 47import android.content.pm.ResolveInfo; 48import android.content.pm.ServiceInfo; 49import android.content.pm.UserInfo; 50import android.content.res.Resources; 51import android.content.res.TypedArray; 52import android.content.res.XmlResourceParser; 53import android.graphics.Bitmap; 54import android.graphics.Point; 55import android.graphics.drawable.Drawable; 56import android.net.Uri; 57import android.os.Binder; 58import android.os.Bundle; 59import android.os.Environment; 60import android.os.Handler; 61import android.os.IBinder; 62import android.os.Looper; 63import android.os.Message; 64import android.os.Process; 65import android.os.RemoteException; 66import android.os.SystemClock; 67import android.os.UserHandle; 68import android.os.UserManager; 69import android.text.TextUtils; 70import android.util.ArraySet; 71import android.util.AtomicFile; 72import android.util.AttributeSet; 73import android.util.Pair; 74import android.util.Slog; 75import android.util.SparseArray; 76import android.util.SparseIntArray; 77import android.util.TypedValue; 78import android.util.Xml; 79import android.view.Display; 80import android.view.View; 81import android.view.WindowManager; 82import android.widget.RemoteViews; 83 84import com.android.internal.R; 85import com.android.internal.app.UnlaunchableAppActivity; 86import com.android.internal.appwidget.IAppWidgetHost; 87import com.android.internal.appwidget.IAppWidgetService; 88import com.android.internal.os.BackgroundThread; 89import com.android.internal.os.SomeArgs; 90import com.android.internal.util.FastXmlSerializer; 91import com.android.internal.widget.IRemoteViewsAdapterConnection; 92import com.android.internal.widget.IRemoteViewsFactory; 93import com.android.server.LocalServices; 94import com.android.server.WidgetBackupProvider; 95import com.android.server.policy.IconUtilities; 96 97import libcore.io.IoUtils; 98 99import org.xmlpull.v1.XmlPullParser; 100import org.xmlpull.v1.XmlPullParserException; 101import org.xmlpull.v1.XmlSerializer; 102 103import java.io.ByteArrayInputStream; 104import java.io.ByteArrayOutputStream; 105import java.io.File; 106import java.io.FileDescriptor; 107import java.io.FileInputStream; 108import java.io.FileNotFoundException; 109import java.io.FileOutputStream; 110import java.io.IOException; 111import java.io.PrintWriter; 112import java.nio.charset.StandardCharsets; 113import java.util.ArrayList; 114import java.util.Arrays; 115import java.util.Collections; 116import java.util.HashMap; 117import java.util.HashSet; 118import java.util.Iterator; 119import java.util.List; 120import java.util.Locale; 121import java.util.Map; 122import java.util.Set; 123 124class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider, 125 OnCrossProfileWidgetProvidersChangeListener { 126 private static final String TAG = "AppWidgetServiceImpl"; 127 128 private static boolean DEBUG = false; 129 130 private static final String OLD_KEYGUARD_HOST_PACKAGE = "android"; 131 private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard"; 132 private static final int KEYGUARD_HOST_ID = 0x4b455947; 133 134 private static final String STATE_FILENAME = "appwidgets.xml"; 135 136 private static final int MIN_UPDATE_PERIOD = DEBUG ? 0 : 30 * 60 * 1000; // 30 minutes 137 138 private static final int TAG_UNDEFINED = -1; 139 140 private static final int UNKNOWN_UID = -1; 141 142 private static final int LOADED_PROFILE_ID = -1; 143 144 private static final int UNKNOWN_USER_ID = -10; 145 146 // Bump if the stored widgets need to be upgraded. 147 private static final int CURRENT_VERSION = 1; 148 149 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 150 @Override 151 public void onReceive(Context context, Intent intent) { 152 final String action = intent.getAction(); 153 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 154 155 if (DEBUG) { 156 Slog.i(TAG, "Received broadcast: " + action + " on user " + userId); 157 } 158 159 if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { 160 onConfigurationChanged(); 161 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { 162 onUserUnlocked(userId); 163 } else if (Intent.ACTION_USER_STOPPED.equals(action)) { 164 onUserStopped(userId); 165 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { 166 reloadWidgetsMaskedStateForGroup(userId); 167 } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) 168 || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { 169 synchronized (mLock) { 170 reloadWidgetsMaskedState(userId); 171 } 172 } else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) { 173 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 174 updateWidgetPackageSuspensionMaskedState(packages, true, getSendingUserId()); 175 } else if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(action)) { 176 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 177 updateWidgetPackageSuspensionMaskedState(packages, false, getSendingUserId()); 178 } else { 179 onPackageBroadcastReceived(intent, userId); 180 } 181 } 182 }; 183 184 // Manages active connections to RemoteViewsServices. 185 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> 186 mBoundRemoteViewsServices = new HashMap<>(); 187 188 // Manages persistent references to RemoteViewsServices from different App Widgets. 189 private final HashMap<Pair<Integer, FilterComparison>, HashSet<Integer>> 190 mRemoteViewsServicesAppWidgets = new HashMap<>(); 191 192 private final Object mLock = new Object(); 193 194 private final ArrayList<Widget> mWidgets = new ArrayList<>(); 195 private final ArrayList<Host> mHosts = new ArrayList<>(); 196 private final ArrayList<Provider> mProviders = new ArrayList<>(); 197 198 private final ArraySet<Pair<Integer, String>> mPackagesWithBindWidgetPermission = 199 new ArraySet<>(); 200 201 private final SparseIntArray mLoadedUserIds = new SparseIntArray(); 202 203 private final SparseArray<ArraySet<String>> mWidgetPackages = new SparseArray<>(); 204 205 private final BackupRestoreController mBackupRestoreController; 206 207 private final Context mContext; 208 209 private final IPackageManager mPackageManager; 210 private final AlarmManager mAlarmManager; 211 private final UserManager mUserManager; 212 private final AppOpsManager mAppOpsManager; 213 private final KeyguardManager mKeyguardManager; 214 private final DevicePolicyManagerInternal mDevicePolicyManagerInternal; 215 216 private final SecurityPolicy mSecurityPolicy; 217 218 private final Handler mSaveStateHandler; 219 private final Handler mCallbackHandler; 220 221 private Locale mLocale; 222 223 private final SparseIntArray mNextAppWidgetIds = new SparseIntArray(); 224 225 private boolean mSafeMode; 226 private int mMaxWidgetBitmapMemory; 227 228 private final IconUtilities mIconUtilities; 229 230 AppWidgetServiceImpl(Context context) { 231 mContext = context; 232 mPackageManager = AppGlobals.getPackageManager(); 233 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 234 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 235 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 236 mKeyguardManager = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE); 237 mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class); 238 mSaveStateHandler = BackgroundThread.getHandler(); 239 mCallbackHandler = new CallbackHandler(mContext.getMainLooper()); 240 mBackupRestoreController = new BackupRestoreController(); 241 mSecurityPolicy = new SecurityPolicy(); 242 mIconUtilities = new IconUtilities(context); 243 244 computeMaximumWidgetBitmapMemory(); 245 registerBroadcastReceiver(); 246 registerOnCrossProfileProvidersChangedListener(); 247 } 248 249 private void computeMaximumWidgetBitmapMemory() { 250 WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 251 Display display = wm.getDefaultDisplay(); 252 Point size = new Point(); 253 display.getRealSize(size); 254 // Cap memory usage at 1.5 times the size of the display 255 // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h 256 mMaxWidgetBitmapMemory = 6 * size.x * size.y; 257 } 258 259 private void registerBroadcastReceiver() { 260 // Register for configuration changes so we can update the names 261 // of the widgets when the locale changes. 262 IntentFilter configFilter = new IntentFilter(); 263 configFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 264 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, 265 configFilter, null, null); 266 267 // Register for broadcasts about package install, etc., so we can 268 // update the provider list. 269 IntentFilter packageFilter = new IntentFilter(); 270 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 271 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 272 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 273 packageFilter.addDataScheme("package"); 274 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, 275 packageFilter, null, null); 276 277 // Register for events related to sdcard installation. 278 IntentFilter sdFilter = new IntentFilter(); 279 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 280 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 281 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, 282 sdFilter, null, null); 283 284 IntentFilter userFilter = new IntentFilter(); 285 userFilter.addAction(Intent.ACTION_USER_UNLOCKED); 286 userFilter.addAction(Intent.ACTION_USER_STOPPED); 287 userFilter.addAction(Intent.ACTION_USER_SWITCHED); 288 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, 289 userFilter, null, null); 290 291 IntentFilter offModeFilter = new IntentFilter(); 292 offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); 293 offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); 294 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, 295 offModeFilter, null, null); 296 297 IntentFilter suspendPackageFilter = new IntentFilter(); 298 suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); 299 suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); 300 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, 301 suspendPackageFilter, null, null); 302 } 303 304 private void registerOnCrossProfileProvidersChangedListener() { 305 // The device policy is an optional component. 306 if (mDevicePolicyManagerInternal != null) { 307 mDevicePolicyManagerInternal.addOnCrossProfileWidgetProvidersChangeListener(this); 308 } 309 } 310 311 public void setSafeMode(boolean safeMode) { 312 mSafeMode = safeMode; 313 } 314 315 private void onConfigurationChanged() { 316 if (DEBUG) { 317 Slog.i(TAG, "onConfigurationChanged()"); 318 } 319 320 Locale revised = Locale.getDefault(); 321 if (revised == null || mLocale == null || !revised.equals(mLocale)) { 322 mLocale = revised; 323 324 synchronized (mLock) { 325 SparseIntArray changedGroups = null; 326 327 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the 328 // list of installed providers and skip providers that we don't need to update. 329 // Also note that remove the provider does not clear the Provider component data. 330 ArrayList<Provider> installedProviders = new ArrayList<>(mProviders); 331 HashSet<ProviderId> removedProviders = new HashSet<>(); 332 333 int N = installedProviders.size(); 334 for (int i = N - 1; i >= 0; i--) { 335 Provider provider = installedProviders.get(i); 336 337 final int userId = provider.getUserId(); 338 if (!mUserManager.isUserUnlocked(userId) || 339 isProfileWithLockedParent(userId)) { 340 continue; 341 } 342 ensureGroupStateLoadedLocked(userId); 343 344 if (!removedProviders.contains(provider.id)) { 345 final boolean changed = updateProvidersForPackageLocked( 346 provider.id.componentName.getPackageName(), 347 provider.getUserId(), removedProviders); 348 349 if (changed) { 350 if (changedGroups == null) { 351 changedGroups = new SparseIntArray(); 352 } 353 final int groupId = mSecurityPolicy.getGroupParent( 354 provider.getUserId()); 355 changedGroups.put(groupId, groupId); 356 } 357 } 358 } 359 360 if (changedGroups != null) { 361 final int groupCount = changedGroups.size(); 362 for (int i = 0; i < groupCount; i++) { 363 final int groupId = changedGroups.get(i); 364 saveGroupStateAsync(groupId); 365 } 366 } 367 } 368 } 369 } 370 371 private void onPackageBroadcastReceived(Intent intent, int userId) { 372 if (!mUserManager.isUserUnlocked(userId) || 373 isProfileWithLockedParent(userId)) { 374 return; 375 } 376 377 final String action = intent.getAction(); 378 boolean added = false; 379 boolean changed = false; 380 boolean componentsModified = false; 381 382 String pkgList[] = null; 383 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { 384 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 385 added = true; 386 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { 387 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 388 added = false; 389 } else { 390 Uri uri = intent.getData(); 391 if (uri == null) { 392 return; 393 } 394 String pkgName = uri.getSchemeSpecificPart(); 395 if (pkgName == null) { 396 return; 397 } 398 pkgList = new String[] { pkgName }; 399 added = Intent.ACTION_PACKAGE_ADDED.equals(action); 400 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action); 401 } 402 if (pkgList == null || pkgList.length == 0) { 403 return; 404 } 405 406 synchronized (mLock) { 407 ensureGroupStateLoadedLocked(userId); 408 409 Bundle extras = intent.getExtras(); 410 411 if (added || changed) { 412 final boolean newPackageAdded = added && (extras == null 413 || !extras.getBoolean(Intent.EXTRA_REPLACING, false)); 414 415 for (String pkgName : pkgList) { 416 // Fix up the providers - add/remove/update. 417 componentsModified |= updateProvidersForPackageLocked(pkgName, userId, null); 418 419 // ... and see if these are hosts we've been awaiting. 420 // NOTE: We are backing up and restoring only the owner. 421 // TODO: http://b/22388012 422 if (newPackageAdded && userId == UserHandle.USER_SYSTEM) { 423 final int uid = getUidForPackage(pkgName, userId); 424 if (uid >= 0 ) { 425 resolveHostUidLocked(pkgName, uid); 426 } 427 } 428 } 429 } else { 430 // If the package is being updated, we'll receive a PACKAGE_ADDED 431 // shortly, otherwise it is removed permanently. 432 final boolean packageRemovedPermanently = (extras == null 433 || !extras.getBoolean(Intent.EXTRA_REPLACING, false)); 434 435 if (packageRemovedPermanently) { 436 for (String pkgName : pkgList) { 437 componentsModified |= removeHostsAndProvidersForPackageLocked( 438 pkgName, userId); 439 } 440 } 441 } 442 443 if (componentsModified) { 444 saveGroupStateAsync(userId); 445 446 // If the set of providers has been modified, notify each active AppWidgetHost 447 scheduleNotifyGroupHostsForProvidersChangedLocked(userId); 448 } 449 } 450 } 451 452 /** 453 * Reload all widgets' masked state for the given user and its associated profiles, including 454 * due to user not being available and package suspension. 455 * userId must be the group parent. 456 */ 457 private void reloadWidgetsMaskedStateForGroup(int userId) { 458 if (!mUserManager.isUserUnlocked(userId)) { 459 return; 460 } 461 synchronized (mLock) { 462 reloadWidgetsMaskedState(userId); 463 List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId); 464 if (profiles != null) { 465 for (int i = 0; i < profiles.size(); i++) { 466 UserInfo user = profiles.get(i); 467 reloadWidgetsMaskedState(user.id); 468 } 469 } 470 } 471 } 472 473 private void reloadWidgetsMaskedState(int userId) { 474 final long identity = Binder.clearCallingIdentity(); 475 try { 476 UserInfo user = mUserManager.getUserInfo(userId); 477 478 boolean lockedProfile = !mUserManager.isUserUnlocked(userId); 479 boolean quietProfile = user.isQuietModeEnabled(); 480 final int N = mProviders.size(); 481 for (int i = 0; i < N; i++) { 482 Provider provider = mProviders.get(i); 483 int providerUserId = provider.getUserId(); 484 if (providerUserId != userId) { 485 continue; 486 } 487 488 boolean changed = provider.setMaskedByLockedProfileLocked(lockedProfile); 489 changed |= provider.setMaskedByQuietProfileLocked(quietProfile); 490 try { 491 boolean suspended; 492 try { 493 suspended = mPackageManager.isPackageSuspendedForUser( 494 provider.info.provider.getPackageName(), provider.getUserId()); 495 } catch (IllegalArgumentException ex) { 496 // Package not found. 497 suspended = false; 498 } 499 changed |= provider.setMaskedBySuspendedPackageLocked(suspended); 500 } catch (RemoteException e) { 501 Slog.e(TAG, "Failed to query application info", e); 502 } 503 if (changed) { 504 if (provider.isMaskedLocked()) { 505 maskWidgetsViewsLocked(provider, null); 506 } else { 507 unmaskWidgetsViewsLocked(provider); 508 } 509 } 510 } 511 } finally { 512 Binder.restoreCallingIdentity(identity); 513 } 514 } 515 516 /** 517 * Incrementally update the masked state due to package suspension state. 518 */ 519 private void updateWidgetPackageSuspensionMaskedState(String[] packagesArray, boolean suspended, 520 int profileId) { 521 if (packagesArray == null) { 522 return; 523 } 524 Set<String> packages = new ArraySet<String>(Arrays.asList(packagesArray)); 525 synchronized (mLock) { 526 final int N = mProviders.size(); 527 for (int i = 0; i < N; i++) { 528 Provider provider = mProviders.get(i); 529 int providerUserId = provider.getUserId(); 530 if (providerUserId != profileId 531 || !packages.contains(provider.info.provider.getPackageName())) { 532 continue; 533 } 534 if (provider.setMaskedBySuspendedPackageLocked(suspended)) { 535 if (provider.isMaskedLocked()) { 536 maskWidgetsViewsLocked(provider, null); 537 } else { 538 unmaskWidgetsViewsLocked(provider); 539 } 540 } 541 } 542 } 543 } 544 545 private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) { 546 final long identity = Binder.clearCallingIdentity(); 547 try { 548 // Load the unbadged application icon and pass it to the widget to appear on 549 // the masked view. 550 Context userContext = mContext.createPackageContextAsUser(providerPackage, 0, 551 UserHandle.of(providerUserId)); 552 PackageManager pm = userContext.getPackageManager(); 553 Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm); 554 // Create a bitmap of the icon which is what the widget's remoteview requires. 555 return mIconUtilities.createIconBitmap(icon); 556 } catch (NameNotFoundException e) { 557 Slog.e(TAG, "Fail to get application icon", e); 558 // Provider package removed, no need to mask its views as its state will be 559 // purged very soon. 560 return null; 561 } finally { 562 Binder.restoreCallingIdentity(identity); 563 } 564 } 565 566 private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge, 567 PendingIntent onClickIntent) { 568 RemoteViews views = new RemoteViews(mContext.getPackageName(), 569 R.layout.work_widget_mask_view); 570 if (icon != null) { 571 views.setImageViewBitmap(R.id.work_widget_app_icon, icon); 572 } 573 if (!showBadge) { 574 views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE); 575 } 576 if (onClickIntent != null) { 577 views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent); 578 } 579 return views; 580 } 581 582 /** 583 * Mask the target widget belonging to the specified provider, or all active widgets 584 * of the provider if target widget == null. 585 */ 586 private void maskWidgetsViewsLocked(Provider provider, Widget targetWidget) { 587 final int widgetCount = provider.widgets.size(); 588 if (widgetCount == 0) { 589 return; 590 } 591 final String providerPackage = provider.info.provider.getPackageName(); 592 final int providerUserId = provider.getUserId(); 593 Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId); 594 if (iconBitmap == null) { 595 return; 596 } 597 final boolean showBadge; 598 final Intent onClickIntent; 599 if (provider.maskedBySuspendedPackage) { 600 final long identity = Binder.clearCallingIdentity(); 601 try { 602 UserInfo userInfo = mUserManager.getUserInfo(providerUserId); 603 showBadge = userInfo.isManagedProfile(); 604 onClickIntent = mDevicePolicyManagerInternal.createPackageSuspendedDialogIntent( 605 providerPackage, providerUserId); 606 } finally { 607 Binder.restoreCallingIdentity(identity); 608 } 609 } else if (provider.maskedByQuietProfile) { 610 showBadge = true; 611 onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent( 612 providerUserId); 613 } else /* provider.maskedByLockedProfile */ { 614 showBadge = true; 615 onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null, 616 providerUserId); 617 if (onClickIntent != null) { 618 onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 619 } 620 } 621 622 for (int j = 0; j < widgetCount; j++) { 623 Widget widget = provider.widgets.get(j); 624 if (targetWidget != null && targetWidget != widget) continue; 625 PendingIntent intent = null; 626 if (onClickIntent != null) { 627 intent = PendingIntent.getActivity(mContext, widget.appWidgetId, 628 onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT); 629 } 630 RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent); 631 if (widget.replaceWithMaskedViewsLocked(views)) { 632 scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked()); 633 } 634 } 635 } 636 637 private void unmaskWidgetsViewsLocked(Provider provider) { 638 final int widgetCount = provider.widgets.size(); 639 for (int j = 0; j < widgetCount; j++) { 640 Widget widget = provider.widgets.get(j); 641 if (widget.clearMaskedViewsLocked()) { 642 scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked()); 643 } 644 } 645 } 646 647 private void resolveHostUidLocked(String pkg, int uid) { 648 final int N = mHosts.size(); 649 for (int i = 0; i < N; i++) { 650 Host host = mHosts.get(i); 651 if (host.id.uid == UNKNOWN_UID && pkg.equals(host.id.packageName)) { 652 if (DEBUG) { 653 Slog.i(TAG, "host " + host.id + " resolved to uid " + uid); 654 } 655 host.id = new HostId(uid, host.id.hostId, host.id.packageName); 656 return; 657 } 658 } 659 } 660 661 private void ensureGroupStateLoadedLocked(int userId) { 662 if (!mUserManager.isUserUnlocked(userId)) { 663 throw new IllegalStateException( 664 "User " + userId + " must be unlocked for widgets to be available"); 665 } 666 if (isProfileWithLockedParent(userId)) { 667 throw new IllegalStateException( 668 "Profile " + userId + " must have unlocked parent"); 669 } 670 final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId); 671 672 // Careful lad, we may have already loaded the state for some 673 // group members, so check before loading and read only the 674 // state for the new member(s). 675 int newMemberCount = 0; 676 final int profileIdCount = profileIds.length; 677 for (int i = 0; i < profileIdCount; i++) { 678 final int profileId = profileIds[i]; 679 if (mLoadedUserIds.indexOfKey(profileId) >= 0) { 680 profileIds[i] = LOADED_PROFILE_ID; 681 } else { 682 newMemberCount++; 683 } 684 } 685 686 if (newMemberCount <= 0) { 687 return; 688 } 689 690 int newMemberIndex = 0; 691 final int[] newProfileIds = new int[newMemberCount]; 692 for (int i = 0; i < profileIdCount; i++) { 693 final int profileId = profileIds[i]; 694 if (profileId != LOADED_PROFILE_ID) { 695 mLoadedUserIds.put(profileId, profileId); 696 newProfileIds[newMemberIndex] = profileId; 697 newMemberIndex++; 698 } 699 } 700 701 clearProvidersAndHostsTagsLocked(); 702 703 loadGroupWidgetProvidersLocked(newProfileIds); 704 loadGroupStateLocked(newProfileIds); 705 } 706 707 @Override 708 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 709 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, 710 "Permission Denial: can't dump from from pid=" 711 + Binder.getCallingPid() 712 + ", uid=" + Binder.getCallingUid()); 713 714 synchronized (mLock) { 715 int N = mProviders.size(); 716 pw.println("Providers:"); 717 for (int i = 0; i < N; i++) { 718 dumpProvider(mProviders.get(i), i, pw); 719 } 720 721 N = mWidgets.size(); 722 pw.println(" "); 723 pw.println("Widgets:"); 724 for (int i = 0; i < N; i++) { 725 dumpWidget(mWidgets.get(i), i, pw); 726 } 727 728 N = mHosts.size(); 729 pw.println(" "); 730 pw.println("Hosts:"); 731 for (int i = 0; i < N; i++) { 732 dumpHost(mHosts.get(i), i, pw); 733 } 734 735 736 N = mPackagesWithBindWidgetPermission.size(); 737 pw.println(" "); 738 pw.println("Grants:"); 739 for (int i = 0; i < N; i++) { 740 Pair<Integer, String> grant = mPackagesWithBindWidgetPermission.valueAt(i); 741 dumpGrant(grant, i, pw); 742 } 743 } 744 } 745 746 @Override 747 public int[] startListening(IAppWidgetHost callbacks, String callingPackage, 748 int hostId, List<RemoteViews> updatedViews) { 749 final int userId = UserHandle.getCallingUserId(); 750 751 if (DEBUG) { 752 Slog.i(TAG, "startListening() " + userId); 753 } 754 755 // Make sure the package runs under the caller uid. 756 mSecurityPolicy.enforceCallFromPackage(callingPackage); 757 758 synchronized (mLock) { 759 ensureGroupStateLoadedLocked(userId); 760 761 // NOTE: The lookup is enforcing security across users by making 762 // sure the caller can only access hosts it owns. 763 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage); 764 Host host = lookupOrAddHostLocked(id); 765 766 host.callbacks = callbacks; 767 768 updatedViews.clear(); 769 770 ArrayList<Widget> instances = host.widgets; 771 int N = instances.size(); 772 int[] updatedIds = new int[N]; 773 for (int i = 0; i < N; i++) { 774 Widget widget = instances.get(i); 775 updatedIds[i] = widget.appWidgetId; 776 updatedViews.add(cloneIfLocalBinder(widget.getEffectiveViewsLocked())); 777 } 778 779 return updatedIds; 780 } 781 } 782 783 @Override 784 public void stopListening(String callingPackage, int hostId) { 785 final int userId = UserHandle.getCallingUserId(); 786 787 if (DEBUG) { 788 Slog.i(TAG, "stopListening() " + userId); 789 } 790 791 // Make sure the package runs under the caller uid. 792 mSecurityPolicy.enforceCallFromPackage(callingPackage); 793 794 synchronized (mLock) { 795 ensureGroupStateLoadedLocked(userId); 796 797 // NOTE: The lookup is enforcing security across users by making 798 // sure the caller can only access hosts it owns. 799 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage); 800 Host host = lookupHostLocked(id); 801 802 if (host != null) { 803 host.callbacks = null; 804 pruneHostLocked(host); 805 } 806 } 807 } 808 809 @Override 810 public int allocateAppWidgetId(String callingPackage, int hostId) { 811 final int userId = UserHandle.getCallingUserId(); 812 813 if (DEBUG) { 814 Slog.i(TAG, "allocateAppWidgetId() " + userId); 815 } 816 817 // Make sure the package runs under the caller uid. 818 mSecurityPolicy.enforceCallFromPackage(callingPackage); 819 820 synchronized (mLock) { 821 ensureGroupStateLoadedLocked(userId); 822 823 if (mNextAppWidgetIds.indexOfKey(userId) < 0) { 824 mNextAppWidgetIds.put(userId, AppWidgetManager.INVALID_APPWIDGET_ID + 1); 825 } 826 827 final int appWidgetId = incrementAndGetAppWidgetIdLocked(userId); 828 829 // NOTE: The lookup is enforcing security across users by making 830 // sure the caller can only access hosts it owns. 831 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage); 832 Host host = lookupOrAddHostLocked(id); 833 834 Widget widget = new Widget(); 835 widget.appWidgetId = appWidgetId; 836 widget.host = host; 837 838 host.widgets.add(widget); 839 addWidgetLocked(widget); 840 841 saveGroupStateAsync(userId); 842 843 if (DEBUG) { 844 Slog.i(TAG, "Allocated widget id " + appWidgetId 845 + " for host " + host.id); 846 } 847 848 return appWidgetId; 849 } 850 } 851 852 @Override 853 public void deleteAppWidgetId(String callingPackage, int appWidgetId) { 854 final int userId = UserHandle.getCallingUserId(); 855 856 if (DEBUG) { 857 Slog.i(TAG, "deleteAppWidgetId() " + userId); 858 } 859 860 // Make sure the package runs under the caller uid. 861 mSecurityPolicy.enforceCallFromPackage(callingPackage); 862 863 synchronized (mLock) { 864 ensureGroupStateLoadedLocked(userId); 865 866 // NOTE: The lookup is enforcing security across users by making 867 // sure the caller can only access widgets it hosts or provides. 868 Widget widget = lookupWidgetLocked(appWidgetId, 869 Binder.getCallingUid(), callingPackage); 870 871 if (widget == null) { 872 return; 873 } 874 875 deleteAppWidgetLocked(widget); 876 877 saveGroupStateAsync(userId); 878 879 if (DEBUG) { 880 Slog.i(TAG, "Deleted widget id " + appWidgetId 881 + " for host " + widget.host.id); 882 } 883 } 884 } 885 886 @Override 887 public boolean hasBindAppWidgetPermission(String packageName, int grantId) { 888 if (DEBUG) { 889 Slog.i(TAG, "hasBindAppWidgetPermission() " + UserHandle.getCallingUserId()); 890 } 891 892 // A special permission is required for managing white listing. 893 mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName); 894 895 synchronized (mLock) { 896 // The grants are stored in user state wich gets the grant. 897 ensureGroupStateLoadedLocked(grantId); 898 899 final int packageUid = getUidForPackage(packageName, grantId); 900 if (packageUid < 0) { 901 return false; 902 } 903 904 Pair<Integer, String> packageId = Pair.create(grantId, packageName); 905 return mPackagesWithBindWidgetPermission.contains(packageId); 906 } 907 } 908 909 @Override 910 public void setBindAppWidgetPermission(String packageName, int grantId, 911 boolean grantPermission) { 912 if (DEBUG) { 913 Slog.i(TAG, "setBindAppWidgetPermission() " + UserHandle.getCallingUserId()); 914 } 915 916 // A special permission is required for managing white listing. 917 mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName); 918 919 synchronized (mLock) { 920 // The grants are stored in user state wich gets the grant. 921 ensureGroupStateLoadedLocked(grantId); 922 923 final int packageUid = getUidForPackage(packageName, grantId); 924 if (packageUid < 0) { 925 return; 926 } 927 928 Pair<Integer, String> packageId = Pair.create(grantId, packageName); 929 if (grantPermission) { 930 mPackagesWithBindWidgetPermission.add(packageId); 931 } else { 932 mPackagesWithBindWidgetPermission.remove(packageId); 933 } 934 935 saveGroupStateAsync(grantId); 936 } 937 } 938 939 @Override 940 public IntentSender createAppWidgetConfigIntentSender(String callingPackage, int appWidgetId, 941 final int intentFlags) { 942 final int userId = UserHandle.getCallingUserId(); 943 944 if (DEBUG) { 945 Slog.i(TAG, "createAppWidgetConfigIntentSender() " + userId); 946 } 947 948 // Make sure the package runs under the caller uid. 949 mSecurityPolicy.enforceCallFromPackage(callingPackage); 950 951 synchronized (mLock) { 952 ensureGroupStateLoadedLocked(userId); 953 954 // NOTE: The lookup is enforcing security across users by making 955 // sure the caller can only access widgets it hosts or provides. 956 Widget widget = lookupWidgetLocked(appWidgetId, 957 Binder.getCallingUid(), callingPackage); 958 959 if (widget == null) { 960 throw new IllegalArgumentException("Bad widget id " + appWidgetId); 961 } 962 963 Provider provider = widget.provider; 964 if (provider == null) { 965 throw new IllegalArgumentException("Widget not bound " + appWidgetId); 966 } 967 968 // Make sure only safe flags can be passed it. 969 final int secureFlags = intentFlags & ~Intent.IMMUTABLE_FLAGS; 970 971 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); 972 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 973 intent.setComponent(provider.info.configure); 974 intent.setFlags(secureFlags); 975 976 // All right, create the sender. 977 final long identity = Binder.clearCallingIdentity(); 978 try { 979 return PendingIntent.getActivityAsUser( 980 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT 981 | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT, 982 null, new UserHandle(provider.getUserId())) 983 .getIntentSender(); 984 } finally { 985 Binder.restoreCallingIdentity(identity); 986 } 987 } 988 } 989 990 @Override 991 public boolean bindAppWidgetId(String callingPackage, int appWidgetId, 992 int providerProfileId, ComponentName providerComponent, Bundle options) { 993 final int userId = UserHandle.getCallingUserId(); 994 995 if (DEBUG) { 996 Slog.i(TAG, "bindAppWidgetId() " + userId); 997 } 998 999 // Make sure the package runs under the caller uid. 1000 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1001 1002 // Check that if a cross-profile binding is attempted, it is allowed. 1003 if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)) { 1004 return false; 1005 } 1006 1007 // If the provider is not under the calling user, make sure this 1008 // provider is white listed for access from the parent. 1009 if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed( 1010 providerComponent.getPackageName(), providerProfileId)) { 1011 return false; 1012 } 1013 1014 synchronized (mLock) { 1015 ensureGroupStateLoadedLocked(userId); 1016 1017 // A special permission or white listing is required to bind widgets. 1018 if (!mSecurityPolicy.hasCallerBindPermissionOrBindWhiteListedLocked( 1019 callingPackage)) { 1020 return false; 1021 } 1022 1023 // NOTE: The lookup is enforcing security across users by making 1024 // sure the caller can only access widgets it hosts or provides. 1025 Widget widget = lookupWidgetLocked(appWidgetId, 1026 Binder.getCallingUid(), callingPackage); 1027 1028 if (widget == null) { 1029 Slog.e(TAG, "Bad widget id " + appWidgetId); 1030 return false; 1031 } 1032 1033 if (widget.provider != null) { 1034 Slog.e(TAG, "Widget id " + appWidgetId 1035 + " already bound to: " + widget.provider.id); 1036 return false; 1037 } 1038 1039 final int providerUid = getUidForPackage(providerComponent.getPackageName(), 1040 providerProfileId); 1041 if (providerUid < 0) { 1042 Slog.e(TAG, "Package " + providerComponent.getPackageName() + " not installed " 1043 + " for profile " + providerProfileId); 1044 return false; 1045 } 1046 1047 // NOTE: The lookup is enforcing security across users by making 1048 // sure the provider is in the already vetted user profile. 1049 ProviderId providerId = new ProviderId(providerUid, providerComponent); 1050 Provider provider = lookupProviderLocked(providerId); 1051 1052 if (provider == null) { 1053 Slog.e(TAG, "No widget provider " + providerComponent + " for profile " 1054 + providerProfileId); 1055 return false; 1056 } 1057 1058 if (provider.zombie) { 1059 Slog.e(TAG, "Can't bind to a 3rd party provider in" 1060 + " safe mode " + provider); 1061 return false; 1062 } 1063 1064 widget.provider = provider; 1065 widget.options = (options != null) ? cloneIfLocalBinder(options) : new Bundle(); 1066 1067 onWidgetProviderAddedOrChangedLocked(widget); 1068 1069 // We need to provide a default value for the widget category if it is not specified 1070 if (!widget.options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) { 1071 widget.options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 1072 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); 1073 } 1074 1075 provider.widgets.add(widget); 1076 1077 final int widgetCount = provider.widgets.size(); 1078 if (widgetCount == 1) { 1079 // Tell the provider that it's ready. 1080 sendEnableIntentLocked(provider); 1081 } 1082 1083 // Send an update now -- We need this update now, and just for this appWidgetId. 1084 // It's less critical when the next one happens, so when we schedule the next one, 1085 // we add updatePeriodMillis to its start time. That time will have some slop, 1086 // but that's okay. 1087 sendUpdateIntentLocked(provider, new int[] {appWidgetId}); 1088 1089 // Schedule the future updates. 1090 registerForBroadcastsLocked(provider, getWidgetIds(provider.widgets)); 1091 1092 saveGroupStateAsync(userId); 1093 1094 if (DEBUG) { 1095 Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id); 1096 } 1097 } 1098 1099 return true; 1100 } 1101 1102 @Override 1103 public int[] getAppWidgetIds(ComponentName componentName) { 1104 final int userId = UserHandle.getCallingUserId(); 1105 1106 if (DEBUG) { 1107 Slog.i(TAG, "getAppWidgetIds() " + userId); 1108 } 1109 1110 // Make sure the package runs under the caller uid. 1111 mSecurityPolicy.enforceCallFromPackage(componentName.getPackageName()); 1112 1113 synchronized (mLock) { 1114 ensureGroupStateLoadedLocked(userId); 1115 1116 // NOTE: The lookup is enforcing security across users by making 1117 // sure the caller can access only its providers. 1118 ProviderId providerId = new ProviderId(Binder.getCallingUid(), componentName); 1119 Provider provider = lookupProviderLocked(providerId); 1120 1121 if (provider != null) { 1122 return getWidgetIds(provider.widgets); 1123 } 1124 1125 return new int[0]; 1126 } 1127 } 1128 1129 @Override 1130 public int[] getAppWidgetIdsForHost(String callingPackage, int hostId) { 1131 final int userId = UserHandle.getCallingUserId(); 1132 1133 if (DEBUG) { 1134 Slog.i(TAG, "getAppWidgetIdsForHost() " + userId); 1135 } 1136 1137 // Make sure the package runs under the caller uid. 1138 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1139 1140 synchronized (mLock) { 1141 ensureGroupStateLoadedLocked(userId); 1142 1143 // NOTE: The lookup is enforcing security across users by making 1144 // sure the caller can only access its hosts. 1145 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage); 1146 Host host = lookupHostLocked(id); 1147 1148 if (host != null) { 1149 return getWidgetIds(host.widgets); 1150 } 1151 1152 return new int[0]; 1153 } 1154 } 1155 1156 @Override 1157 public void bindRemoteViewsService(String callingPackage, int appWidgetId, 1158 Intent intent, IBinder callbacks) { 1159 final int userId = UserHandle.getCallingUserId(); 1160 1161 if (DEBUG) { 1162 Slog.i(TAG, "bindRemoteViewsService() " + userId); 1163 } 1164 1165 // Make sure the package runs under the caller uid. 1166 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1167 1168 synchronized (mLock) { 1169 ensureGroupStateLoadedLocked(userId); 1170 1171 // NOTE: The lookup is enforcing security across users by making 1172 // sure the caller can only access widgets it hosts or provides. 1173 Widget widget = lookupWidgetLocked(appWidgetId, 1174 Binder.getCallingUid(), callingPackage); 1175 1176 if (widget == null) { 1177 throw new IllegalArgumentException("Bad widget id"); 1178 } 1179 1180 // Make sure the widget has a provider. 1181 if (widget.provider == null) { 1182 throw new IllegalArgumentException("No provider for widget " 1183 + appWidgetId); 1184 } 1185 1186 ComponentName componentName = intent.getComponent(); 1187 1188 // Ensure that the service belongs to the same package as the provider. 1189 // But this is not enough as they may be under different users - see below... 1190 String providerPackage = widget.provider.id.componentName.getPackageName(); 1191 String servicePackage = componentName.getPackageName(); 1192 if (!servicePackage.equals(providerPackage)) { 1193 throw new SecurityException("The taget service not in the same package" 1194 + " as the widget provider"); 1195 } 1196 1197 // Make sure this service exists under the same user as the provider and 1198 // requires a permission which allows only the system to bind to it. 1199 mSecurityPolicy.enforceServiceExistsAndRequiresBindRemoteViewsPermission( 1200 componentName, widget.provider.getUserId()); 1201 1202 // Good to go - the service pakcage is correct, it exists for the correct 1203 // user, and requires the bind permission. 1204 1205 // If there is already a connection made for this service intent, then 1206 // disconnect from that first. (This does not allow multiple connections 1207 // to the same service under the same key). 1208 ServiceConnectionProxy connection = null; 1209 FilterComparison fc = new FilterComparison(intent); 1210 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc); 1211 1212 if (mBoundRemoteViewsServices.containsKey(key)) { 1213 connection = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key); 1214 connection.disconnect(); 1215 unbindService(connection); 1216 mBoundRemoteViewsServices.remove(key); 1217 } 1218 1219 // Bind to the RemoteViewsService (which will trigger a callback to the 1220 // RemoteViewsAdapter.onServiceConnected()) 1221 connection = new ServiceConnectionProxy(callbacks); 1222 bindService(intent, connection, widget.provider.info.getProfile()); 1223 mBoundRemoteViewsServices.put(key, connection); 1224 1225 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we 1226 // can determine when we can call back to the RemoteViewsService later to 1227 // destroy associated factories. 1228 Pair<Integer, FilterComparison> serviceId = Pair.create(widget.provider.id.uid, fc); 1229 incrementAppWidgetServiceRefCount(appWidgetId, serviceId); 1230 } 1231 } 1232 1233 @Override 1234 public void unbindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent) { 1235 final int userId = UserHandle.getCallingUserId(); 1236 1237 if (DEBUG) { 1238 Slog.i(TAG, "unbindRemoteViewsService() " + userId); 1239 } 1240 1241 // Make sure the package runs under the caller uid. 1242 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1243 1244 synchronized (mLock) { 1245 ensureGroupStateLoadedLocked(userId); 1246 1247 // Unbind from the RemoteViewsService (which will trigger a callback to the bound 1248 // RemoteViewsAdapter) 1249 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, 1250 new FilterComparison(intent)); 1251 if (mBoundRemoteViewsServices.containsKey(key)) { 1252 // We don't need to use the appWidgetId until after we are sure there is something 1253 // to unbind. Note that this may mask certain issues with apps calling unbind() 1254 // more than necessary. 1255 1256 // NOTE: The lookup is enforcing security across users by making 1257 // sure the caller can only access widgets it hosts or provides. 1258 Widget widget = lookupWidgetLocked(appWidgetId, 1259 Binder.getCallingUid(), callingPackage); 1260 1261 if (widget == null) { 1262 throw new IllegalArgumentException("Bad widget id " + appWidgetId); 1263 } 1264 1265 ServiceConnectionProxy connection = (ServiceConnectionProxy) 1266 mBoundRemoteViewsServices.get(key); 1267 connection.disconnect(); 1268 mContext.unbindService(connection); 1269 mBoundRemoteViewsServices.remove(key); 1270 } 1271 } 1272 } 1273 1274 @Override 1275 public void deleteHost(String callingPackage, int hostId) { 1276 final int userId = UserHandle.getCallingUserId(); 1277 1278 if (DEBUG) { 1279 Slog.i(TAG, "deleteHost() " + userId); 1280 } 1281 1282 // Make sure the package runs under the caller uid. 1283 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1284 1285 synchronized (mLock) { 1286 ensureGroupStateLoadedLocked(userId); 1287 1288 // NOTE: The lookup is enforcing security across users by making 1289 // sure the caller can only access hosts in its uid and package. 1290 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage); 1291 Host host = lookupHostLocked(id); 1292 1293 if (host == null) { 1294 return; 1295 } 1296 1297 deleteHostLocked(host); 1298 1299 saveGroupStateAsync(userId); 1300 1301 if (DEBUG) { 1302 Slog.i(TAG, "Deleted host " + host.id); 1303 } 1304 } 1305 } 1306 1307 @Override 1308 public void deleteAllHosts() { 1309 final int userId = UserHandle.getCallingUserId(); 1310 1311 if (DEBUG) { 1312 Slog.i(TAG, "deleteAllHosts() " + userId); 1313 } 1314 1315 synchronized (mLock) { 1316 ensureGroupStateLoadedLocked(userId); 1317 1318 boolean changed = false; 1319 1320 final int N = mHosts.size(); 1321 for (int i = N - 1; i >= 0; i--) { 1322 Host host = mHosts.get(i); 1323 1324 // Delete only hosts in the calling uid. 1325 if (host.id.uid == Binder.getCallingUid()) { 1326 deleteHostLocked(host); 1327 changed = true; 1328 1329 if (DEBUG) { 1330 Slog.i(TAG, "Deleted host " + host.id); 1331 } 1332 } 1333 } 1334 1335 if (changed) { 1336 saveGroupStateAsync(userId); 1337 } 1338 } 1339 } 1340 1341 @Override 1342 public AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId) { 1343 final int userId = UserHandle.getCallingUserId(); 1344 1345 if (DEBUG) { 1346 Slog.i(TAG, "getAppWidgetInfo() " + userId); 1347 } 1348 1349 // Make sure the package runs under the caller uid. 1350 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1351 1352 synchronized (mLock) { 1353 ensureGroupStateLoadedLocked(userId); 1354 1355 // NOTE: The lookup is enforcing security across users by making 1356 // sure the caller can only access widgets it hosts or provides. 1357 Widget widget = lookupWidgetLocked(appWidgetId, 1358 Binder.getCallingUid(), callingPackage); 1359 1360 if (widget != null && widget.provider != null && !widget.provider.zombie) { 1361 return cloneIfLocalBinder(widget.provider.info); 1362 } 1363 1364 return null; 1365 } 1366 } 1367 1368 @Override 1369 public RemoteViews getAppWidgetViews(String callingPackage, int appWidgetId) { 1370 final int userId = UserHandle.getCallingUserId(); 1371 1372 if (DEBUG) { 1373 Slog.i(TAG, "getAppWidgetViews() " + userId); 1374 } 1375 1376 // Make sure the package runs under the caller uid. 1377 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1378 1379 synchronized (mLock) { 1380 ensureGroupStateLoadedLocked(userId); 1381 1382 // NOTE: The lookup is enforcing security across users by making 1383 // sure the caller can only access widgets it hosts or provides. 1384 Widget widget = lookupWidgetLocked(appWidgetId, 1385 Binder.getCallingUid(), callingPackage); 1386 1387 if (widget != null) { 1388 return cloneIfLocalBinder(widget.getEffectiveViewsLocked()); 1389 } 1390 1391 return null; 1392 } 1393 } 1394 1395 @Override 1396 public void updateAppWidgetOptions(String callingPackage, int appWidgetId, Bundle options) { 1397 final int userId = UserHandle.getCallingUserId(); 1398 1399 if (DEBUG) { 1400 Slog.i(TAG, "updateAppWidgetOptions() " + userId); 1401 } 1402 1403 // Make sure the package runs under the caller uid. 1404 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1405 1406 synchronized (mLock) { 1407 ensureGroupStateLoadedLocked(userId); 1408 1409 // NOTE: The lookup is enforcing security across users by making 1410 // sure the caller can only access widgets it hosts or provides. 1411 Widget widget = lookupWidgetLocked(appWidgetId, 1412 Binder.getCallingUid(), callingPackage); 1413 1414 if (widget == null) { 1415 return; 1416 } 1417 1418 // Merge the options. 1419 widget.options.putAll(options); 1420 1421 // Send the broacast to notify the provider that options changed. 1422 sendOptionsChangedIntentLocked(widget); 1423 1424 saveGroupStateAsync(userId); 1425 } 1426 } 1427 1428 @Override 1429 public Bundle getAppWidgetOptions(String callingPackage, int appWidgetId) { 1430 final int userId = UserHandle.getCallingUserId(); 1431 1432 if (DEBUG) { 1433 Slog.i(TAG, "getAppWidgetOptions() " + userId); 1434 } 1435 1436 // Make sure the package runs under the caller uid. 1437 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1438 1439 synchronized (mLock) { 1440 ensureGroupStateLoadedLocked(userId); 1441 1442 // NOTE: The lookup is enforcing security across users by making 1443 // sure the caller can only access widgets it hosts or provides. 1444 Widget widget = lookupWidgetLocked(appWidgetId, 1445 Binder.getCallingUid(), callingPackage); 1446 1447 if (widget != null && widget.options != null) { 1448 return cloneIfLocalBinder(widget.options); 1449 } 1450 1451 return Bundle.EMPTY; 1452 } 1453 } 1454 1455 @Override 1456 public void updateAppWidgetIds(String callingPackage, int[] appWidgetIds, 1457 RemoteViews views) { 1458 if (DEBUG) { 1459 Slog.i(TAG, "updateAppWidgetIds() " + UserHandle.getCallingUserId()); 1460 } 1461 1462 updateAppWidgetIds(callingPackage, appWidgetIds, views, false); 1463 } 1464 1465 @Override 1466 public void partiallyUpdateAppWidgetIds(String callingPackage, int[] appWidgetIds, 1467 RemoteViews views) { 1468 if (DEBUG) { 1469 Slog.i(TAG, "partiallyUpdateAppWidgetIds() " + UserHandle.getCallingUserId()); 1470 } 1471 1472 updateAppWidgetIds(callingPackage, appWidgetIds, views, true); 1473 } 1474 1475 @Override 1476 public void notifyAppWidgetViewDataChanged(String callingPackage, int[] appWidgetIds, 1477 int viewId) { 1478 final int userId = UserHandle.getCallingUserId(); 1479 1480 if (DEBUG) { 1481 Slog.i(TAG, "notifyAppWidgetViewDataChanged() " + userId); 1482 } 1483 1484 // Make sure the package runs under the caller uid. 1485 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1486 1487 if (appWidgetIds == null || appWidgetIds.length == 0) { 1488 return; 1489 } 1490 1491 synchronized (mLock) { 1492 ensureGroupStateLoadedLocked(userId); 1493 1494 final int N = appWidgetIds.length; 1495 for (int i = 0; i < N; i++) { 1496 final int appWidgetId = appWidgetIds[i]; 1497 1498 // NOTE: The lookup is enforcing security across users by making 1499 // sure the caller can only access widgets it hosts or provides. 1500 Widget widget = lookupWidgetLocked(appWidgetId, 1501 Binder.getCallingUid(), callingPackage); 1502 1503 if (widget != null) { 1504 scheduleNotifyAppWidgetViewDataChanged(widget, viewId); 1505 } 1506 } 1507 } 1508 } 1509 1510 @Override 1511 public void updateAppWidgetProvider(ComponentName componentName, RemoteViews views) { 1512 final int userId = UserHandle.getCallingUserId(); 1513 1514 if (DEBUG) { 1515 Slog.i(TAG, "updateAppWidgetProvider() " + userId); 1516 } 1517 1518 // Make sure the package runs under the caller uid. 1519 mSecurityPolicy.enforceCallFromPackage(componentName.getPackageName()); 1520 1521 synchronized (mLock) { 1522 ensureGroupStateLoadedLocked(userId); 1523 1524 // NOTE: The lookup is enforcing security across users by making 1525 // sure the caller can access only its providers. 1526 ProviderId providerId = new ProviderId(Binder.getCallingUid(), componentName); 1527 Provider provider = lookupProviderLocked(providerId); 1528 1529 if (provider == null) { 1530 Slog.w(TAG, "Provider doesn't exist " + providerId); 1531 return; 1532 } 1533 1534 ArrayList<Widget> instances = provider.widgets; 1535 final int N = instances.size(); 1536 for (int i = 0; i < N; i++) { 1537 Widget widget = instances.get(i); 1538 updateAppWidgetInstanceLocked(widget, views, false); 1539 } 1540 } 1541 } 1542 1543 @Override 1544 public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter, 1545 int profileId) { 1546 final int userId = UserHandle.getCallingUserId(); 1547 1548 if (DEBUG) { 1549 Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId); 1550 } 1551 1552 // Ensure the profile is in the group and enabled. 1553 if (!mSecurityPolicy.isEnabledGroupProfile(profileId)) { 1554 return null; 1555 } 1556 1557 synchronized (mLock) { 1558 ensureGroupStateLoadedLocked(userId); 1559 1560 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(); 1561 1562 final int providerCount = mProviders.size(); 1563 for (int i = 0; i < providerCount; i++) { 1564 Provider provider = mProviders.get(i); 1565 AppWidgetProviderInfo info = provider.info; 1566 1567 // Ignore an invalid provider or one not matching the filter. 1568 if (provider.zombie || (info.widgetCategory & categoryFilter) == 0) { 1569 continue; 1570 } 1571 1572 // Add providers only for the requested profile that are white-listed. 1573 final int providerProfileId = info.getProfile().getIdentifier(); 1574 if (providerProfileId == profileId 1575 && mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed( 1576 provider.id.componentName.getPackageName(), providerProfileId)) { 1577 result.add(cloneIfLocalBinder(info)); 1578 } 1579 } 1580 1581 return new ParceledListSlice<AppWidgetProviderInfo>(result); 1582 } 1583 } 1584 1585 private void updateAppWidgetIds(String callingPackage, int[] appWidgetIds, 1586 RemoteViews views, boolean partially) { 1587 final int userId = UserHandle.getCallingUserId(); 1588 1589 if (appWidgetIds == null || appWidgetIds.length == 0) { 1590 return; 1591 } 1592 1593 // Make sure the package runs under the caller uid. 1594 mSecurityPolicy.enforceCallFromPackage(callingPackage); 1595 1596 final int bitmapMemoryUsage = (views != null) ? views.estimateMemoryUsage() : 0; 1597 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) { 1598 throw new IllegalArgumentException("RemoteViews for widget update exceeds" 1599 + " maximum bitmap memory usage (used: " + bitmapMemoryUsage 1600 + ", max: " + mMaxWidgetBitmapMemory + ")"); 1601 } 1602 1603 synchronized (mLock) { 1604 ensureGroupStateLoadedLocked(userId); 1605 1606 final int N = appWidgetIds.length; 1607 for (int i = 0; i < N; i++) { 1608 final int appWidgetId = appWidgetIds[i]; 1609 1610 // NOTE: The lookup is enforcing security across users by making 1611 // sure the caller can only access widgets it hosts or provides. 1612 Widget widget = lookupWidgetLocked(appWidgetId, 1613 Binder.getCallingUid(), callingPackage); 1614 1615 if (widget != null) { 1616 updateAppWidgetInstanceLocked(widget, views, partially); 1617 } 1618 } 1619 } 1620 } 1621 1622 private int incrementAndGetAppWidgetIdLocked(int userId) { 1623 final int appWidgetId = peekNextAppWidgetIdLocked(userId) + 1; 1624 mNextAppWidgetIds.put(userId, appWidgetId); 1625 return appWidgetId; 1626 } 1627 1628 private void setMinAppWidgetIdLocked(int userId, int minWidgetId) { 1629 final int nextAppWidgetId = peekNextAppWidgetIdLocked(userId); 1630 if (nextAppWidgetId < minWidgetId) { 1631 mNextAppWidgetIds.put(userId, minWidgetId); 1632 } 1633 } 1634 1635 private int peekNextAppWidgetIdLocked(int userId) { 1636 if (mNextAppWidgetIds.indexOfKey(userId) < 0) { 1637 return AppWidgetManager.INVALID_APPWIDGET_ID + 1; 1638 } else { 1639 return mNextAppWidgetIds.get(userId); 1640 } 1641 } 1642 1643 private Host lookupOrAddHostLocked(HostId id) { 1644 Host host = lookupHostLocked(id); 1645 if (host != null) { 1646 return host; 1647 } 1648 1649 host = new Host(); 1650 host.id = id; 1651 mHosts.add(host); 1652 1653 return host; 1654 } 1655 1656 private void deleteHostLocked(Host host) { 1657 final int N = host.widgets.size(); 1658 for (int i = N - 1; i >= 0; i--) { 1659 Widget widget = host.widgets.remove(i); 1660 deleteAppWidgetLocked(widget); 1661 } 1662 mHosts.remove(host); 1663 1664 // it's gone or going away, abruptly drop the callback connection 1665 host.callbacks = null; 1666 } 1667 1668 private void deleteAppWidgetLocked(Widget widget) { 1669 // We first unbind all services that are bound to this id 1670 unbindAppWidgetRemoteViewsServicesLocked(widget); 1671 1672 Host host = widget.host; 1673 host.widgets.remove(widget); 1674 pruneHostLocked(host); 1675 1676 removeWidgetLocked(widget); 1677 1678 Provider provider = widget.provider; 1679 if (provider != null) { 1680 provider.widgets.remove(widget); 1681 if (!provider.zombie) { 1682 // send the broacast saying that this appWidgetId has been deleted 1683 sendDeletedIntentLocked(widget); 1684 1685 if (provider.widgets.isEmpty()) { 1686 // cancel the future updates 1687 cancelBroadcasts(provider); 1688 1689 // send the broacast saying that the provider is not in use any more 1690 sendDisabledIntentLocked(provider); 1691 } 1692 } 1693 } 1694 } 1695 1696 private void cancelBroadcasts(Provider provider) { 1697 if (DEBUG) { 1698 Slog.i(TAG, "cancelBroadcasts() for " + provider); 1699 } 1700 if (provider.broadcast != null) { 1701 mAlarmManager.cancel(provider.broadcast); 1702 long token = Binder.clearCallingIdentity(); 1703 try { 1704 provider.broadcast.cancel(); 1705 } finally { 1706 Binder.restoreCallingIdentity(token); 1707 } 1708 provider.broadcast = null; 1709 } 1710 } 1711 1712 // Unbinds from a RemoteViewsService when we delete an app widget 1713 private void unbindAppWidgetRemoteViewsServicesLocked(Widget widget) { 1714 int appWidgetId = widget.appWidgetId; 1715 // Unbind all connections to Services bound to this AppWidgetId 1716 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet() 1717 .iterator(); 1718 while (it.hasNext()) { 1719 final Pair<Integer, Intent.FilterComparison> key = it.next(); 1720 if (key.first == appWidgetId) { 1721 final ServiceConnectionProxy conn = (ServiceConnectionProxy) 1722 mBoundRemoteViewsServices.get(key); 1723 conn.disconnect(); 1724 mContext.unbindService(conn); 1725 it.remove(); 1726 } 1727 } 1728 1729 // Check if we need to destroy any services (if no other app widgets are 1730 // referencing the same service) 1731 decrementAppWidgetServiceRefCount(widget); 1732 } 1733 1734 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent 1735 private void destroyRemoteViewsService(final Intent intent, Widget widget) { 1736 final ServiceConnection conn = new ServiceConnection() { 1737 @Override 1738 public void onServiceConnected(ComponentName name, IBinder service) { 1739 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service); 1740 try { 1741 cb.onDestroy(intent); 1742 } catch (RemoteException re) { 1743 Slog.e(TAG, "Error calling remove view factory", re); 1744 } 1745 mContext.unbindService(this); 1746 } 1747 1748 @Override 1749 public void onServiceDisconnected(ComponentName name) { 1750 // Do nothing 1751 } 1752 }; 1753 1754 // Bind to the service and remove the static intent->factory mapping in the 1755 // RemoteViewsService. 1756 final long token = Binder.clearCallingIdentity(); 1757 try { 1758 mContext.bindServiceAsUser(intent, conn, 1759 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, 1760 widget.provider.info.getProfile()); 1761 } finally { 1762 Binder.restoreCallingIdentity(token); 1763 } 1764 } 1765 1766 // Adds to the ref-count for a given RemoteViewsService intent 1767 private void incrementAppWidgetServiceRefCount(int appWidgetId, 1768 Pair<Integer, FilterComparison> serviceId) { 1769 HashSet<Integer> appWidgetIds = null; 1770 if (mRemoteViewsServicesAppWidgets.containsKey(serviceId)) { 1771 appWidgetIds = mRemoteViewsServicesAppWidgets.get(serviceId); 1772 } else { 1773 appWidgetIds = new HashSet<>(); 1774 mRemoteViewsServicesAppWidgets.put(serviceId, appWidgetIds); 1775 } 1776 appWidgetIds.add(appWidgetId); 1777 } 1778 1779 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if 1780 // the ref-count reaches zero. 1781 private void decrementAppWidgetServiceRefCount(Widget widget) { 1782 Iterator<Pair<Integer, FilterComparison>> it = mRemoteViewsServicesAppWidgets 1783 .keySet().iterator(); 1784 while (it.hasNext()) { 1785 final Pair<Integer, FilterComparison> key = it.next(); 1786 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key); 1787 if (ids.remove(widget.appWidgetId)) { 1788 // If we have removed the last app widget referencing this service, then we 1789 // should destroy it and remove it from this set 1790 if (ids.isEmpty()) { 1791 destroyRemoteViewsService(key.second.getIntent(), widget); 1792 it.remove(); 1793 } 1794 } 1795 } 1796 } 1797 1798 private void saveGroupStateAsync(int groupId) { 1799 mSaveStateHandler.post(new SaveStateRunnable(groupId)); 1800 } 1801 1802 private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views, 1803 boolean isPartialUpdate) { 1804 if (widget != null && widget.provider != null 1805 && !widget.provider.zombie && !widget.host.zombie) { 1806 1807 if (isPartialUpdate && widget.views != null) { 1808 // For a partial update, we merge the new RemoteViews with the old. 1809 widget.views.mergeRemoteViews(views); 1810 } else { 1811 // For a full update we replace the RemoteViews completely. 1812 widget.views = views; 1813 } 1814 scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked()); 1815 } 1816 } 1817 1818 private void scheduleNotifyAppWidgetViewDataChanged(Widget widget, int viewId) { 1819 if (widget == null || widget.host == null || widget.host.zombie 1820 || widget.host.callbacks == null || widget.provider == null 1821 || widget.provider.zombie) { 1822 return; 1823 } 1824 1825 SomeArgs args = SomeArgs.obtain(); 1826 args.arg1 = widget.host; 1827 args.arg2 = widget.host.callbacks; 1828 args.argi1 = widget.appWidgetId; 1829 args.argi2 = viewId; 1830 1831 mCallbackHandler.obtainMessage( 1832 CallbackHandler.MSG_NOTIFY_VIEW_DATA_CHANGED, 1833 args).sendToTarget(); 1834 } 1835 1836 1837 private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks, 1838 int appWidgetId, int viewId) { 1839 try { 1840 callbacks.viewDataChanged(appWidgetId, viewId); 1841 } catch (RemoteException re) { 1842 // It failed; remove the callback. No need to prune because 1843 // we know that this host is still referenced by this instance. 1844 callbacks = null; 1845 } 1846 1847 // If the host is unavailable, then we call the associated 1848 // RemoteViewsFactory.onDataSetChanged() directly 1849 synchronized (mLock) { 1850 if (callbacks == null) { 1851 host.callbacks = null; 1852 1853 Set<Pair<Integer, FilterComparison>> keys = mRemoteViewsServicesAppWidgets.keySet(); 1854 for (Pair<Integer, FilterComparison> key : keys) { 1855 if (mRemoteViewsServicesAppWidgets.get(key).contains(appWidgetId)) { 1856 final ServiceConnection connection = new ServiceConnection() { 1857 @Override 1858 public void onServiceConnected(ComponentName name, IBinder service) { 1859 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub 1860 .asInterface(service); 1861 try { 1862 cb.onDataSetChangedAsync(); 1863 } catch (RemoteException e) { 1864 Slog.e(TAG, "Error calling onDataSetChangedAsync()", e); 1865 } 1866 mContext.unbindService(this); 1867 } 1868 1869 @Override 1870 public void onServiceDisconnected(android.content.ComponentName name) { 1871 // Do nothing 1872 } 1873 }; 1874 1875 final int userId = UserHandle.getUserId(key.first); 1876 Intent intent = key.second.getIntent(); 1877 1878 // Bind to the service and call onDataSetChanged() 1879 bindService(intent, connection, new UserHandle(userId)); 1880 } 1881 } 1882 } 1883 } 1884 } 1885 1886 private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) { 1887 if (widget == null || widget.provider == null || widget.provider.zombie 1888 || widget.host.callbacks == null || widget.host.zombie) { 1889 return; 1890 } 1891 1892 SomeArgs args = SomeArgs.obtain(); 1893 args.arg1 = widget.host; 1894 args.arg2 = widget.host.callbacks; 1895 args.arg3 = updateViews; 1896 args.argi1 = widget.appWidgetId; 1897 1898 mCallbackHandler.obtainMessage( 1899 CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET, 1900 args).sendToTarget(); 1901 } 1902 1903 private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks, 1904 int appWidgetId, RemoteViews views) { 1905 try { 1906 callbacks.updateAppWidget(appWidgetId, views); 1907 } catch (RemoteException re) { 1908 synchronized (mLock) { 1909 Slog.e(TAG, "Widget host dead: " + host.id, re); 1910 host.callbacks = null; 1911 } 1912 } 1913 } 1914 1915 private void scheduleNotifyProviderChangedLocked(Widget widget) { 1916 if (widget == null || widget.provider == null || widget.provider.zombie 1917 || widget.host.callbacks == null || widget.host.zombie) { 1918 return; 1919 } 1920 1921 SomeArgs args = SomeArgs.obtain(); 1922 args.arg1 = widget.host; 1923 args.arg2 = widget.host.callbacks; 1924 args.arg3 = widget.provider.info; 1925 args.argi1 = widget.appWidgetId; 1926 1927 mCallbackHandler.obtainMessage( 1928 CallbackHandler.MSG_NOTIFY_PROVIDER_CHANGED, 1929 args).sendToTarget(); 1930 } 1931 1932 private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks, 1933 int appWidgetId, AppWidgetProviderInfo info) { 1934 try { 1935 callbacks.providerChanged(appWidgetId, info); 1936 } catch (RemoteException re) { 1937 synchronized (mLock){ 1938 Slog.e(TAG, "Widget host dead: " + host.id, re); 1939 host.callbacks = null; 1940 } 1941 } 1942 } 1943 1944 private void scheduleNotifyGroupHostsForProvidersChangedLocked(int userId) { 1945 final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId); 1946 1947 final int N = mHosts.size(); 1948 for (int i = N - 1; i >= 0; i--) { 1949 Host host = mHosts.get(i); 1950 1951 boolean hostInGroup = false; 1952 final int M = profileIds.length; 1953 for (int j = 0; j < M; j++) { 1954 final int profileId = profileIds[j]; 1955 if (host.getUserId() == profileId) { 1956 hostInGroup = true; 1957 break; 1958 } 1959 } 1960 1961 if (!hostInGroup) { 1962 continue; 1963 } 1964 1965 if (host == null || host.zombie || host.callbacks == null) { 1966 continue; 1967 } 1968 1969 SomeArgs args = SomeArgs.obtain(); 1970 args.arg1 = host; 1971 args.arg2 = host.callbacks; 1972 1973 mCallbackHandler.obtainMessage( 1974 CallbackHandler.MSG_NOTIFY_PROVIDERS_CHANGED, 1975 args).sendToTarget(); 1976 } 1977 } 1978 1979 private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) { 1980 try { 1981 callbacks.providersChanged(); 1982 } catch (RemoteException re) { 1983 synchronized (mLock) { 1984 Slog.e(TAG, "Widget host dead: " + host.id, re); 1985 host.callbacks = null; 1986 } 1987 } 1988 } 1989 1990 private static boolean isLocalBinder() { 1991 return Process.myPid() == Binder.getCallingPid(); 1992 } 1993 1994 private static RemoteViews cloneIfLocalBinder(RemoteViews rv) { 1995 if (isLocalBinder() && rv != null) { 1996 return rv.clone(); 1997 } 1998 return rv; 1999 } 2000 2001 private static AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) { 2002 if (isLocalBinder() && info != null) { 2003 return info.clone(); 2004 } 2005 return info; 2006 } 2007 2008 private static Bundle cloneIfLocalBinder(Bundle bundle) { 2009 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic 2010 // if we start adding objects to the options. Further, it would only be an issue if keyguard 2011 // used such options. 2012 if (isLocalBinder() && bundle != null) { 2013 return (Bundle) bundle.clone(); 2014 } 2015 return bundle; 2016 } 2017 2018 private Widget lookupWidgetLocked(int appWidgetId, int uid, String packageName) { 2019 final int N = mWidgets.size(); 2020 for (int i = 0; i < N; i++) { 2021 Widget widget = mWidgets.get(i); 2022 if (widget.appWidgetId == appWidgetId 2023 && mSecurityPolicy.canAccessAppWidget(widget, uid, packageName)) { 2024 return widget; 2025 } 2026 } 2027 return null; 2028 } 2029 2030 private Provider lookupProviderLocked(ProviderId id) { 2031 final int N = mProviders.size(); 2032 for (int i = 0; i < N; i++) { 2033 Provider provider = mProviders.get(i); 2034 if (provider.id.equals(id)) { 2035 return provider; 2036 } 2037 } 2038 return null; 2039 } 2040 2041 private Host lookupHostLocked(HostId hostId) { 2042 final int N = mHosts.size(); 2043 for (int i = 0; i < N; i++) { 2044 Host host = mHosts.get(i); 2045 if (host.id.equals(hostId)) { 2046 return host; 2047 } 2048 } 2049 return null; 2050 } 2051 2052 private void pruneHostLocked(Host host) { 2053 if (host.widgets.size() == 0 && host.callbacks == null) { 2054 if (DEBUG) { 2055 Slog.i(TAG, "Pruning host " + host.id); 2056 } 2057 mHosts.remove(host); 2058 } 2059 } 2060 2061 private void loadGroupWidgetProvidersLocked(int[] profileIds) { 2062 List<ResolveInfo> allReceivers = null; 2063 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 2064 2065 final int profileCount = profileIds.length; 2066 for (int i = 0; i < profileCount; i++) { 2067 final int profileId = profileIds[i]; 2068 2069 List<ResolveInfo> receivers = queryIntentReceivers(intent, profileId); 2070 if (receivers != null && !receivers.isEmpty()) { 2071 if (allReceivers == null) { 2072 allReceivers = new ArrayList<>(); 2073 } 2074 allReceivers.addAll(receivers); 2075 } 2076 } 2077 2078 final int N = (allReceivers == null) ? 0 : allReceivers.size(); 2079 for (int i = 0; i < N; i++) { 2080 ResolveInfo receiver = allReceivers.get(i); 2081 addProviderLocked(receiver); 2082 } 2083 } 2084 2085 private boolean addProviderLocked(ResolveInfo ri) { 2086 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 2087 return false; 2088 } 2089 2090 if (!ri.activityInfo.isEnabled()) { 2091 return false; 2092 } 2093 2094 ComponentName componentName = new ComponentName(ri.activityInfo.packageName, 2095 ri.activityInfo.name); 2096 ProviderId providerId = new ProviderId(ri.activityInfo.applicationInfo.uid, componentName); 2097 2098 Provider provider = parseProviderInfoXml(providerId, ri); 2099 if (provider != null) { 2100 // we might have an inactive entry for this provider already due to 2101 // a preceding restore operation. if so, fix it up in place; otherwise 2102 // just add this new one. 2103 Provider existing = lookupProviderLocked(providerId); 2104 2105 // If the provider was not found it may be because it was restored and 2106 // we did not know its UID so let us find if there is such one. 2107 if (existing == null) { 2108 ProviderId restoredProviderId = new ProviderId(UNKNOWN_UID, componentName); 2109 existing = lookupProviderLocked(restoredProviderId); 2110 } 2111 2112 if (existing != null) { 2113 if (existing.zombie && !mSafeMode) { 2114 // it's a placeholder that was set up during an app restore 2115 existing.id = providerId; 2116 existing.zombie = false; 2117 existing.info = provider.info; // the real one filled out from the ResolveInfo 2118 if (DEBUG) { 2119 Slog.i(TAG, "Provider placeholder now reified: " + existing); 2120 } 2121 } 2122 } else { 2123 mProviders.add(provider); 2124 } 2125 return true; 2126 } 2127 2128 return false; 2129 } 2130 2131 // Remove widgets for provider that are hosted in userId. 2132 private void deleteWidgetsLocked(Provider provider, int userId) { 2133 final int N = provider.widgets.size(); 2134 for (int i = N - 1; i >= 0; i--) { 2135 Widget widget = provider.widgets.get(i); 2136 if (userId == UserHandle.USER_ALL 2137 || userId == widget.host.getUserId()) { 2138 provider.widgets.remove(i); 2139 // Call back with empty RemoteViews 2140 updateAppWidgetInstanceLocked(widget, null, false); 2141 // clear out references to this appWidgetId 2142 widget.host.widgets.remove(widget); 2143 removeWidgetLocked(widget); 2144 widget.provider = null; 2145 pruneHostLocked(widget.host); 2146 widget.host = null; 2147 } 2148 } 2149 } 2150 2151 private void deleteProviderLocked(Provider provider) { 2152 deleteWidgetsLocked(provider, UserHandle.USER_ALL); 2153 mProviders.remove(provider); 2154 2155 // no need to send the DISABLE broadcast, since the receiver is gone anyway 2156 cancelBroadcasts(provider); 2157 } 2158 2159 private void sendEnableIntentLocked(Provider p) { 2160 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED); 2161 intent.setComponent(p.info.provider); 2162 sendBroadcastAsUser(intent, p.info.getProfile()); 2163 } 2164 2165 private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds) { 2166 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 2167 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 2168 intent.setComponent(provider.info.provider); 2169 sendBroadcastAsUser(intent, provider.info.getProfile()); 2170 } 2171 2172 private void sendDeletedIntentLocked(Widget widget) { 2173 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED); 2174 intent.setComponent(widget.provider.info.provider); 2175 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId); 2176 sendBroadcastAsUser(intent, widget.provider.info.getProfile()); 2177 } 2178 2179 private void sendDisabledIntentLocked(Provider provider) { 2180 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED); 2181 intent.setComponent(provider.info.provider); 2182 sendBroadcastAsUser(intent, provider.info.getProfile()); 2183 } 2184 2185 public void sendOptionsChangedIntentLocked(Widget widget) { 2186 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED); 2187 intent.setComponent(widget.provider.info.provider); 2188 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId); 2189 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, widget.options); 2190 sendBroadcastAsUser(intent, widget.provider.info.getProfile()); 2191 } 2192 2193 private void registerForBroadcastsLocked(Provider provider, int[] appWidgetIds) { 2194 if (provider.info.updatePeriodMillis > 0) { 2195 // if this is the first instance, set the alarm. otherwise, 2196 // rely on the fact that we've already set it and that 2197 // PendingIntent.getBroadcast will update the extras. 2198 boolean alreadyRegistered = provider.broadcast != null; 2199 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 2200 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 2201 intent.setComponent(provider.info.provider); 2202 long token = Binder.clearCallingIdentity(); 2203 try { 2204 provider.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent, 2205 PendingIntent.FLAG_UPDATE_CURRENT, provider.info.getProfile()); 2206 } finally { 2207 Binder.restoreCallingIdentity(token); 2208 } 2209 if (!alreadyRegistered) { 2210 long period = provider.info.updatePeriodMillis; 2211 if (period < MIN_UPDATE_PERIOD) { 2212 period = MIN_UPDATE_PERIOD; 2213 } 2214 final long oldId = Binder.clearCallingIdentity(); 2215 try { 2216 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2217 SystemClock.elapsedRealtime() + period, period, provider.broadcast); 2218 } finally { 2219 Binder.restoreCallingIdentity(oldId); 2220 } 2221 } 2222 } 2223 } 2224 2225 private static int[] getWidgetIds(ArrayList<Widget> widgets) { 2226 int instancesSize = widgets.size(); 2227 int appWidgetIds[] = new int[instancesSize]; 2228 for (int i = 0; i < instancesSize; i++) { 2229 appWidgetIds[i] = widgets.get(i).appWidgetId; 2230 } 2231 return appWidgetIds; 2232 } 2233 2234 private static void dumpProvider(Provider provider, int index, PrintWriter pw) { 2235 AppWidgetProviderInfo info = provider.info; 2236 pw.print(" ["); pw.print(index); pw.print("] provider "); 2237 pw.println(provider.id); 2238 pw.print(" min=("); pw.print(info.minWidth); 2239 pw.print("x"); pw.print(info.minHeight); 2240 pw.print(") minResize=("); pw.print(info.minResizeWidth); 2241 pw.print("x"); pw.print(info.minResizeHeight); 2242 pw.print(") updatePeriodMillis="); 2243 pw.print(info.updatePeriodMillis); 2244 pw.print(" resizeMode="); 2245 pw.print(info.resizeMode); 2246 pw.print(info.widgetCategory); 2247 pw.print(" autoAdvanceViewId="); 2248 pw.print(info.autoAdvanceViewId); 2249 pw.print(" initialLayout=#"); 2250 pw.print(Integer.toHexString(info.initialLayout)); 2251 pw.print(" initialKeyguardLayout=#"); 2252 pw.print(Integer.toHexString(info.initialKeyguardLayout)); 2253 pw.print(" zombie="); pw.println(provider.zombie); 2254 } 2255 2256 private static void dumpHost(Host host, int index, PrintWriter pw) { 2257 pw.print(" ["); pw.print(index); pw.print("] hostId="); 2258 pw.println(host.id); 2259 pw.print(" callbacks="); pw.println(host.callbacks); 2260 pw.print(" widgets.size="); pw.print(host.widgets.size()); 2261 pw.print(" zombie="); pw.println(host.zombie); 2262 } 2263 2264 private static void dumpGrant(Pair<Integer, String> grant, int index, PrintWriter pw) { 2265 pw.print(" ["); pw.print(index); pw.print(']'); 2266 pw.print(" user="); pw.print(grant.first); 2267 pw.print(" package="); pw.println(grant.second); 2268 } 2269 2270 private static void dumpWidget(Widget widget, int index, PrintWriter pw) { 2271 pw.print(" ["); pw.print(index); pw.print("] id="); 2272 pw.println(widget.appWidgetId); 2273 pw.print(" host="); 2274 pw.println(widget.host.id); 2275 if (widget.provider != null) { 2276 pw.print(" provider="); pw.println(widget.provider.id); 2277 } 2278 if (widget.host != null) { 2279 pw.print(" host.callbacks="); pw.println(widget.host.callbacks); 2280 } 2281 if (widget.views != null) { 2282 pw.print(" views="); pw.println(widget.views); 2283 } 2284 } 2285 2286 private static void serializeProvider(XmlSerializer out, Provider p) throws IOException { 2287 out.startTag(null, "p"); 2288 out.attribute(null, "pkg", p.info.provider.getPackageName()); 2289 out.attribute(null, "cl", p.info.provider.getClassName()); 2290 out.attribute(null, "tag", Integer.toHexString(p.tag)); 2291 out.endTag(null, "p"); 2292 } 2293 2294 private static void serializeHost(XmlSerializer out, Host host) throws IOException { 2295 out.startTag(null, "h"); 2296 out.attribute(null, "pkg", host.id.packageName); 2297 out.attribute(null, "id", Integer.toHexString(host.id.hostId)); 2298 out.attribute(null, "tag", Integer.toHexString(host.tag)); 2299 out.endTag(null, "h"); 2300 } 2301 2302 private static void serializeAppWidget(XmlSerializer out, Widget widget) throws IOException { 2303 out.startTag(null, "g"); 2304 out.attribute(null, "id", Integer.toHexString(widget.appWidgetId)); 2305 out.attribute(null, "rid", Integer.toHexString(widget.restoredId)); 2306 out.attribute(null, "h", Integer.toHexString(widget.host.tag)); 2307 if (widget.provider != null) { 2308 out.attribute(null, "p", Integer.toHexString(widget.provider.tag)); 2309 } 2310 if (widget.options != null) { 2311 out.attribute(null, "min_width", Integer.toHexString(widget.options.getInt( 2312 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH))); 2313 out.attribute(null, "min_height", Integer.toHexString(widget.options.getInt( 2314 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT))); 2315 out.attribute(null, "max_width", Integer.toHexString(widget.options.getInt( 2316 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH))); 2317 out.attribute(null, "max_height", Integer.toHexString(widget.options.getInt( 2318 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT))); 2319 out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt( 2320 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY))); 2321 } 2322 out.endTag(null, "g"); 2323 } 2324 2325 @Override 2326 public List<String> getWidgetParticipants(int userId) { 2327 return mBackupRestoreController.getWidgetParticipants(userId); 2328 } 2329 2330 @Override 2331 public byte[] getWidgetState(String packageName, int userId) { 2332 return mBackupRestoreController.getWidgetState(packageName, userId); 2333 } 2334 2335 @Override 2336 public void restoreStarting(int userId) { 2337 mBackupRestoreController.restoreStarting(userId); 2338 } 2339 2340 @Override 2341 public void restoreWidgetState(String packageName, byte[] restoredState, int userId) { 2342 mBackupRestoreController.restoreWidgetState(packageName, restoredState, userId); 2343 } 2344 2345 @Override 2346 public void restoreFinished(int userId) { 2347 mBackupRestoreController.restoreFinished(userId); 2348 } 2349 2350 @SuppressWarnings("deprecation") 2351 private Provider parseProviderInfoXml(ProviderId providerId, ResolveInfo ri) { 2352 Provider provider = null; 2353 2354 ActivityInfo activityInfo = ri.activityInfo; 2355 XmlResourceParser parser = null; 2356 try { 2357 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(), 2358 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER); 2359 if (parser == null) { 2360 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER 2361 + " meta-data for " + "AppWidget provider '" + providerId + '\''); 2362 return null; 2363 } 2364 2365 AttributeSet attrs = Xml.asAttributeSet(parser); 2366 2367 int type; 2368 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 2369 && type != XmlPullParser.START_TAG) { 2370 // drain whitespace, comments, etc. 2371 } 2372 2373 String nodeName = parser.getName(); 2374 if (!"appwidget-provider".equals(nodeName)) { 2375 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for" 2376 + " AppWidget provider " + providerId.componentName 2377 + " for user " + providerId.uid); 2378 return null; 2379 } 2380 2381 provider = new Provider(); 2382 provider.id = providerId; 2383 AppWidgetProviderInfo info = provider.info = new AppWidgetProviderInfo(); 2384 info.provider = providerId.componentName; 2385 info.providerInfo = activityInfo; 2386 2387 final Resources resources; 2388 final long identity = Binder.clearCallingIdentity(); 2389 try { 2390 final PackageManager pm = mContext.getPackageManager(); 2391 final int userId = UserHandle.getUserId(providerId.uid); 2392 final ApplicationInfo app = pm.getApplicationInfoAsUser(activityInfo.packageName, 2393 0, userId); 2394 resources = pm.getResourcesForApplication(app); 2395 } finally { 2396 Binder.restoreCallingIdentity(identity); 2397 } 2398 2399 TypedArray sa = resources.obtainAttributes(attrs, 2400 com.android.internal.R.styleable.AppWidgetProviderInfo); 2401 2402 // These dimensions has to be resolved in the application's context. 2403 // We simply send back the raw complex data, which will be 2404 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}. 2405 TypedValue value = sa 2406 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth); 2407 info.minWidth = value != null ? value.data : 0; 2408 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight); 2409 info.minHeight = value != null ? value.data : 0; 2410 value = sa.peekValue( 2411 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth); 2412 info.minResizeWidth = value != null ? value.data : info.minWidth; 2413 value = sa.peekValue( 2414 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight); 2415 info.minResizeHeight = value != null ? value.data : info.minHeight; 2416 info.updatePeriodMillis = sa.getInt( 2417 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); 2418 info.initialLayout = sa.getResourceId( 2419 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0); 2420 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable. 2421 AppWidgetProviderInfo_initialKeyguardLayout, 0); 2422 2423 String className = sa 2424 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure); 2425 if (className != null) { 2426 info.configure = new ComponentName(providerId.componentName.getPackageName(), 2427 className); 2428 } 2429 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString(); 2430 info.icon = ri.getIconResource(); 2431 info.previewImage = sa.getResourceId( 2432 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0); 2433 info.autoAdvanceViewId = sa.getResourceId( 2434 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1); 2435 info.resizeMode = sa.getInt( 2436 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode, 2437 AppWidgetProviderInfo.RESIZE_NONE); 2438 info.widgetCategory = sa.getInt( 2439 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory, 2440 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); 2441 2442 sa.recycle(); 2443 } catch (IOException | PackageManager.NameNotFoundException | XmlPullParserException e) { 2444 // Ok to catch Exception here, because anything going wrong because 2445 // of what a client process passes to us should not be fatal for the 2446 // system process. 2447 Slog.w(TAG, "XML parsing failed for AppWidget provider " 2448 + providerId.componentName + " for user " + providerId.uid, e); 2449 return null; 2450 } finally { 2451 if (parser != null) { 2452 parser.close(); 2453 } 2454 } 2455 return provider; 2456 } 2457 2458 private int getUidForPackage(String packageName, int userId) { 2459 PackageInfo pkgInfo = null; 2460 2461 final long identity = Binder.clearCallingIdentity(); 2462 try { 2463 pkgInfo = mPackageManager.getPackageInfo(packageName, 0, userId); 2464 } catch (RemoteException re) { 2465 // Shouldn't happen, local call 2466 } finally { 2467 Binder.restoreCallingIdentity(identity); 2468 } 2469 2470 if (pkgInfo == null || pkgInfo.applicationInfo == null) { 2471 return -1; 2472 } 2473 2474 return pkgInfo.applicationInfo.uid; 2475 } 2476 2477 private ActivityInfo getProviderInfo(ComponentName componentName, int userId) { 2478 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 2479 intent.setComponent(componentName); 2480 2481 List<ResolveInfo> receivers = queryIntentReceivers(intent, userId); 2482 // We are setting component, so there is only one or none. 2483 if (!receivers.isEmpty()) { 2484 return receivers.get(0).activityInfo; 2485 } 2486 2487 return null; 2488 } 2489 2490 private List<ResolveInfo> queryIntentReceivers(Intent intent, int userId) { 2491 final long identity = Binder.clearCallingIdentity(); 2492 try { 2493 int flags = PackageManager.GET_META_DATA; 2494 2495 // We really need packages to be around and parsed to know if they 2496 // provide widgets. 2497 flags |= PackageManager.MATCH_DEBUG_TRIAGED_MISSING; 2498 2499 // Widget hosts that are non-crypto aware may be hosting widgets 2500 // from a profile that is still locked, so let them see those 2501 // widgets. 2502 if (isProfileWithUnlockedParent(userId)) { 2503 flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE 2504 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 2505 } 2506 2507 // Widgets referencing shared libraries need to have their 2508 // dependencies loaded. 2509 flags |= PackageManager.GET_SHARED_LIBRARY_FILES; 2510 2511 return mPackageManager.queryIntentReceivers(intent, 2512 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 2513 flags, userId).getList(); 2514 } catch (RemoteException re) { 2515 return Collections.emptyList(); 2516 } finally { 2517 Binder.restoreCallingIdentity(identity); 2518 } 2519 } 2520 2521 private void onUserUnlocked(int userId) { 2522 if (isProfileWithLockedParent(userId)) { 2523 return; 2524 } 2525 synchronized (mLock) { 2526 ensureGroupStateLoadedLocked(userId); 2527 reloadWidgetsMaskedStateForGroup(mSecurityPolicy.getGroupParent(userId)); 2528 2529 final int N = mProviders.size(); 2530 for (int i = 0; i < N; i++) { 2531 Provider provider = mProviders.get(i); 2532 2533 // Send broadcast only to the providers of the user. 2534 if (provider.getUserId() != userId) { 2535 continue; 2536 } 2537 2538 if (provider.widgets.size() > 0) { 2539 sendEnableIntentLocked(provider); 2540 int[] appWidgetIds = getWidgetIds(provider.widgets); 2541 sendUpdateIntentLocked(provider, appWidgetIds); 2542 registerForBroadcastsLocked(provider, appWidgetIds); 2543 } 2544 } 2545 } 2546 } 2547 2548 // only call from initialization -- it assumes that the data structures are all empty 2549 private void loadGroupStateLocked(int[] profileIds) { 2550 // We can bind the widgets to host and providers only after 2551 // reading the host and providers for all users since a widget 2552 // can have a host and a provider in different users. 2553 List<LoadedWidgetState> loadedWidgets = new ArrayList<>(); 2554 2555 int version = 0; 2556 2557 final int profileIdCount = profileIds.length; 2558 for (int i = 0; i < profileIdCount; i++) { 2559 final int profileId = profileIds[i]; 2560 2561 // No file written for this user - nothing to do. 2562 AtomicFile file = getSavedStateFile(profileId); 2563 try { 2564 FileInputStream stream = file.openRead(); 2565 version = readProfileStateFromFileLocked(stream, profileId, loadedWidgets); 2566 IoUtils.closeQuietly(stream); 2567 } catch (FileNotFoundException e) { 2568 Slog.w(TAG, "Failed to read state: " + e); 2569 } 2570 } 2571 2572 if (version >= 0) { 2573 // Hooke'm up... 2574 bindLoadedWidgetsLocked(loadedWidgets); 2575 2576 // upgrade the database if needed 2577 performUpgradeLocked(version); 2578 } else { 2579 // failed reading, clean up 2580 Slog.w(TAG, "Failed to read state, clearing widgets and hosts."); 2581 clearWidgetsLocked(); 2582 mHosts.clear(); 2583 final int N = mProviders.size(); 2584 for (int i = 0; i < N; i++) { 2585 mProviders.get(i).widgets.clear(); 2586 } 2587 } 2588 } 2589 2590 private void bindLoadedWidgetsLocked(List<LoadedWidgetState> loadedWidgets) { 2591 final int loadedWidgetCount = loadedWidgets.size(); 2592 for (int i = loadedWidgetCount - 1; i >= 0; i--) { 2593 LoadedWidgetState loadedWidget = loadedWidgets.remove(i); 2594 Widget widget = loadedWidget.widget; 2595 2596 widget.provider = findProviderByTag(loadedWidget.providerTag); 2597 if (widget.provider == null) { 2598 // This provider is gone. We just let the host figure out 2599 // that this happened when it fails to load it. 2600 continue; 2601 } 2602 2603 widget.host = findHostByTag(loadedWidget.hostTag); 2604 if (widget.host == null) { 2605 // This host is gone. 2606 continue; 2607 } 2608 2609 widget.provider.widgets.add(widget); 2610 widget.host.widgets.add(widget); 2611 addWidgetLocked(widget); 2612 } 2613 } 2614 2615 private Provider findProviderByTag(int tag) { 2616 if (tag < 0) { 2617 return null; 2618 } 2619 final int providerCount = mProviders.size(); 2620 for (int i = 0; i < providerCount; i++) { 2621 Provider provider = mProviders.get(i); 2622 if (provider.tag == tag) { 2623 return provider; 2624 } 2625 } 2626 return null; 2627 } 2628 2629 private Host findHostByTag(int tag) { 2630 if (tag < 0) { 2631 return null; 2632 } 2633 final int hostCount = mHosts.size(); 2634 for (int i = 0; i < hostCount; i++) { 2635 Host host = mHosts.get(i); 2636 if (host.tag == tag) { 2637 return host; 2638 } 2639 } 2640 return null; 2641 } 2642 2643 /** 2644 * Adds the widget to mWidgets and tracks the package name in mWidgetPackages. 2645 */ 2646 void addWidgetLocked(Widget widget) { 2647 mWidgets.add(widget); 2648 2649 onWidgetProviderAddedOrChangedLocked(widget); 2650 } 2651 2652 /** 2653 * Checks if the provider is assigned and updates the mWidgetPackages to track packages 2654 * that have bound widgets. 2655 */ 2656 void onWidgetProviderAddedOrChangedLocked(Widget widget) { 2657 if (widget.provider == null) return; 2658 2659 int userId = widget.provider.getUserId(); 2660 ArraySet<String> packages = mWidgetPackages.get(userId); 2661 if (packages == null) { 2662 mWidgetPackages.put(userId, packages = new ArraySet<String>()); 2663 } 2664 packages.add(widget.provider.info.provider.getPackageName()); 2665 2666 // If we are adding a widget it might be for a provider that 2667 // is currently masked, if so mask the widget. 2668 if (widget.provider.isMaskedLocked()) { 2669 maskWidgetsViewsLocked(widget.provider, widget); 2670 } else { 2671 widget.clearMaskedViewsLocked(); 2672 } 2673 } 2674 2675 /** 2676 * Removes a widget from mWidgets and updates the cache of bound widget provider packages. 2677 * If there are other widgets with the same package, leaves it in the cache, otherwise it 2678 * removes the associated package from the cache. 2679 */ 2680 void removeWidgetLocked(Widget widget) { 2681 mWidgets.remove(widget); 2682 2683 onWidgetRemovedLocked(widget); 2684 } 2685 2686 private void onWidgetRemovedLocked(Widget widget) { 2687 if (widget.provider == null) return; 2688 2689 final int userId = widget.provider.getUserId(); 2690 final String packageName = widget.provider.info.provider.getPackageName(); 2691 ArraySet<String> packages = mWidgetPackages.get(userId); 2692 if (packages == null) { 2693 return; 2694 } 2695 // Check if there is any other widget with the same package name. 2696 // Remove packageName if none. 2697 final int N = mWidgets.size(); 2698 for (int i = 0; i < N; i++) { 2699 Widget w = mWidgets.get(i); 2700 if (w.provider == null) continue; 2701 if (w.provider.getUserId() == userId 2702 && packageName.equals(w.provider.info.provider.getPackageName())) { 2703 return; 2704 } 2705 } 2706 packages.remove(packageName); 2707 } 2708 2709 /** 2710 * Clears all widgets and associated cache of packages with bound widgets. 2711 */ 2712 void clearWidgetsLocked() { 2713 mWidgets.clear(); 2714 2715 onWidgetsClearedLocked(); 2716 } 2717 2718 private void onWidgetsClearedLocked() { 2719 mWidgetPackages.clear(); 2720 } 2721 2722 @Override 2723 public boolean isBoundWidgetPackage(String packageName, int userId) { 2724 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 2725 throw new SecurityException("Only the system process can call this"); 2726 } 2727 synchronized (mLock) { 2728 final ArraySet<String> packages = mWidgetPackages.get(userId); 2729 if (packages != null) { 2730 return packages.contains(packageName); 2731 } 2732 } 2733 return false; 2734 } 2735 2736 private void saveStateLocked(int userId) { 2737 tagProvidersAndHosts(); 2738 2739 final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId); 2740 2741 final int profileCount = profileIds.length; 2742 for (int i = 0; i < profileCount; i++) { 2743 final int profileId = profileIds[i]; 2744 2745 AtomicFile file = getSavedStateFile(profileId); 2746 FileOutputStream stream; 2747 try { 2748 stream = file.startWrite(); 2749 if (writeProfileStateToFileLocked(stream, profileId)) { 2750 file.finishWrite(stream); 2751 } else { 2752 file.failWrite(stream); 2753 Slog.w(TAG, "Failed to save state, restoring backup."); 2754 } 2755 } catch (IOException e) { 2756 Slog.w(TAG, "Failed open state file for write: " + e); 2757 } 2758 } 2759 } 2760 2761 private void tagProvidersAndHosts() { 2762 final int providerCount = mProviders.size(); 2763 for (int i = 0; i < providerCount; i++) { 2764 Provider provider = mProviders.get(i); 2765 provider.tag = i; 2766 } 2767 2768 final int hostCount = mHosts.size(); 2769 for (int i = 0; i < hostCount; i++) { 2770 Host host = mHosts.get(i); 2771 host.tag = i; 2772 } 2773 } 2774 2775 private void clearProvidersAndHostsTagsLocked() { 2776 final int providerCount = mProviders.size(); 2777 for (int i = 0; i < providerCount; i++) { 2778 Provider provider = mProviders.get(i); 2779 provider.tag = TAG_UNDEFINED; 2780 } 2781 2782 final int hostCount = mHosts.size(); 2783 for (int i = 0; i < hostCount; i++) { 2784 Host host = mHosts.get(i); 2785 host.tag = TAG_UNDEFINED; 2786 } 2787 } 2788 2789 private boolean writeProfileStateToFileLocked(FileOutputStream stream, int userId) { 2790 int N; 2791 2792 try { 2793 XmlSerializer out = new FastXmlSerializer(); 2794 out.setOutput(stream, StandardCharsets.UTF_8.name()); 2795 out.startDocument(null, true); 2796 out.startTag(null, "gs"); 2797 out.attribute(null, "version", String.valueOf(CURRENT_VERSION)); 2798 2799 N = mProviders.size(); 2800 for (int i = 0; i < N; i++) { 2801 Provider provider = mProviders.get(i); 2802 // Save only providers for the user. 2803 if (provider.getUserId() != userId) { 2804 continue; 2805 } 2806 if (provider.widgets.size() > 0) { 2807 serializeProvider(out, provider); 2808 } 2809 } 2810 2811 N = mHosts.size(); 2812 for (int i = 0; i < N; i++) { 2813 Host host = mHosts.get(i); 2814 // Save only hosts for the user. 2815 if (host.getUserId() != userId) { 2816 continue; 2817 } 2818 serializeHost(out, host); 2819 } 2820 2821 N = mWidgets.size(); 2822 for (int i = 0; i < N; i++) { 2823 Widget widget = mWidgets.get(i); 2824 // Save only widgets hosted by the user. 2825 if (widget.host.getUserId() != userId) { 2826 continue; 2827 } 2828 serializeAppWidget(out, widget); 2829 } 2830 2831 Iterator<Pair<Integer, String>> it = mPackagesWithBindWidgetPermission.iterator(); 2832 while (it.hasNext()) { 2833 Pair<Integer, String> binding = it.next(); 2834 // Save only white listings for the user. 2835 if (binding.first != userId) { 2836 continue; 2837 } 2838 out.startTag(null, "b"); 2839 out.attribute(null, "packageName", binding.second); 2840 out.endTag(null, "b"); 2841 } 2842 2843 out.endTag(null, "gs"); 2844 out.endDocument(); 2845 return true; 2846 } catch (IOException e) { 2847 Slog.w(TAG, "Failed to write state: " + e); 2848 return false; 2849 } 2850 } 2851 2852 private int readProfileStateFromFileLocked(FileInputStream stream, int userId, 2853 List<LoadedWidgetState> outLoadedWidgets) { 2854 int version = -1; 2855 try { 2856 XmlPullParser parser = Xml.newPullParser(); 2857 parser.setInput(stream, StandardCharsets.UTF_8.name()); 2858 2859 int legacyProviderIndex = -1; 2860 int legacyHostIndex = -1; 2861 int type; 2862 do { 2863 type = parser.next(); 2864 if (type == XmlPullParser.START_TAG) { 2865 String tag = parser.getName(); 2866 if ("gs".equals(tag)) { 2867 String attributeValue = parser.getAttributeValue(null, "version"); 2868 try { 2869 version = Integer.parseInt(attributeValue); 2870 } catch (NumberFormatException e) { 2871 version = 0; 2872 } 2873 } else if ("p".equals(tag)) { 2874 legacyProviderIndex++; 2875 // TODO: do we need to check that this package has the same signature 2876 // as before? 2877 String pkg = parser.getAttributeValue(null, "pkg"); 2878 String cl = parser.getAttributeValue(null, "cl"); 2879 2880 pkg = getCanonicalPackageName(pkg, cl, userId); 2881 if (pkg == null) { 2882 continue; 2883 } 2884 2885 final int uid = getUidForPackage(pkg, userId); 2886 if (uid < 0) { 2887 continue; 2888 } 2889 2890 ComponentName componentName = new ComponentName(pkg, cl); 2891 2892 ActivityInfo providerInfo = getProviderInfo(componentName, userId); 2893 if (providerInfo == null) { 2894 continue; 2895 } 2896 2897 ProviderId providerId = new ProviderId(uid, componentName); 2898 Provider provider = lookupProviderLocked(providerId); 2899 2900 if (provider == null && mSafeMode) { 2901 // if we're in safe mode, make a temporary one 2902 provider = new Provider(); 2903 provider.info = new AppWidgetProviderInfo(); 2904 provider.info.provider = providerId.componentName; 2905 provider.info.providerInfo = providerInfo; 2906 provider.zombie = true; 2907 provider.id = providerId; 2908 mProviders.add(provider); 2909 } 2910 2911 String tagAttribute = parser.getAttributeValue(null, "tag"); 2912 final int providerTag = !TextUtils.isEmpty(tagAttribute) 2913 ? Integer.parseInt(tagAttribute, 16) : legacyProviderIndex; 2914 provider.tag = providerTag; 2915 } else if ("h".equals(tag)) { 2916 legacyHostIndex++; 2917 Host host = new Host(); 2918 // TODO: do we need to check that this package has the same signature 2919 // as before? 2920 String pkg = parser.getAttributeValue(null, "pkg"); 2921 2922 final int uid = getUidForPackage(pkg, userId); 2923 if (uid < 0) { 2924 host.zombie = true; 2925 } 2926 2927 if (!host.zombie || mSafeMode) { 2928 // In safe mode, we don't discard the hosts we don't recognize 2929 // so that they're not pruned from our list. Otherwise, we do. 2930 final int hostId = Integer.parseInt(parser.getAttributeValue( 2931 null, "id"), 16); 2932 2933 String tagAttribute = parser.getAttributeValue(null, "tag"); 2934 final int hostTag = !TextUtils.isEmpty(tagAttribute) 2935 ? Integer.parseInt(tagAttribute, 16) : legacyHostIndex; 2936 2937 host.tag = hostTag; 2938 host.id = new HostId(uid, hostId, pkg); 2939 mHosts.add(host); 2940 } 2941 } else if ("b".equals(tag)) { 2942 String packageName = parser.getAttributeValue(null, "packageName"); 2943 final int uid = getUidForPackage(packageName, userId); 2944 if (uid >= 0) { 2945 Pair<Integer, String> packageId = Pair.create(userId, packageName); 2946 mPackagesWithBindWidgetPermission.add(packageId); 2947 } 2948 } else if ("g".equals(tag)) { 2949 Widget widget = new Widget(); 2950 widget.appWidgetId = Integer.parseInt(parser.getAttributeValue( 2951 null, "id"), 16); 2952 setMinAppWidgetIdLocked(userId, widget.appWidgetId + 1); 2953 2954 // restored ID is allowed to be absent 2955 String restoredIdString = parser.getAttributeValue(null, "rid"); 2956 widget.restoredId = (restoredIdString == null) ? 0 2957 : Integer.parseInt(restoredIdString, 16); 2958 2959 Bundle options = new Bundle(); 2960 String minWidthString = parser.getAttributeValue(null, "min_width"); 2961 if (minWidthString != null) { 2962 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 2963 Integer.parseInt(minWidthString, 16)); 2964 } 2965 String minHeightString = parser.getAttributeValue(null, "min_height"); 2966 if (minHeightString != null) { 2967 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2968 Integer.parseInt(minHeightString, 16)); 2969 } 2970 String maxWidthString = parser.getAttributeValue(null, "max_width"); 2971 if (maxWidthString != null) { 2972 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 2973 Integer.parseInt(maxWidthString, 16)); 2974 } 2975 String maxHeightString = parser.getAttributeValue(null, "max_height"); 2976 if (maxHeightString != null) { 2977 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 2978 Integer.parseInt(maxHeightString, 16)); 2979 } 2980 String categoryString = parser.getAttributeValue(null, "host_category"); 2981 if (categoryString != null) { 2982 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 2983 Integer.parseInt(categoryString, 16)); 2984 } 2985 widget.options = options; 2986 2987 final int hostTag = Integer.parseInt(parser.getAttributeValue( 2988 null, "h"), 16); 2989 String providerString = parser.getAttributeValue(null, "p"); 2990 final int providerTag = (providerString != null) ? Integer.parseInt( 2991 parser.getAttributeValue(null, "p"), 16) : TAG_UNDEFINED; 2992 2993 // We can match widgets with hosts and providers only after hosts 2994 // and providers for all users have been loaded since the widget 2995 // host and provider can be in different user profiles. 2996 LoadedWidgetState loadedWidgets = new LoadedWidgetState(widget, 2997 hostTag, providerTag); 2998 outLoadedWidgets.add(loadedWidgets); 2999 } 3000 } 3001 } while (type != XmlPullParser.END_DOCUMENT); 3002 } catch (NullPointerException 3003 | NumberFormatException 3004 | XmlPullParserException 3005 | IOException 3006 | IndexOutOfBoundsException e) { 3007 Slog.w(TAG, "failed parsing " + e); 3008 return -1; 3009 } 3010 3011 return version; 3012 } 3013 3014 private void performUpgradeLocked(int fromVersion) { 3015 if (fromVersion < CURRENT_VERSION) { 3016 Slog.v(TAG, "Upgrading widget database from " + fromVersion + " to " 3017 + CURRENT_VERSION); 3018 } 3019 3020 int version = fromVersion; 3021 3022 // Update 1: keyguard moved from package "android" to "com.android.keyguard" 3023 if (version == 0) { 3024 HostId oldHostId = new HostId(Process.myUid(), 3025 KEYGUARD_HOST_ID, OLD_KEYGUARD_HOST_PACKAGE); 3026 3027 Host host = lookupHostLocked(oldHostId); 3028 if (host != null) { 3029 final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE, 3030 UserHandle.USER_SYSTEM); 3031 if (uid >= 0) { 3032 host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE); 3033 } 3034 } 3035 3036 version = 1; 3037 } 3038 3039 if (version != CURRENT_VERSION) { 3040 throw new IllegalStateException("Failed to upgrade widget database"); 3041 } 3042 } 3043 3044 private static File getStateFile(int userId) { 3045 return new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME); 3046 } 3047 3048 private static AtomicFile getSavedStateFile(int userId) { 3049 File dir = Environment.getUserSystemDirectory(userId); 3050 File settingsFile = getStateFile(userId); 3051 if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) { 3052 if (!dir.exists()) { 3053 dir.mkdirs(); 3054 } 3055 // Migrate old data 3056 File oldFile = new File("/data/system/" + STATE_FILENAME); 3057 // Method doesn't throw an exception on failure. Ignore any errors 3058 // in moving the file (like non-existence) 3059 oldFile.renameTo(settingsFile); 3060 } 3061 return new AtomicFile(settingsFile); 3062 } 3063 3064 private void onUserStopped(int userId) { 3065 synchronized (mLock) { 3066 boolean crossProfileWidgetsChanged = false; 3067 3068 // Remove widgets that have both host and provider in the user. 3069 final int widgetCount = mWidgets.size(); 3070 for (int i = widgetCount - 1; i >= 0; i--) { 3071 Widget widget = mWidgets.get(i); 3072 3073 final boolean hostInUser = widget.host.getUserId() == userId; 3074 final boolean hasProvider = widget.provider != null; 3075 final boolean providerInUser = hasProvider && widget.provider.getUserId() == userId; 3076 3077 // If both host and provider are in the user, just drop the widgets 3078 // as we do not want to make host callbacks and provider broadcasts 3079 // as the host and the provider will be killed. 3080 if (hostInUser && (!hasProvider || providerInUser)) { 3081 removeWidgetLocked(widget); 3082 widget.host.widgets.remove(widget); 3083 widget.host = null; 3084 if (hasProvider) { 3085 widget.provider.widgets.remove(widget); 3086 widget.provider = null; 3087 } 3088 } 3089 } 3090 3091 // Remove hosts and notify providers in other profiles. 3092 final int hostCount = mHosts.size(); 3093 for (int i = hostCount - 1; i >= 0; i--) { 3094 Host host = mHosts.get(i); 3095 if (host.getUserId() == userId) { 3096 crossProfileWidgetsChanged |= !host.widgets.isEmpty(); 3097 deleteHostLocked(host); 3098 } 3099 } 3100 3101 // Leave the providers present as hosts will show the widgets 3102 // masked while the user is stopped. 3103 3104 // Remove grants for this user. 3105 final int grantCount = mPackagesWithBindWidgetPermission.size(); 3106 for (int i = grantCount - 1; i >= 0; i--) { 3107 Pair<Integer, String> packageId = mPackagesWithBindWidgetPermission.valueAt(i); 3108 if (packageId.first == userId) { 3109 mPackagesWithBindWidgetPermission.removeAt(i); 3110 } 3111 } 3112 3113 // Take a note we no longer have state for this user. 3114 final int userIndex = mLoadedUserIds.indexOfKey(userId); 3115 if (userIndex >= 0) { 3116 mLoadedUserIds.removeAt(userIndex); 3117 } 3118 3119 // Remove the widget id counter. 3120 final int nextIdIndex = mNextAppWidgetIds.indexOfKey(userId); 3121 if (nextIdIndex >= 0) { 3122 mNextAppWidgetIds.removeAt(nextIdIndex); 3123 } 3124 3125 // Save state if removing a profile changed the group state. 3126 // Nothing will be saved if the group parent was removed. 3127 if (crossProfileWidgetsChanged) { 3128 saveGroupStateAsync(userId); 3129 } 3130 } 3131 } 3132 3133 /** 3134 * Updates all providers with the specified package names, and records any providers that were 3135 * pruned. 3136 * 3137 * @return whether any providers were updated 3138 */ 3139 private boolean updateProvidersForPackageLocked(String packageName, int userId, 3140 Set<ProviderId> removedProviders) { 3141 boolean providersUpdated = false; 3142 3143 HashSet<ProviderId> keep = new HashSet<>(); 3144 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 3145 intent.setPackage(packageName); 3146 List<ResolveInfo> broadcastReceivers = queryIntentReceivers(intent, userId); 3147 3148 // add the missing ones and collect which ones to keep 3149 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 3150 for (int i = 0; i < N; i++) { 3151 ResolveInfo ri = broadcastReceivers.get(i); 3152 ActivityInfo ai = ri.activityInfo; 3153 3154 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 3155 continue; 3156 } 3157 3158 if (packageName.equals(ai.packageName)) { 3159 ProviderId providerId = new ProviderId(ai.applicationInfo.uid, 3160 new ComponentName(ai.packageName, ai.name)); 3161 3162 Provider provider = lookupProviderLocked(providerId); 3163 if (provider == null) { 3164 if (addProviderLocked(ri)) { 3165 keep.add(providerId); 3166 providersUpdated = true; 3167 } 3168 } else { 3169 Provider parsed = parseProviderInfoXml(providerId, ri); 3170 if (parsed != null) { 3171 keep.add(providerId); 3172 // Use the new AppWidgetProviderInfo. 3173 provider.info = parsed.info; 3174 // If it's enabled 3175 final int M = provider.widgets.size(); 3176 if (M > 0) { 3177 int[] appWidgetIds = getWidgetIds(provider.widgets); 3178 // Reschedule for the new updatePeriodMillis (don't worry about handling 3179 // it specially if updatePeriodMillis didn't change because we just sent 3180 // an update, and the next one will be updatePeriodMillis from now). 3181 cancelBroadcasts(provider); 3182 registerForBroadcastsLocked(provider, appWidgetIds); 3183 // If it's currently showing, call back with the new 3184 // AppWidgetProviderInfo. 3185 for (int j = 0; j < M; j++) { 3186 Widget widget = provider.widgets.get(j); 3187 widget.views = null; 3188 scheduleNotifyProviderChangedLocked(widget); 3189 } 3190 // Now that we've told the host, push out an update. 3191 sendUpdateIntentLocked(provider, appWidgetIds); 3192 } 3193 } 3194 providersUpdated = true; 3195 } 3196 } 3197 } 3198 3199 // prune the ones we don't want to keep 3200 N = mProviders.size(); 3201 for (int i = N - 1; i >= 0; i--) { 3202 Provider provider = mProviders.get(i); 3203 if (packageName.equals(provider.info.provider.getPackageName()) 3204 && provider.getUserId() == userId 3205 && !keep.contains(provider.id)) { 3206 if (removedProviders != null) { 3207 removedProviders.add(provider.id); 3208 } 3209 deleteProviderLocked(provider); 3210 providersUpdated = true; 3211 } 3212 } 3213 3214 return providersUpdated; 3215 } 3216 3217 // Remove widgets for provider in userId that are hosted in parentUserId 3218 private void removeWidgetsForPackageLocked(String pkgName, int userId, int parentUserId) { 3219 final int N = mProviders.size(); 3220 for (int i = 0; i < N; ++i) { 3221 Provider provider = mProviders.get(i); 3222 if (pkgName.equals(provider.info.provider.getPackageName()) 3223 && provider.getUserId() == userId 3224 && provider.widgets.size() > 0) { 3225 deleteWidgetsLocked(provider, parentUserId); 3226 } 3227 } 3228 } 3229 3230 private boolean removeProvidersForPackageLocked(String pkgName, int userId) { 3231 boolean removed = false; 3232 3233 final int N = mProviders.size(); 3234 for (int i = N - 1; i >= 0; i--) { 3235 Provider provider = mProviders.get(i); 3236 if (pkgName.equals(provider.info.provider.getPackageName()) 3237 && provider.getUserId() == userId) { 3238 deleteProviderLocked(provider); 3239 removed = true; 3240 } 3241 } 3242 return removed; 3243 } 3244 3245 private boolean removeHostsAndProvidersForPackageLocked(String pkgName, int userId) { 3246 boolean removed = removeProvidersForPackageLocked(pkgName, userId); 3247 3248 // Delete the hosts for this package too 3249 // By now, we have removed any AppWidgets that were in any hosts here, 3250 // so we don't need to worry about sending DISABLE broadcasts to them. 3251 final int N = mHosts.size(); 3252 for (int i = N - 1; i >= 0; i--) { 3253 Host host = mHosts.get(i); 3254 if (pkgName.equals(host.id.packageName) 3255 && host.getUserId() == userId) { 3256 deleteHostLocked(host); 3257 removed = true; 3258 } 3259 } 3260 3261 return removed; 3262 } 3263 3264 private String getCanonicalPackageName(String packageName, String className, int userId) { 3265 final long identity = Binder.clearCallingIdentity(); 3266 try { 3267 try { 3268 AppGlobals.getPackageManager().getReceiverInfo(new ComponentName(packageName, 3269 className), 0, userId); 3270 return packageName; 3271 } catch (RemoteException re) { 3272 String[] packageNames = mContext.getPackageManager() 3273 .currentToCanonicalPackageNames(new String[]{packageName}); 3274 if (packageNames != null && packageNames.length > 0) { 3275 return packageNames[0]; 3276 } 3277 } 3278 } finally { 3279 Binder.restoreCallingIdentity(identity); 3280 } 3281 return null; 3282 } 3283 3284 private void sendBroadcastAsUser(Intent intent, UserHandle userHandle) { 3285 final long identity = Binder.clearCallingIdentity(); 3286 try { 3287 mContext.sendBroadcastAsUser(intent, userHandle); 3288 } finally { 3289 Binder.restoreCallingIdentity(identity); 3290 } 3291 } 3292 3293 private void bindService(Intent intent, ServiceConnection connection, 3294 UserHandle userHandle) { 3295 final long token = Binder.clearCallingIdentity(); 3296 try { 3297 mContext.bindServiceAsUser(intent, connection, 3298 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, 3299 userHandle); 3300 } finally { 3301 Binder.restoreCallingIdentity(token); 3302 } 3303 } 3304 3305 private void unbindService(ServiceConnection connection) { 3306 final long token = Binder.clearCallingIdentity(); 3307 try { 3308 mContext.unbindService(connection); 3309 } finally { 3310 Binder.restoreCallingIdentity(token); 3311 } 3312 } 3313 3314 @Override 3315 public void onCrossProfileWidgetProvidersChanged(int userId, List<String> packages) { 3316 final int parentId = mSecurityPolicy.getProfileParent(userId); 3317 // We care only if the white-listed package is in a profile of 3318 // the group parent as only the parent can add widgets from the 3319 // profile and not the other way around. 3320 if (parentId != userId) { 3321 synchronized (mLock) { 3322 boolean providersChanged = false; 3323 3324 ArraySet<String> previousPackages = new ArraySet<String>(); 3325 final int providerCount = mProviders.size(); 3326 for (int i = 0; i < providerCount; ++i) { 3327 Provider provider = mProviders.get(i); 3328 if (provider.getUserId() == userId) { 3329 previousPackages.add(provider.id.componentName.getPackageName()); 3330 } 3331 } 3332 3333 final int packageCount = packages.size(); 3334 for (int i = 0; i < packageCount; i++) { 3335 String packageName = packages.get(i); 3336 previousPackages.remove(packageName); 3337 providersChanged |= updateProvidersForPackageLocked(packageName, 3338 userId, null); 3339 } 3340 3341 // Remove widgets from hosts in parent user for packages not in the whitelist 3342 final int removedCount = previousPackages.size(); 3343 for (int i = 0; i < removedCount; ++i) { 3344 removeWidgetsForPackageLocked(previousPackages.valueAt(i), 3345 userId, parentId); 3346 } 3347 3348 if (providersChanged || removedCount > 0) { 3349 saveGroupStateAsync(userId); 3350 scheduleNotifyGroupHostsForProvidersChangedLocked(userId); 3351 } 3352 } 3353 } 3354 } 3355 3356 private boolean isProfileWithLockedParent(int userId) { 3357 long token = Binder.clearCallingIdentity(); 3358 try { 3359 UserInfo userInfo = mUserManager.getUserInfo(userId); 3360 if (userInfo != null && userInfo.isManagedProfile()) { 3361 UserInfo parentInfo = mUserManager.getProfileParent(userId); 3362 if (parentInfo != null 3363 && !mUserManager.isUserUnlocked(parentInfo.getUserHandle())) { 3364 return true; 3365 } 3366 } 3367 } finally { 3368 Binder.restoreCallingIdentity(token); 3369 } 3370 return false; 3371 } 3372 3373 private boolean isProfileWithUnlockedParent(int userId) { 3374 UserInfo userInfo = mUserManager.getUserInfo(userId); 3375 if (userInfo != null && userInfo.isManagedProfile()) { 3376 UserInfo parentInfo = mUserManager.getProfileParent(userId); 3377 if (parentInfo != null 3378 && mUserManager.isUserUnlocked(parentInfo.getUserHandle())) { 3379 return true; 3380 } 3381 } 3382 return false; 3383 } 3384 3385 private final class CallbackHandler extends Handler { 3386 public static final int MSG_NOTIFY_UPDATE_APP_WIDGET = 1; 3387 public static final int MSG_NOTIFY_PROVIDER_CHANGED = 2; 3388 public static final int MSG_NOTIFY_PROVIDERS_CHANGED = 3; 3389 public static final int MSG_NOTIFY_VIEW_DATA_CHANGED = 4; 3390 3391 public CallbackHandler(Looper looper) { 3392 super(looper, null, false); 3393 } 3394 3395 @Override 3396 public void handleMessage(Message message) { 3397 switch (message.what) { 3398 case MSG_NOTIFY_UPDATE_APP_WIDGET: { 3399 SomeArgs args = (SomeArgs) message.obj; 3400 Host host = (Host) args.arg1; 3401 IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2; 3402 RemoteViews views = (RemoteViews) args.arg3; 3403 final int appWidgetId = args.argi1; 3404 args.recycle(); 3405 3406 handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views); 3407 } break; 3408 3409 case MSG_NOTIFY_PROVIDER_CHANGED: { 3410 SomeArgs args = (SomeArgs) message.obj; 3411 Host host = (Host) args.arg1; 3412 IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2; 3413 AppWidgetProviderInfo info = (AppWidgetProviderInfo)args.arg3; 3414 final int appWidgetId = args.argi1; 3415 args.recycle(); 3416 3417 handleNotifyProviderChanged(host, callbacks, appWidgetId, info); 3418 } break; 3419 3420 case MSG_NOTIFY_PROVIDERS_CHANGED: { 3421 SomeArgs args = (SomeArgs) message.obj; 3422 Host host = (Host) args.arg1; 3423 IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2; 3424 args.recycle(); 3425 3426 handleNotifyProvidersChanged(host, callbacks); 3427 } break; 3428 3429 case MSG_NOTIFY_VIEW_DATA_CHANGED: { 3430 SomeArgs args = (SomeArgs) message.obj; 3431 Host host = (Host) args.arg1; 3432 IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2; 3433 final int appWidgetId = args.argi1; 3434 final int viewId = args.argi2; 3435 args.recycle(); 3436 3437 handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId); 3438 } break; 3439 } 3440 } 3441 } 3442 3443 private final class SecurityPolicy { 3444 3445 public boolean isEnabledGroupProfile(int profileId) { 3446 final int parentId = UserHandle.getCallingUserId(); 3447 return isParentOrProfile(parentId, profileId) && isProfileEnabled(profileId); 3448 } 3449 3450 public int[] getEnabledGroupProfileIds(int userId) { 3451 final int parentId = getGroupParent(userId); 3452 3453 final List<UserInfo> profiles; 3454 final long identity = Binder.clearCallingIdentity(); 3455 try { 3456 profiles = mUserManager.getProfiles(parentId); 3457 } finally { 3458 Binder.restoreCallingIdentity(identity); 3459 } 3460 3461 int enabledProfileCount = 0; 3462 final int profileCount = profiles.size(); 3463 for (int i = 0; i < profileCount; i++) { 3464 if (profiles.get(i).isEnabled()) { 3465 enabledProfileCount++; 3466 } 3467 } 3468 3469 int enabledProfileIndex = 0; 3470 final int[] profileIds = new int[enabledProfileCount]; 3471 for (int i = 0; i < profileCount; i++) { 3472 UserInfo profile = profiles.get(i); 3473 if (profile.isEnabled()) { 3474 profileIds[enabledProfileIndex] = profile.getUserHandle().getIdentifier(); 3475 enabledProfileIndex++; 3476 } 3477 } 3478 3479 return profileIds; 3480 } 3481 3482 public void enforceServiceExistsAndRequiresBindRemoteViewsPermission( 3483 ComponentName componentName, int userId) { 3484 final long identity = Binder.clearCallingIdentity(); 3485 try { 3486 ServiceInfo serviceInfo = mPackageManager.getServiceInfo(componentName, 3487 PackageManager.GET_PERMISSIONS, userId); 3488 if (serviceInfo == null) { 3489 throw new SecurityException("Service " + componentName 3490 + " not installed for user " + userId); 3491 } 3492 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(serviceInfo.permission)) { 3493 throw new SecurityException("Service " + componentName 3494 + " in user " + userId + "does not require " 3495 + android.Manifest.permission.BIND_REMOTEVIEWS); 3496 } 3497 } catch (RemoteException re) { 3498 // Local call - shouldn't happen. 3499 } finally { 3500 Binder.restoreCallingIdentity(identity); 3501 } 3502 } 3503 3504 public void enforceModifyAppWidgetBindPermissions(String packageName) { 3505 mContext.enforceCallingPermission( 3506 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS, 3507 "hasBindAppWidgetPermission packageName=" + packageName); 3508 } 3509 3510 public void enforceCallFromPackage(String packageName) { 3511 mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName); 3512 } 3513 3514 public boolean hasCallerBindPermissionOrBindWhiteListedLocked(String packageName) { 3515 try { 3516 mContext.enforceCallingOrSelfPermission( 3517 android.Manifest.permission.BIND_APPWIDGET, null); 3518 } catch (SecurityException se) { 3519 if (!isCallerBindAppWidgetWhiteListedLocked(packageName)) { 3520 return false; 3521 } 3522 } 3523 return true; 3524 } 3525 3526 private boolean isCallerBindAppWidgetWhiteListedLocked(String packageName) { 3527 final int userId = UserHandle.getCallingUserId(); 3528 final int packageUid = getUidForPackage(packageName, userId); 3529 if (packageUid < 0) { 3530 throw new IllegalArgumentException("No package " + packageName 3531 + " for user " + userId); 3532 } 3533 synchronized (mLock) { 3534 ensureGroupStateLoadedLocked(userId); 3535 3536 Pair<Integer, String> packageId = Pair.create(userId, packageName); 3537 if (mPackagesWithBindWidgetPermission.contains(packageId)) { 3538 return true; 3539 } 3540 } 3541 3542 return false; 3543 } 3544 3545 public boolean canAccessAppWidget(Widget widget, int uid, String packageName) { 3546 if (isHostInPackageForUid(widget.host, uid, packageName)) { 3547 // Apps hosting the AppWidget have access to it. 3548 return true; 3549 } 3550 if (isProviderInPackageForUid(widget.provider, uid, packageName)) { 3551 // Apps providing the AppWidget have access to it. 3552 return true; 3553 } 3554 if (isHostAccessingProvider(widget.host, widget.provider, uid, packageName)) { 3555 // Apps hosting the AppWidget get to bind to a remote view service in the provider. 3556 return true; 3557 } 3558 final int userId = UserHandle.getUserId(uid); 3559 if ((widget.host.getUserId() == userId || (widget.provider != null 3560 && widget.provider.getUserId() == userId)) 3561 && mContext.checkCallingPermission(android.Manifest.permission.BIND_APPWIDGET) 3562 == PackageManager.PERMISSION_GRANTED) { 3563 // Apps that run in the same user as either the host or the provider and 3564 // have the bind widget permission have access to the widget. 3565 return true; 3566 } 3567 return false; 3568 } 3569 3570 private boolean isParentOrProfile(int parentId, int profileId) { 3571 if (parentId == profileId) { 3572 return true; 3573 } 3574 return getProfileParent(profileId) == parentId; 3575 } 3576 3577 public boolean isProviderInCallerOrInProfileAndWhitelListed(String packageName, 3578 int profileId) { 3579 final int callerId = UserHandle.getCallingUserId(); 3580 if (profileId == callerId) { 3581 return true; 3582 } 3583 final int parentId = getProfileParent(profileId); 3584 if (parentId != callerId) { 3585 return false; 3586 } 3587 return isProviderWhiteListed(packageName, profileId); 3588 } 3589 3590 public boolean isProviderWhiteListed(String packageName, int profileId) { 3591 // If the policy manager is not available on the device we deny it all. 3592 if (mDevicePolicyManagerInternal == null) { 3593 return false; 3594 } 3595 3596 List<String> crossProfilePackages = mDevicePolicyManagerInternal 3597 .getCrossProfileWidgetProviders(profileId); 3598 3599 return crossProfilePackages.contains(packageName); 3600 } 3601 3602 public int getProfileParent(int profileId) { 3603 final long identity = Binder.clearCallingIdentity(); 3604 try { 3605 UserInfo parent = mUserManager.getProfileParent(profileId); 3606 if (parent != null) { 3607 return parent.getUserHandle().getIdentifier(); 3608 } 3609 } finally { 3610 Binder.restoreCallingIdentity(identity); 3611 } 3612 return UNKNOWN_USER_ID; 3613 } 3614 3615 public int getGroupParent(int profileId) { 3616 final int parentId = mSecurityPolicy.getProfileParent(profileId); 3617 return (parentId != UNKNOWN_USER_ID) ? parentId : profileId; 3618 } 3619 3620 public boolean isHostInPackageForUid(Host host, int uid, String packageName) { 3621 return host.id.uid == uid && host.id.packageName.equals(packageName); 3622 } 3623 3624 public boolean isProviderInPackageForUid(Provider provider, int uid, 3625 String packageName) { 3626 // Packages providing the AppWidget have access to it. 3627 return provider != null && provider.id.uid == uid 3628 && provider.id.componentName.getPackageName().equals(packageName); 3629 } 3630 3631 public boolean isHostAccessingProvider(Host host, Provider provider, int uid, 3632 String packageName) { 3633 // The host creates a package context to bind to remote views service in the provider. 3634 return host.id.uid == uid && provider != null 3635 && provider.id.componentName.getPackageName().equals(packageName); 3636 } 3637 3638 private boolean isProfileEnabled(int profileId) { 3639 final long identity = Binder.clearCallingIdentity(); 3640 try { 3641 UserInfo userInfo = mUserManager.getUserInfo(profileId); 3642 if (userInfo == null || !userInfo.isEnabled()) { 3643 return false; 3644 } 3645 } finally { 3646 Binder.restoreCallingIdentity(identity); 3647 } 3648 return true; 3649 } 3650 } 3651 3652 private static final class Provider { 3653 ProviderId id; 3654 AppWidgetProviderInfo info; 3655 ArrayList<Widget> widgets = new ArrayList<>(); 3656 PendingIntent broadcast; 3657 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it 3658 3659 boolean maskedByLockedProfile; 3660 boolean maskedByQuietProfile; 3661 boolean maskedBySuspendedPackage; 3662 3663 int tag = TAG_UNDEFINED; // for use while saving state (the index) 3664 3665 public int getUserId() { 3666 return UserHandle.getUserId(id.uid); 3667 } 3668 3669 public boolean isInPackageForUser(String packageName, int userId) { 3670 return getUserId() == userId 3671 && id.componentName.getPackageName().equals(packageName); 3672 } 3673 3674 // is there an instance of this provider hosted by the given app? 3675 public boolean hostedByPackageForUser(String packageName, int userId) { 3676 final int N = widgets.size(); 3677 for (int i = 0; i < N; i++) { 3678 Widget widget = widgets.get(i); 3679 if (packageName.equals(widget.host.id.packageName) 3680 && widget.host.getUserId() == userId) { 3681 return true; 3682 } 3683 } 3684 return false; 3685 } 3686 3687 @Override 3688 public String toString() { 3689 return "Provider{" + id + (zombie ? " Z" : "") + '}'; 3690 } 3691 3692 // returns true if it's different from previous state. 3693 public boolean setMaskedByQuietProfileLocked(boolean masked) { 3694 boolean oldState = maskedByQuietProfile; 3695 maskedByQuietProfile = masked; 3696 return masked != oldState; 3697 } 3698 3699 // returns true if it's different from previous state. 3700 public boolean setMaskedByLockedProfileLocked(boolean masked) { 3701 boolean oldState = maskedByLockedProfile; 3702 maskedByLockedProfile = masked; 3703 return masked != oldState; 3704 } 3705 3706 // returns true if it's different from previous state. 3707 public boolean setMaskedBySuspendedPackageLocked(boolean masked) { 3708 boolean oldState = maskedBySuspendedPackage; 3709 maskedBySuspendedPackage = masked; 3710 return masked != oldState; 3711 } 3712 3713 public boolean isMaskedLocked() { 3714 return maskedByQuietProfile || maskedByLockedProfile || maskedBySuspendedPackage; 3715 } 3716 } 3717 3718 private static final class ProviderId { 3719 final int uid; 3720 final ComponentName componentName; 3721 3722 private ProviderId(int uid, ComponentName componentName) { 3723 this.uid = uid; 3724 this.componentName = componentName; 3725 } 3726 3727 @Override 3728 public boolean equals(Object obj) { 3729 if (this == obj) { 3730 return true; 3731 } 3732 if (obj == null) { 3733 return false; 3734 } 3735 if (getClass() != obj.getClass()) { 3736 return false; 3737 } 3738 ProviderId other = (ProviderId) obj; 3739 if (uid != other.uid) { 3740 return false; 3741 } 3742 if (componentName == null) { 3743 if (other.componentName != null) { 3744 return false; 3745 } 3746 } else if (!componentName.equals(other.componentName)) { 3747 return false; 3748 } 3749 return true; 3750 } 3751 3752 @Override 3753 public int hashCode() { 3754 int result = uid; 3755 result = 31 * result + ((componentName != null) 3756 ? componentName.hashCode() : 0); 3757 return result; 3758 } 3759 3760 @Override 3761 public String toString() { 3762 return "ProviderId{user:" + UserHandle.getUserId(uid) + ", app:" 3763 + UserHandle.getAppId(uid) + ", cmp:" + componentName + '}'; 3764 } 3765 } 3766 3767 private static final class Host { 3768 HostId id; 3769 ArrayList<Widget> widgets = new ArrayList<>(); 3770 IAppWidgetHost callbacks; 3771 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it 3772 3773 int tag = TAG_UNDEFINED; // for use while saving state (the index) 3774 3775 public int getUserId() { 3776 return UserHandle.getUserId(id.uid); 3777 } 3778 3779 public boolean isInPackageForUser(String packageName, int userId) { 3780 return getUserId() == userId && id.packageName.equals(packageName); 3781 } 3782 3783 private boolean hostsPackageForUser(String pkg, int userId) { 3784 final int N = widgets.size(); 3785 for (int i = 0; i < N; i++) { 3786 Provider provider = widgets.get(i).provider; 3787 if (provider != null && provider.getUserId() == userId && provider.info != null 3788 && pkg.equals(provider.info.provider.getPackageName())) { 3789 return true; 3790 } 3791 } 3792 return false; 3793 } 3794 3795 @Override 3796 public String toString() { 3797 return "Host{" + id + (zombie ? " Z" : "") + '}'; 3798 } 3799 } 3800 3801 private static final class HostId { 3802 final int uid; 3803 final int hostId; 3804 final String packageName; 3805 3806 public HostId(int uid, int hostId, String packageName) { 3807 this.uid = uid; 3808 this.hostId = hostId; 3809 this.packageName = packageName; 3810 } 3811 3812 @Override 3813 public boolean equals(Object obj) { 3814 if (this == obj) { 3815 return true; 3816 } 3817 if (obj == null) { 3818 return false; 3819 } 3820 if (getClass() != obj.getClass()) { 3821 return false; 3822 } 3823 HostId other = (HostId) obj; 3824 if (uid != other.uid) { 3825 return false; 3826 } 3827 if (hostId != other.hostId) { 3828 return false; 3829 } 3830 if (packageName == null) { 3831 if (other.packageName != null) { 3832 return false; 3833 } 3834 } else if (!packageName.equals(other.packageName)) { 3835 return false; 3836 } 3837 return true; 3838 } 3839 3840 @Override 3841 public int hashCode() { 3842 int result = uid; 3843 result = 31 * result + hostId; 3844 result = 31 * result + ((packageName != null) 3845 ? packageName.hashCode() : 0); 3846 return result; 3847 } 3848 3849 @Override 3850 public String toString() { 3851 return "HostId{user:" + UserHandle.getUserId(uid) + ", app:" 3852 + UserHandle.getAppId(uid) + ", hostId:" + hostId 3853 + ", pkg:" + packageName + '}'; 3854 } 3855 } 3856 3857 private static final class Widget { 3858 int appWidgetId; 3859 int restoredId; // tracking & remapping any restored state 3860 Provider provider; 3861 RemoteViews views; 3862 RemoteViews maskedViews; 3863 Bundle options; 3864 Host host; 3865 3866 @Override 3867 public String toString() { 3868 return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}'; 3869 } 3870 3871 private boolean replaceWithMaskedViewsLocked(RemoteViews views) { 3872 maskedViews = views; 3873 return true; 3874 } 3875 3876 private boolean clearMaskedViewsLocked() { 3877 if (maskedViews != null) { 3878 maskedViews = null; 3879 return true; 3880 } else { 3881 return false; 3882 } 3883 } 3884 3885 public RemoteViews getEffectiveViewsLocked() { 3886 return maskedViews != null ? maskedViews : views; 3887 } 3888 } 3889 3890 /** 3891 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This 3892 * needs to be a static inner class since a reference to the ServiceConnection is held globally 3893 * and may lead us to leak AppWidgetService instances (if there were more than one). 3894 */ 3895 private static final class ServiceConnectionProxy implements ServiceConnection { 3896 private final IRemoteViewsAdapterConnection mConnectionCb; 3897 3898 ServiceConnectionProxy(IBinder connectionCb) { 3899 mConnectionCb = IRemoteViewsAdapterConnection.Stub 3900 .asInterface(connectionCb); 3901 } 3902 3903 public void onServiceConnected(ComponentName name, IBinder service) { 3904 try { 3905 mConnectionCb.onServiceConnected(service); 3906 } catch (RemoteException re) { 3907 Slog.e(TAG, "Error passing service interface", re); 3908 } 3909 } 3910 3911 public void onServiceDisconnected(ComponentName name) { 3912 disconnect(); 3913 } 3914 3915 public void disconnect() { 3916 try { 3917 mConnectionCb.onServiceDisconnected(); 3918 } catch (RemoteException re) { 3919 Slog.e(TAG, "Error clearing service interface", re); 3920 } 3921 } 3922 } 3923 3924 private class LoadedWidgetState { 3925 final Widget widget; 3926 final int hostTag; 3927 final int providerTag; 3928 3929 public LoadedWidgetState(Widget widget, int hostTag, int providerTag) { 3930 this.widget = widget; 3931 this.hostTag = hostTag; 3932 this.providerTag = providerTag; 3933 } 3934 } 3935 3936 private final class SaveStateRunnable implements Runnable { 3937 final int mUserId; 3938 3939 public SaveStateRunnable(int userId) { 3940 mUserId = userId; 3941 } 3942 3943 @Override 3944 public void run() { 3945 synchronized (mLock) { 3946 ensureGroupStateLoadedLocked(mUserId); 3947 saveStateLocked(mUserId); 3948 } 3949 } 3950 } 3951 3952 /** 3953 * This class encapsulates the backup and restore logic for a user group state. 3954 */ 3955 private final class BackupRestoreController { 3956 private static final String TAG = "BackupRestoreController"; 3957 3958 private static final boolean DEBUG = true; 3959 3960 // Version of backed-up widget state. 3961 private static final int WIDGET_STATE_VERSION = 2; 3962 3963 // We need to make sure to wipe the pre-restore widget state only once for 3964 // a given package. Keep track of what we've done so far here; the list is 3965 // cleared at the start of every system restore pass, but preserved through 3966 // any install-time restore operations. 3967 private final HashSet<String> mPrunedApps = new HashSet<>(); 3968 3969 private final HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider = 3970 new HashMap<>(); 3971 private final HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost = 3972 new HashMap<>(); 3973 3974 public List<String> getWidgetParticipants(int userId) { 3975 if (DEBUG) { 3976 Slog.i(TAG, "Getting widget participants for user: " + userId); 3977 } 3978 3979 HashSet<String> packages = new HashSet<>(); 3980 synchronized (mLock) { 3981 final int N = mWidgets.size(); 3982 for (int i = 0; i < N; i++) { 3983 Widget widget = mWidgets.get(i); 3984 3985 // Skip cross-user widgets. 3986 if (!isProviderAndHostInUser(widget, userId)) { 3987 continue; 3988 } 3989 3990 packages.add(widget.host.id.packageName); 3991 Provider provider = widget.provider; 3992 if (provider != null) { 3993 packages.add(provider.id.componentName.getPackageName()); 3994 } 3995 } 3996 } 3997 return new ArrayList<>(packages); 3998 } 3999 4000 public byte[] getWidgetState(String backedupPackage, int userId) { 4001 if (DEBUG) { 4002 Slog.i(TAG, "Getting widget state for user: " + userId); 4003 } 4004 4005 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 4006 synchronized (mLock) { 4007 // Preflight: if this app neither hosts nor provides any live widgets 4008 // we have no work to do. 4009 if (!packageNeedsWidgetBackupLocked(backedupPackage, userId)) { 4010 return null; 4011 } 4012 4013 try { 4014 XmlSerializer out = new FastXmlSerializer(); 4015 out.setOutput(stream, StandardCharsets.UTF_8.name()); 4016 out.startDocument(null, true); 4017 out.startTag(null, "ws"); // widget state 4018 out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION)); 4019 out.attribute(null, "pkg", backedupPackage); 4020 4021 // Remember all the providers that are currently hosted or published 4022 // by this package: that is, all of the entities related to this app 4023 // which will need to be told about id remapping. 4024 int index = 0; 4025 int N = mProviders.size(); 4026 for (int i = 0; i < N; i++) { 4027 Provider provider = mProviders.get(i); 4028 4029 if (!provider.widgets.isEmpty() 4030 && (provider.isInPackageForUser(backedupPackage, userId) 4031 || provider.hostedByPackageForUser(backedupPackage, userId))) { 4032 provider.tag = index; 4033 serializeProvider(out, provider); 4034 index++; 4035 } 4036 } 4037 4038 N = mHosts.size(); 4039 index = 0; 4040 for (int i = 0; i < N; i++) { 4041 Host host = mHosts.get(i); 4042 4043 if (!host.widgets.isEmpty() 4044 && (host.isInPackageForUser(backedupPackage, userId) 4045 || host.hostsPackageForUser(backedupPackage, userId))) { 4046 host.tag = index; 4047 serializeHost(out, host); 4048 index++; 4049 } 4050 } 4051 4052 // All widget instances involving this package, 4053 // either as host or as provider 4054 N = mWidgets.size(); 4055 for (int i = 0; i < N; i++) { 4056 Widget widget = mWidgets.get(i); 4057 4058 Provider provider = widget.provider; 4059 if (widget.host.isInPackageForUser(backedupPackage, userId) 4060 || (provider != null 4061 && provider.isInPackageForUser(backedupPackage, userId))) { 4062 serializeAppWidget(out, widget); 4063 } 4064 } 4065 4066 out.endTag(null, "ws"); 4067 out.endDocument(); 4068 } catch (IOException e) { 4069 Slog.w(TAG, "Unable to save widget state for " + backedupPackage); 4070 return null; 4071 } 4072 } 4073 4074 return stream.toByteArray(); 4075 } 4076 4077 public void restoreStarting(int userId) { 4078 if (DEBUG) { 4079 Slog.i(TAG, "Restore starting for user: " + userId); 4080 } 4081 4082 synchronized (mLock) { 4083 // We're starting a new "system" restore operation, so any widget restore 4084 // state that we see from here on is intended to replace the current 4085 // widget configuration of any/all of the affected apps. 4086 mPrunedApps.clear(); 4087 mUpdatesByProvider.clear(); 4088 mUpdatesByHost.clear(); 4089 } 4090 } 4091 4092 public void restoreWidgetState(String packageName, byte[] restoredState, int userId) { 4093 if (DEBUG) { 4094 Slog.i(TAG, "Restoring widget state for user:" + userId 4095 + " package: " + packageName); 4096 } 4097 4098 ByteArrayInputStream stream = new ByteArrayInputStream(restoredState); 4099 try { 4100 // Providers mentioned in the widget dataset by ordinal 4101 ArrayList<Provider> restoredProviders = new ArrayList<>(); 4102 4103 // Hosts mentioned in the widget dataset by ordinal 4104 ArrayList<Host> restoredHosts = new ArrayList<>(); 4105 4106 XmlPullParser parser = Xml.newPullParser(); 4107 parser.setInput(stream, StandardCharsets.UTF_8.name()); 4108 4109 synchronized (mLock) { 4110 int type; 4111 do { 4112 type = parser.next(); 4113 if (type == XmlPullParser.START_TAG) { 4114 final String tag = parser.getName(); 4115 if ("ws".equals(tag)) { 4116 String version = parser.getAttributeValue(null, "version"); 4117 4118 final int versionNumber = Integer.parseInt(version); 4119 if (versionNumber > WIDGET_STATE_VERSION) { 4120 Slog.w(TAG, "Unable to process state version " + version); 4121 return; 4122 } 4123 4124 // TODO: fix up w.r.t. canonical vs current package names 4125 String pkg = parser.getAttributeValue(null, "pkg"); 4126 if (!packageName.equals(pkg)) { 4127 Slog.w(TAG, "Package mismatch in ws"); 4128 return; 4129 } 4130 } else if ("p".equals(tag)) { 4131 String pkg = parser.getAttributeValue(null, "pkg"); 4132 String cl = parser.getAttributeValue(null, "cl"); 4133 4134 // hostedProviders index will match 'p' attribute in widget's 4135 // entry in the xml file being restored 4136 // If there's no live entry for this provider, add an inactive one 4137 // so that widget IDs referring to them can be properly allocated 4138 4139 // Backup and resotre only for the parent profile. 4140 ComponentName componentName = new ComponentName(pkg, cl); 4141 4142 Provider p = findProviderLocked(componentName, userId); 4143 if (p == null) { 4144 p = new Provider(); 4145 p.id = new ProviderId(UNKNOWN_UID, componentName); 4146 p.info = new AppWidgetProviderInfo(); 4147 p.info.provider = componentName; 4148 p.zombie = true; 4149 mProviders.add(p); 4150 } 4151 if (DEBUG) { 4152 Slog.i(TAG, " provider " + p.id); 4153 } 4154 restoredProviders.add(p); 4155 } else if ("h".equals(tag)) { 4156 // The host app may not yet exist on the device. If it's here we 4157 // just use the existing Host entry, otherwise we create a 4158 // placeholder whose uid will be fixed up at PACKAGE_ADDED time. 4159 String pkg = parser.getAttributeValue(null, "pkg"); 4160 4161 final int uid = getUidForPackage(pkg, userId); 4162 final int hostId = Integer.parseInt( 4163 parser.getAttributeValue(null, "id"), 16); 4164 4165 HostId id = new HostId(uid, hostId, pkg); 4166 Host h = lookupOrAddHostLocked(id); 4167 restoredHosts.add(h); 4168 4169 if (DEBUG) { 4170 Slog.i(TAG, " host[" + restoredHosts.size() 4171 + "]: {" + h.id + "}"); 4172 } 4173 } else if ("g".equals(tag)) { 4174 int restoredId = Integer.parseInt( 4175 parser.getAttributeValue(null, "id"), 16); 4176 int hostIndex = Integer.parseInt( 4177 parser.getAttributeValue(null, "h"), 16); 4178 Host host = restoredHosts.get(hostIndex); 4179 Provider p = null; 4180 String prov = parser.getAttributeValue(null, "p"); 4181 if (prov != null) { 4182 // could have been null if the app had allocated an id 4183 // but not yet established a binding under that id 4184 int which = Integer.parseInt(prov, 16); 4185 p = restoredProviders.get(which); 4186 } 4187 4188 // We'll be restoring widget state for both the host and 4189 // provider sides of this widget ID, so make sure we are 4190 // beginning from a clean slate on both fronts. 4191 pruneWidgetStateLocked(host.id.packageName, userId); 4192 if (p != null) { 4193 pruneWidgetStateLocked(p.id.componentName.getPackageName(), 4194 userId); 4195 } 4196 4197 // Have we heard about this ancestral widget instance before? 4198 Widget id = findRestoredWidgetLocked(restoredId, host, p); 4199 if (id == null) { 4200 id = new Widget(); 4201 id.appWidgetId = incrementAndGetAppWidgetIdLocked(userId); 4202 id.restoredId = restoredId; 4203 id.options = parseWidgetIdOptions(parser); 4204 id.host = host; 4205 id.host.widgets.add(id); 4206 id.provider = p; 4207 if (id.provider != null) { 4208 id.provider.widgets.add(id); 4209 } 4210 if (DEBUG) { 4211 Slog.i(TAG, "New restored id " + restoredId 4212 + " now " + id); 4213 } 4214 addWidgetLocked(id); 4215 } 4216 if (id.provider.info != null) { 4217 stashProviderRestoreUpdateLocked(id.provider, 4218 restoredId, id.appWidgetId); 4219 } else { 4220 Slog.w(TAG, "Missing provider for restored widget " + id); 4221 } 4222 stashHostRestoreUpdateLocked(id.host, restoredId, id.appWidgetId); 4223 4224 if (DEBUG) { 4225 Slog.i(TAG, " instance: " + restoredId 4226 + " -> " + id.appWidgetId 4227 + " :: p=" + id.provider); 4228 } 4229 } 4230 } 4231 } while (type != XmlPullParser.END_DOCUMENT); 4232 4233 // We've updated our own bookkeeping. We'll need to notify the hosts and 4234 // providers about the changes, but we can't do that yet because the restore 4235 // target is not necessarily fully live at this moment. Set aside the 4236 // information for now; the backup manager will call us once more at the 4237 // end of the process when all of the targets are in a known state, and we 4238 // will update at that point. 4239 } 4240 } catch (XmlPullParserException | IOException e) { 4241 Slog.w(TAG, "Unable to restore widget state for " + packageName); 4242 } finally { 4243 saveGroupStateAsync(userId); 4244 } 4245 } 4246 4247 // Called once following the conclusion of a restore operation. This is when we 4248 // send out updates to apps involved in widget-state restore telling them about 4249 // the new widget ID space. 4250 public void restoreFinished(int userId) { 4251 if (DEBUG) { 4252 Slog.i(TAG, "restoreFinished for " + userId); 4253 } 4254 4255 final UserHandle userHandle = new UserHandle(userId); 4256 synchronized (mLock) { 4257 // Build the providers' broadcasts and send them off 4258 Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries 4259 = mUpdatesByProvider.entrySet(); 4260 for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) { 4261 // For each provider there's a list of affected IDs 4262 Provider provider = e.getKey(); 4263 ArrayList<RestoreUpdateRecord> updates = e.getValue(); 4264 final int pending = countPendingUpdates(updates); 4265 if (DEBUG) { 4266 Slog.i(TAG, "Provider " + provider + " pending: " + pending); 4267 } 4268 if (pending > 0) { 4269 int[] oldIds = new int[pending]; 4270 int[] newIds = new int[pending]; 4271 final int N = updates.size(); 4272 int nextPending = 0; 4273 for (int i = 0; i < N; i++) { 4274 RestoreUpdateRecord r = updates.get(i); 4275 if (!r.notified) { 4276 r.notified = true; 4277 oldIds[nextPending] = r.oldId; 4278 newIds[nextPending] = r.newId; 4279 nextPending++; 4280 if (DEBUG) { 4281 Slog.i(TAG, " " + r.oldId + " => " + r.newId); 4282 } 4283 } 4284 } 4285 sendWidgetRestoreBroadcastLocked( 4286 AppWidgetManager.ACTION_APPWIDGET_RESTORED, 4287 provider, null, oldIds, newIds, userHandle); 4288 } 4289 } 4290 4291 // same thing per host 4292 Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries 4293 = mUpdatesByHost.entrySet(); 4294 for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) { 4295 Host host = e.getKey(); 4296 if (host.id.uid != UNKNOWN_UID) { 4297 ArrayList<RestoreUpdateRecord> updates = e.getValue(); 4298 final int pending = countPendingUpdates(updates); 4299 if (DEBUG) { 4300 Slog.i(TAG, "Host " + host + " pending: " + pending); 4301 } 4302 if (pending > 0) { 4303 int[] oldIds = new int[pending]; 4304 int[] newIds = new int[pending]; 4305 final int N = updates.size(); 4306 int nextPending = 0; 4307 for (int i = 0; i < N; i++) { 4308 RestoreUpdateRecord r = updates.get(i); 4309 if (!r.notified) { 4310 r.notified = true; 4311 oldIds[nextPending] = r.oldId; 4312 newIds[nextPending] = r.newId; 4313 nextPending++; 4314 if (DEBUG) { 4315 Slog.i(TAG, " " + r.oldId + " => " + r.newId); 4316 } 4317 } 4318 } 4319 sendWidgetRestoreBroadcastLocked( 4320 AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED, 4321 null, host, oldIds, newIds, userHandle); 4322 } 4323 } 4324 } 4325 } 4326 } 4327 4328 private Provider findProviderLocked(ComponentName componentName, int userId) { 4329 final int providerCount = mProviders.size(); 4330 for (int i = 0; i < providerCount; i++) { 4331 Provider provider = mProviders.get(i); 4332 if (provider.getUserId() == userId 4333 && provider.id.componentName.equals(componentName)) { 4334 return provider; 4335 } 4336 } 4337 return null; 4338 } 4339 4340 private Widget findRestoredWidgetLocked(int restoredId, Host host, Provider p) { 4341 if (DEBUG) { 4342 Slog.i(TAG, "Find restored widget: id=" + restoredId 4343 + " host=" + host + " provider=" + p); 4344 } 4345 4346 if (p == null || host == null) { 4347 return null; 4348 } 4349 4350 final int N = mWidgets.size(); 4351 for (int i = 0; i < N; i++) { 4352 Widget widget = mWidgets.get(i); 4353 if (widget.restoredId == restoredId 4354 && widget.host.id.equals(host.id) 4355 && widget.provider.id.equals(p.id)) { 4356 if (DEBUG) { 4357 Slog.i(TAG, " Found at " + i + " : " + widget); 4358 } 4359 return widget; 4360 } 4361 } 4362 return null; 4363 } 4364 4365 private boolean packageNeedsWidgetBackupLocked(String packageName, int userId) { 4366 int N = mWidgets.size(); 4367 for (int i = 0; i < N; i++) { 4368 Widget widget = mWidgets.get(i); 4369 4370 // Skip cross-user widgets. 4371 if (!isProviderAndHostInUser(widget, userId)) { 4372 continue; 4373 } 4374 4375 if (widget.host.isInPackageForUser(packageName, userId)) { 4376 // this package is hosting widgets, so it knows widget IDs. 4377 return true; 4378 } 4379 4380 Provider provider = widget.provider; 4381 if (provider != null && provider.isInPackageForUser(packageName, userId)) { 4382 // someone is hosting this app's widgets, so it knows widget IDs. 4383 return true; 4384 } 4385 } 4386 return false; 4387 } 4388 4389 private void stashProviderRestoreUpdateLocked(Provider provider, int oldId, int newId) { 4390 ArrayList<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider); 4391 if (r == null) { 4392 r = new ArrayList<>(); 4393 mUpdatesByProvider.put(provider, r); 4394 } else { 4395 // don't duplicate 4396 if (alreadyStashed(r, oldId, newId)) { 4397 if (DEBUG) { 4398 Slog.i(TAG, "ID remap " + oldId + " -> " + newId 4399 + " already stashed for " + provider); 4400 } 4401 return; 4402 } 4403 } 4404 r.add(new RestoreUpdateRecord(oldId, newId)); 4405 } 4406 4407 private boolean alreadyStashed(ArrayList<RestoreUpdateRecord> stash, 4408 final int oldId, final int newId) { 4409 final int N = stash.size(); 4410 for (int i = 0; i < N; i++) { 4411 RestoreUpdateRecord r = stash.get(i); 4412 if (r.oldId == oldId && r.newId == newId) { 4413 return true; 4414 } 4415 } 4416 return false; 4417 } 4418 4419 private void stashHostRestoreUpdateLocked(Host host, int oldId, int newId) { 4420 ArrayList<RestoreUpdateRecord> r = mUpdatesByHost.get(host); 4421 if (r == null) { 4422 r = new ArrayList<>(); 4423 mUpdatesByHost.put(host, r); 4424 } else { 4425 if (alreadyStashed(r, oldId, newId)) { 4426 if (DEBUG) { 4427 Slog.i(TAG, "ID remap " + oldId + " -> " + newId 4428 + " already stashed for " + host); 4429 } 4430 return; 4431 } 4432 } 4433 r.add(new RestoreUpdateRecord(oldId, newId)); 4434 } 4435 4436 private void sendWidgetRestoreBroadcastLocked(String action, Provider provider, 4437 Host host, int[] oldIds, int[] newIds, UserHandle userHandle) { 4438 Intent intent = new Intent(action); 4439 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds); 4440 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds); 4441 if (provider != null) { 4442 intent.setComponent(provider.info.provider); 4443 sendBroadcastAsUser(intent, userHandle); 4444 } 4445 if (host != null) { 4446 intent.setComponent(null); 4447 intent.setPackage(host.id.packageName); 4448 intent.putExtra(AppWidgetManager.EXTRA_HOST_ID, host.id.hostId); 4449 sendBroadcastAsUser(intent, userHandle); 4450 } 4451 } 4452 4453 // We're restoring widget state for 'pkg', so we start by wiping (a) all widget 4454 // instances that are hosted by that app, and (b) all instances in other hosts 4455 // for which 'pkg' is the provider. We assume that we'll be restoring all of 4456 // these hosts & providers, so will be reconstructing a correct live state. 4457 private void pruneWidgetStateLocked(String pkg, int userId) { 4458 if (!mPrunedApps.contains(pkg)) { 4459 if (DEBUG) { 4460 Slog.i(TAG, "pruning widget state for restoring package " + pkg); 4461 } 4462 for (int i = mWidgets.size() - 1; i >= 0; i--) { 4463 Widget widget = mWidgets.get(i); 4464 4465 Host host = widget.host; 4466 Provider provider = widget.provider; 4467 4468 if (host.hostsPackageForUser(pkg, userId) 4469 || (provider != null && provider.isInPackageForUser(pkg, userId))) { 4470 // 'pkg' is either the host or the provider for this instances, 4471 // so we tear it down in anticipation of it (possibly) being 4472 // reconstructed due to the restore 4473 host.widgets.remove(widget); 4474 provider.widgets.remove(widget); 4475 unbindAppWidgetRemoteViewsServicesLocked(widget); 4476 removeWidgetLocked(widget); 4477 } 4478 } 4479 mPrunedApps.add(pkg); 4480 } else { 4481 if (DEBUG) { 4482 Slog.i(TAG, "already pruned " + pkg + ", continuing normally"); 4483 } 4484 } 4485 } 4486 4487 private boolean isProviderAndHostInUser(Widget widget, int userId) { 4488 // Backup only widgets hosted or provided by the owner profile. 4489 return widget.host.getUserId() == userId && (widget.provider == null 4490 || widget.provider.getUserId() == userId); 4491 } 4492 4493 private Bundle parseWidgetIdOptions(XmlPullParser parser) { 4494 Bundle options = new Bundle(); 4495 String minWidthString = parser.getAttributeValue(null, "min_width"); 4496 if (minWidthString != null) { 4497 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 4498 Integer.parseInt(minWidthString, 16)); 4499 } 4500 String minHeightString = parser.getAttributeValue(null, "min_height"); 4501 if (minHeightString != null) { 4502 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 4503 Integer.parseInt(minHeightString, 16)); 4504 } 4505 String maxWidthString = parser.getAttributeValue(null, "max_width"); 4506 if (maxWidthString != null) { 4507 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 4508 Integer.parseInt(maxWidthString, 16)); 4509 } 4510 String maxHeightString = parser.getAttributeValue(null, "max_height"); 4511 if (maxHeightString != null) { 4512 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4513 Integer.parseInt(maxHeightString, 16)); 4514 } 4515 String categoryString = parser.getAttributeValue(null, "host_category"); 4516 if (categoryString != null) { 4517 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 4518 Integer.parseInt(categoryString, 16)); 4519 } 4520 return options; 4521 } 4522 4523 private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) { 4524 int pending = 0; 4525 final int N = updates.size(); 4526 for (int i = 0; i < N; i++) { 4527 RestoreUpdateRecord r = updates.get(i); 4528 if (!r.notified) { 4529 pending++; 4530 } 4531 } 4532 return pending; 4533 } 4534 4535 // Accumulate a list of updates that affect the given provider for a final 4536 // coalesced notification broadcast once restore is over. 4537 private class RestoreUpdateRecord { 4538 public int oldId; 4539 public int newId; 4540 public boolean notified; 4541 4542 public RestoreUpdateRecord(int theOldId, int theNewId) { 4543 oldId = theOldId; 4544 newId = theNewId; 4545 notified = false; 4546 } 4547 } 4548 } 4549} 4550