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