Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2007 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;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.PendingIntent;
     21 import android.appwidget.AppWidgetManager;
     22 import android.appwidget.AppWidgetProviderInfo;
     23 import android.content.BroadcastReceiver;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.ActivityInfo;
     29 import android.content.pm.ApplicationInfo;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.PackageInfo;
     32 import android.content.pm.ResolveInfo;
     33 import android.content.res.Resources;
     34 import android.content.res.TypedArray;
     35 import android.content.res.XmlResourceParser;
     36 import android.net.Uri;
     37 import android.os.Binder;
     38 import android.os.Bundle;
     39 import android.os.Process;
     40 import android.os.RemoteException;
     41 import android.os.SystemClock;
     42 import android.util.AttributeSet;
     43 import android.util.Slog;
     44 import android.util.TypedValue;
     45 import android.util.Xml;
     46 import android.widget.RemoteViews;
     47 
     48 import java.io.IOException;
     49 import java.io.File;
     50 import java.io.FileDescriptor;
     51 import java.io.FileInputStream;
     52 import java.io.FileOutputStream;
     53 import java.io.PrintWriter;
     54 import java.util.ArrayList;
     55 import java.util.List;
     56 import java.util.Locale;
     57 import java.util.HashMap;
     58 import java.util.HashSet;
     59 
     60 import com.android.internal.appwidget.IAppWidgetService;
     61 import com.android.internal.appwidget.IAppWidgetHost;
     62 import com.android.internal.util.FastXmlSerializer;
     63 
     64 import org.xmlpull.v1.XmlPullParser;
     65 import org.xmlpull.v1.XmlPullParserException;
     66 import org.xmlpull.v1.XmlSerializer;
     67 
     68 class AppWidgetService extends IAppWidgetService.Stub
     69 {
     70     private static final String TAG = "AppWidgetService";
     71 
     72     private static final String SETTINGS_FILENAME = "appwidgets.xml";
     73     private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
     74     private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
     75 
     76     /*
     77      * When identifying a Host or Provider based on the calling process, use the uid field.
     78      * When identifying a Host or Provider based on a package manager broadcast, use the
     79      * package given.
     80      */
     81 
     82     static class Provider {
     83         int uid;
     84         AppWidgetProviderInfo info;
     85         ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
     86         PendingIntent broadcast;
     87         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
     88 
     89         int tag;    // for use while saving state (the index)
     90     }
     91 
     92     static class Host {
     93         int uid;
     94         int hostId;
     95         String packageName;
     96         ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
     97         IAppWidgetHost callbacks;
     98         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
     99 
    100         int tag;    // for use while saving state (the index)
    101     }
    102 
    103     static class AppWidgetId {
    104         int appWidgetId;
    105         Provider provider;
    106         RemoteViews views;
    107         Host host;
    108     }
    109 
    110     Context mContext;
    111     Locale mLocale;
    112     PackageManager mPackageManager;
    113     AlarmManager mAlarmManager;
    114     ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
    115     int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
    116     final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
    117     ArrayList<Host> mHosts = new ArrayList<Host>();
    118     boolean mSafeMode;
    119 
    120     AppWidgetService(Context context) {
    121         mContext = context;
    122         mPackageManager = context.getPackageManager();
    123         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
    124     }
    125 
    126     public void systemReady(boolean safeMode) {
    127         mSafeMode = safeMode;
    128 
    129         loadAppWidgetList();
    130         loadStateLocked();
    131 
    132         // Register for the boot completed broadcast, so we can send the
    133         // ENABLE broacasts.  If we try to send them now, they time out,
    134         // because the system isn't ready to handle them yet.
    135         mContext.registerReceiver(mBroadcastReceiver,
    136                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
    137 
    138         // Register for configuration changes so we can update the names
    139         // of the widgets when the locale changes.
    140         mContext.registerReceiver(mBroadcastReceiver,
    141                 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
    142 
    143         // Register for broadcasts about package install, etc., so we can
    144         // update the provider list.
    145         IntentFilter filter = new IntentFilter();
    146         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
    147         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    148         filter.addDataScheme("package");
    149         mContext.registerReceiver(mBroadcastReceiver, filter);
    150         // Register for events related to sdcard installation.
    151         IntentFilter sdFilter = new IntentFilter();
    152         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
    153         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
    154         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
    155     }
    156 
    157     @Override
    158     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    159         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    160                 != PackageManager.PERMISSION_GRANTED) {
    161             pw.println("Permission Denial: can't dump from from pid="
    162                     + Binder.getCallingPid()
    163                     + ", uid=" + Binder.getCallingUid());
    164             return;
    165         }
    166 
    167         synchronized (mAppWidgetIds) {
    168             int N = mInstalledProviders.size();
    169             pw.println("Providers:");
    170             for (int i=0; i<N; i++) {
    171                 Provider p = mInstalledProviders.get(i);
    172                 AppWidgetProviderInfo info = p.info;
    173                 pw.print("  ["); pw.print(i); pw.print("] provider ");
    174                         pw.print(info.provider.flattenToShortString());
    175                         pw.println(':');
    176                 pw.print("    min=("); pw.print(info.minWidth);
    177                         pw.print("x"); pw.print(info.minHeight);
    178                         pw.print(") updatePeriodMillis=");
    179                         pw.print(info.updatePeriodMillis);
    180                         pw.print(" initialLayout=#");
    181                         pw.print(Integer.toHexString(info.initialLayout));
    182                         pw.print(" zombie="); pw.println(p.zombie);
    183             }
    184 
    185             N = mAppWidgetIds.size();
    186             pw.println(" ");
    187             pw.println("AppWidgetIds:");
    188             for (int i=0; i<N; i++) {
    189                 AppWidgetId id = mAppWidgetIds.get(i);
    190                 pw.print("  ["); pw.print(i); pw.print("] id=");
    191                         pw.println(id.appWidgetId);
    192                 pw.print("    hostId=");
    193                         pw.print(id.host.hostId); pw.print(' ');
    194                         pw.print(id.host.packageName); pw.print('/');
    195                         pw.println(id.host.uid);
    196                 if (id.provider != null) {
    197                     pw.print("    provider=");
    198                             pw.println(id.provider.info.provider.flattenToShortString());
    199                 }
    200                 if (id.host != null) {
    201                     pw.print("    host.callbacks="); pw.println(id.host.callbacks);
    202                 }
    203                 if (id.views != null) {
    204                     pw.print("    views="); pw.println(id.views);
    205                 }
    206             }
    207 
    208             N = mHosts.size();
    209             pw.println(" ");
    210             pw.println("Hosts:");
    211             for (int i=0; i<N; i++) {
    212                 Host host = mHosts.get(i);
    213                 pw.print("  ["); pw.print(i); pw.print("] hostId=");
    214                         pw.print(host.hostId); pw.print(' ');
    215                         pw.print(host.packageName); pw.print('/');
    216                         pw.print(host.uid); pw.println(':');
    217                 pw.print("    callbacks="); pw.println(host.callbacks);
    218                 pw.print("    instances.size="); pw.print(host.instances.size());
    219                         pw.print(" zombie="); pw.println(host.zombie);
    220             }
    221         }
    222     }
    223 
    224     public int allocateAppWidgetId(String packageName, int hostId) {
    225         int callingUid = enforceCallingUid(packageName);
    226         synchronized (mAppWidgetIds) {
    227             int appWidgetId = mNextAppWidgetId++;
    228 
    229             Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
    230 
    231             AppWidgetId id = new AppWidgetId();
    232             id.appWidgetId = appWidgetId;
    233             id.host = host;
    234 
    235             host.instances.add(id);
    236             mAppWidgetIds.add(id);
    237 
    238             saveStateLocked();
    239 
    240             return appWidgetId;
    241         }
    242     }
    243 
    244     public void deleteAppWidgetId(int appWidgetId) {
    245         synchronized (mAppWidgetIds) {
    246             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
    247             if (id != null) {
    248                 deleteAppWidgetLocked(id);
    249                 saveStateLocked();
    250             }
    251         }
    252     }
    253 
    254     public void deleteHost(int hostId) {
    255         synchronized (mAppWidgetIds) {
    256             int callingUid = getCallingUid();
    257             Host host = lookupHostLocked(callingUid, hostId);
    258             if (host != null) {
    259                 deleteHostLocked(host);
    260                 saveStateLocked();
    261             }
    262         }
    263     }
    264 
    265     public void deleteAllHosts() {
    266         synchronized (mAppWidgetIds) {
    267             int callingUid = getCallingUid();
    268             final int N = mHosts.size();
    269             boolean changed = false;
    270             for (int i=N-1; i>=0; i--) {
    271                 Host host = mHosts.get(i);
    272                 if (host.uid == callingUid) {
    273                     deleteHostLocked(host);
    274                     changed = true;
    275                 }
    276             }
    277             if (changed) {
    278                 saveStateLocked();
    279             }
    280         }
    281     }
    282 
    283     void deleteHostLocked(Host host) {
    284         final int N = host.instances.size();
    285         for (int i=N-1; i>=0; i--) {
    286             AppWidgetId id = host.instances.get(i);
    287             deleteAppWidgetLocked(id);
    288         }
    289         host.instances.clear();
    290         mHosts.remove(host);
    291         // it's gone or going away, abruptly drop the callback connection
    292         host.callbacks = null;
    293     }
    294 
    295     void deleteAppWidgetLocked(AppWidgetId id) {
    296         Host host = id.host;
    297         host.instances.remove(id);
    298         pruneHostLocked(host);
    299 
    300         mAppWidgetIds.remove(id);
    301 
    302         Provider p = id.provider;
    303         if (p != null) {
    304             p.instances.remove(id);
    305             if (!p.zombie) {
    306                 // send the broacast saying that this appWidgetId has been deleted
    307                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
    308                 intent.setComponent(p.info.provider);
    309                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
    310                 mContext.sendBroadcast(intent);
    311                 if (p.instances.size() == 0) {
    312                     // cancel the future updates
    313                     cancelBroadcasts(p);
    314 
    315                     // send the broacast saying that the provider is not in use any more
    316                     intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
    317                     intent.setComponent(p.info.provider);
    318                     mContext.sendBroadcast(intent);
    319                 }
    320             }
    321         }
    322     }
    323 
    324     void cancelBroadcasts(Provider p) {
    325         if (p.broadcast != null) {
    326             mAlarmManager.cancel(p.broadcast);
    327             long token = Binder.clearCallingIdentity();
    328             try {
    329                 p.broadcast.cancel();
    330             } finally {
    331                 Binder.restoreCallingIdentity(token);
    332             }
    333             p.broadcast = null;
    334         }
    335     }
    336 
    337     public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
    338         mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
    339                 "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
    340         synchronized (mAppWidgetIds) {
    341             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
    342             if (id == null) {
    343                 throw new IllegalArgumentException("bad appWidgetId");
    344             }
    345             if (id.provider != null) {
    346                 throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
    347                         + id.provider.info.provider);
    348             }
    349             Provider p = lookupProviderLocked(provider);
    350             if (p == null) {
    351                 throw new IllegalArgumentException("not a appwidget provider: " + provider);
    352             }
    353             if (p.zombie) {
    354                 throw new IllegalArgumentException("can't bind to a 3rd party provider in"
    355                         + " safe mode: " + provider);
    356             }
    357 
    358             id.provider = p;
    359             p.instances.add(id);
    360             int instancesSize = p.instances.size();
    361             if (instancesSize == 1) {
    362                 // tell the provider that it's ready
    363                 sendEnableIntentLocked(p);
    364             }
    365 
    366             // send an update now -- We need this update now, and just for this appWidgetId.
    367             // It's less critical when the next one happens, so when we schdule the next one,
    368             // we add updatePeriodMillis to its start time.  That time will have some slop,
    369             // but that's okay.
    370             sendUpdateIntentLocked(p, new int[] { appWidgetId });
    371 
    372             // schedule the future updates
    373             registerForBroadcastsLocked(p, getAppWidgetIds(p));
    374             saveStateLocked();
    375         }
    376     }
    377 
    378     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
    379         synchronized (mAppWidgetIds) {
    380             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
    381             if (id != null && id.provider != null && !id.provider.zombie) {
    382                 return id.provider.info;
    383             }
    384             return null;
    385         }
    386     }
    387 
    388     public RemoteViews getAppWidgetViews(int appWidgetId) {
    389         synchronized (mAppWidgetIds) {
    390             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
    391             if (id != null) {
    392                 return id.views;
    393             }
    394             return null;
    395         }
    396     }
    397 
    398     public List<AppWidgetProviderInfo> getInstalledProviders() {
    399         synchronized (mAppWidgetIds) {
    400             final int N = mInstalledProviders.size();
    401             ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
    402             for (int i=0; i<N; i++) {
    403                 Provider p = mInstalledProviders.get(i);
    404                 if (!p.zombie) {
    405                     result.add(p.info);
    406                 }
    407             }
    408             return result;
    409         }
    410     }
    411 
    412     public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
    413         if (appWidgetIds == null) {
    414             return;
    415         }
    416         if (appWidgetIds.length == 0) {
    417             return;
    418         }
    419         final int N = appWidgetIds.length;
    420 
    421         synchronized (mAppWidgetIds) {
    422             for (int i=0; i<N; i++) {
    423                 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
    424                 updateAppWidgetInstanceLocked(id, views);
    425             }
    426         }
    427     }
    428 
    429     public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
    430         synchronized (mAppWidgetIds) {
    431             Provider p = lookupProviderLocked(provider);
    432             if (p == null) {
    433                 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
    434                 return;
    435             }
    436             ArrayList<AppWidgetId> instances = p.instances;
    437             final int N = instances.size();
    438             for (int i=0; i<N; i++) {
    439                 AppWidgetId id = instances.get(i);
    440                 updateAppWidgetInstanceLocked(id, views);
    441             }
    442         }
    443     }
    444 
    445     void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
    446         // allow for stale appWidgetIds and other badness
    447         // lookup also checks that the calling process can access the appWidgetId
    448         // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
    449         if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
    450             id.views = views;
    451 
    452             // is anyone listening?
    453             if (id.host.callbacks != null) {
    454                 try {
    455                     // the lock is held, but this is a oneway call
    456                     id.host.callbacks.updateAppWidget(id.appWidgetId, views);
    457                 } catch (RemoteException e) {
    458                     // It failed; remove the callback. No need to prune because
    459                     // we know that this host is still referenced by this instance.
    460                     id.host.callbacks = null;
    461                 }
    462             }
    463         }
    464     }
    465 
    466     public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
    467             List<RemoteViews> updatedViews) {
    468         int callingUid = enforceCallingUid(packageName);
    469         synchronized (mAppWidgetIds) {
    470             Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
    471             host.callbacks = callbacks;
    472 
    473             updatedViews.clear();
    474 
    475             ArrayList<AppWidgetId> instances = host.instances;
    476             int N = instances.size();
    477             int[] updatedIds = new int[N];
    478             for (int i=0; i<N; i++) {
    479                 AppWidgetId id = instances.get(i);
    480                 updatedIds[i] = id.appWidgetId;
    481                 updatedViews.add(id.views);
    482             }
    483             return updatedIds;
    484         }
    485     }
    486 
    487     public void stopListening(int hostId) {
    488         synchronized (mAppWidgetIds) {
    489             Host host = lookupHostLocked(getCallingUid(), hostId);
    490             if (host != null) {
    491                 host.callbacks = null;
    492                 pruneHostLocked(host);
    493             }
    494         }
    495     }
    496 
    497     boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
    498         if (id.host.uid == callingUid) {
    499             // Apps hosting the AppWidget have access to it.
    500             return true;
    501         }
    502         if (id.provider != null && id.provider.uid == callingUid) {
    503             // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
    504             return true;
    505         }
    506         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
    507                 == PackageManager.PERMISSION_GRANTED) {
    508             // Apps that can bind have access to all appWidgetIds.
    509             return true;
    510         }
    511         // Nobody else can access it.
    512         return false;
    513     }
    514 
    515    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
    516         int callingUid = getCallingUid();
    517         final int N = mAppWidgetIds.size();
    518         for (int i=0; i<N; i++) {
    519             AppWidgetId id = mAppWidgetIds.get(i);
    520             if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
    521                 return id;
    522             }
    523         }
    524         return null;
    525     }
    526 
    527     Provider lookupProviderLocked(ComponentName provider) {
    528         final String className = provider.getClassName();
    529         final int N = mInstalledProviders.size();
    530         for (int i=0; i<N; i++) {
    531             Provider p = mInstalledProviders.get(i);
    532             if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
    533                 return p;
    534             }
    535         }
    536         return null;
    537     }
    538 
    539     Host lookupHostLocked(int uid, int hostId) {
    540         final int N = mHosts.size();
    541         for (int i=0; i<N; i++) {
    542             Host h = mHosts.get(i);
    543             if (h.uid == uid && h.hostId == hostId) {
    544                 return h;
    545             }
    546         }
    547         return null;
    548     }
    549 
    550     Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
    551         final int N = mHosts.size();
    552         for (int i=0; i<N; i++) {
    553             Host h = mHosts.get(i);
    554             if (h.hostId == hostId && h.packageName.equals(packageName)) {
    555                 return h;
    556             }
    557         }
    558         Host host = new Host();
    559         host.packageName = packageName;
    560         host.uid = uid;
    561         host.hostId = hostId;
    562         mHosts.add(host);
    563         return host;
    564     }
    565 
    566     void pruneHostLocked(Host host) {
    567         if (host.instances.size() == 0 && host.callbacks == null) {
    568             mHosts.remove(host);
    569         }
    570     }
    571 
    572     void loadAppWidgetList() {
    573         PackageManager pm = mPackageManager;
    574 
    575         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    576         List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
    577                 PackageManager.GET_META_DATA);
    578 
    579         final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
    580         for (int i=0; i<N; i++) {
    581             ResolveInfo ri = broadcastReceivers.get(i);
    582             addProviderLocked(ri);
    583         }
    584     }
    585 
    586     boolean addProviderLocked(ResolveInfo ri) {
    587         Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
    588                     ri.activityInfo.name), ri);
    589         if (p != null) {
    590             mInstalledProviders.add(p);
    591             return true;
    592         } else {
    593             return false;
    594         }
    595     }
    596 
    597     void removeProviderLocked(int index, Provider p) {
    598         int N = p.instances.size();
    599         for (int i=0; i<N; i++) {
    600             AppWidgetId id = p.instances.get(i);
    601             // Call back with empty RemoteViews
    602             updateAppWidgetInstanceLocked(id, null);
    603             // Stop telling the host about updates for this from now on
    604             cancelBroadcasts(p);
    605             // clear out references to this appWidgetId
    606             id.host.instances.remove(id);
    607             mAppWidgetIds.remove(id);
    608             id.provider = null;
    609             pruneHostLocked(id.host);
    610             id.host = null;
    611         }
    612         p.instances.clear();
    613         mInstalledProviders.remove(index);
    614         // no need to send the DISABLE broadcast, since the receiver is gone anyway
    615         cancelBroadcasts(p);
    616     }
    617 
    618     void sendEnableIntentLocked(Provider p) {
    619         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
    620         intent.setComponent(p.info.provider);
    621         mContext.sendBroadcast(intent);
    622     }
    623 
    624     void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
    625         if (appWidgetIds != null && appWidgetIds.length > 0) {
    626             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    627             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
    628             intent.setComponent(p.info.provider);
    629             mContext.sendBroadcast(intent);
    630         }
    631     }
    632 
    633     void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
    634         if (p.info.updatePeriodMillis > 0) {
    635             // if this is the first instance, set the alarm.  otherwise,
    636             // rely on the fact that we've already set it and that
    637             // PendingIntent.getBroadcast will update the extras.
    638             boolean alreadyRegistered = p.broadcast != null;
    639             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    640             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
    641             intent.setComponent(p.info.provider);
    642             long token = Binder.clearCallingIdentity();
    643             try {
    644                 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
    645                         PendingIntent.FLAG_UPDATE_CURRENT);
    646             } finally {
    647                 Binder.restoreCallingIdentity(token);
    648             }
    649             if (!alreadyRegistered) {
    650                 long period = p.info.updatePeriodMillis;
    651                 if (period < MIN_UPDATE_PERIOD) {
    652                     period = MIN_UPDATE_PERIOD;
    653                 }
    654                 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    655                         SystemClock.elapsedRealtime() + period, period, p.broadcast);
    656             }
    657         }
    658     }
    659 
    660     static int[] getAppWidgetIds(Provider p) {
    661         int instancesSize = p.instances.size();
    662         int appWidgetIds[] = new int[instancesSize];
    663         for (int i=0; i<instancesSize; i++) {
    664             appWidgetIds[i] = p.instances.get(i).appWidgetId;
    665         }
    666         return appWidgetIds;
    667     }
    668 
    669     public int[] getAppWidgetIds(ComponentName provider) {
    670         synchronized (mAppWidgetIds) {
    671             Provider p = lookupProviderLocked(provider);
    672             if (p != null && getCallingUid() == p.uid) {
    673                 return getAppWidgetIds(p);
    674             } else {
    675                 return new int[0];
    676             }
    677         }
    678     }
    679 
    680     private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
    681         Provider p = null;
    682 
    683         ActivityInfo activityInfo = ri.activityInfo;
    684         XmlResourceParser parser = null;
    685         try {
    686             parser = activityInfo.loadXmlMetaData(mPackageManager,
    687                     AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
    688             if (parser == null) {
    689                 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
    690                         + "AppWidget provider '" + component + '\'');
    691                 return null;
    692             }
    693 
    694             AttributeSet attrs = Xml.asAttributeSet(parser);
    695 
    696             int type;
    697             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
    698                     && type != XmlPullParser.START_TAG) {
    699                 // drain whitespace, comments, etc.
    700             }
    701 
    702             String nodeName = parser.getName();
    703             if (!"appwidget-provider".equals(nodeName)) {
    704                 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
    705                         + " AppWidget provider '" + component + '\'');
    706                 return null;
    707             }
    708 
    709             p = new Provider();
    710             AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
    711             // If metaData was null, we would have returned earlier when getting
    712             // the parser No need to do the check here
    713             info.oldName = activityInfo.metaData.getString(
    714                     AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
    715 
    716             info.provider = component;
    717             p.uid = activityInfo.applicationInfo.uid;
    718 
    719             Resources res = mPackageManager.getResourcesForApplication(
    720                     activityInfo.applicationInfo);
    721 
    722             TypedArray sa = res.obtainAttributes(attrs,
    723                     com.android.internal.R.styleable.AppWidgetProviderInfo);
    724 
    725             // These dimensions has to be resolved in the application's context.
    726             // We simply send back the raw complex data, which will be
    727             // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
    728             TypedValue value = sa.peekValue(
    729                     com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
    730             info.minWidth = value != null ? value.data : 0;
    731             value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
    732             info.minHeight = value != null ? value.data : 0;
    733 
    734             info.updatePeriodMillis = sa.getInt(
    735                     com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
    736             info.initialLayout = sa.getResourceId(
    737                     com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
    738             String className = sa.getString(
    739                     com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
    740             if (className != null) {
    741                 info.configure = new ComponentName(component.getPackageName(), className);
    742             }
    743             info.label = activityInfo.loadLabel(mPackageManager).toString();
    744             info.icon = ri.getIconResource();
    745             sa.recycle();
    746         } catch (Exception e) {
    747             // Ok to catch Exception here, because anything going wrong because
    748             // of what a client process passes to us should not be fatal for the
    749             // system process.
    750             Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
    751             return null;
    752         } finally {
    753             if (parser != null) parser.close();
    754         }
    755         return p;
    756     }
    757 
    758     int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
    759         PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
    760         if (pkgInfo == null || pkgInfo.applicationInfo == null) {
    761             throw new PackageManager.NameNotFoundException();
    762         }
    763         return pkgInfo.applicationInfo.uid;
    764     }
    765 
    766     int enforceCallingUid(String packageName) throws IllegalArgumentException {
    767         int callingUid = getCallingUid();
    768         int packageUid;
    769         try {
    770             packageUid = getUidForPackage(packageName);
    771         } catch (PackageManager.NameNotFoundException ex) {
    772             throw new IllegalArgumentException("packageName and uid don't match packageName="
    773                     + packageName);
    774         }
    775         if (callingUid != packageUid && Process.supportsProcesses()) {
    776             throw new IllegalArgumentException("packageName and uid don't match packageName="
    777                     + packageName);
    778         }
    779         return callingUid;
    780     }
    781 
    782     void sendInitialBroadcasts() {
    783         synchronized (mAppWidgetIds) {
    784             final int N = mInstalledProviders.size();
    785             for (int i=0; i<N; i++) {
    786                 Provider p = mInstalledProviders.get(i);
    787                 if (p.instances.size() > 0) {
    788                     sendEnableIntentLocked(p);
    789                     int[] appWidgetIds = getAppWidgetIds(p);
    790                     sendUpdateIntentLocked(p, appWidgetIds);
    791                     registerForBroadcastsLocked(p, appWidgetIds);
    792                 }
    793             }
    794         }
    795     }
    796 
    797     // only call from initialization -- it assumes that the data structures are all empty
    798     void loadStateLocked() {
    799         File temp = savedStateTempFile();
    800         File real = savedStateRealFile();
    801 
    802         // prefer the real file.  If it doesn't exist, use the temp one, and then copy it to the
    803         // real one.  if there is both a real file and a temp one, assume that the temp one isn't
    804         // fully written and delete it.
    805         if (real.exists()) {
    806             readStateFromFileLocked(real);
    807             if (temp.exists()) {
    808                 //noinspection ResultOfMethodCallIgnored
    809                 temp.delete();
    810             }
    811         } else if (temp.exists()) {
    812             readStateFromFileLocked(temp);
    813             //noinspection ResultOfMethodCallIgnored
    814             temp.renameTo(real);
    815         }
    816     }
    817 
    818     void saveStateLocked() {
    819         File temp = savedStateTempFile();
    820         File real = savedStateRealFile();
    821 
    822         if (!real.exists()) {
    823             // If the real one doesn't exist, it's either because this is the first time
    824             // or because something went wrong while copying them.  In this case, we can't
    825             // trust anything that's in temp.  In order to have the loadState code not
    826             // use the temporary one until it's fully written, create an empty file
    827             // for real, which will we'll shortly delete.
    828             try {
    829                 //noinspection ResultOfMethodCallIgnored
    830                 real.createNewFile();
    831             } catch (IOException e) {
    832                 // Ignore
    833             }
    834         }
    835 
    836         if (temp.exists()) {
    837             //noinspection ResultOfMethodCallIgnored
    838             temp.delete();
    839         }
    840 
    841         if (!writeStateToFileLocked(temp)) {
    842             Slog.w(TAG, "Failed to persist new settings");
    843             return;
    844         }
    845 
    846         //noinspection ResultOfMethodCallIgnored
    847         real.delete();
    848         //noinspection ResultOfMethodCallIgnored
    849         temp.renameTo(real);
    850     }
    851 
    852     boolean writeStateToFileLocked(File file) {
    853         FileOutputStream stream = null;
    854         int N;
    855 
    856         try {
    857             stream = new FileOutputStream(file, false);
    858             XmlSerializer out = new FastXmlSerializer();
    859             out.setOutput(stream, "utf-8");
    860             out.startDocument(null, true);
    861 
    862 
    863             out.startTag(null, "gs");
    864 
    865             int providerIndex = 0;
    866             N = mInstalledProviders.size();
    867             for (int i=0; i<N; i++) {
    868                 Provider p = mInstalledProviders.get(i);
    869                 if (p.instances.size() > 0) {
    870                     out.startTag(null, "p");
    871                     out.attribute(null, "pkg", p.info.provider.getPackageName());
    872                     out.attribute(null, "cl", p.info.provider.getClassName());
    873                     out.endTag(null, "p");
    874                     p.tag = providerIndex;
    875                     providerIndex++;
    876                 }
    877             }
    878 
    879             N = mHosts.size();
    880             for (int i=0; i<N; i++) {
    881                 Host host = mHosts.get(i);
    882                 out.startTag(null, "h");
    883                 out.attribute(null, "pkg", host.packageName);
    884                 out.attribute(null, "id", Integer.toHexString(host.hostId));
    885                 out.endTag(null, "h");
    886                 host.tag = i;
    887             }
    888 
    889             N = mAppWidgetIds.size();
    890             for (int i=0; i<N; i++) {
    891                 AppWidgetId id = mAppWidgetIds.get(i);
    892                 out.startTag(null, "g");
    893                 out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
    894                 out.attribute(null, "h", Integer.toHexString(id.host.tag));
    895                 if (id.provider != null) {
    896                     out.attribute(null, "p", Integer.toHexString(id.provider.tag));
    897                 }
    898                 out.endTag(null, "g");
    899             }
    900 
    901             out.endTag(null, "gs");
    902 
    903             out.endDocument();
    904             stream.close();
    905             return true;
    906         } catch (IOException e) {
    907             try {
    908                 if (stream != null) {
    909                     stream.close();
    910                 }
    911             } catch (IOException ex) {
    912                 // Ignore
    913             }
    914             if (file.exists()) {
    915                 //noinspection ResultOfMethodCallIgnored
    916                 file.delete();
    917             }
    918             return false;
    919         }
    920     }
    921 
    922     void readStateFromFileLocked(File file) {
    923         FileInputStream stream = null;
    924 
    925         boolean success = false;
    926 
    927         try {
    928             stream = new FileInputStream(file);
    929             XmlPullParser parser = Xml.newPullParser();
    930             parser.setInput(stream, null);
    931 
    932             int type;
    933             int providerIndex = 0;
    934             HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
    935             do {
    936                 type = parser.next();
    937                 if (type == XmlPullParser.START_TAG) {
    938                     String tag = parser.getName();
    939                     if ("p".equals(tag)) {
    940                         // TODO: do we need to check that this package has the same signature
    941                         // as before?
    942                         String pkg = parser.getAttributeValue(null, "pkg");
    943                         String cl = parser.getAttributeValue(null, "cl");
    944 
    945                         final PackageManager packageManager = mContext.getPackageManager();
    946                         try {
    947                             packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
    948                         } catch (PackageManager.NameNotFoundException e) {
    949                             String[] pkgs = packageManager.currentToCanonicalPackageNames(
    950                                     new String[] { pkg });
    951                             pkg = pkgs[0];
    952                         }
    953 
    954                         Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
    955                         if (p == null && mSafeMode) {
    956                             // if we're in safe mode, make a temporary one
    957                             p = new Provider();
    958                             p.info = new AppWidgetProviderInfo();
    959                             p.info.provider = new ComponentName(pkg, cl);
    960                             p.zombie = true;
    961                             mInstalledProviders.add(p);
    962                         }
    963                         if (p != null) {
    964                             // if it wasn't uninstalled or something
    965                             loadedProviders.put(providerIndex, p);
    966                         }
    967                         providerIndex++;
    968                     }
    969                     else if ("h".equals(tag)) {
    970                         Host host = new Host();
    971 
    972                         // TODO: do we need to check that this package has the same signature
    973                         // as before?
    974                         host.packageName = parser.getAttributeValue(null, "pkg");
    975                         try {
    976                             host.uid = getUidForPackage(host.packageName);
    977                         } catch (PackageManager.NameNotFoundException ex) {
    978                             host.zombie = true;
    979                         }
    980                         if (!host.zombie || mSafeMode) {
    981                             // In safe mode, we don't discard the hosts we don't recognize
    982                             // so that they're not pruned from our list.  Otherwise, we do.
    983                             host.hostId = Integer.parseInt(
    984                                     parser.getAttributeValue(null, "id"), 16);
    985                             mHosts.add(host);
    986                         }
    987                     }
    988                     else if ("g".equals(tag)) {
    989                         AppWidgetId id = new AppWidgetId();
    990                         id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
    991                         if (id.appWidgetId >= mNextAppWidgetId) {
    992                             mNextAppWidgetId = id.appWidgetId + 1;
    993                         }
    994 
    995                         String providerString = parser.getAttributeValue(null, "p");
    996                         if (providerString != null) {
    997                             // there's no provider if it hasn't been bound yet.
    998                             // maybe we don't have to save this, but it brings the system
    999                             // to the state it was in.
   1000                             int pIndex = Integer.parseInt(providerString, 16);
   1001                             id.provider = loadedProviders.get(pIndex);
   1002                             if (false) {
   1003                                 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
   1004                                         + pIndex + " which is " + id.provider);
   1005                             }
   1006                             if (id.provider == null) {
   1007                                 // This provider is gone.  We just let the host figure out
   1008                                 // that this happened when it fails to load it.
   1009                                 continue;
   1010                             }
   1011                         }
   1012 
   1013                         int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
   1014                         id.host = mHosts.get(hIndex);
   1015                         if (id.host == null) {
   1016                             // This host is gone.
   1017                             continue;
   1018                         }
   1019 
   1020                         if (id.provider != null) {
   1021                             id.provider.instances.add(id);
   1022                         }
   1023                         id.host.instances.add(id);
   1024                         mAppWidgetIds.add(id);
   1025                     }
   1026                 }
   1027             } while (type != XmlPullParser.END_DOCUMENT);
   1028             success = true;
   1029         } catch (NullPointerException e) {
   1030             Slog.w(TAG, "failed parsing " + file, e);
   1031         } catch (NumberFormatException e) {
   1032             Slog.w(TAG, "failed parsing " + file, e);
   1033         } catch (XmlPullParserException e) {
   1034             Slog.w(TAG, "failed parsing " + file, e);
   1035         } catch (IOException e) {
   1036             Slog.w(TAG, "failed parsing " + file, e);
   1037         } catch (IndexOutOfBoundsException e) {
   1038             Slog.w(TAG, "failed parsing " + file, e);
   1039         }
   1040         try {
   1041             if (stream != null) {
   1042                 stream.close();
   1043             }
   1044         } catch (IOException e) {
   1045             // Ignore
   1046         }
   1047 
   1048         if (success) {
   1049             // delete any hosts that didn't manage to get connected (should happen)
   1050             // if it matters, they'll be reconnected.
   1051             for (int i=mHosts.size()-1; i>=0; i--) {
   1052                 pruneHostLocked(mHosts.get(i));
   1053             }
   1054         } else {
   1055             // failed reading, clean up
   1056             mAppWidgetIds.clear();
   1057             mHosts.clear();
   1058             final int N = mInstalledProviders.size();
   1059             for (int i=0; i<N; i++) {
   1060                 mInstalledProviders.get(i).instances.clear();
   1061             }
   1062         }
   1063     }
   1064 
   1065     File savedStateTempFile() {
   1066         return new File("/data/system/" + SETTINGS_TMP_FILENAME);
   1067         //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
   1068     }
   1069 
   1070     File savedStateRealFile() {
   1071         return new File("/data/system/" + SETTINGS_FILENAME);
   1072         //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
   1073     }
   1074 
   1075     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
   1076         public void onReceive(Context context, Intent intent) {
   1077             String action = intent.getAction();
   1078             //Slog.d(TAG, "received " + action);
   1079             if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
   1080                 sendInitialBroadcasts();
   1081             } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
   1082                 Locale revised = Locale.getDefault();
   1083                 if (revised == null || mLocale == null ||
   1084                     !(revised.equals(mLocale))) {
   1085                     mLocale = revised;
   1086 
   1087                     synchronized (mAppWidgetIds) {
   1088                         int N = mInstalledProviders.size();
   1089                         for (int i=N-1; i>=0; i--) {
   1090                             Provider p = mInstalledProviders.get(i);
   1091                             String pkgName = p.info.provider.getPackageName();
   1092                             updateProvidersForPackageLocked(pkgName);
   1093                         }
   1094                         saveStateLocked();
   1095                     }
   1096                 }
   1097             } else {
   1098                 boolean added = false;
   1099                 String pkgList[] = null;
   1100                 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
   1101                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
   1102                     added = true;
   1103                 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
   1104                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
   1105                     added = false;
   1106                 } else  {
   1107                     Uri uri = intent.getData();
   1108                     if (uri == null) {
   1109                         return;
   1110                     }
   1111                     String pkgName = uri.getSchemeSpecificPart();
   1112                     if (pkgName == null) {
   1113                         return;
   1114                     }
   1115                     pkgList = new String[] { pkgName };
   1116                     added = Intent.ACTION_PACKAGE_ADDED.equals(action);
   1117                 }
   1118                 if (pkgList == null || pkgList.length == 0) {
   1119                     return;
   1120                 }
   1121                 if (added) {
   1122                     synchronized (mAppWidgetIds) {
   1123                         Bundle extras = intent.getExtras();
   1124                         if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
   1125                             for (String pkgName : pkgList) {
   1126                                 // The package was just upgraded
   1127                                 updateProvidersForPackageLocked(pkgName);
   1128                             }
   1129                         } else {
   1130                             // The package was just added
   1131                             for (String pkgName : pkgList) {
   1132                                 addProvidersForPackageLocked(pkgName);
   1133                             }
   1134                         }
   1135                         saveStateLocked();
   1136                     }
   1137                 } else {
   1138                     Bundle extras = intent.getExtras();
   1139                     if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
   1140                         // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
   1141                     } else {
   1142                         synchronized (mAppWidgetIds) {
   1143                             for (String pkgName : pkgList) {
   1144                                 removeProvidersForPackageLocked(pkgName);
   1145                                 saveStateLocked();
   1146                             }
   1147                         }
   1148                     }
   1149                 }
   1150             }
   1151         }
   1152     };
   1153 
   1154     void addProvidersForPackageLocked(String pkgName) {
   1155         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
   1156         intent.setPackage(pkgName);
   1157         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
   1158                 PackageManager.GET_META_DATA);
   1159 
   1160         final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
   1161         for (int i=0; i<N; i++) {
   1162             ResolveInfo ri = broadcastReceivers.get(i);
   1163             ActivityInfo ai = ri.activityInfo;
   1164             if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
   1165                 continue;
   1166             }
   1167             if (pkgName.equals(ai.packageName)) {
   1168                 addProviderLocked(ri);
   1169             }
   1170         }
   1171     }
   1172 
   1173     void updateProvidersForPackageLocked(String pkgName) {
   1174         HashSet<String> keep = new HashSet<String>();
   1175         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
   1176         intent.setPackage(pkgName);
   1177         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
   1178                 PackageManager.GET_META_DATA);
   1179 
   1180         // add the missing ones and collect which ones to keep
   1181         int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
   1182         for (int i=0; i<N; i++) {
   1183             ResolveInfo ri = broadcastReceivers.get(i);
   1184             ActivityInfo ai = ri.activityInfo;
   1185             if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
   1186                 continue;
   1187             }
   1188             if (pkgName.equals(ai.packageName)) {
   1189                 ComponentName component = new ComponentName(ai.packageName, ai.name);
   1190                 Provider p = lookupProviderLocked(component);
   1191                 if (p == null) {
   1192                     if (addProviderLocked(ri)) {
   1193                         keep.add(ai.name);
   1194                     }
   1195                 } else {
   1196                     Provider parsed = parseProviderInfoXml(component, ri);
   1197                     if (parsed != null) {
   1198                         keep.add(ai.name);
   1199                         // Use the new AppWidgetProviderInfo.
   1200                         p.info = parsed.info;
   1201                         // If it's enabled
   1202                         final int M = p.instances.size();
   1203                         if (M > 0) {
   1204                             int[] appWidgetIds = getAppWidgetIds(p);
   1205                             // Reschedule for the new updatePeriodMillis (don't worry about handling
   1206                             // it specially if updatePeriodMillis didn't change because we just sent
   1207                             // an update, and the next one will be updatePeriodMillis from now).
   1208                             cancelBroadcasts(p);
   1209                             registerForBroadcastsLocked(p, appWidgetIds);
   1210                             // If it's currently showing, call back with the new AppWidgetProviderInfo.
   1211                             for (int j=0; j<M; j++) {
   1212                                 AppWidgetId id = p.instances.get(j);
   1213                                 id.views = null;
   1214                                 if (id.host != null && id.host.callbacks != null) {
   1215                                     try {
   1216                                         id.host.callbacks.providerChanged(id.appWidgetId, p.info);
   1217                                     } catch (RemoteException ex) {
   1218                                         // It failed; remove the callback. No need to prune because
   1219                                         // we know that this host is still referenced by this
   1220                                         // instance.
   1221                                         id.host.callbacks = null;
   1222                                     }
   1223                                 }
   1224                             }
   1225                             // Now that we've told the host, push out an update.
   1226                             sendUpdateIntentLocked(p, appWidgetIds);
   1227                         }
   1228                     }
   1229                 }
   1230             }
   1231         }
   1232 
   1233         // prune the ones we don't want to keep
   1234         N = mInstalledProviders.size();
   1235         for (int i=N-1; i>=0; i--) {
   1236             Provider p = mInstalledProviders.get(i);
   1237             if (pkgName.equals(p.info.provider.getPackageName())
   1238                     && !keep.contains(p.info.provider.getClassName())) {
   1239                 removeProviderLocked(i, p);
   1240             }
   1241         }
   1242     }
   1243 
   1244     void removeProvidersForPackageLocked(String pkgName) {
   1245         int N = mInstalledProviders.size();
   1246         for (int i=N-1; i>=0; i--) {
   1247             Provider p = mInstalledProviders.get(i);
   1248             if (pkgName.equals(p.info.provider.getPackageName())) {
   1249                 removeProviderLocked(i, p);
   1250             }
   1251         }
   1252 
   1253         // Delete the hosts for this package too
   1254         //
   1255         // By now, we have removed any AppWidgets that were in any hosts here,
   1256         // so we don't need to worry about sending DISABLE broadcasts to them.
   1257         N = mHosts.size();
   1258         for (int i=N-1; i>=0; i--) {
   1259             Host host = mHosts.get(i);
   1260             if (pkgName.equals(host.packageName)) {
   1261                 deleteHostLocked(host);
   1262             }
   1263         }
   1264     }
   1265 }
   1266 
   1267