2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.appwidget;
19 import static android.content.Context.KEYGUARD_SERVICE;
20 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
21 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
23 import android.app.AlarmManager;
24 import android.app.AppGlobals;
25 import android.app.AppOpsManager;
26 import android.app.KeyguardManager;
27 import android.app.PendingIntent;
28 import android.app.admin.DevicePolicyManagerInternal;
29 import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener;
30 import android.appwidget.AppWidgetManager;
31 import android.appwidget.AppWidgetProviderInfo;
32 import android.content.BroadcastReceiver;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.Intent.FilterComparison;
37 import android.content.IntentFilter;
38 import android.content.IntentSender;
39 import android.content.ServiceConnection;
40 import android.content.pm.ActivityInfo;
41 import android.content.pm.ApplicationInfo;
42 import android.content.pm.IPackageManager;
43 import android.content.pm.PackageInfo;
44 import android.content.pm.PackageManager;
45 import android.content.pm.PackageManager.NameNotFoundException;
46 import android.content.pm.ParceledListSlice;
47 import android.content.pm.ResolveInfo;
48 import android.content.pm.ServiceInfo;
49 import android.content.pm.UserInfo;
50 import android.content.res.Resources;
51 import android.content.res.TypedArray;
52 import android.content.res.XmlResourceParser;
53 import android.graphics.Bitmap;
54 import android.graphics.Point;
55 import android.graphics.drawable.Drawable;
56 import android.net.Uri;
57 import android.os.Binder;
58 import android.os.Bundle;
59 import android.os.Environment;
60 import android.os.Handler;
61 import android.os.IBinder;
62 import android.os.Looper;
63 import android.os.Message;
64 import android.os.Process;
65 import android.os.RemoteException;
66 import android.os.SystemClock;
67 import android.os.UserHandle;
68 import android.os.UserManager;
69 import android.text.TextUtils;
70 import android.util.ArraySet;
71 import android.util.AtomicFile;
72 import android.util.AttributeSet;
73 import android.util.Pair;
74 import android.util.Slog;
75 import android.util.SparseArray;
76 import android.util.SparseIntArray;
77 import android.util.TypedValue;
78 import android.util.Xml;
79 import android.view.Display;
80 import android.view.View;
81 import android.view.WindowManager;
82 import android.widget.RemoteViews;
84 import com.android.internal.R;
85 import com.android.internal.app.UnlaunchableAppActivity;
86 import com.android.internal.appwidget.IAppWidgetHost;
87 import com.android.internal.appwidget.IAppWidgetService;
88 import com.android.internal.os.BackgroundThread;
89 import com.android.internal.os.SomeArgs;
90 import com.android.internal.util.FastXmlSerializer;
91 import com.android.internal.widget.IRemoteViewsAdapterConnection;
92 import com.android.internal.widget.IRemoteViewsFactory;
93 import com.android.server.LocalServices;
94 import com.android.server.WidgetBackupProvider;
95 import com.android.server.policy.IconUtilities;
97 import libcore.io.IoUtils;
99 import org.xmlpull.v1.XmlPullParser;
100 import org.xmlpull.v1.XmlPullParserException;
101 import org.xmlpull.v1.XmlSerializer;
103 import java.io.ByteArrayInputStream;
104 import java.io.ByteArrayOutputStream;
106 import java.io.FileDescriptor;
107 import java.io.FileInputStream;
108 import java.io.FileNotFoundException;
109 import java.io.FileOutputStream;
110 import java.io.IOException;
111 import java.io.PrintWriter;
112 import java.nio.charset.StandardCharsets;
113 import java.util.ArrayList;
114 import java.util.Arrays;
115 import java.util.Collections;
116 import java.util.HashMap;
117 import java.util.HashSet;
118 import java.util.Iterator;
119 import java.util.List;
120 import java.util.Locale;
121 import java.util.Map;
122 import java.util.Set;
124 class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider,
125 OnCrossProfileWidgetProvidersChangeListener {
126 private static final String TAG = "AppWidgetServiceImpl";
128 private static boolean DEBUG = false;
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;
134 private static final String STATE_FILENAME = "appwidgets.xml";
136 private static final int MIN_UPDATE_PERIOD = DEBUG ? 0 : 30 * 60 * 1000; // 30 minutes
138 private static final int TAG_UNDEFINED = -1;
140 private static final int UNKNOWN_UID = -1;
142 private static final int LOADED_PROFILE_ID = -1;
144 private static final int UNKNOWN_USER_ID = -10;
146 // Bump if the stored widgets need to be upgraded.
147 private static final int CURRENT_VERSION = 1;
149 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
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);
156 Slog.i(TAG, "Received broadcast: " + action + " on user " + userId);
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);
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());
179 onPackageBroadcastReceived(intent, userId);
184 // Manages active connections to RemoteViewsServices.
185 private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
186 mBoundRemoteViewsServices = new HashMap<>();
188 // Manages persistent references to RemoteViewsServices from different App Widgets.
189 private final HashMap<Pair<Integer, FilterComparison>, HashSet<Integer>>
190 mRemoteViewsServicesAppWidgets = new HashMap<>();
192 private final Object mLock = new Object();
194 private final ArrayList<Widget> mWidgets = new ArrayList<>();
195 private final ArrayList<Host> mHosts = new ArrayList<>();
196 private final ArrayList<Provider> mProviders = new ArrayList<>();
198 private final ArraySet<Pair<Integer, String>> mPackagesWithBindWidgetPermission =
201 private final SparseIntArray mLoadedUserIds = new SparseIntArray();
203 private final SparseArray<ArraySet<String>> mWidgetPackages = new SparseArray<>();
205 private final BackupRestoreController mBackupRestoreController;
207 private final Context mContext;
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;
216 private final SecurityPolicy mSecurityPolicy;
218 private final Handler mSaveStateHandler;
219 private final Handler mCallbackHandler;
221 private Locale mLocale;
223 private final SparseIntArray mNextAppWidgetIds = new SparseIntArray();
225 private boolean mSafeMode;
226 private int mMaxWidgetBitmapMemory;
228 private final IconUtilities mIconUtilities;
230 AppWidgetServiceImpl(Context 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);
244 computeMaximumWidgetBitmapMemory();
245 registerBroadcastReceiver();
246 registerOnCrossProfileProvidersChangedListener();
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;
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);
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);
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);
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);
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);
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);
304 private void registerOnCrossProfileProvidersChangedListener() {
305 // The device policy is an optional component.
306 if (mDevicePolicyManagerInternal != null) {
307 mDevicePolicyManagerInternal.addOnCrossProfileWidgetProvidersChangeListener(this);
311 public void setSafeMode(boolean safeMode) {
312 mSafeMode = safeMode;
315 private void onConfigurationChanged() {
317 Slog.i(TAG, "onConfigurationChanged()");
320 Locale revised = Locale.getDefault();
321 if (revised == null || mLocale == null || !revised.equals(mLocale)) {
324 synchronized (mLock) {
325 SparseIntArray changedGroups = null;
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<>();
333 int N = installedProviders.size();
334 for (int i = N - 1; i >= 0; i--) {
335 Provider provider = installedProviders.get(i);
337 final int userId = provider.getUserId();
338 if (!mUserManager.isUserUnlocked(userId) ||
339 isProfileWithLockedParent(userId)) {
342 ensureGroupStateLoadedLocked(userId);
344 if (!removedProviders.contains(provider.id)) {
345 final boolean changed = updateProvidersForPackageLocked(
346 provider.id.componentName.getPackageName(),
347 provider.getUserId(), removedProviders);
350 if (changedGroups == null) {
351 changedGroups = new SparseIntArray();
353 final int groupId = mSecurityPolicy.getGroupParent(
354 provider.getUserId());
355 changedGroups.put(groupId, groupId);
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);
371 private void onPackageBroadcastReceived(Intent intent, int userId) {
372 if (!mUserManager.isUserUnlocked(userId) ||
373 isProfileWithLockedParent(userId)) {
377 final String action = intent.getAction();
378 boolean added = false;
379 boolean changed = false;
380 boolean componentsModified = false;
382 String pkgList[] = null;
383 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
384 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
386 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
387 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
390 Uri uri = intent.getData();
394 String pkgName = uri.getSchemeSpecificPart();
395 if (pkgName == null) {
398 pkgList = new String[] { pkgName };
399 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
400 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
402 if (pkgList == null || pkgList.length == 0) {
406 synchronized (mLock) {
407 ensureGroupStateLoadedLocked(userId);
409 Bundle extras = intent.getExtras();
411 if (added || changed) {
412 final boolean newPackageAdded = added && (extras == null
413 || !extras.getBoolean(Intent.EXTRA_REPLACING, false));
415 for (String pkgName : pkgList) {
416 // Fix up the providers - add/remove/update.
417 componentsModified |= updateProvidersForPackageLocked(pkgName, userId, null);
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);
425 resolveHostUidLocked(pkgName, uid);
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));
435 if (packageRemovedPermanently) {
436 for (String pkgName : pkgList) {
437 componentsModified |= removeHostsAndProvidersForPackageLocked(
443 if (componentsModified) {
444 saveGroupStateAsync(userId);
446 // If the set of providers has been modified, notify each active AppWidgetHost
447 scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
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.
457 private void reloadWidgetsMaskedStateForGroup(int userId) {
458 if (!mUserManager.isUserUnlocked(userId)) {
461 synchronized (mLock) {
462 reloadWidgetsMaskedState(userId);
463 int[] profileIds = mUserManager.getEnabledProfileIds(userId);
464 for (int profileId : profileIds) {
465 reloadWidgetsMaskedState(profileId);
470 private void reloadWidgetsMaskedState(int userId) {
471 final long identity = Binder.clearCallingIdentity();
473 UserInfo user = mUserManager.getUserInfo(userId);
475 boolean lockedProfile = !mUserManager.isUserUnlocked(userId);
476 boolean quietProfile = user.isQuietModeEnabled();
477 final int N = mProviders.size();
478 for (int i = 0; i < N; i++) {
479 Provider provider = mProviders.get(i);
480 int providerUserId = provider.getUserId();
481 if (providerUserId != userId) {
485 boolean changed = provider.setMaskedByLockedProfileLocked(lockedProfile);
486 changed |= provider.setMaskedByQuietProfileLocked(quietProfile);
490 suspended = mPackageManager.isPackageSuspendedForUser(
491 provider.info.provider.getPackageName(), provider.getUserId());
492 } catch (IllegalArgumentException ex) {
493 // Package not found.
496 changed |= provider.setMaskedBySuspendedPackageLocked(suspended);
497 } catch (RemoteException e) {
498 Slog.e(TAG, "Failed to query application info", e);
501 if (provider.isMaskedLocked()) {
502 maskWidgetsViewsLocked(provider, null);
504 unmaskWidgetsViewsLocked(provider);
509 Binder.restoreCallingIdentity(identity);
514 * Incrementally update the masked state due to package suspension state.
516 private void updateWidgetPackageSuspensionMaskedState(String[] packagesArray, boolean suspended,
518 if (packagesArray == null) {
521 Set<String> packages = new ArraySet<String>(Arrays.asList(packagesArray));
522 synchronized (mLock) {
523 final int N = mProviders.size();
524 for (int i = 0; i < N; i++) {
525 Provider provider = mProviders.get(i);
526 int providerUserId = provider.getUserId();
527 if (providerUserId != profileId
528 || !packages.contains(provider.info.provider.getPackageName())) {
531 if (provider.setMaskedBySuspendedPackageLocked(suspended)) {
532 if (provider.isMaskedLocked()) {
533 maskWidgetsViewsLocked(provider, null);
535 unmaskWidgetsViewsLocked(provider);
542 private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) {
543 final long identity = Binder.clearCallingIdentity();
545 // Load the unbadged application icon and pass it to the widget to appear on
547 Context userContext = mContext.createPackageContextAsUser(providerPackage, 0,
548 UserHandle.of(providerUserId));
549 PackageManager pm = userContext.getPackageManager();
550 Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm);
551 // Create a bitmap of the icon which is what the widget's remoteview requires.
552 return mIconUtilities.createIconBitmap(icon);
553 } catch (NameNotFoundException e) {
554 Slog.e(TAG, "Fail to get application icon", e);
555 // Provider package removed, no need to mask its views as its state will be
559 Binder.restoreCallingIdentity(identity);
563 private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge,
564 PendingIntent onClickIntent) {
565 RemoteViews views = new RemoteViews(mContext.getPackageName(),
566 R.layout.work_widget_mask_view);
568 views.setImageViewBitmap(R.id.work_widget_app_icon, icon);
571 views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
573 if (onClickIntent != null) {
574 views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent);
580 * Mask the target widget belonging to the specified provider, or all active widgets
581 * of the provider if target widget == null.
583 private void maskWidgetsViewsLocked(Provider provider, Widget targetWidget) {
584 final int widgetCount = provider.widgets.size();
585 if (widgetCount == 0) {
588 final String providerPackage = provider.info.provider.getPackageName();
589 final int providerUserId = provider.getUserId();
590 Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId);
591 if (iconBitmap == null) {
594 final boolean showBadge;
595 final Intent onClickIntent;
596 final long identity = Binder.clearCallingIdentity();
598 if (provider.maskedBySuspendedPackage) {
599 UserInfo userInfo = mUserManager.getUserInfo(providerUserId);
600 showBadge = userInfo.isManagedProfile();
601 onClickIntent = mDevicePolicyManagerInternal.createPackageSuspendedDialogIntent(
602 providerPackage, providerUserId);
603 } else if (provider.maskedByQuietProfile) {
605 onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
607 } else /* provider.maskedByLockedProfile */ {
609 onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
611 if (onClickIntent != null) {
612 onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
613 | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
616 for (int j = 0; j < widgetCount; j++) {
617 Widget widget = provider.widgets.get(j);
618 if (targetWidget != null && targetWidget != widget) continue;
619 PendingIntent intent = null;
620 if (onClickIntent != null) {
621 intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
622 onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
624 RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
625 if (widget.replaceWithMaskedViewsLocked(views)) {
626 scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
630 Binder.restoreCallingIdentity(identity);
634 private void unmaskWidgetsViewsLocked(Provider provider) {
635 final int widgetCount = provider.widgets.size();
636 for (int j = 0; j < widgetCount; j++) {
637 Widget widget = provider.widgets.get(j);
638 if (widget.clearMaskedViewsLocked()) {
639 scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
644 private void resolveHostUidLocked(String pkg, int uid) {
645 final int N = mHosts.size();
646 for (int i = 0; i < N; i++) {
647 Host host = mHosts.get(i);
648 if (host.id.uid == UNKNOWN_UID && pkg.equals(host.id.packageName)) {
650 Slog.i(TAG, "host " + host.id + " resolved to uid " + uid);
652 host.id = new HostId(uid, host.id.hostId, host.id.packageName);
658 private void ensureGroupStateLoadedLocked(int userId) {
659 if (!mUserManager.isUserUnlocked(userId)) {
660 throw new IllegalStateException(
661 "User " + userId + " must be unlocked for widgets to be available");
663 if (isProfileWithLockedParent(userId)) {
664 throw new IllegalStateException(
665 "Profile " + userId + " must have unlocked parent");
667 final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
669 // Careful lad, we may have already loaded the state for some
670 // group members, so check before loading and read only the
671 // state for the new member(s).
672 int newMemberCount = 0;
673 final int profileIdCount = profileIds.length;
674 for (int i = 0; i < profileIdCount; i++) {
675 final int profileId = profileIds[i];
676 if (mLoadedUserIds.indexOfKey(profileId) >= 0) {
677 profileIds[i] = LOADED_PROFILE_ID;
683 if (newMemberCount <= 0) {
687 int newMemberIndex = 0;
688 final int[] newProfileIds = new int[newMemberCount];
689 for (int i = 0; i < profileIdCount; i++) {
690 final int profileId = profileIds[i];
691 if (profileId != LOADED_PROFILE_ID) {
692 mLoadedUserIds.put(profileId, profileId);
693 newProfileIds[newMemberIndex] = profileId;
698 clearProvidersAndHostsTagsLocked();
700 loadGroupWidgetProvidersLocked(newProfileIds);
701 loadGroupStateLocked(newProfileIds);
705 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
706 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
707 "Permission Denial: can't dump from from pid="
708 + Binder.getCallingPid()
709 + ", uid=" + Binder.getCallingUid());
711 synchronized (mLock) {
712 int N = mProviders.size();
713 pw.println("Providers:");
714 for (int i = 0; i < N; i++) {
715 dumpProvider(mProviders.get(i), i, pw);
720 pw.println("Widgets:");
721 for (int i = 0; i < N; i++) {
722 dumpWidget(mWidgets.get(i), i, pw);
727 pw.println("Hosts:");
728 for (int i = 0; i < N; i++) {
729 dumpHost(mHosts.get(i), i, pw);
733 N = mPackagesWithBindWidgetPermission.size();
735 pw.println("Grants:");
736 for (int i = 0; i < N; i++) {
737 Pair<Integer, String> grant = mPackagesWithBindWidgetPermission.valueAt(i);
738 dumpGrant(grant, i, pw);
744 public ParceledListSlice<RemoteViews> startListening(IAppWidgetHost callbacks,
745 String callingPackage, int hostId, int[] appWidgetIds, int[] updatedIds) {
746 final int userId = UserHandle.getCallingUserId();
749 Slog.i(TAG, "startListening() " + userId);
752 // Make sure the package runs under the caller uid.
753 mSecurityPolicy.enforceCallFromPackage(callingPackage);
755 synchronized (mLock) {
756 ensureGroupStateLoadedLocked(userId);
758 // NOTE: The lookup is enforcing security across users by making
759 // sure the caller can only access hosts it owns.
760 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
761 Host host = lookupOrAddHostLocked(id);
762 host.callbacks = callbacks;
764 int N = appWidgetIds.length;
765 ArrayList<RemoteViews> outViews = new ArrayList<>(N);
768 for (int i = 0; i < N; i++) {
769 rv = host.getPendingViewsForId(appWidgetIds[i]);
771 updatedIds[added] = appWidgetIds[i];
776 return new ParceledListSlice<>(outViews);
781 public void stopListening(String callingPackage, int hostId) {
782 final int userId = UserHandle.getCallingUserId();
785 Slog.i(TAG, "stopListening() " + userId);
788 // Make sure the package runs under the caller uid.
789 mSecurityPolicy.enforceCallFromPackage(callingPackage);
791 synchronized (mLock) {
792 ensureGroupStateLoadedLocked(userId);
794 // NOTE: The lookup is enforcing security across users by making
795 // sure the caller can only access hosts it owns.
796 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
797 Host host = lookupHostLocked(id);
800 host.callbacks = null;
801 pruneHostLocked(host);
807 public int allocateAppWidgetId(String callingPackage, int hostId) {
808 final int userId = UserHandle.getCallingUserId();
811 Slog.i(TAG, "allocateAppWidgetId() " + userId);
814 // Make sure the package runs under the caller uid.
815 mSecurityPolicy.enforceCallFromPackage(callingPackage);
817 synchronized (mLock) {
818 ensureGroupStateLoadedLocked(userId);
820 if (mNextAppWidgetIds.indexOfKey(userId) < 0) {
821 mNextAppWidgetIds.put(userId, AppWidgetManager.INVALID_APPWIDGET_ID + 1);
824 final int appWidgetId = incrementAndGetAppWidgetIdLocked(userId);
826 // NOTE: The lookup is enforcing security across users by making
827 // sure the caller can only access hosts it owns.
828 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
829 Host host = lookupOrAddHostLocked(id);
831 Widget widget = new Widget();
832 widget.appWidgetId = appWidgetId;
835 host.widgets.add(widget);
836 addWidgetLocked(widget);
838 saveGroupStateAsync(userId);
841 Slog.i(TAG, "Allocated widget id " + appWidgetId
842 + " for host " + host.id);
850 public void deleteAppWidgetId(String callingPackage, int appWidgetId) {
851 final int userId = UserHandle.getCallingUserId();
854 Slog.i(TAG, "deleteAppWidgetId() " + userId);
857 // Make sure the package runs under the caller uid.
858 mSecurityPolicy.enforceCallFromPackage(callingPackage);
860 synchronized (mLock) {
861 ensureGroupStateLoadedLocked(userId);
863 // NOTE: The lookup is enforcing security across users by making
864 // sure the caller can only access widgets it hosts or provides.
865 Widget widget = lookupWidgetLocked(appWidgetId,
866 Binder.getCallingUid(), callingPackage);
868 if (widget == null) {
872 deleteAppWidgetLocked(widget);
874 saveGroupStateAsync(userId);
877 Slog.i(TAG, "Deleted widget id " + appWidgetId
878 + " for host " + widget.host.id);
884 public boolean hasBindAppWidgetPermission(String packageName, int grantId) {
886 Slog.i(TAG, "hasBindAppWidgetPermission() " + UserHandle.getCallingUserId());
889 // A special permission is required for managing white listing.
890 mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName);
892 synchronized (mLock) {
893 // The grants are stored in user state wich gets the grant.
894 ensureGroupStateLoadedLocked(grantId);
896 final int packageUid = getUidForPackage(packageName, grantId);
897 if (packageUid < 0) {
901 Pair<Integer, String> packageId = Pair.create(grantId, packageName);
902 return mPackagesWithBindWidgetPermission.contains(packageId);
907 public void setBindAppWidgetPermission(String packageName, int grantId,
908 boolean grantPermission) {
910 Slog.i(TAG, "setBindAppWidgetPermission() " + UserHandle.getCallingUserId());
913 // A special permission is required for managing white listing.
914 mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName);
916 synchronized (mLock) {
917 // The grants are stored in user state wich gets the grant.
918 ensureGroupStateLoadedLocked(grantId);
920 final int packageUid = getUidForPackage(packageName, grantId);
921 if (packageUid < 0) {
925 Pair<Integer, String> packageId = Pair.create(grantId, packageName);
926 if (grantPermission) {
927 mPackagesWithBindWidgetPermission.add(packageId);
929 mPackagesWithBindWidgetPermission.remove(packageId);
932 saveGroupStateAsync(grantId);
937 public IntentSender createAppWidgetConfigIntentSender(String callingPackage, int appWidgetId,
938 final int intentFlags) {
939 final int userId = UserHandle.getCallingUserId();
942 Slog.i(TAG, "createAppWidgetConfigIntentSender() " + userId);
945 // Make sure the package runs under the caller uid.
946 mSecurityPolicy.enforceCallFromPackage(callingPackage);
948 synchronized (mLock) {
949 ensureGroupStateLoadedLocked(userId);
951 // NOTE: The lookup is enforcing security across users by making
952 // sure the caller can only access widgets it hosts or provides.
953 Widget widget = lookupWidgetLocked(appWidgetId,
954 Binder.getCallingUid(), callingPackage);
956 if (widget == null) {
957 throw new IllegalArgumentException("Bad widget id " + appWidgetId);
960 Provider provider = widget.provider;
961 if (provider == null) {
962 throw new IllegalArgumentException("Widget not bound " + appWidgetId);
965 // Make sure only safe flags can be passed it.
966 final int secureFlags = intentFlags & ~Intent.IMMUTABLE_FLAGS;
968 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
969 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
970 intent.setComponent(provider.info.configure);
971 intent.setFlags(secureFlags);
973 // All right, create the sender.
974 final long identity = Binder.clearCallingIdentity();
976 return PendingIntent.getActivityAsUser(
977 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
978 | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT,
979 null, new UserHandle(provider.getUserId()))
982 Binder.restoreCallingIdentity(identity);
988 public boolean bindAppWidgetId(String callingPackage, int appWidgetId,
989 int providerProfileId, ComponentName providerComponent, Bundle options) {
990 final int userId = UserHandle.getCallingUserId();
993 Slog.i(TAG, "bindAppWidgetId() " + userId);
996 // Make sure the package runs under the caller uid.
997 mSecurityPolicy.enforceCallFromPackage(callingPackage);
999 // Check that if a cross-profile binding is attempted, it is allowed.
1000 if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)) {
1004 // If the provider is not under the calling user, make sure this
1005 // provider is white listed for access from the parent.
1006 if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
1007 providerComponent.getPackageName(), providerProfileId)) {
1011 synchronized (mLock) {
1012 ensureGroupStateLoadedLocked(userId);
1014 // A special permission or white listing is required to bind widgets.
1015 if (!mSecurityPolicy.hasCallerBindPermissionOrBindWhiteListedLocked(
1020 // NOTE: The lookup is enforcing security across users by making
1021 // sure the caller can only access widgets it hosts or provides.
1022 Widget widget = lookupWidgetLocked(appWidgetId,
1023 Binder.getCallingUid(), callingPackage);
1025 if (widget == null) {
1026 Slog.e(TAG, "Bad widget id " + appWidgetId);
1030 if (widget.provider != null) {
1031 Slog.e(TAG, "Widget id " + appWidgetId
1032 + " already bound to: " + widget.provider.id);
1036 final int providerUid = getUidForPackage(providerComponent.getPackageName(),
1038 if (providerUid < 0) {
1039 Slog.e(TAG, "Package " + providerComponent.getPackageName() + " not installed "
1040 + " for profile " + providerProfileId);
1044 // NOTE: The lookup is enforcing security across users by making
1045 // sure the provider is in the already vetted user profile.
1046 ProviderId providerId = new ProviderId(providerUid, providerComponent);
1047 Provider provider = lookupProviderLocked(providerId);
1049 if (provider == null) {
1050 Slog.e(TAG, "No widget provider " + providerComponent + " for profile "
1051 + providerProfileId);
1055 if (provider.zombie) {
1056 Slog.e(TAG, "Can't bind to a 3rd party provider in"
1057 + " safe mode " + provider);
1061 widget.provider = provider;
1062 widget.options = (options != null) ? cloneIfLocalBinder(options) : new Bundle();
1064 // We need to provide a default value for the widget category if it is not specified
1065 if (!widget.options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
1066 widget.options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1067 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
1070 provider.widgets.add(widget);
1072 onWidgetProviderAddedOrChangedLocked(widget);
1074 final int widgetCount = provider.widgets.size();
1075 if (widgetCount == 1) {
1076 // Tell the provider that it's ready.
1077 sendEnableIntentLocked(provider);
1080 // Send an update now -- We need this update now, and just for this appWidgetId.
1081 // It's less critical when the next one happens, so when we schedule the next one,
1082 // we add updatePeriodMillis to its start time. That time will have some slop,
1084 sendUpdateIntentLocked(provider, new int[] {appWidgetId});
1086 // Schedule the future updates.
1087 registerForBroadcastsLocked(provider, getWidgetIds(provider.widgets));
1089 saveGroupStateAsync(userId);
1092 Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id);
1100 public int[] getAppWidgetIds(ComponentName componentName) {
1101 final int userId = UserHandle.getCallingUserId();
1104 Slog.i(TAG, "getAppWidgetIds() " + userId);
1107 // Make sure the package runs under the caller uid.
1108 mSecurityPolicy.enforceCallFromPackage(componentName.getPackageName());
1110 synchronized (mLock) {
1111 ensureGroupStateLoadedLocked(userId);
1113 // NOTE: The lookup is enforcing security across users by making
1114 // sure the caller can access only its providers.
1115 ProviderId providerId = new ProviderId(Binder.getCallingUid(), componentName);
1116 Provider provider = lookupProviderLocked(providerId);
1118 if (provider != null) {
1119 return getWidgetIds(provider.widgets);
1127 public int[] getAppWidgetIdsForHost(String callingPackage, int hostId) {
1128 final int userId = UserHandle.getCallingUserId();
1131 Slog.i(TAG, "getAppWidgetIdsForHost() " + userId);
1134 // Make sure the package runs under the caller uid.
1135 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1137 synchronized (mLock) {
1138 ensureGroupStateLoadedLocked(userId);
1140 // NOTE: The lookup is enforcing security across users by making
1141 // sure the caller can only access its hosts.
1142 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
1143 Host host = lookupHostLocked(id);
1146 return getWidgetIds(host.widgets);
1154 public void bindRemoteViewsService(String callingPackage, int appWidgetId,
1155 Intent intent, IBinder callbacks) {
1156 final int userId = UserHandle.getCallingUserId();
1159 Slog.i(TAG, "bindRemoteViewsService() " + userId);
1162 // Make sure the package runs under the caller uid.
1163 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1165 synchronized (mLock) {
1166 ensureGroupStateLoadedLocked(userId);
1168 // NOTE: The lookup is enforcing security across users by making
1169 // sure the caller can only access widgets it hosts or provides.
1170 Widget widget = lookupWidgetLocked(appWidgetId,
1171 Binder.getCallingUid(), callingPackage);
1173 if (widget == null) {
1174 throw new IllegalArgumentException("Bad widget id");
1177 // Make sure the widget has a provider.
1178 if (widget.provider == null) {
1179 throw new IllegalArgumentException("No provider for widget "
1183 ComponentName componentName = intent.getComponent();
1185 // Ensure that the service belongs to the same package as the provider.
1186 // But this is not enough as they may be under different users - see below...
1187 String providerPackage = widget.provider.id.componentName.getPackageName();
1188 String servicePackage = componentName.getPackageName();
1189 if (!servicePackage.equals(providerPackage)) {
1190 throw new SecurityException("The taget service not in the same package"
1191 + " as the widget provider");
1194 // Make sure this service exists under the same user as the provider and
1195 // requires a permission which allows only the system to bind to it.
1196 mSecurityPolicy.enforceServiceExistsAndRequiresBindRemoteViewsPermission(
1197 componentName, widget.provider.getUserId());
1199 // Good to go - the service pakcage is correct, it exists for the correct
1200 // user, and requires the bind permission.
1202 // If there is already a connection made for this service intent, then
1203 // disconnect from that first. (This does not allow multiple connections
1204 // to the same service under the same key).
1205 ServiceConnectionProxy connection = null;
1206 FilterComparison fc = new FilterComparison(intent);
1207 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
1209 if (mBoundRemoteViewsServices.containsKey(key)) {
1210 connection = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
1211 connection.disconnect();
1212 unbindService(connection);
1213 mBoundRemoteViewsServices.remove(key);
1216 // Bind to the RemoteViewsService (which will trigger a callback to the
1217 // RemoteViewsAdapter.onServiceConnected())
1218 connection = new ServiceConnectionProxy(callbacks);
1219 bindService(intent, connection, widget.provider.info.getProfile());
1220 mBoundRemoteViewsServices.put(key, connection);
1222 // Add it to the mapping of RemoteViewsService to appWidgetIds so that we
1223 // can determine when we can call back to the RemoteViewsService later to
1224 // destroy associated factories.
1225 Pair<Integer, FilterComparison> serviceId = Pair.create(widget.provider.id.uid, fc);
1226 incrementAppWidgetServiceRefCount(appWidgetId, serviceId);
1231 public void unbindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent) {
1232 final int userId = UserHandle.getCallingUserId();
1235 Slog.i(TAG, "unbindRemoteViewsService() " + userId);
1238 // Make sure the package runs under the caller uid.
1239 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1241 synchronized (mLock) {
1242 ensureGroupStateLoadedLocked(userId);
1244 // Unbind from the RemoteViewsService (which will trigger a callback to the bound
1245 // RemoteViewsAdapter)
1246 Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
1247 new FilterComparison(intent));
1248 if (mBoundRemoteViewsServices.containsKey(key)) {
1249 // We don't need to use the appWidgetId until after we are sure there is something
1250 // to unbind. Note that this may mask certain issues with apps calling unbind()
1251 // more than necessary.
1253 // NOTE: The lookup is enforcing security across users by making
1254 // sure the caller can only access widgets it hosts or provides.
1255 Widget widget = lookupWidgetLocked(appWidgetId,
1256 Binder.getCallingUid(), callingPackage);
1258 if (widget == null) {
1259 throw new IllegalArgumentException("Bad widget id " + appWidgetId);
1262 ServiceConnectionProxy connection = (ServiceConnectionProxy)
1263 mBoundRemoteViewsServices.get(key);
1264 connection.disconnect();
1265 mContext.unbindService(connection);
1266 mBoundRemoteViewsServices.remove(key);
1272 public void deleteHost(String callingPackage, int hostId) {
1273 final int userId = UserHandle.getCallingUserId();
1276 Slog.i(TAG, "deleteHost() " + userId);
1279 // Make sure the package runs under the caller uid.
1280 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1282 synchronized (mLock) {
1283 ensureGroupStateLoadedLocked(userId);
1285 // NOTE: The lookup is enforcing security across users by making
1286 // sure the caller can only access hosts in its uid and package.
1287 HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
1288 Host host = lookupHostLocked(id);
1294 deleteHostLocked(host);
1296 saveGroupStateAsync(userId);
1299 Slog.i(TAG, "Deleted host " + host.id);
1305 public void deleteAllHosts() {
1306 final int userId = UserHandle.getCallingUserId();
1309 Slog.i(TAG, "deleteAllHosts() " + userId);
1312 synchronized (mLock) {
1313 ensureGroupStateLoadedLocked(userId);
1315 boolean changed = false;
1317 final int N = mHosts.size();
1318 for (int i = N - 1; i >= 0; i--) {
1319 Host host = mHosts.get(i);
1321 // Delete only hosts in the calling uid.
1322 if (host.id.uid == Binder.getCallingUid()) {
1323 deleteHostLocked(host);
1327 Slog.i(TAG, "Deleted host " + host.id);
1333 saveGroupStateAsync(userId);
1339 public AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId) {
1340 final int userId = UserHandle.getCallingUserId();
1343 Slog.i(TAG, "getAppWidgetInfo() " + userId);
1346 // Make sure the package runs under the caller uid.
1347 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1349 synchronized (mLock) {
1350 ensureGroupStateLoadedLocked(userId);
1352 // NOTE: The lookup is enforcing security across users by making
1353 // sure the caller can only access widgets it hosts or provides.
1354 Widget widget = lookupWidgetLocked(appWidgetId,
1355 Binder.getCallingUid(), callingPackage);
1357 if (widget != null && widget.provider != null && !widget.provider.zombie) {
1358 return cloneIfLocalBinder(widget.provider.info);
1366 public RemoteViews getAppWidgetViews(String callingPackage, int appWidgetId) {
1367 final int userId = UserHandle.getCallingUserId();
1370 Slog.i(TAG, "getAppWidgetViews() " + userId);
1373 // Make sure the package runs under the caller uid.
1374 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1376 synchronized (mLock) {
1377 ensureGroupStateLoadedLocked(userId);
1379 // NOTE: The lookup is enforcing security across users by making
1380 // sure the caller can only access widgets it hosts or provides.
1381 Widget widget = lookupWidgetLocked(appWidgetId,
1382 Binder.getCallingUid(), callingPackage);
1384 if (widget != null) {
1385 return cloneIfLocalBinder(widget.getEffectiveViewsLocked());
1393 public void updateAppWidgetOptions(String callingPackage, int appWidgetId, Bundle options) {
1394 final int userId = UserHandle.getCallingUserId();
1397 Slog.i(TAG, "updateAppWidgetOptions() " + userId);
1400 // Make sure the package runs under the caller uid.
1401 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1403 synchronized (mLock) {
1404 ensureGroupStateLoadedLocked(userId);
1406 // NOTE: The lookup is enforcing security across users by making
1407 // sure the caller can only access widgets it hosts or provides.
1408 Widget widget = lookupWidgetLocked(appWidgetId,
1409 Binder.getCallingUid(), callingPackage);
1411 if (widget == null) {
1415 // Merge the options.
1416 widget.options.putAll(options);
1418 // Send the broacast to notify the provider that options changed.
1419 sendOptionsChangedIntentLocked(widget);
1421 saveGroupStateAsync(userId);
1426 public Bundle getAppWidgetOptions(String callingPackage, int appWidgetId) {
1427 final int userId = UserHandle.getCallingUserId();
1430 Slog.i(TAG, "getAppWidgetOptions() " + userId);
1433 // Make sure the package runs under the caller uid.
1434 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1436 synchronized (mLock) {
1437 ensureGroupStateLoadedLocked(userId);
1439 // NOTE: The lookup is enforcing security across users by making
1440 // sure the caller can only access widgets it hosts or provides.
1441 Widget widget = lookupWidgetLocked(appWidgetId,
1442 Binder.getCallingUid(), callingPackage);
1444 if (widget != null && widget.options != null) {
1445 return cloneIfLocalBinder(widget.options);
1448 return Bundle.EMPTY;
1453 public void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
1454 RemoteViews views) {
1456 Slog.i(TAG, "updateAppWidgetIds() " + UserHandle.getCallingUserId());
1459 updateAppWidgetIds(callingPackage, appWidgetIds, views, false);
1463 public void partiallyUpdateAppWidgetIds(String callingPackage, int[] appWidgetIds,
1464 RemoteViews views) {
1466 Slog.i(TAG, "partiallyUpdateAppWidgetIds() " + UserHandle.getCallingUserId());
1469 updateAppWidgetIds(callingPackage, appWidgetIds, views, true);
1473 public void notifyAppWidgetViewDataChanged(String callingPackage, int[] appWidgetIds,
1475 final int userId = UserHandle.getCallingUserId();
1478 Slog.i(TAG, "notifyAppWidgetViewDataChanged() " + userId);
1481 // Make sure the package runs under the caller uid.
1482 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1484 if (appWidgetIds == null || appWidgetIds.length == 0) {
1488 synchronized (mLock) {
1489 ensureGroupStateLoadedLocked(userId);
1491 final int N = appWidgetIds.length;
1492 for (int i = 0; i < N; i++) {
1493 final int appWidgetId = appWidgetIds[i];
1495 // NOTE: The lookup is enforcing security across users by making
1496 // sure the caller can only access widgets it hosts or provides.
1497 Widget widget = lookupWidgetLocked(appWidgetId,
1498 Binder.getCallingUid(), callingPackage);
1500 if (widget != null) {
1501 scheduleNotifyAppWidgetViewDataChanged(widget, viewId);
1508 public void updateAppWidgetProvider(ComponentName componentName, RemoteViews views) {
1509 final int userId = UserHandle.getCallingUserId();
1512 Slog.i(TAG, "updateAppWidgetProvider() " + userId);
1515 // Make sure the package runs under the caller uid.
1516 mSecurityPolicy.enforceCallFromPackage(componentName.getPackageName());
1518 synchronized (mLock) {
1519 ensureGroupStateLoadedLocked(userId);
1521 // NOTE: The lookup is enforcing security across users by making
1522 // sure the caller can access only its providers.
1523 ProviderId providerId = new ProviderId(Binder.getCallingUid(), componentName);
1524 Provider provider = lookupProviderLocked(providerId);
1526 if (provider == null) {
1527 Slog.w(TAG, "Provider doesn't exist " + providerId);
1531 ArrayList<Widget> instances = provider.widgets;
1532 final int N = instances.size();
1533 for (int i = 0; i < N; i++) {
1534 Widget widget = instances.get(i);
1535 updateAppWidgetInstanceLocked(widget, views, false);
1541 public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
1543 final int userId = UserHandle.getCallingUserId();
1546 Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
1549 // Ensure the profile is in the group and enabled.
1550 if (!mSecurityPolicy.isEnabledGroupProfile(profileId)) {
1554 synchronized (mLock) {
1555 ensureGroupStateLoadedLocked(userId);
1557 ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>();
1559 final int providerCount = mProviders.size();
1560 for (int i = 0; i < providerCount; i++) {
1561 Provider provider = mProviders.get(i);
1562 AppWidgetProviderInfo info = provider.info;
1564 // Ignore an invalid provider or one not matching the filter.
1565 if (provider.zombie || (info.widgetCategory & categoryFilter) == 0) {
1569 // Add providers only for the requested profile that are white-listed.
1570 final int providerProfileId = info.getProfile().getIdentifier();
1571 if (providerProfileId == profileId
1572 && mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
1573 provider.id.componentName.getPackageName(), providerProfileId)) {
1574 result.add(cloneIfLocalBinder(info));
1578 return new ParceledListSlice<AppWidgetProviderInfo>(result);
1582 private void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
1583 RemoteViews views, boolean partially) {
1584 final int userId = UserHandle.getCallingUserId();
1586 if (appWidgetIds == null || appWidgetIds.length == 0) {
1590 // Make sure the package runs under the caller uid.
1591 mSecurityPolicy.enforceCallFromPackage(callingPackage);
1593 final int bitmapMemoryUsage = (views != null) ? views.estimateMemoryUsage() : 0;
1594 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
1595 throw new IllegalArgumentException("RemoteViews for widget update exceeds"
1596 + " maximum bitmap memory usage (used: " + bitmapMemoryUsage
1597 + ", max: " + mMaxWidgetBitmapMemory + ")");
1600 synchronized (mLock) {
1601 ensureGroupStateLoadedLocked(userId);
1603 final int N = appWidgetIds.length;
1604 for (int i = 0; i < N; i++) {
1605 final int appWidgetId = appWidgetIds[i];
1607 // NOTE: The lookup is enforcing security across users by making
1608 // sure the caller can only access widgets it hosts or provides.
1609 Widget widget = lookupWidgetLocked(appWidgetId,
1610 Binder.getCallingUid(), callingPackage);
1612 if (widget != null) {
1613 updateAppWidgetInstanceLocked(widget, views, partially);
1619 private int incrementAndGetAppWidgetIdLocked(int userId) {
1620 final int appWidgetId = peekNextAppWidgetIdLocked(userId) + 1;
1621 mNextAppWidgetIds.put(userId, appWidgetId);
1625 private void setMinAppWidgetIdLocked(int userId, int minWidgetId) {
1626 final int nextAppWidgetId = peekNextAppWidgetIdLocked(userId);
1627 if (nextAppWidgetId < minWidgetId) {
1628 mNextAppWidgetIds.put(userId, minWidgetId);
1632 private int peekNextAppWidgetIdLocked(int userId) {
1633 if (mNextAppWidgetIds.indexOfKey(userId) < 0) {
1634 return AppWidgetManager.INVALID_APPWIDGET_ID + 1;
1636 return mNextAppWidgetIds.get(userId);
1640 private Host lookupOrAddHostLocked(HostId id) {
1641 Host host = lookupHostLocked(id);
1653 private void deleteHostLocked(Host host) {
1654 final int N = host.widgets.size();
1655 for (int i = N - 1; i >= 0; i--) {
1656 Widget widget = host.widgets.remove(i);
1657 deleteAppWidgetLocked(widget);
1659 mHosts.remove(host);
1661 // it's gone or going away, abruptly drop the callback connection
1662 host.callbacks = null;
1665 private void deleteAppWidgetLocked(Widget widget) {
1666 // We first unbind all services that are bound to this id
1667 unbindAppWidgetRemoteViewsServicesLocked(widget);
1669 Host host = widget.host;
1670 host.widgets.remove(widget);
1671 pruneHostLocked(host);
1673 removeWidgetLocked(widget);
1675 Provider provider = widget.provider;
1676 if (provider != null) {
1677 provider.widgets.remove(widget);
1678 if (!provider.zombie) {
1679 // send the broacast saying that this appWidgetId has been deleted
1680 sendDeletedIntentLocked(widget);
1682 if (provider.widgets.isEmpty()) {
1683 // cancel the future updates
1684 cancelBroadcasts(provider);
1686 // send the broacast saying that the provider is not in use any more
1687 sendDisabledIntentLocked(provider);
1693 private void cancelBroadcasts(Provider provider) {
1695 Slog.i(TAG, "cancelBroadcasts() for " + provider);
1697 if (provider.broadcast != null) {
1698 mAlarmManager.cancel(provider.broadcast);
1699 long token = Binder.clearCallingIdentity();
1701 provider.broadcast.cancel();
1703 Binder.restoreCallingIdentity(token);
1705 provider.broadcast = null;
1709 // Unbinds from a RemoteViewsService when we delete an app widget
1710 private void unbindAppWidgetRemoteViewsServicesLocked(Widget widget) {
1711 int appWidgetId = widget.appWidgetId;
1712 // Unbind all connections to Services bound to this AppWidgetId
1713 Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
1715 while (it.hasNext()) {
1716 final Pair<Integer, Intent.FilterComparison> key = it.next();
1717 if (key.first == appWidgetId) {
1718 final ServiceConnectionProxy conn = (ServiceConnectionProxy)
1719 mBoundRemoteViewsServices.get(key);
1721 mContext.unbindService(conn);
1726 // Check if we need to destroy any services (if no other app widgets are
1727 // referencing the same service)
1728 decrementAppWidgetServiceRefCount(widget);
1731 // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
1732 private void destroyRemoteViewsService(final Intent intent, Widget widget) {
1733 final ServiceConnection conn = new ServiceConnection() {
1735 public void onServiceConnected(ComponentName name, IBinder service) {
1736 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
1738 cb.onDestroy(intent);
1739 } catch (RemoteException re) {
1740 Slog.e(TAG, "Error calling remove view factory", re);
1742 mContext.unbindService(this);
1746 public void onServiceDisconnected(ComponentName name) {
1751 // Bind to the service and remove the static intent->factory mapping in the
1752 // RemoteViewsService.
1753 final long token = Binder.clearCallingIdentity();
1755 mContext.bindServiceAsUser(intent, conn,
1756 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
1757 widget.provider.info.getProfile());
1759 Binder.restoreCallingIdentity(token);
1763 // Adds to the ref-count for a given RemoteViewsService intent
1764 private void incrementAppWidgetServiceRefCount(int appWidgetId,
1765 Pair<Integer, FilterComparison> serviceId) {
1766 HashSet<Integer> appWidgetIds = null;
1767 if (mRemoteViewsServicesAppWidgets.containsKey(serviceId)) {
1768 appWidgetIds = mRemoteViewsServicesAppWidgets.get(serviceId);
1770 appWidgetIds = new HashSet<>();
1771 mRemoteViewsServicesAppWidgets.put(serviceId, appWidgetIds);
1773 appWidgetIds.add(appWidgetId);
1776 // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
1777 // the ref-count reaches zero.
1778 private void decrementAppWidgetServiceRefCount(Widget widget) {
1779 Iterator<Pair<Integer, FilterComparison>> it = mRemoteViewsServicesAppWidgets
1780 .keySet().iterator();
1781 while (it.hasNext()) {
1782 final Pair<Integer, FilterComparison> key = it.next();
1783 final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
1784 if (ids.remove(widget.appWidgetId)) {
1785 // If we have removed the last app widget referencing this service, then we
1786 // should destroy it and remove it from this set
1787 if (ids.isEmpty()) {
1788 destroyRemoteViewsService(key.second.getIntent(), widget);
1795 private void saveGroupStateAsync(int groupId) {
1796 mSaveStateHandler.post(new SaveStateRunnable(groupId));
1799 private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views,
1800 boolean isPartialUpdate) {
1801 if (widget != null && widget.provider != null
1802 && !widget.provider.zombie && !widget.host.zombie) {
1804 if (isPartialUpdate && widget.views != null) {
1805 // For a partial update, we merge the new RemoteViews with the old.
1806 widget.views.mergeRemoteViews(views);
1808 // For a full update we replace the RemoteViews completely.
1809 widget.views = views;
1811 scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
1815 private void scheduleNotifyAppWidgetViewDataChanged(Widget widget, int viewId) {
1816 if (widget == null || widget.host == null || widget.host.zombie
1817 || widget.host.callbacks == null || widget.provider == null
1818 || widget.provider.zombie) {
1822 SomeArgs args = SomeArgs.obtain();
1823 args.arg1 = widget.host;
1824 args.arg2 = widget.host.callbacks;
1825 args.argi1 = widget.appWidgetId;
1826 args.argi2 = viewId;
1828 mCallbackHandler.obtainMessage(
1829 CallbackHandler.MSG_NOTIFY_VIEW_DATA_CHANGED,
1830 args).sendToTarget();
1834 private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
1835 int appWidgetId, int viewId) {
1837 callbacks.viewDataChanged(appWidgetId, viewId);
1838 } catch (RemoteException re) {
1839 // It failed; remove the callback. No need to prune because
1840 // we know that this host is still referenced by this instance.
1844 // If the host is unavailable, then we call the associated
1845 // RemoteViewsFactory.onDataSetChanged() directly
1846 synchronized (mLock) {
1847 if (callbacks == null) {
1848 host.callbacks = null;
1850 Set<Pair<Integer, FilterComparison>> keys = mRemoteViewsServicesAppWidgets.keySet();
1851 for (Pair<Integer, FilterComparison> key : keys) {
1852 if (mRemoteViewsServicesAppWidgets.get(key).contains(appWidgetId)) {
1853 final ServiceConnection connection = new ServiceConnection() {
1855 public void onServiceConnected(ComponentName name, IBinder service) {
1856 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1857 .asInterface(service);
1859 cb.onDataSetChangedAsync();
1860 } catch (RemoteException e) {
1861 Slog.e(TAG, "Error calling onDataSetChangedAsync()", e);
1863 mContext.unbindService(this);
1867 public void onServiceDisconnected(android.content.ComponentName name) {
1872 final int userId = UserHandle.getUserId(key.first);
1873 Intent intent = key.second.getIntent();
1875 // Bind to the service and call onDataSetChanged()
1876 bindService(intent, connection, new UserHandle(userId));
1883 private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
1884 long requestTime = SystemClock.uptimeMillis();
1885 if (widget != null) {
1886 widget.lastUpdateTime = requestTime;
1888 if (widget == null || widget.provider == null || widget.provider.zombie
1889 || widget.host.callbacks == null || widget.host.zombie) {
1893 SomeArgs args = SomeArgs.obtain();
1894 args.arg1 = widget.host;
1895 args.arg2 = widget.host.callbacks;
1896 args.arg3 = updateViews;
1897 args.arg4 = requestTime;
1898 args.argi1 = widget.appWidgetId;
1900 mCallbackHandler.obtainMessage(
1901 CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET,
1902 args).sendToTarget();
1905 private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
1906 int appWidgetId, RemoteViews views, long requestTime) {
1908 callbacks.updateAppWidget(appWidgetId, views);
1909 host.lastWidgetUpdateTime = requestTime;
1910 } catch (RemoteException re) {
1911 synchronized (mLock) {
1912 Slog.e(TAG, "Widget host dead: " + host.id, re);
1913 host.callbacks = null;
1918 private void scheduleNotifyProviderChangedLocked(Widget widget) {
1919 if (widget == null || widget.provider == null || widget.provider.zombie
1920 || widget.host.callbacks == null || widget.host.zombie) {
1924 SomeArgs args = SomeArgs.obtain();
1925 args.arg1 = widget.host;
1926 args.arg2 = widget.host.callbacks;
1927 args.arg3 = widget.provider.info;
1928 args.argi1 = widget.appWidgetId;
1930 mCallbackHandler.obtainMessage(
1931 CallbackHandler.MSG_NOTIFY_PROVIDER_CHANGED,
1932 args).sendToTarget();
1935 private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks,
1936 int appWidgetId, AppWidgetProviderInfo info) {
1938 callbacks.providerChanged(appWidgetId, info);
1939 } catch (RemoteException re) {
1940 synchronized (mLock){
1941 Slog.e(TAG, "Widget host dead: " + host.id, re);
1942 host.callbacks = null;
1947 private void scheduleNotifyGroupHostsForProvidersChangedLocked(int userId) {
1948 final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
1950 final int N = mHosts.size();
1951 for (int i = N - 1; i >= 0; i--) {
1952 Host host = mHosts.get(i);
1954 boolean hostInGroup = false;
1955 final int M = profileIds.length;
1956 for (int j = 0; j < M; j++) {
1957 final int profileId = profileIds[j];
1958 if (host.getUserId() == profileId) {
1968 if (host == null || host.zombie || host.callbacks == null) {
1972 SomeArgs args = SomeArgs.obtain();
1974 args.arg2 = host.callbacks;
1976 mCallbackHandler.obtainMessage(
1977 CallbackHandler.MSG_NOTIFY_PROVIDERS_CHANGED,
1978 args).sendToTarget();
1982 private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) {
1984 callbacks.providersChanged();
1985 } catch (RemoteException re) {
1986 synchronized (mLock) {
1987 Slog.e(TAG, "Widget host dead: " + host.id, re);
1988 host.callbacks = null;
1993 private static boolean isLocalBinder() {
1994 return Process.myPid() == Binder.getCallingPid();
1997 private static RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1998 if (isLocalBinder() && rv != null) {
2004 private static AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
2005 if (isLocalBinder() && info != null) {
2006 return info.clone();
2011 private static Bundle cloneIfLocalBinder(Bundle bundle) {
2012 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
2013 // if we start adding objects to the options. Further, it would only be an issue if keyguard
2014 // used such options.
2015 if (isLocalBinder() && bundle != null) {
2016 return (Bundle) bundle.clone();
2021 private Widget lookupWidgetLocked(int appWidgetId, int uid, String packageName) {
2022 final int N = mWidgets.size();
2023 for (int i = 0; i < N; i++) {
2024 Widget widget = mWidgets.get(i);
2025 if (widget.appWidgetId == appWidgetId
2026 && mSecurityPolicy.canAccessAppWidget(widget, uid, packageName)) {
2033 private Provider lookupProviderLocked(ProviderId id) {
2034 final int N = mProviders.size();
2035 for (int i = 0; i < N; i++) {
2036 Provider provider = mProviders.get(i);
2037 if (provider.id.equals(id)) {
2044 private Host lookupHostLocked(HostId hostId) {
2045 final int N = mHosts.size();
2046 for (int i = 0; i < N; i++) {
2047 Host host = mHosts.get(i);
2048 if (host.id.equals(hostId)) {
2055 private void pruneHostLocked(Host host) {
2056 if (host.widgets.size() == 0 && host.callbacks == null) {
2058 Slog.i(TAG, "Pruning host " + host.id);
2060 mHosts.remove(host);
2064 private void loadGroupWidgetProvidersLocked(int[] profileIds) {
2065 List<ResolveInfo> allReceivers = null;
2066 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
2068 final int profileCount = profileIds.length;
2069 for (int i = 0; i < profileCount; i++) {
2070 final int profileId = profileIds[i];
2072 List<ResolveInfo> receivers = queryIntentReceivers(intent, profileId);
2073 if (receivers != null && !receivers.isEmpty()) {
2074 if (allReceivers == null) {
2075 allReceivers = new ArrayList<>();
2077 allReceivers.addAll(receivers);
2081 final int N = (allReceivers == null) ? 0 : allReceivers.size();
2082 for (int i = 0; i < N; i++) {
2083 ResolveInfo receiver = allReceivers.get(i);
2084 addProviderLocked(receiver);
2088 private boolean addProviderLocked(ResolveInfo ri) {
2089 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
2093 if (!ri.activityInfo.isEnabled()) {
2097 ComponentName componentName = new ComponentName(ri.activityInfo.packageName,
2098 ri.activityInfo.name);
2099 ProviderId providerId = new ProviderId(ri.activityInfo.applicationInfo.uid, componentName);
2101 Provider provider = parseProviderInfoXml(providerId, ri);
2102 if (provider != null) {
2103 // we might have an inactive entry for this provider already due to
2104 // a preceding restore operation. if so, fix it up in place; otherwise
2105 // just add this new one.
2106 Provider existing = lookupProviderLocked(providerId);
2108 // If the provider was not found it may be because it was restored and
2109 // we did not know its UID so let us find if there is such one.
2110 if (existing == null) {
2111 ProviderId restoredProviderId = new ProviderId(UNKNOWN_UID, componentName);
2112 existing = lookupProviderLocked(restoredProviderId);
2115 if (existing != null) {
2116 if (existing.zombie && !mSafeMode) {
2117 // it's a placeholder that was set up during an app restore
2118 existing.id = providerId;
2119 existing.zombie = false;
2120 existing.info = provider.info; // the real one filled out from the ResolveInfo
2122 Slog.i(TAG, "Provider placeholder now reified: " + existing);
2126 mProviders.add(provider);
2134 // Remove widgets for provider that are hosted in userId.
2135 private void deleteWidgetsLocked(Provider provider, int userId) {
2136 final int N = provider.widgets.size();
2137 for (int i = N - 1; i >= 0; i--) {
2138 Widget widget = provider.widgets.get(i);
2139 if (userId == UserHandle.USER_ALL
2140 || userId == widget.host.getUserId()) {
2141 provider.widgets.remove(i);
2142 // Call back with empty RemoteViews
2143 updateAppWidgetInstanceLocked(widget, null, false);
2144 // clear out references to this appWidgetId
2145 widget.host.widgets.remove(widget);
2146 removeWidgetLocked(widget);
2147 widget.provider = null;
2148 pruneHostLocked(widget.host);
2154 private void deleteProviderLocked(Provider provider) {
2155 deleteWidgetsLocked(provider, UserHandle.USER_ALL);
2156 mProviders.remove(provider);
2158 // no need to send the DISABLE broadcast, since the receiver is gone anyway
2159 cancelBroadcasts(provider);
2162 private void sendEnableIntentLocked(Provider p) {
2163 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
2164 intent.setComponent(p.info.provider);
2165 sendBroadcastAsUser(intent, p.info.getProfile());
2168 private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds) {
2169 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
2170 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
2171 intent.setComponent(provider.info.provider);
2172 sendBroadcastAsUser(intent, provider.info.getProfile());
2175 private void sendDeletedIntentLocked(Widget widget) {
2176 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
2177 intent.setComponent(widget.provider.info.provider);
2178 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId);
2179 sendBroadcastAsUser(intent, widget.provider.info.getProfile());
2182 private void sendDisabledIntentLocked(Provider provider) {
2183 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
2184 intent.setComponent(provider.info.provider);
2185 sendBroadcastAsUser(intent, provider.info.getProfile());
2188 public void sendOptionsChangedIntentLocked(Widget widget) {
2189 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
2190 intent.setComponent(widget.provider.info.provider);
2191 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId);
2192 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, widget.options);
2193 sendBroadcastAsUser(intent, widget.provider.info.getProfile());
2196 private void registerForBroadcastsLocked(Provider provider, int[] appWidgetIds) {
2197 if (provider.info.updatePeriodMillis > 0) {
2198 // if this is the first instance, set the alarm. otherwise,
2199 // rely on the fact that we've already set it and that
2200 // PendingIntent.getBroadcast will update the extras.
2201 boolean alreadyRegistered = provider.broadcast != null;
2202 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
2203 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
2204 intent.setComponent(provider.info.provider);
2205 long token = Binder.clearCallingIdentity();
2207 provider.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
2208 PendingIntent.FLAG_UPDATE_CURRENT, provider.info.getProfile());
2210 Binder.restoreCallingIdentity(token);
2212 if (!alreadyRegistered) {
2213 long period = provider.info.updatePeriodMillis;
2214 if (period < MIN_UPDATE_PERIOD) {
2215 period = MIN_UPDATE_PERIOD;
2217 final long oldId = Binder.clearCallingIdentity();
2219 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2220 SystemClock.elapsedRealtime() + period, period, provider.broadcast);
2222 Binder.restoreCallingIdentity(oldId);
2228 private static int[] getWidgetIds(ArrayList<Widget> widgets) {
2229 int instancesSize = widgets.size();
2230 int appWidgetIds[] = new int[instancesSize];
2231 for (int i = 0; i < instancesSize; i++) {
2232 appWidgetIds[i] = widgets.get(i).appWidgetId;
2234 return appWidgetIds;
2237 private static void dumpProvider(Provider provider, int index, PrintWriter pw) {
2238 AppWidgetProviderInfo info = provider.info;
2239 pw.print(" ["); pw.print(index); pw.print("] provider ");
2240 pw.println(provider.id);
2241 pw.print(" min=("); pw.print(info.minWidth);
2242 pw.print("x"); pw.print(info.minHeight);
2243 pw.print(") minResize=("); pw.print(info.minResizeWidth);
2244 pw.print("x"); pw.print(info.minResizeHeight);
2245 pw.print(") updatePeriodMillis=");
2246 pw.print(info.updatePeriodMillis);
2247 pw.print(" resizeMode=");
2248 pw.print(info.resizeMode);
2249 pw.print(info.widgetCategory);
2250 pw.print(" autoAdvanceViewId=");
2251 pw.print(info.autoAdvanceViewId);
2252 pw.print(" initialLayout=#");
2253 pw.print(Integer.toHexString(info.initialLayout));
2254 pw.print(" initialKeyguardLayout=#");
2255 pw.print(Integer.toHexString(info.initialKeyguardLayout));
2256 pw.print(" zombie="); pw.println(provider.zombie);
2259 private static void dumpHost(Host host, int index, PrintWriter pw) {
2260 pw.print(" ["); pw.print(index); pw.print("] hostId=");
2261 pw.println(host.id);
2262 pw.print(" callbacks="); pw.println(host.callbacks);
2263 pw.print(" widgets.size="); pw.print(host.widgets.size());
2264 pw.print(" zombie="); pw.println(host.zombie);
2267 private static void dumpGrant(Pair<Integer, String> grant, int index, PrintWriter pw) {
2268 pw.print(" ["); pw.print(index); pw.print(']');
2269 pw.print(" user="); pw.print(grant.first);
2270 pw.print(" package="); pw.println(grant.second);
2273 private static void dumpWidget(Widget widget, int index, PrintWriter pw) {
2274 pw.print(" ["); pw.print(index); pw.print("] id=");
2275 pw.println(widget.appWidgetId);
2277 pw.println(widget.host.id);
2278 if (widget.provider != null) {
2279 pw.print(" provider="); pw.println(widget.provider.id);
2281 if (widget.host != null) {
2282 pw.print(" host.callbacks="); pw.println(widget.host.callbacks);
2284 if (widget.views != null) {
2285 pw.print(" views="); pw.println(widget.views);
2289 private static void serializeProvider(XmlSerializer out, Provider p) throws IOException {
2290 out.startTag(null, "p");
2291 out.attribute(null, "pkg", p.info.provider.getPackageName());
2292 out.attribute(null, "cl", p.info.provider.getClassName());
2293 out.attribute(null, "tag", Integer.toHexString(p.tag));
2294 out.endTag(null, "p");
2297 private static void serializeHost(XmlSerializer out, Host host) throws IOException {
2298 out.startTag(null, "h");
2299 out.attribute(null, "pkg", host.id.packageName);
2300 out.attribute(null, "id", Integer.toHexString(host.id.hostId));
2301 out.attribute(null, "tag", Integer.toHexString(host.tag));
2302 out.endTag(null, "h");
2305 private static void serializeAppWidget(XmlSerializer out, Widget widget) throws IOException {
2306 out.startTag(null, "g");
2307 out.attribute(null, "id", Integer.toHexString(widget.appWidgetId));
2308 out.attribute(null, "rid", Integer.toHexString(widget.restoredId));
2309 out.attribute(null, "h", Integer.toHexString(widget.host.tag));
2310 if (widget.provider != null) {
2311 out.attribute(null, "p", Integer.toHexString(widget.provider.tag));
2313 if (widget.options != null) {
2314 out.attribute(null, "min_width", Integer.toHexString(widget.options.getInt(
2315 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
2316 out.attribute(null, "min_height", Integer.toHexString(widget.options.getInt(
2317 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
2318 out.attribute(null, "max_width", Integer.toHexString(widget.options.getInt(
2319 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
2320 out.attribute(null, "max_height", Integer.toHexString(widget.options.getInt(
2321 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
2322 out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt(
2323 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
2325 out.endTag(null, "g");
2329 public List<String> getWidgetParticipants(int userId) {
2330 return mBackupRestoreController.getWidgetParticipants(userId);
2334 public byte[] getWidgetState(String packageName, int userId) {
2335 return mBackupRestoreController.getWidgetState(packageName, userId);
2339 public void restoreStarting(int userId) {
2340 mBackupRestoreController.restoreStarting(userId);
2344 public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
2345 mBackupRestoreController.restoreWidgetState(packageName, restoredState, userId);
2349 public void restoreFinished(int userId) {
2350 mBackupRestoreController.restoreFinished(userId);
2353 @SuppressWarnings("deprecation")
2354 private Provider parseProviderInfoXml(ProviderId providerId, ResolveInfo ri) {
2355 Provider provider = null;
2357 ActivityInfo activityInfo = ri.activityInfo;
2358 XmlResourceParser parser = null;
2360 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
2361 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
2362 if (parser == null) {
2363 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
2364 + " meta-data for " + "AppWidget provider '" + providerId + '\'');
2368 AttributeSet attrs = Xml.asAttributeSet(parser);
2371 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
2372 && type != XmlPullParser.START_TAG) {
2373 // drain whitespace, comments, etc.
2376 String nodeName = parser.getName();
2377 if (!"appwidget-provider".equals(nodeName)) {
2378 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
2379 + " AppWidget provider " + providerId.componentName
2380 + " for user " + providerId.uid);
2384 provider = new Provider();
2385 provider.id = providerId;
2386 AppWidgetProviderInfo info = provider.info = new AppWidgetProviderInfo();
2387 info.provider = providerId.componentName;
2388 info.providerInfo = activityInfo;
2390 final Resources resources;
2391 final long identity = Binder.clearCallingIdentity();
2393 final PackageManager pm = mContext.getPackageManager();
2394 final int userId = UserHandle.getUserId(providerId.uid);
2395 final ApplicationInfo app = pm.getApplicationInfoAsUser(activityInfo.packageName,
2397 resources = pm.getResourcesForApplication(app);
2399 Binder.restoreCallingIdentity(identity);
2402 TypedArray sa = resources.obtainAttributes(attrs,
2403 com.android.internal.R.styleable.AppWidgetProviderInfo);
2405 // These dimensions has to be resolved in the application's context.
2406 // We simply send back the raw complex data, which will be
2407 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
2408 TypedValue value = sa
2409 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
2410 info.minWidth = value != null ? value.data : 0;
2411 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
2412 info.minHeight = value != null ? value.data : 0;
2413 value = sa.peekValue(
2414 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
2415 info.minResizeWidth = value != null ? value.data : info.minWidth;
2416 value = sa.peekValue(
2417 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
2418 info.minResizeHeight = value != null ? value.data : info.minHeight;
2419 info.updatePeriodMillis = sa.getInt(
2420 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
2421 info.initialLayout = sa.getResourceId(
2422 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
2423 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
2424 AppWidgetProviderInfo_initialKeyguardLayout, 0);
2426 String className = sa
2427 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
2428 if (className != null) {
2429 info.configure = new ComponentName(providerId.componentName.getPackageName(),
2432 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
2433 info.icon = ri.getIconResource();
2434 info.previewImage = sa.getResourceId(
2435 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
2436 info.autoAdvanceViewId = sa.getResourceId(
2437 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
2438 info.resizeMode = sa.getInt(
2439 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
2440 AppWidgetProviderInfo.RESIZE_NONE);
2441 info.widgetCategory = sa.getInt(
2442 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
2443 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
2446 } catch (IOException | PackageManager.NameNotFoundException | XmlPullParserException e) {
2447 // Ok to catch Exception here, because anything going wrong because
2448 // of what a client process passes to us should not be fatal for the
2450 Slog.w(TAG, "XML parsing failed for AppWidget provider "
2451 + providerId.componentName + " for user " + providerId.uid, e);
2454 if (parser != null) {
2461 private int getUidForPackage(String packageName, int userId) {
2462 PackageInfo pkgInfo = null;
2464 final long identity = Binder.clearCallingIdentity();
2466 pkgInfo = mPackageManager.getPackageInfo(packageName, 0, userId);
2467 } catch (RemoteException re) {
2468 // Shouldn't happen, local call
2470 Binder.restoreCallingIdentity(identity);
2473 if (pkgInfo == null || pkgInfo.applicationInfo == null) {
2477 return pkgInfo.applicationInfo.uid;
2480 private ActivityInfo getProviderInfo(ComponentName componentName, int userId) {
2481 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
2482 intent.setComponent(componentName);
2484 List<ResolveInfo> receivers = queryIntentReceivers(intent, userId);
2485 // We are setting component, so there is only one or none.
2486 if (!receivers.isEmpty()) {
2487 return receivers.get(0).activityInfo;
2493 private List<ResolveInfo> queryIntentReceivers(Intent intent, int userId) {
2494 final long identity = Binder.clearCallingIdentity();
2496 int flags = PackageManager.GET_META_DATA;
2498 // We really need packages to be around and parsed to know if they
2500 flags |= PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
2502 // Widget hosts that are non-crypto aware may be hosting widgets
2503 // from a profile that is still locked, so let them see those
2505 if (isProfileWithUnlockedParent(userId)) {
2506 flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
2507 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
2510 // Widgets referencing shared libraries need to have their
2511 // dependencies loaded.
2512 flags |= PackageManager.GET_SHARED_LIBRARY_FILES;
2514 return mPackageManager.queryIntentReceivers(intent,
2515 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
2516 flags, userId).getList();
2517 } catch (RemoteException re) {
2518 return Collections.emptyList();
2520 Binder.restoreCallingIdentity(identity);
2524 private void onUserUnlocked(int userId) {
2525 if (isProfileWithLockedParent(userId)) {
2528 synchronized (mLock) {
2529 ensureGroupStateLoadedLocked(userId);
2530 reloadWidgetsMaskedStateForGroup(mSecurityPolicy.getGroupParent(userId));
2532 final int N = mProviders.size();
2533 for (int i = 0; i < N; i++) {
2534 Provider provider = mProviders.get(i);
2536 // Send broadcast only to the providers of the user.
2537 if (provider.getUserId() != userId) {
2541 if (provider.widgets.size() > 0) {
2542 sendEnableIntentLocked(provider);
2543 int[] appWidgetIds = getWidgetIds(provider.widgets);
2544 sendUpdateIntentLocked(provider, appWidgetIds);
2545 registerForBroadcastsLocked(provider, appWidgetIds);
2551 // only call from initialization -- it assumes that the data structures are all empty
2552 private void loadGroupStateLocked(int[] profileIds) {
2553 // We can bind the widgets to host and providers only after
2554 // reading the host and providers for all users since a widget
2555 // can have a host and a provider in different users.
2556 List<LoadedWidgetState> loadedWidgets = new ArrayList<>();
2560 final int profileIdCount = profileIds.length;
2561 for (int i = 0; i < profileIdCount; i++) {
2562 final int profileId = profileIds[i];
2564 // No file written for this user - nothing to do.
2565 AtomicFile file = getSavedStateFile(profileId);
2567 FileInputStream stream = file.openRead();
2568 version = readProfileStateFromFileLocked(stream, profileId, loadedWidgets);
2569 IoUtils.closeQuietly(stream);
2570 } catch (FileNotFoundException e) {
2571 Slog.w(TAG, "Failed to read state: " + e);
2577 bindLoadedWidgetsLocked(loadedWidgets);
2579 // upgrade the database if needed
2580 performUpgradeLocked(version);
2582 // failed reading, clean up
2583 Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
2584 clearWidgetsLocked();
2586 final int N = mProviders.size();
2587 for (int i = 0; i < N; i++) {
2588 mProviders.get(i).widgets.clear();
2593 private void bindLoadedWidgetsLocked(List<LoadedWidgetState> loadedWidgets) {
2594 final int loadedWidgetCount = loadedWidgets.size();
2595 for (int i = loadedWidgetCount - 1; i >= 0; i--) {
2596 LoadedWidgetState loadedWidget = loadedWidgets.remove(i);
2597 Widget widget = loadedWidget.widget;
2599 widget.provider = findProviderByTag(loadedWidget.providerTag);
2600 if (widget.provider == null) {
2601 // This provider is gone. We just let the host figure out
2602 // that this happened when it fails to load it.
2606 widget.host = findHostByTag(loadedWidget.hostTag);
2607 if (widget.host == null) {
2608 // This host is gone.
2612 widget.provider.widgets.add(widget);
2613 widget.host.widgets.add(widget);
2614 addWidgetLocked(widget);
2618 private Provider findProviderByTag(int tag) {
2622 final int providerCount = mProviders.size();
2623 for (int i = 0; i < providerCount; i++) {
2624 Provider provider = mProviders.get(i);
2625 if (provider.tag == tag) {
2632 private Host findHostByTag(int tag) {
2636 final int hostCount = mHosts.size();
2637 for (int i = 0; i < hostCount; i++) {
2638 Host host = mHosts.get(i);
2639 if (host.tag == tag) {
2647 * Adds the widget to mWidgets and tracks the package name in mWidgetPackages.
2649 void addWidgetLocked(Widget widget) {
2650 mWidgets.add(widget);
2652 onWidgetProviderAddedOrChangedLocked(widget);
2656 * Checks if the provider is assigned and updates the mWidgetPackages to track packages
2657 * that have bound widgets.
2659 void onWidgetProviderAddedOrChangedLocked(Widget widget) {
2660 if (widget.provider == null) return;
2662 int userId = widget.provider.getUserId();
2663 ArraySet<String> packages = mWidgetPackages.get(userId);
2664 if (packages == null) {
2665 mWidgetPackages.put(userId, packages = new ArraySet<String>());
2667 packages.add(widget.provider.info.provider.getPackageName());
2669 // If we are adding a widget it might be for a provider that
2670 // is currently masked, if so mask the widget.
2671 if (widget.provider.isMaskedLocked()) {
2672 maskWidgetsViewsLocked(widget.provider, widget);
2674 widget.clearMaskedViewsLocked();
2679 * Removes a widget from mWidgets and updates the cache of bound widget provider packages.
2680 * If there are other widgets with the same package, leaves it in the cache, otherwise it
2681 * removes the associated package from the cache.
2683 void removeWidgetLocked(Widget widget) {
2684 mWidgets.remove(widget);
2686 onWidgetRemovedLocked(widget);
2689 private void onWidgetRemovedLocked(Widget widget) {
2690 if (widget.provider == null) return;
2692 final int userId = widget.provider.getUserId();
2693 final String packageName = widget.provider.info.provider.getPackageName();
2694 ArraySet<String> packages = mWidgetPackages.get(userId);
2695 if (packages == null) {
2698 // Check if there is any other widget with the same package name.
2699 // Remove packageName if none.
2700 final int N = mWidgets.size();
2701 for (int i = 0; i < N; i++) {
2702 Widget w = mWidgets.get(i);
2703 if (w.provider == null) continue;
2704 if (w.provider.getUserId() == userId
2705 && packageName.equals(w.provider.info.provider.getPackageName())) {
2709 packages.remove(packageName);
2713 * Clears all widgets and associated cache of packages with bound widgets.
2715 void clearWidgetsLocked() {
2718 onWidgetsClearedLocked();
2721 private void onWidgetsClearedLocked() {
2722 mWidgetPackages.clear();
2726 public boolean isBoundWidgetPackage(String packageName, int userId) {
2727 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2728 throw new SecurityException("Only the system process can call this");
2730 synchronized (mLock) {
2731 final ArraySet<String> packages = mWidgetPackages.get(userId);
2732 if (packages != null) {
2733 return packages.contains(packageName);
2739 private void saveStateLocked(int userId) {
2740 tagProvidersAndHosts();
2742 final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
2744 final int profileCount = profileIds.length;
2745 for (int i = 0; i < profileCount; i++) {
2746 final int profileId = profileIds[i];
2748 AtomicFile file = getSavedStateFile(profileId);
2749 FileOutputStream stream;
2751 stream = file.startWrite();
2752 if (writeProfileStateToFileLocked(stream, profileId)) {
2753 file.finishWrite(stream);
2755 file.failWrite(stream);
2756 Slog.w(TAG, "Failed to save state, restoring backup.");
2758 } catch (IOException e) {
2759 Slog.w(TAG, "Failed open state file for write: " + e);
2764 private void tagProvidersAndHosts() {
2765 final int providerCount = mProviders.size();
2766 for (int i = 0; i < providerCount; i++) {
2767 Provider provider = mProviders.get(i);
2771 final int hostCount = mHosts.size();
2772 for (int i = 0; i < hostCount; i++) {
2773 Host host = mHosts.get(i);
2778 private void clearProvidersAndHostsTagsLocked() {
2779 final int providerCount = mProviders.size();
2780 for (int i = 0; i < providerCount; i++) {
2781 Provider provider = mProviders.get(i);
2782 provider.tag = TAG_UNDEFINED;
2785 final int hostCount = mHosts.size();
2786 for (int i = 0; i < hostCount; i++) {
2787 Host host = mHosts.get(i);
2788 host.tag = TAG_UNDEFINED;
2792 private boolean writeProfileStateToFileLocked(FileOutputStream stream, int userId) {
2796 XmlSerializer out = new FastXmlSerializer();
2797 out.setOutput(stream, StandardCharsets.UTF_8.name());
2798 out.startDocument(null, true);
2799 out.startTag(null, "gs");
2800 out.attribute(null, "version", String.valueOf(CURRENT_VERSION));
2802 N = mProviders.size();
2803 for (int i = 0; i < N; i++) {
2804 Provider provider = mProviders.get(i);
2805 // Save only providers for the user.
2806 if (provider.getUserId() != userId) {
2809 if (provider.widgets.size() > 0) {
2810 serializeProvider(out, provider);
2815 for (int i = 0; i < N; i++) {
2816 Host host = mHosts.get(i);
2817 // Save only hosts for the user.
2818 if (host.getUserId() != userId) {
2821 serializeHost(out, host);
2824 N = mWidgets.size();
2825 for (int i = 0; i < N; i++) {
2826 Widget widget = mWidgets.get(i);
2827 // Save only widgets hosted by the user.
2828 if (widget.host.getUserId() != userId) {
2831 serializeAppWidget(out, widget);
2834 Iterator<Pair<Integer, String>> it = mPackagesWithBindWidgetPermission.iterator();
2835 while (it.hasNext()) {
2836 Pair<Integer, String> binding = it.next();
2837 // Save only white listings for the user.
2838 if (binding.first != userId) {
2841 out.startTag(null, "b");
2842 out.attribute(null, "packageName", binding.second);
2843 out.endTag(null, "b");
2846 out.endTag(null, "gs");
2849 } catch (IOException e) {
2850 Slog.w(TAG, "Failed to write state: " + e);
2855 private int readProfileStateFromFileLocked(FileInputStream stream, int userId,
2856 List<LoadedWidgetState> outLoadedWidgets) {
2859 XmlPullParser parser = Xml.newPullParser();
2860 parser.setInput(stream, StandardCharsets.UTF_8.name());
2862 int legacyProviderIndex = -1;
2863 int legacyHostIndex = -1;
2866 type = parser.next();
2867 if (type == XmlPullParser.START_TAG) {
2868 String tag = parser.getName();
2869 if ("gs".equals(tag)) {
2870 String attributeValue = parser.getAttributeValue(null, "version");
2872 version = Integer.parseInt(attributeValue);
2873 } catch (NumberFormatException e) {
2876 } else if ("p".equals(tag)) {
2877 legacyProviderIndex++;
2878 // TODO: do we need to check that this package has the same signature
2880 String pkg = parser.getAttributeValue(null, "pkg");
2881 String cl = parser.getAttributeValue(null, "cl");
2883 pkg = getCanonicalPackageName(pkg, cl, userId);
2888 final int uid = getUidForPackage(pkg, userId);
2893 ComponentName componentName = new ComponentName(pkg, cl);
2895 ActivityInfo providerInfo = getProviderInfo(componentName, userId);
2896 if (providerInfo == null) {
2900 ProviderId providerId = new ProviderId(uid, componentName);
2901 Provider provider = lookupProviderLocked(providerId);
2903 if (provider == null && mSafeMode) {
2904 // if we're in safe mode, make a temporary one
2905 provider = new Provider();
2906 provider.info = new AppWidgetProviderInfo();
2907 provider.info.provider = providerId.componentName;
2908 provider.info.providerInfo = providerInfo;
2909 provider.zombie = true;
2910 provider.id = providerId;
2911 mProviders.add(provider);
2914 String tagAttribute = parser.getAttributeValue(null, "tag");
2915 final int providerTag = !TextUtils.isEmpty(tagAttribute)
2916 ? Integer.parseInt(tagAttribute, 16) : legacyProviderIndex;
2917 provider.tag = providerTag;
2918 } else if ("h".equals(tag)) {
2920 Host host = new Host();
2921 // TODO: do we need to check that this package has the same signature
2923 String pkg = parser.getAttributeValue(null, "pkg");
2925 final int uid = getUidForPackage(pkg, userId);
2930 if (!host.zombie || mSafeMode) {
2931 // In safe mode, we don't discard the hosts we don't recognize
2932 // so that they're not pruned from our list. Otherwise, we do.
2933 final int hostId = Integer.parseInt(parser.getAttributeValue(
2936 String tagAttribute = parser.getAttributeValue(null, "tag");
2937 final int hostTag = !TextUtils.isEmpty(tagAttribute)
2938 ? Integer.parseInt(tagAttribute, 16) : legacyHostIndex;
2941 host.id = new HostId(uid, hostId, pkg);
2944 } else if ("b".equals(tag)) {
2945 String packageName = parser.getAttributeValue(null, "packageName");
2946 final int uid = getUidForPackage(packageName, userId);
2948 Pair<Integer, String> packageId = Pair.create(userId, packageName);
2949 mPackagesWithBindWidgetPermission.add(packageId);
2951 } else if ("g".equals(tag)) {
2952 Widget widget = new Widget();
2953 widget.appWidgetId = Integer.parseInt(parser.getAttributeValue(
2955 setMinAppWidgetIdLocked(userId, widget.appWidgetId + 1);
2957 // restored ID is allowed to be absent
2958 String restoredIdString = parser.getAttributeValue(null, "rid");
2959 widget.restoredId = (restoredIdString == null) ? 0
2960 : Integer.parseInt(restoredIdString, 16);
2962 Bundle options = new Bundle();
2963 String minWidthString = parser.getAttributeValue(null, "min_width");
2964 if (minWidthString != null) {
2965 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
2966 Integer.parseInt(minWidthString, 16));
2968 String minHeightString = parser.getAttributeValue(null, "min_height");
2969 if (minHeightString != null) {
2970 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
2971 Integer.parseInt(minHeightString, 16));
2973 String maxWidthString = parser.getAttributeValue(null, "max_width");
2974 if (maxWidthString != null) {
2975 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
2976 Integer.parseInt(maxWidthString, 16));
2978 String maxHeightString = parser.getAttributeValue(null, "max_height");
2979 if (maxHeightString != null) {
2980 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
2981 Integer.parseInt(maxHeightString, 16));
2983 String categoryString = parser.getAttributeValue(null, "host_category");
2984 if (categoryString != null) {
2985 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
2986 Integer.parseInt(categoryString, 16));
2988 widget.options = options;
2990 final int hostTag = Integer.parseInt(parser.getAttributeValue(
2992 String providerString = parser.getAttributeValue(null, "p");
2993 final int providerTag = (providerString != null) ? Integer.parseInt(
2994 parser.getAttributeValue(null, "p"), 16) : TAG_UNDEFINED;
2996 // We can match widgets with hosts and providers only after hosts
2997 // and providers for all users have been loaded since the widget
2998 // host and provider can be in different user profiles.
2999 LoadedWidgetState loadedWidgets = new LoadedWidgetState(widget,
3000 hostTag, providerTag);
3001 outLoadedWidgets.add(loadedWidgets);
3004 } while (type != XmlPullParser.END_DOCUMENT);
3005 } catch (NullPointerException
3006 | NumberFormatException
3007 | XmlPullParserException
3009 | IndexOutOfBoundsException e) {
3010 Slog.w(TAG, "failed parsing " + e);
3017 private void performUpgradeLocked(int fromVersion) {
3018 if (fromVersion < CURRENT_VERSION) {
3019 Slog.v(TAG, "Upgrading widget database from " + fromVersion + " to "
3023 int version = fromVersion;
3025 // Update 1: keyguard moved from package "android" to "com.android.keyguard"
3027 HostId oldHostId = new HostId(Process.myUid(),
3028 KEYGUARD_HOST_ID, OLD_KEYGUARD_HOST_PACKAGE);
3030 Host host = lookupHostLocked(oldHostId);
3032 final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
3033 UserHandle.USER_SYSTEM);
3035 host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
3042 if (version != CURRENT_VERSION) {
3043 throw new IllegalStateException("Failed to upgrade widget database");
3047 private static File getStateFile(int userId) {
3048 return new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME);
3051 private static AtomicFile getSavedStateFile(int userId) {
3052 File dir = Environment.getUserSystemDirectory(userId);
3053 File settingsFile = getStateFile(userId);
3054 if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) {
3055 if (!dir.exists()) {
3059 File oldFile = new File("/data/system/" + STATE_FILENAME);
3060 // Method doesn't throw an exception on failure. Ignore any errors
3061 // in moving the file (like non-existence)
3062 oldFile.renameTo(settingsFile);
3064 return new AtomicFile(settingsFile);
3067 private void onUserStopped(int userId) {
3068 synchronized (mLock) {
3069 boolean crossProfileWidgetsChanged = false;
3071 // Remove widgets that have both host and provider in the user.
3072 final int widgetCount = mWidgets.size();
3073 for (int i = widgetCount - 1; i >= 0; i--) {
3074 Widget widget = mWidgets.get(i);
3076 final boolean hostInUser = widget.host.getUserId() == userId;
3077 final boolean hasProvider = widget.provider != null;
3078 final boolean providerInUser = hasProvider && widget.provider.getUserId() == userId;
3080 // If both host and provider are in the user, just drop the widgets
3081 // as we do not want to make host callbacks and provider broadcasts
3082 // as the host and the provider will be killed.
3083 if (hostInUser && (!hasProvider || providerInUser)) {
3084 removeWidgetLocked(widget);
3085 widget.host.widgets.remove(widget);
3088 widget.provider.widgets.remove(widget);
3089 widget.provider = null;
3094 // Remove hosts and notify providers in other profiles.
3095 final int hostCount = mHosts.size();
3096 for (int i = hostCount - 1; i >= 0; i--) {
3097 Host host = mHosts.get(i);
3098 if (host.getUserId() == userId) {
3099 crossProfileWidgetsChanged |= !host.widgets.isEmpty();
3100 deleteHostLocked(host);
3104 // Leave the providers present as hosts will show the widgets
3105 // masked while the user is stopped.
3107 // Remove grants for this user.
3108 final int grantCount = mPackagesWithBindWidgetPermission.size();
3109 for (int i = grantCount - 1; i >= 0; i--) {
3110 Pair<Integer, String> packageId = mPackagesWithBindWidgetPermission.valueAt(i);
3111 if (packageId.first == userId) {
3112 mPackagesWithBindWidgetPermission.removeAt(i);
3116 // Take a note we no longer have state for this user.
3117 final int userIndex = mLoadedUserIds.indexOfKey(userId);
3118 if (userIndex >= 0) {
3119 mLoadedUserIds.removeAt(userIndex);
3122 // Remove the widget id counter.
3123 final int nextIdIndex = mNextAppWidgetIds.indexOfKey(userId);
3124 if (nextIdIndex >= 0) {
3125 mNextAppWidgetIds.removeAt(nextIdIndex);
3128 // Save state if removing a profile changed the group state.
3129 // Nothing will be saved if the group parent was removed.
3130 if (crossProfileWidgetsChanged) {
3131 saveGroupStateAsync(userId);
3137 * Updates all providers with the specified package names, and records any providers that were
3140 * @return whether any providers were updated
3142 private boolean updateProvidersForPackageLocked(String packageName, int userId,
3143 Set<ProviderId> removedProviders) {
3144 boolean providersUpdated = false;
3146 HashSet<ProviderId> keep = new HashSet<>();
3147 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
3148 intent.setPackage(packageName);
3149 List<ResolveInfo> broadcastReceivers = queryIntentReceivers(intent, userId);
3151 // add the missing ones and collect which ones to keep
3152 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
3153 for (int i = 0; i < N; i++) {
3154 ResolveInfo ri = broadcastReceivers.get(i);
3155 ActivityInfo ai = ri.activityInfo;
3157 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
3161 if (packageName.equals(ai.packageName)) {
3162 ProviderId providerId = new ProviderId(ai.applicationInfo.uid,
3163 new ComponentName(ai.packageName, ai.name));
3165 Provider provider = lookupProviderLocked(providerId);
3166 if (provider == null) {
3167 if (addProviderLocked(ri)) {
3168 keep.add(providerId);
3169 providersUpdated = true;
3172 Provider parsed = parseProviderInfoXml(providerId, ri);
3173 if (parsed != null) {
3174 keep.add(providerId);
3175 // Use the new AppWidgetProviderInfo.
3176 provider.info = parsed.info;
3178 final int M = provider.widgets.size();
3180 int[] appWidgetIds = getWidgetIds(provider.widgets);
3181 // Reschedule for the new updatePeriodMillis (don't worry about handling
3182 // it specially if updatePeriodMillis didn't change because we just sent
3183 // an update, and the next one will be updatePeriodMillis from now).
3184 cancelBroadcasts(provider);
3185 registerForBroadcastsLocked(provider, appWidgetIds);
3186 // If it's currently showing, call back with the new
3187 // AppWidgetProviderInfo.
3188 for (int j = 0; j < M; j++) {
3189 Widget widget = provider.widgets.get(j);
3190 widget.views = null;
3191 scheduleNotifyProviderChangedLocked(widget);
3193 // Now that we've told the host, push out an update.
3194 sendUpdateIntentLocked(provider, appWidgetIds);
3197 providersUpdated = true;
3202 // prune the ones we don't want to keep
3203 N = mProviders.size();
3204 for (int i = N - 1; i >= 0; i--) {
3205 Provider provider = mProviders.get(i);
3206 if (packageName.equals(provider.info.provider.getPackageName())
3207 && provider.getUserId() == userId
3208 && !keep.contains(provider.id)) {
3209 if (removedProviders != null) {
3210 removedProviders.add(provider.id);
3212 deleteProviderLocked(provider);
3213 providersUpdated = true;
3217 return providersUpdated;
3220 // Remove widgets for provider in userId that are hosted in parentUserId
3221 private void removeWidgetsForPackageLocked(String pkgName, int userId, int parentUserId) {
3222 final int N = mProviders.size();
3223 for (int i = 0; i < N; ++i) {
3224 Provider provider = mProviders.get(i);
3225 if (pkgName.equals(provider.info.provider.getPackageName())
3226 && provider.getUserId() == userId
3227 && provider.widgets.size() > 0) {
3228 deleteWidgetsLocked(provider, parentUserId);
3233 private boolean removeProvidersForPackageLocked(String pkgName, int userId) {
3234 boolean removed = false;
3236 final int N = mProviders.size();
3237 for (int i = N - 1; i >= 0; i--) {
3238 Provider provider = mProviders.get(i);
3239 if (pkgName.equals(provider.info.provider.getPackageName())
3240 && provider.getUserId() == userId) {
3241 deleteProviderLocked(provider);
3248 private boolean removeHostsAndProvidersForPackageLocked(String pkgName, int userId) {
3249 boolean removed = removeProvidersForPackageLocked(pkgName, userId);
3251 // Delete the hosts for this package too
3252 // By now, we have removed any AppWidgets that were in any hosts here,
3253 // so we don't need to worry about sending DISABLE broadcasts to them.
3254 final int N = mHosts.size();
3255 for (int i = N - 1; i >= 0; i--) {
3256 Host host = mHosts.get(i);
3257 if (pkgName.equals(host.id.packageName)
3258 && host.getUserId() == userId) {
3259 deleteHostLocked(host);
3267 private String getCanonicalPackageName(String packageName, String className, int userId) {
3268 final long identity = Binder.clearCallingIdentity();
3271 AppGlobals.getPackageManager().getReceiverInfo(new ComponentName(packageName,
3272 className), 0, userId);
3274 } catch (RemoteException re) {
3275 String[] packageNames = mContext.getPackageManager()
3276 .currentToCanonicalPackageNames(new String[]{packageName});
3277 if (packageNames != null && packageNames.length > 0) {
3278 return packageNames[0];
3282 Binder.restoreCallingIdentity(identity);
3287 private void sendBroadcastAsUser(Intent intent, UserHandle userHandle) {
3288 final long identity = Binder.clearCallingIdentity();
3290 mContext.sendBroadcastAsUser(intent, userHandle);
3292 Binder.restoreCallingIdentity(identity);
3296 private void bindService(Intent intent, ServiceConnection connection,
3297 UserHandle userHandle) {
3298 final long token = Binder.clearCallingIdentity();
3300 mContext.bindServiceAsUser(intent, connection,
3301 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
3304 Binder.restoreCallingIdentity(token);
3308 private void unbindService(ServiceConnection connection) {
3309 final long token = Binder.clearCallingIdentity();
3311 mContext.unbindService(connection);
3313 Binder.restoreCallingIdentity(token);
3318 public void onCrossProfileWidgetProvidersChanged(int userId, List<String> packages) {
3319 final int parentId = mSecurityPolicy.getProfileParent(userId);
3320 // We care only if the white-listed package is in a profile of
3321 // the group parent as only the parent can add widgets from the
3322 // profile and not the other way around.
3323 if (parentId != userId) {
3324 synchronized (mLock) {
3325 boolean providersChanged = false;
3327 ArraySet<String> previousPackages = new ArraySet<String>();
3328 final int providerCount = mProviders.size();
3329 for (int i = 0; i < providerCount; ++i) {
3330 Provider provider = mProviders.get(i);
3331 if (provider.getUserId() == userId) {
3332 previousPackages.add(provider.id.componentName.getPackageName());
3336 final int packageCount = packages.size();
3337 for (int i = 0; i < packageCount; i++) {
3338 String packageName = packages.get(i);
3339 previousPackages.remove(packageName);
3340 providersChanged |= updateProvidersForPackageLocked(packageName,
3344 // Remove widgets from hosts in parent user for packages not in the whitelist
3345 final int removedCount = previousPackages.size();
3346 for (int i = 0; i < removedCount; ++i) {
3347 removeWidgetsForPackageLocked(previousPackages.valueAt(i),
3351 if (providersChanged || removedCount > 0) {
3352 saveGroupStateAsync(userId);
3353 scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
3359 private boolean isProfileWithLockedParent(int userId) {
3360 long token = Binder.clearCallingIdentity();
3362 UserInfo userInfo = mUserManager.getUserInfo(userId);
3363 if (userInfo != null && userInfo.isManagedProfile()) {
3364 UserInfo parentInfo = mUserManager.getProfileParent(userId);
3365 if (parentInfo != null
3366 && !mUserManager.isUserUnlocked(parentInfo.getUserHandle())) {
3371 Binder.restoreCallingIdentity(token);
3376 private boolean isProfileWithUnlockedParent(int userId) {
3377 UserInfo userInfo = mUserManager.getUserInfo(userId);
3378 if (userInfo != null && userInfo.isManagedProfile()) {
3379 UserInfo parentInfo = mUserManager.getProfileParent(userId);
3380 if (parentInfo != null
3381 && mUserManager.isUserUnlocked(parentInfo.getUserHandle())) {
3388 private final class CallbackHandler extends Handler {
3389 public static final int MSG_NOTIFY_UPDATE_APP_WIDGET = 1;
3390 public static final int MSG_NOTIFY_PROVIDER_CHANGED = 2;
3391 public static final int MSG_NOTIFY_PROVIDERS_CHANGED = 3;
3392 public static final int MSG_NOTIFY_VIEW_DATA_CHANGED = 4;
3394 public CallbackHandler(Looper looper) {
3395 super(looper, null, false);
3399 public void handleMessage(Message message) {
3400 switch (message.what) {
3401 case MSG_NOTIFY_UPDATE_APP_WIDGET: {
3402 SomeArgs args = (SomeArgs) message.obj;
3403 Host host = (Host) args.arg1;
3404 IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
3405 RemoteViews views = (RemoteViews) args.arg3;
3406 long requestTime = (Long) args.arg4;
3407 final int appWidgetId = args.argi1;
3410 handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views, requestTime);
3413 case MSG_NOTIFY_PROVIDER_CHANGED: {
3414 SomeArgs args = (SomeArgs) message.obj;
3415 Host host = (Host) args.arg1;
3416 IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
3417 AppWidgetProviderInfo info = (AppWidgetProviderInfo)args.arg3;
3418 final int appWidgetId = args.argi1;
3421 handleNotifyProviderChanged(host, callbacks, appWidgetId, info);
3424 case MSG_NOTIFY_PROVIDERS_CHANGED: {
3425 SomeArgs args = (SomeArgs) message.obj;
3426 Host host = (Host) args.arg1;
3427 IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
3430 handleNotifyProvidersChanged(host, callbacks);
3433 case MSG_NOTIFY_VIEW_DATA_CHANGED: {
3434 SomeArgs args = (SomeArgs) message.obj;
3435 Host host = (Host) args.arg1;
3436 IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
3437 final int appWidgetId = args.argi1;
3438 final int viewId = args.argi2;
3441 handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId);
3447 private final class SecurityPolicy {
3449 public boolean isEnabledGroupProfile(int profileId) {
3450 final int parentId = UserHandle.getCallingUserId();
3451 return isParentOrProfile(parentId, profileId) && isProfileEnabled(profileId);
3454 public int[] getEnabledGroupProfileIds(int userId) {
3455 final int parentId = getGroupParent(userId);
3457 final long identity = Binder.clearCallingIdentity();
3459 return mUserManager.getEnabledProfileIds(parentId);
3461 Binder.restoreCallingIdentity(identity);
3465 public void enforceServiceExistsAndRequiresBindRemoteViewsPermission(
3466 ComponentName componentName, int userId) {
3467 final long identity = Binder.clearCallingIdentity();
3469 ServiceInfo serviceInfo = mPackageManager.getServiceInfo(componentName,
3470 PackageManager.GET_PERMISSIONS, userId);
3471 if (serviceInfo == null) {
3472 throw new SecurityException("Service " + componentName
3473 + " not installed for user " + userId);
3475 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(serviceInfo.permission)) {
3476 throw new SecurityException("Service " + componentName
3477 + " in user " + userId + "does not require "
3478 + android.Manifest.permission.BIND_REMOTEVIEWS);
3480 } catch (RemoteException re) {
3481 // Local call - shouldn't happen.
3483 Binder.restoreCallingIdentity(identity);
3487 public void enforceModifyAppWidgetBindPermissions(String packageName) {
3488 mContext.enforceCallingPermission(
3489 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
3490 "hasBindAppWidgetPermission packageName=" + packageName);
3493 public void enforceCallFromPackage(String packageName) {
3494 mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
3497 public boolean hasCallerBindPermissionOrBindWhiteListedLocked(String packageName) {
3499 mContext.enforceCallingOrSelfPermission(
3500 android.Manifest.permission.BIND_APPWIDGET, null);
3501 } catch (SecurityException se) {
3502 if (!isCallerBindAppWidgetWhiteListedLocked(packageName)) {
3509 private boolean isCallerBindAppWidgetWhiteListedLocked(String packageName) {
3510 final int userId = UserHandle.getCallingUserId();
3511 final int packageUid = getUidForPackage(packageName, userId);
3512 if (packageUid < 0) {
3513 throw new IllegalArgumentException("No package " + packageName
3514 + " for user " + userId);
3516 synchronized (mLock) {
3517 ensureGroupStateLoadedLocked(userId);
3519 Pair<Integer, String> packageId = Pair.create(userId, packageName);
3520 if (mPackagesWithBindWidgetPermission.contains(packageId)) {
3528 public boolean canAccessAppWidget(Widget widget, int uid, String packageName) {
3529 if (isHostInPackageForUid(widget.host, uid, packageName)) {
3530 // Apps hosting the AppWidget have access to it.
3533 if (isProviderInPackageForUid(widget.provider, uid, packageName)) {
3534 // Apps providing the AppWidget have access to it.
3537 if (isHostAccessingProvider(widget.host, widget.provider, uid, packageName)) {
3538 // Apps hosting the AppWidget get to bind to a remote view service in the provider.
3541 final int userId = UserHandle.getUserId(uid);
3542 if ((widget.host.getUserId() == userId || (widget.provider != null
3543 && widget.provider.getUserId() == userId))
3544 && mContext.checkCallingPermission(android.Manifest.permission.BIND_APPWIDGET)
3545 == PackageManager.PERMISSION_GRANTED) {
3546 // Apps that run in the same user as either the host or the provider and
3547 // have the bind widget permission have access to the widget.
3553 private boolean isParentOrProfile(int parentId, int profileId) {
3554 if (parentId == profileId) {
3557 return getProfileParent(profileId) == parentId;
3560 public boolean isProviderInCallerOrInProfileAndWhitelListed(String packageName,
3562 final int callerId = UserHandle.getCallingUserId();
3563 if (profileId == callerId) {
3566 final int parentId = getProfileParent(profileId);
3567 if (parentId != callerId) {
3570 return isProviderWhiteListed(packageName, profileId);
3573 public boolean isProviderWhiteListed(String packageName, int profileId) {
3574 // If the policy manager is not available on the device we deny it all.
3575 if (mDevicePolicyManagerInternal == null) {
3579 List<String> crossProfilePackages = mDevicePolicyManagerInternal
3580 .getCrossProfileWidgetProviders(profileId);
3582 return crossProfilePackages.contains(packageName);
3585 public int getProfileParent(int profileId) {
3586 final long identity = Binder.clearCallingIdentity();
3588 UserInfo parent = mUserManager.getProfileParent(profileId);
3589 if (parent != null) {
3590 return parent.getUserHandle().getIdentifier();
3593 Binder.restoreCallingIdentity(identity);
3595 return UNKNOWN_USER_ID;
3598 public int getGroupParent(int profileId) {
3599 final int parentId = mSecurityPolicy.getProfileParent(profileId);
3600 return (parentId != UNKNOWN_USER_ID) ? parentId : profileId;
3603 public boolean isHostInPackageForUid(Host host, int uid, String packageName) {
3604 return host.id.uid == uid && host.id.packageName.equals(packageName);
3607 public boolean isProviderInPackageForUid(Provider provider, int uid,
3608 String packageName) {
3609 // Packages providing the AppWidget have access to it.
3610 return provider != null && provider.id.uid == uid
3611 && provider.id.componentName.getPackageName().equals(packageName);
3614 public boolean isHostAccessingProvider(Host host, Provider provider, int uid,
3615 String packageName) {
3616 // The host creates a package context to bind to remote views service in the provider.
3617 return host.id.uid == uid && provider != null
3618 && provider.id.componentName.getPackageName().equals(packageName);
3621 private boolean isProfileEnabled(int profileId) {
3622 final long identity = Binder.clearCallingIdentity();
3624 UserInfo userInfo = mUserManager.getUserInfo(profileId);
3625 if (userInfo == null || !userInfo.isEnabled()) {
3629 Binder.restoreCallingIdentity(identity);
3635 private static final class Provider {
3637 AppWidgetProviderInfo info;
3638 ArrayList<Widget> widgets = new ArrayList<>();
3639 PendingIntent broadcast;
3640 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
3642 boolean maskedByLockedProfile;
3643 boolean maskedByQuietProfile;
3644 boolean maskedBySuspendedPackage;
3646 int tag = TAG_UNDEFINED; // for use while saving state (the index)
3648 public int getUserId() {
3649 return UserHandle.getUserId(id.uid);
3652 public boolean isInPackageForUser(String packageName, int userId) {
3653 return getUserId() == userId
3654 && id.componentName.getPackageName().equals(packageName);
3657 // is there an instance of this provider hosted by the given app?
3658 public boolean hostedByPackageForUser(String packageName, int userId) {
3659 final int N = widgets.size();
3660 for (int i = 0; i < N; i++) {
3661 Widget widget = widgets.get(i);
3662 if (packageName.equals(widget.host.id.packageName)
3663 && widget.host.getUserId() == userId) {
3671 public String toString() {
3672 return "Provider{" + id + (zombie ? " Z" : "") + '}';
3675 // returns true if it's different from previous state.
3676 public boolean setMaskedByQuietProfileLocked(boolean masked) {
3677 boolean oldState = maskedByQuietProfile;
3678 maskedByQuietProfile = masked;
3679 return masked != oldState;
3682 // returns true if it's different from previous state.
3683 public boolean setMaskedByLockedProfileLocked(boolean masked) {
3684 boolean oldState = maskedByLockedProfile;
3685 maskedByLockedProfile = masked;
3686 return masked != oldState;
3689 // returns true if it's different from previous state.
3690 public boolean setMaskedBySuspendedPackageLocked(boolean masked) {
3691 boolean oldState = maskedBySuspendedPackage;
3692 maskedBySuspendedPackage = masked;
3693 return masked != oldState;
3696 public boolean isMaskedLocked() {
3697 return maskedByQuietProfile || maskedByLockedProfile || maskedBySuspendedPackage;
3701 private static final class ProviderId {
3703 final ComponentName componentName;
3705 private ProviderId(int uid, ComponentName componentName) {
3707 this.componentName = componentName;
3711 public boolean equals(Object obj) {
3718 if (getClass() != obj.getClass()) {
3721 ProviderId other = (ProviderId) obj;
3722 if (uid != other.uid) {
3725 if (componentName == null) {
3726 if (other.componentName != null) {
3729 } else if (!componentName.equals(other.componentName)) {
3736 public int hashCode() {
3738 result = 31 * result + ((componentName != null)
3739 ? componentName.hashCode() : 0);
3744 public String toString() {
3745 return "ProviderId{user:" + UserHandle.getUserId(uid) + ", app:"
3746 + UserHandle.getAppId(uid) + ", cmp:" + componentName + '}';
3750 private static final class Host {
3752 ArrayList<Widget> widgets = new ArrayList<>();
3753 IAppWidgetHost callbacks;
3754 boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
3756 int tag = TAG_UNDEFINED; // for use while saving state (the index)
3757 long lastWidgetUpdateTime; // last time we were successfully able to send an update.
3759 public int getUserId() {
3760 return UserHandle.getUserId(id.uid);
3763 public boolean isInPackageForUser(String packageName, int userId) {
3764 return getUserId() == userId && id.packageName.equals(packageName);
3767 private boolean hostsPackageForUser(String pkg, int userId) {
3768 final int N = widgets.size();
3769 for (int i = 0; i < N; i++) {
3770 Provider provider = widgets.get(i).provider;
3771 if (provider != null && provider.getUserId() == userId && provider.info != null
3772 && pkg.equals(provider.info.provider.getPackageName())) {
3780 * Returns the RemoveViews for the provided widget id if an update is pending
3783 public RemoteViews getPendingViewsForId(int appWidgetId) {
3784 long updateTime = lastWidgetUpdateTime;
3785 int N = widgets.size();
3786 for (int i = 0; i < N; i++) {
3787 Widget widget = widgets.get(i);
3788 if (widget.appWidgetId == appWidgetId
3789 && widget.lastUpdateTime > updateTime) {
3790 return cloneIfLocalBinder(widget.getEffectiveViewsLocked());
3797 public String toString() {
3798 return "Host{" + id + (zombie ? " Z" : "") + '}';
3802 private static final class HostId {
3805 final String packageName;
3807 public HostId(int uid, int hostId, String packageName) {
3809 this.hostId = hostId;
3810 this.packageName = packageName;
3814 public boolean equals(Object obj) {
3821 if (getClass() != obj.getClass()) {
3824 HostId other = (HostId) obj;
3825 if (uid != other.uid) {
3828 if (hostId != other.hostId) {
3831 if (packageName == null) {
3832 if (other.packageName != null) {
3835 } else if (!packageName.equals(other.packageName)) {
3842 public int hashCode() {
3844 result = 31 * result + hostId;
3845 result = 31 * result + ((packageName != null)
3846 ? packageName.hashCode() : 0);
3851 public String toString() {
3852 return "HostId{user:" + UserHandle.getUserId(uid) + ", app:"
3853 + UserHandle.getAppId(uid) + ", hostId:" + hostId
3854 + ", pkg:" + packageName + '}';
3858 private static final class Widget {
3860 int restoredId; // tracking & remapping any restored state
3863 RemoteViews maskedViews;
3866 long lastUpdateTime;
3869 public String toString() {
3870 return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}';
3873 private boolean replaceWithMaskedViewsLocked(RemoteViews views) {
3874 maskedViews = views;
3878 private boolean clearMaskedViewsLocked() {
3879 if (maskedViews != null) {
3887 public RemoteViews getEffectiveViewsLocked() {
3888 return maskedViews != null ? maskedViews : views;
3893 * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
3894 * needs to be a static inner class since a reference to the ServiceConnection is held globally
3895 * and may lead us to leak AppWidgetService instances (if there were more than one).
3897 private static final class ServiceConnectionProxy implements ServiceConnection {
3898 private final IRemoteViewsAdapterConnection mConnectionCb;
3900 ServiceConnectionProxy(IBinder connectionCb) {
3901 mConnectionCb = IRemoteViewsAdapterConnection.Stub
3902 .asInterface(connectionCb);
3905 public void onServiceConnected(ComponentName name, IBinder service) {
3907 mConnectionCb.onServiceConnected(service);
3908 } catch (RemoteException re) {
3909 Slog.e(TAG, "Error passing service interface", re);
3913 public void onServiceDisconnected(ComponentName name) {
3917 public void disconnect() {
3919 mConnectionCb.onServiceDisconnected();
3920 } catch (RemoteException re) {
3921 Slog.e(TAG, "Error clearing service interface", re);
3926 private class LoadedWidgetState {
3927 final Widget widget;
3929 final int providerTag;
3931 public LoadedWidgetState(Widget widget, int hostTag, int providerTag) {
3932 this.widget = widget;
3933 this.hostTag = hostTag;
3934 this.providerTag = providerTag;
3938 private final class SaveStateRunnable implements Runnable {
3941 public SaveStateRunnable(int userId) {
3947 synchronized (mLock) {
3948 ensureGroupStateLoadedLocked(mUserId);
3949 saveStateLocked(mUserId);
3955 * This class encapsulates the backup and restore logic for a user group state.
3957 private final class BackupRestoreController {
3958 private static final String TAG = "BackupRestoreController";
3960 private static final boolean DEBUG = true;
3962 // Version of backed-up widget state.
3963 private static final int WIDGET_STATE_VERSION = 2;
3965 // We need to make sure to wipe the pre-restore widget state only once for
3966 // a given package. Keep track of what we've done so far here; the list is
3967 // cleared at the start of every system restore pass, but preserved through
3968 // any install-time restore operations.
3969 private final HashSet<String> mPrunedApps = new HashSet<>();
3971 private final HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider =
3973 private final HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost =
3976 public List<String> getWidgetParticipants(int userId) {
3978 Slog.i(TAG, "Getting widget participants for user: " + userId);
3981 HashSet<String> packages = new HashSet<>();
3982 synchronized (mLock) {
3983 final int N = mWidgets.size();
3984 for (int i = 0; i < N; i++) {
3985 Widget widget = mWidgets.get(i);
3987 // Skip cross-user widgets.
3988 if (!isProviderAndHostInUser(widget, userId)) {
3992 packages.add(widget.host.id.packageName);
3993 Provider provider = widget.provider;
3994 if (provider != null) {
3995 packages.add(provider.id.componentName.getPackageName());
3999 return new ArrayList<>(packages);
4002 public byte[] getWidgetState(String backedupPackage, int userId) {
4004 Slog.i(TAG, "Getting widget state for user: " + userId);
4007 ByteArrayOutputStream stream = new ByteArrayOutputStream();
4008 synchronized (mLock) {
4009 // Preflight: if this app neither hosts nor provides any live widgets
4010 // we have no work to do.
4011 if (!packageNeedsWidgetBackupLocked(backedupPackage, userId)) {
4016 XmlSerializer out = new FastXmlSerializer();
4017 out.setOutput(stream, StandardCharsets.UTF_8.name());
4018 out.startDocument(null, true);
4019 out.startTag(null, "ws"); // widget state
4020 out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION));
4021 out.attribute(null, "pkg", backedupPackage);
4023 // Remember all the providers that are currently hosted or published
4024 // by this package: that is, all of the entities related to this app
4025 // which will need to be told about id remapping.
4027 int N = mProviders.size();
4028 for (int i = 0; i < N; i++) {
4029 Provider provider = mProviders.get(i);
4031 if (!provider.widgets.isEmpty()
4032 && (provider.isInPackageForUser(backedupPackage, userId)
4033 || provider.hostedByPackageForUser(backedupPackage, userId))) {
4034 provider.tag = index;
4035 serializeProvider(out, provider);
4042 for (int i = 0; i < N; i++) {
4043 Host host = mHosts.get(i);
4045 if (!host.widgets.isEmpty()
4046 && (host.isInPackageForUser(backedupPackage, userId)
4047 || host.hostsPackageForUser(backedupPackage, userId))) {
4049 serializeHost(out, host);
4054 // All widget instances involving this package,
4055 // either as host or as provider
4056 N = mWidgets.size();
4057 for (int i = 0; i < N; i++) {
4058 Widget widget = mWidgets.get(i);
4060 Provider provider = widget.provider;
4061 if (widget.host.isInPackageForUser(backedupPackage, userId)
4062 || (provider != null
4063 && provider.isInPackageForUser(backedupPackage, userId))) {
4064 serializeAppWidget(out, widget);
4068 out.endTag(null, "ws");
4070 } catch (IOException e) {
4071 Slog.w(TAG, "Unable to save widget state for " + backedupPackage);
4076 return stream.toByteArray();
4079 public void restoreStarting(int userId) {
4081 Slog.i(TAG, "Restore starting for user: " + userId);
4084 synchronized (mLock) {
4085 // We're starting a new "system" restore operation, so any widget restore
4086 // state that we see from here on is intended to replace the current
4087 // widget configuration of any/all of the affected apps.
4088 mPrunedApps.clear();
4089 mUpdatesByProvider.clear();
4090 mUpdatesByHost.clear();
4094 public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
4096 Slog.i(TAG, "Restoring widget state for user:" + userId
4097 + " package: " + packageName);
4100 ByteArrayInputStream stream = new ByteArrayInputStream(restoredState);
4102 // Providers mentioned in the widget dataset by ordinal
4103 ArrayList<Provider> restoredProviders = new ArrayList<>();
4105 // Hosts mentioned in the widget dataset by ordinal
4106 ArrayList<Host> restoredHosts = new ArrayList<>();
4108 XmlPullParser parser = Xml.newPullParser();
4109 parser.setInput(stream, StandardCharsets.UTF_8.name());
4111 synchronized (mLock) {
4114 type = parser.next();
4115 if (type == XmlPullParser.START_TAG) {
4116 final String tag = parser.getName();
4117 if ("ws".equals(tag)) {
4118 String version = parser.getAttributeValue(null, "version");
4120 final int versionNumber = Integer.parseInt(version);
4121 if (versionNumber > WIDGET_STATE_VERSION) {
4122 Slog.w(TAG, "Unable to process state version " + version);
4126 // TODO: fix up w.r.t. canonical vs current package names
4127 String pkg = parser.getAttributeValue(null, "pkg");
4128 if (!packageName.equals(pkg)) {
4129 Slog.w(TAG, "Package mismatch in ws");
4132 } else if ("p".equals(tag)) {
4133 String pkg = parser.getAttributeValue(null, "pkg");
4134 String cl = parser.getAttributeValue(null, "cl");
4136 // hostedProviders index will match 'p' attribute in widget's
4137 // entry in the xml file being restored
4138 // If there's no live entry for this provider, add an inactive one
4139 // so that widget IDs referring to them can be properly allocated
4141 // Backup and resotre only for the parent profile.
4142 ComponentName componentName = new ComponentName(pkg, cl);
4144 Provider p = findProviderLocked(componentName, userId);
4147 p.id = new ProviderId(UNKNOWN_UID, componentName);
4148 p.info = new AppWidgetProviderInfo();
4149 p.info.provider = componentName;
4154 Slog.i(TAG, " provider " + p.id);
4156 restoredProviders.add(p);
4157 } else if ("h".equals(tag)) {
4158 // The host app may not yet exist on the device. If it's here we
4159 // just use the existing Host entry, otherwise we create a
4160 // placeholder whose uid will be fixed up at PACKAGE_ADDED time.
4161 String pkg = parser.getAttributeValue(null, "pkg");
4163 final int uid = getUidForPackage(pkg, userId);
4164 final int hostId = Integer.parseInt(
4165 parser.getAttributeValue(null, "id"), 16);
4167 HostId id = new HostId(uid, hostId, pkg);
4168 Host h = lookupOrAddHostLocked(id);
4169 restoredHosts.add(h);
4172 Slog.i(TAG, " host[" + restoredHosts.size()
4173 + "]: {" + h.id + "}");
4175 } else if ("g".equals(tag)) {
4176 int restoredId = Integer.parseInt(
4177 parser.getAttributeValue(null, "id"), 16);
4178 int hostIndex = Integer.parseInt(
4179 parser.getAttributeValue(null, "h"), 16);
4180 Host host = restoredHosts.get(hostIndex);
4182 String prov = parser.getAttributeValue(null, "p");
4184 // could have been null if the app had allocated an id
4185 // but not yet established a binding under that id
4186 int which = Integer.parseInt(prov, 16);
4187 p = restoredProviders.get(which);
4190 // We'll be restoring widget state for both the host and
4191 // provider sides of this widget ID, so make sure we are
4192 // beginning from a clean slate on both fronts.
4193 pruneWidgetStateLocked(host.id.packageName, userId);
4195 pruneWidgetStateLocked(p.id.componentName.getPackageName(),
4199 // Have we heard about this ancestral widget instance before?
4200 Widget id = findRestoredWidgetLocked(restoredId, host, p);
4203 id.appWidgetId = incrementAndGetAppWidgetIdLocked(userId);
4204 id.restoredId = restoredId;
4205 id.options = parseWidgetIdOptions(parser);
4207 id.host.widgets.add(id);
4209 if (id.provider != null) {
4210 id.provider.widgets.add(id);
4213 Slog.i(TAG, "New restored id " + restoredId
4216 addWidgetLocked(id);
4218 if (id.provider.info != null) {
4219 stashProviderRestoreUpdateLocked(id.provider,
4220 restoredId, id.appWidgetId);
4222 Slog.w(TAG, "Missing provider for restored widget " + id);
4224 stashHostRestoreUpdateLocked(id.host, restoredId, id.appWidgetId);
4227 Slog.i(TAG, " instance: " + restoredId
4228 + " -> " + id.appWidgetId
4229 + " :: p=" + id.provider);
4233 } while (type != XmlPullParser.END_DOCUMENT);
4235 // We've updated our own bookkeeping. We'll need to notify the hosts and
4236 // providers about the changes, but we can't do that yet because the restore
4237 // target is not necessarily fully live at this moment. Set aside the
4238 // information for now; the backup manager will call us once more at the
4239 // end of the process when all of the targets are in a known state, and we
4240 // will update at that point.
4242 } catch (XmlPullParserException | IOException e) {
4243 Slog.w(TAG, "Unable to restore widget state for " + packageName);
4245 saveGroupStateAsync(userId);
4249 // Called once following the conclusion of a restore operation. This is when we
4250 // send out updates to apps involved in widget-state restore telling them about
4251 // the new widget ID space.
4252 public void restoreFinished(int userId) {
4254 Slog.i(TAG, "restoreFinished for " + userId);
4257 final UserHandle userHandle = new UserHandle(userId);
4258 synchronized (mLock) {
4259 // Build the providers' broadcasts and send them off
4260 Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
4261 = mUpdatesByProvider.entrySet();
4262 for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
4263 // For each provider there's a list of affected IDs
4264 Provider provider = e.getKey();
4265 ArrayList<RestoreUpdateRecord> updates = e.getValue();
4266 final int pending = countPendingUpdates(updates);
4268 Slog.i(TAG, "Provider " + provider + " pending: " + pending);
4271 int[] oldIds = new int[pending];
4272 int[] newIds = new int[pending];
4273 final int N = updates.size();
4274 int nextPending = 0;
4275 for (int i = 0; i < N; i++) {
4276 RestoreUpdateRecord r = updates.get(i);
4279 oldIds[nextPending] = r.oldId;
4280 newIds[nextPending] = r.newId;
4283 Slog.i(TAG, " " + r.oldId + " => " + r.newId);
4287 sendWidgetRestoreBroadcastLocked(
4288 AppWidgetManager.ACTION_APPWIDGET_RESTORED,
4289 provider, null, oldIds, newIds, userHandle);
4293 // same thing per host
4294 Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
4295 = mUpdatesByHost.entrySet();
4296 for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
4297 Host host = e.getKey();
4298 if (host.id.uid != UNKNOWN_UID) {
4299 ArrayList<RestoreUpdateRecord> updates = e.getValue();
4300 final int pending = countPendingUpdates(updates);
4302 Slog.i(TAG, "Host " + host + " pending: " + pending);
4305 int[] oldIds = new int[pending];
4306 int[] newIds = new int[pending];
4307 final int N = updates.size();
4308 int nextPending = 0;
4309 for (int i = 0; i < N; i++) {
4310 RestoreUpdateRecord r = updates.get(i);
4313 oldIds[nextPending] = r.oldId;
4314 newIds[nextPending] = r.newId;
4317 Slog.i(TAG, " " + r.oldId + " => " + r.newId);
4321 sendWidgetRestoreBroadcastLocked(
4322 AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
4323 null, host, oldIds, newIds, userHandle);
4330 private Provider findProviderLocked(ComponentName componentName, int userId) {
4331 final int providerCount = mProviders.size();
4332 for (int i = 0; i < providerCount; i++) {
4333 Provider provider = mProviders.get(i);
4334 if (provider.getUserId() == userId
4335 && provider.id.componentName.equals(componentName)) {
4342 private Widget findRestoredWidgetLocked(int restoredId, Host host, Provider p) {
4344 Slog.i(TAG, "Find restored widget: id=" + restoredId
4345 + " host=" + host + " provider=" + p);
4348 if (p == null || host == null) {
4352 final int N = mWidgets.size();
4353 for (int i = 0; i < N; i++) {
4354 Widget widget = mWidgets.get(i);
4355 if (widget.restoredId == restoredId
4356 && widget.host.id.equals(host.id)
4357 && widget.provider.id.equals(p.id)) {
4359 Slog.i(TAG, " Found at " + i + " : " + widget);
4367 private boolean packageNeedsWidgetBackupLocked(String packageName, int userId) {
4368 int N = mWidgets.size();
4369 for (int i = 0; i < N; i++) {
4370 Widget widget = mWidgets.get(i);
4372 // Skip cross-user widgets.
4373 if (!isProviderAndHostInUser(widget, userId)) {
4377 if (widget.host.isInPackageForUser(packageName, userId)) {
4378 // this package is hosting widgets, so it knows widget IDs.
4382 Provider provider = widget.provider;
4383 if (provider != null && provider.isInPackageForUser(packageName, userId)) {
4384 // someone is hosting this app's widgets, so it knows widget IDs.
4391 private void stashProviderRestoreUpdateLocked(Provider provider, int oldId, int newId) {
4392 ArrayList<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider);
4394 r = new ArrayList<>();
4395 mUpdatesByProvider.put(provider, r);
4398 if (alreadyStashed(r, oldId, newId)) {
4400 Slog.i(TAG, "ID remap " + oldId + " -> " + newId
4401 + " already stashed for " + provider);
4406 r.add(new RestoreUpdateRecord(oldId, newId));
4409 private boolean alreadyStashed(ArrayList<RestoreUpdateRecord> stash,
4410 final int oldId, final int newId) {
4411 final int N = stash.size();
4412 for (int i = 0; i < N; i++) {
4413 RestoreUpdateRecord r = stash.get(i);
4414 if (r.oldId == oldId && r.newId == newId) {
4421 private void stashHostRestoreUpdateLocked(Host host, int oldId, int newId) {
4422 ArrayList<RestoreUpdateRecord> r = mUpdatesByHost.get(host);
4424 r = new ArrayList<>();
4425 mUpdatesByHost.put(host, r);
4427 if (alreadyStashed(r, oldId, newId)) {
4429 Slog.i(TAG, "ID remap " + oldId + " -> " + newId
4430 + " already stashed for " + host);
4435 r.add(new RestoreUpdateRecord(oldId, newId));
4438 private void sendWidgetRestoreBroadcastLocked(String action, Provider provider,
4439 Host host, int[] oldIds, int[] newIds, UserHandle userHandle) {
4440 Intent intent = new Intent(action);
4441 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds);
4442 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds);
4443 if (provider != null) {
4444 intent.setComponent(provider.info.provider);
4445 sendBroadcastAsUser(intent, userHandle);
4448 intent.setComponent(null);
4449 intent.setPackage(host.id.packageName);
4450 intent.putExtra(AppWidgetManager.EXTRA_HOST_ID, host.id.hostId);
4451 sendBroadcastAsUser(intent, userHandle);
4455 // We're restoring widget state for 'pkg', so we start by wiping (a) all widget
4456 // instances that are hosted by that app, and (b) all instances in other hosts
4457 // for which 'pkg' is the provider. We assume that we'll be restoring all of
4458 // these hosts & providers, so will be reconstructing a correct live state.
4459 private void pruneWidgetStateLocked(String pkg, int userId) {
4460 if (!mPrunedApps.contains(pkg)) {
4462 Slog.i(TAG, "pruning widget state for restoring package " + pkg);
4464 for (int i = mWidgets.size() - 1; i >= 0; i--) {
4465 Widget widget = mWidgets.get(i);
4467 Host host = widget.host;
4468 Provider provider = widget.provider;
4470 if (host.hostsPackageForUser(pkg, userId)
4471 || (provider != null && provider.isInPackageForUser(pkg, userId))) {
4472 // 'pkg' is either the host or the provider for this instances,
4473 // so we tear it down in anticipation of it (possibly) being
4474 // reconstructed due to the restore
4475 host.widgets.remove(widget);
4476 provider.widgets.remove(widget);
4477 unbindAppWidgetRemoteViewsServicesLocked(widget);
4478 removeWidgetLocked(widget);
4481 mPrunedApps.add(pkg);
4484 Slog.i(TAG, "already pruned " + pkg + ", continuing normally");
4489 private boolean isProviderAndHostInUser(Widget widget, int userId) {
4490 // Backup only widgets hosted or provided by the owner profile.
4491 return widget.host.getUserId() == userId && (widget.provider == null
4492 || widget.provider.getUserId() == userId);
4495 private Bundle parseWidgetIdOptions(XmlPullParser parser) {
4496 Bundle options = new Bundle();
4497 String minWidthString = parser.getAttributeValue(null, "min_width");
4498 if (minWidthString != null) {
4499 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
4500 Integer.parseInt(minWidthString, 16));
4502 String minHeightString = parser.getAttributeValue(null, "min_height");
4503 if (minHeightString != null) {
4504 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
4505 Integer.parseInt(minHeightString, 16));
4507 String maxWidthString = parser.getAttributeValue(null, "max_width");
4508 if (maxWidthString != null) {
4509 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
4510 Integer.parseInt(maxWidthString, 16));
4512 String maxHeightString = parser.getAttributeValue(null, "max_height");
4513 if (maxHeightString != null) {
4514 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
4515 Integer.parseInt(maxHeightString, 16));
4517 String categoryString = parser.getAttributeValue(null, "host_category");
4518 if (categoryString != null) {
4519 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
4520 Integer.parseInt(categoryString, 16));
4525 private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
4527 final int N = updates.size();
4528 for (int i = 0; i < N; i++) {
4529 RestoreUpdateRecord r = updates.get(i);
4537 // Accumulate a list of updates that affect the given provider for a final
4538 // coalesced notification broadcast once restore is over.
4539 private class RestoreUpdateRecord {
4542 public boolean notified;
4544 public RestoreUpdateRecord(int theOldId, int theNewId) {