AppWidgetServiceImpl.java revision 0aa2d42e87e4a1ed5b83f356690e465d6a3587cc
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.AlarmManager;
20import android.app.AppGlobals;
21import android.app.PendingIntent;
22import android.appwidget.AppWidgetManager;
23import android.appwidget.AppWidgetProviderInfo;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.Intent.FilterComparison;
28import android.content.ServiceConnection;
29import android.content.pm.ActivityInfo;
30import android.content.pm.ApplicationInfo;
31import android.content.pm.IPackageManager;
32import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
35import android.content.pm.ServiceInfo;
36import android.content.res.Resources;
37import android.content.res.TypedArray;
38import android.content.res.XmlResourceParser;
39import android.graphics.Point;
40import android.net.Uri;
41import android.os.Binder;
42import android.os.Bundle;
43import android.os.Environment;
44import android.os.IBinder;
45import android.os.RemoteException;
46import android.os.SystemClock;
47import android.os.UserHandle;
48import android.util.AtomicFile;
49import android.util.AttributeSet;
50import android.util.Log;
51import android.util.Pair;
52import android.util.Slog;
53import android.util.TypedValue;
54import android.util.Xml;
55import android.view.Display;
56import android.view.WindowManager;
57import android.widget.RemoteViews;
58
59import com.android.internal.appwidget.IAppWidgetHost;
60import com.android.internal.util.FastXmlSerializer;
61import com.android.internal.widget.IRemoteViewsAdapterConnection;
62import com.android.internal.widget.IRemoteViewsFactory;
63
64import org.xmlpull.v1.XmlPullParser;
65import org.xmlpull.v1.XmlPullParserException;
66import org.xmlpull.v1.XmlSerializer;
67
68import java.io.File;
69import java.io.FileDescriptor;
70import java.io.FileInputStream;
71import java.io.FileNotFoundException;
72import java.io.FileOutputStream;
73import java.io.IOException;
74import java.io.PrintWriter;
75import java.util.ArrayList;
76import java.util.HashMap;
77import java.util.HashSet;
78import java.util.Iterator;
79import java.util.List;
80import java.util.Locale;
81import java.util.Set;
82
83class AppWidgetServiceImpl {
84
85    private static final String TAG = "AppWidgetServiceImpl";
86    private static final String SETTINGS_FILENAME = "appwidgets.xml";
87    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
88
89    /*
90     * When identifying a Host or Provider based on the calling process, use the uid field. When
91     * identifying a Host or Provider based on a package manager broadcast, use the package given.
92     */
93
94    static class Provider {
95        int uid;
96        AppWidgetProviderInfo info;
97        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
98        PendingIntent broadcast;
99        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
100
101        int tag; // for use while saving state (the index)
102    }
103
104    static class Host {
105        int uid;
106        int hostId;
107        String packageName;
108        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
109        IAppWidgetHost callbacks;
110        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
111
112        int tag; // for use while saving state (the index)
113    }
114
115    static class AppWidgetId {
116        int appWidgetId;
117        Provider provider;
118        RemoteViews views;
119        Bundle options;
120        Host host;
121    }
122
123    /**
124     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
125     * needs to be a static inner class since a reference to the ServiceConnection is held globally
126     * and may lead us to leak AppWidgetService instances (if there were more than one).
127     */
128    static class ServiceConnectionProxy implements ServiceConnection {
129        private final IBinder mConnectionCb;
130
131        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
132            mConnectionCb = connectionCb;
133        }
134
135        public void onServiceConnected(ComponentName name, IBinder service) {
136            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
137                    .asInterface(mConnectionCb);
138            try {
139                cb.onServiceConnected(service);
140            } catch (Exception e) {
141                e.printStackTrace();
142            }
143        }
144
145        public void onServiceDisconnected(ComponentName name) {
146            disconnect();
147        }
148
149        public void disconnect() {
150            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
151                    .asInterface(mConnectionCb);
152            try {
153                cb.onServiceDisconnected();
154            } catch (Exception e) {
155                e.printStackTrace();
156            }
157        }
158    }
159
160    // Manages active connections to RemoteViewsServices
161    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
162    // Manages persistent references to RemoteViewsServices from different App Widgets
163    private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
164
165    Context mContext;
166    Locale mLocale;
167    IPackageManager mPm;
168    AlarmManager mAlarmManager;
169    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
170    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
171    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
172    ArrayList<Host> mHosts = new ArrayList<Host>();
173    // set of package names
174    HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
175    boolean mSafeMode;
176    int mUserId;
177    boolean mStateLoaded;
178    int mMaxWidgetBitmapMemory;
179
180    // These are for debugging only -- widgets are going missing in some rare instances
181    ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
182    ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
183
184    AppWidgetServiceImpl(Context context, int userId) {
185        mContext = context;
186        mPm = AppGlobals.getPackageManager();
187        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
188        mUserId = userId;
189        computeMaximumWidgetBitmapMemory();
190    }
191
192    void computeMaximumWidgetBitmapMemory() {
193        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
194        Display display = wm.getDefaultDisplay();
195        Point size = new Point();
196        display.getRealSize(size);
197        // Cap memory usage at 1.5 times the size of the display
198        // 1.5 * 4 bytes/pixel * w * h ==> 6 * w * h
199        mMaxWidgetBitmapMemory = 6 * size.x * size.y;
200    }
201
202    public void systemReady(boolean safeMode) {
203        mSafeMode = safeMode;
204
205        synchronized (mAppWidgetIds) {
206            ensureStateLoadedLocked();
207        }
208    }
209
210    void onConfigurationChanged() {
211        Locale revised = Locale.getDefault();
212        if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
213            mLocale = revised;
214
215            synchronized (mAppWidgetIds) {
216                ensureStateLoadedLocked();
217                // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
218                // list of installed providers and skip providers that we don't need to update.
219                // Also note that remove the provider does not clear the Provider component data.
220                ArrayList<Provider> installedProviders =
221                        new ArrayList<Provider>(mInstalledProviders);
222                HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
223                int N = installedProviders.size();
224                for (int i = N - 1; i >= 0; i--) {
225                    Provider p = installedProviders.get(i);
226                    ComponentName cn = p.info.provider;
227                    if (!removedProviders.contains(cn)) {
228                        updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
229                    }
230                }
231                saveStateLocked();
232            }
233        }
234    }
235
236    void onBroadcastReceived(Intent intent) {
237        final String action = intent.getAction();
238        boolean added = false;
239        boolean changed = false;
240        boolean providersModified = false;
241        String pkgList[] = null;
242        if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
243            pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
244            added = true;
245        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
246            pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
247            added = false;
248        } else {
249            Uri uri = intent.getData();
250            if (uri == null) {
251                return;
252            }
253            String pkgName = uri.getSchemeSpecificPart();
254            if (pkgName == null) {
255                return;
256            }
257            pkgList = new String[] { pkgName };
258            added = Intent.ACTION_PACKAGE_ADDED.equals(action);
259            changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
260        }
261        if (pkgList == null || pkgList.length == 0) {
262            return;
263        }
264        if (added || changed) {
265            synchronized (mAppWidgetIds) {
266                ensureStateLoadedLocked();
267                Bundle extras = intent.getExtras();
268                if (changed
269                        || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
270                    for (String pkgName : pkgList) {
271                        // The package was just upgraded
272                        providersModified |= updateProvidersForPackageLocked(pkgName, null);
273                    }
274                } else {
275                    // The package was just added
276                    for (String pkgName : pkgList) {
277                        providersModified |= addProvidersForPackageLocked(pkgName);
278                    }
279                }
280                saveStateLocked();
281            }
282        } else {
283            Bundle extras = intent.getExtras();
284            if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
285                // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
286            } else {
287                synchronized (mAppWidgetIds) {
288                    ensureStateLoadedLocked();
289                    for (String pkgName : pkgList) {
290                        providersModified |= removeProvidersForPackageLocked(pkgName);
291                        saveStateLocked();
292                    }
293                }
294            }
295        }
296
297        if (providersModified) {
298            // If the set of providers has been modified, notify each active AppWidgetHost
299            synchronized (mAppWidgetIds) {
300                ensureStateLoadedLocked();
301                notifyHostsForProvidersChangedLocked();
302            }
303        }
304    }
305
306    private void dumpProvider(Provider p, int index, PrintWriter pw) {
307        AppWidgetProviderInfo info = p.info;
308        pw.print("  ["); pw.print(index); pw.print("] provider ");
309                pw.print(info.provider.flattenToShortString());
310                pw.println(':');
311        pw.print("    min=("); pw.print(info.minWidth);
312                pw.print("x"); pw.print(info.minHeight);
313        pw.print(")   minResize=("); pw.print(info.minResizeWidth);
314                pw.print("x"); pw.print(info.minResizeHeight);
315                pw.print(") updatePeriodMillis=");
316                pw.print(info.updatePeriodMillis);
317                pw.print(" resizeMode=");
318                pw.print(info.resizeMode);
319                pw.print(info.widgetCategory);
320                pw.print(info.widgetFeatures);
321                pw.print(" autoAdvanceViewId=");
322                pw.print(info.autoAdvanceViewId);
323                pw.print(" initialLayout=#");
324                pw.print(Integer.toHexString(info.initialLayout));
325                pw.print(" zombie="); pw.println(p.zombie);
326    }
327
328    private void dumpHost(Host host, int index, PrintWriter pw) {
329        pw.print("  ["); pw.print(index); pw.print("] hostId=");
330                pw.print(host.hostId); pw.print(' ');
331                pw.print(host.packageName); pw.print('/');
332        pw.print(host.uid); pw.println(':');
333        pw.print("    callbacks="); pw.println(host.callbacks);
334        pw.print("    instances.size="); pw.print(host.instances.size());
335                pw.print(" zombie="); pw.println(host.zombie);
336    }
337
338    private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
339        pw.print("  ["); pw.print(index); pw.print("] id=");
340                pw.println(id.appWidgetId);
341        pw.print("    hostId=");
342                pw.print(id.host.hostId); pw.print(' ');
343                pw.print(id.host.packageName); pw.print('/');
344                pw.println(id.host.uid);
345        if (id.provider != null) {
346            pw.print("    provider=");
347                    pw.println(id.provider.info.provider.flattenToShortString());
348        }
349        if (id.host != null) {
350            pw.print("    host.callbacks="); pw.println(id.host.callbacks);
351        }
352        if (id.views != null) {
353            pw.print("    views="); pw.println(id.views);
354        }
355    }
356
357    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
358        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
359                != PackageManager.PERMISSION_GRANTED) {
360            pw.println("Permission Denial: can't dump from from pid="
361                    + Binder.getCallingPid()
362                    + ", uid=" + Binder.getCallingUid());
363            return;
364        }
365
366        synchronized (mAppWidgetIds) {
367            int N = mInstalledProviders.size();
368            pw.println("Providers:");
369            for (int i=0; i<N; i++) {
370                dumpProvider(mInstalledProviders.get(i), i, pw);
371            }
372
373            N = mAppWidgetIds.size();
374            pw.println(" ");
375            pw.println("AppWidgetIds:");
376            for (int i=0; i<N; i++) {
377                dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
378            }
379
380            N = mHosts.size();
381            pw.println(" ");
382            pw.println("Hosts:");
383            for (int i=0; i<N; i++) {
384                dumpHost(mHosts.get(i), i, pw);
385            }
386
387            N = mDeletedProviders.size();
388            pw.println(" ");
389            pw.println("Deleted Providers:");
390            for (int i=0; i<N; i++) {
391                dumpProvider(mDeletedProviders.get(i), i, pw);
392            }
393
394            N = mDeletedHosts.size();
395            pw.println(" ");
396            pw.println("Deleted Hosts:");
397            for (int i=0; i<N; i++) {
398                dumpHost(mDeletedHosts.get(i), i, pw);
399            }
400        }
401    }
402
403    private void ensureStateLoadedLocked() {
404        if (!mStateLoaded) {
405            loadAppWidgetList();
406            loadStateLocked();
407            mStateLoaded = true;
408        }
409    }
410
411    public int allocateAppWidgetId(String packageName, int hostId) {
412        int callingUid = enforceCallingUid(packageName);
413        synchronized (mAppWidgetIds) {
414            ensureStateLoadedLocked();
415            int appWidgetId = mNextAppWidgetId++;
416
417            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
418
419            AppWidgetId id = new AppWidgetId();
420            id.appWidgetId = appWidgetId;
421            id.host = host;
422
423            host.instances.add(id);
424            mAppWidgetIds.add(id);
425
426            saveStateLocked();
427
428            return appWidgetId;
429        }
430    }
431
432    public void deleteAppWidgetId(int appWidgetId) {
433        synchronized (mAppWidgetIds) {
434            ensureStateLoadedLocked();
435            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
436            if (id != null) {
437                deleteAppWidgetLocked(id);
438                saveStateLocked();
439            }
440        }
441    }
442
443    public void deleteHost(int hostId) {
444        synchronized (mAppWidgetIds) {
445            ensureStateLoadedLocked();
446            int callingUid = Binder.getCallingUid();
447            Host host = lookupHostLocked(callingUid, hostId);
448            if (host != null) {
449                deleteHostLocked(host);
450                saveStateLocked();
451            }
452        }
453    }
454
455    public void deleteAllHosts() {
456        synchronized (mAppWidgetIds) {
457            ensureStateLoadedLocked();
458            int callingUid = Binder.getCallingUid();
459            final int N = mHosts.size();
460            boolean changed = false;
461            for (int i = N - 1; i >= 0; i--) {
462                Host host = mHosts.get(i);
463                if (host.uid == callingUid) {
464                    deleteHostLocked(host);
465                    changed = true;
466                }
467            }
468            if (changed) {
469                saveStateLocked();
470            }
471        }
472    }
473
474    void deleteHostLocked(Host host) {
475        final int N = host.instances.size();
476        for (int i = N - 1; i >= 0; i--) {
477            AppWidgetId id = host.instances.get(i);
478            deleteAppWidgetLocked(id);
479        }
480        host.instances.clear();
481        mHosts.remove(host);
482        mDeletedHosts.add(host);
483        // it's gone or going away, abruptly drop the callback connection
484        host.callbacks = null;
485    }
486
487    void deleteAppWidgetLocked(AppWidgetId id) {
488        // We first unbind all services that are bound to this id
489        unbindAppWidgetRemoteViewsServicesLocked(id);
490
491        Host host = id.host;
492        host.instances.remove(id);
493        pruneHostLocked(host);
494
495        mAppWidgetIds.remove(id);
496
497        Provider p = id.provider;
498        if (p != null) {
499            p.instances.remove(id);
500            if (!p.zombie) {
501                // send the broacast saying that this appWidgetId has been deleted
502                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
503                intent.setComponent(p.info.provider);
504                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
505                mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
506                if (p.instances.size() == 0) {
507                    // cancel the future updates
508                    cancelBroadcasts(p);
509
510                    // send the broacast saying that the provider is not in use any more
511                    intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
512                    intent.setComponent(p.info.provider);
513                    mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
514                }
515            }
516        }
517    }
518
519    void cancelBroadcasts(Provider p) {
520        if (p.broadcast != null) {
521            mAlarmManager.cancel(p.broadcast);
522            long token = Binder.clearCallingIdentity();
523            try {
524                p.broadcast.cancel();
525            } finally {
526                Binder.restoreCallingIdentity(token);
527            }
528            p.broadcast = null;
529        }
530    }
531
532    private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
533        final long ident = Binder.clearCallingIdentity();
534        try {
535            synchronized (mAppWidgetIds) {
536                ensureStateLoadedLocked();
537                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
538                if (id == null) {
539                    throw new IllegalArgumentException("bad appWidgetId");
540                }
541                if (id.provider != null) {
542                    throw new IllegalArgumentException("appWidgetId " + appWidgetId
543                            + " already bound to " + id.provider.info.provider);
544                }
545                Provider p = lookupProviderLocked(provider);
546                if (p == null) {
547                    throw new IllegalArgumentException("not a appwidget provider: " + provider);
548                }
549                if (p.zombie) {
550                    throw new IllegalArgumentException("can't bind to a 3rd party provider in"
551                            + " safe mode: " + provider);
552                }
553
554                id.provider = p;
555                if (options == null) {
556                    options = new Bundle();
557                }
558                id.options = options;
559
560                // We need to provide a default value for the widget category if it is not specified
561                if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
562                    options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
563                            AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
564                }
565
566                p.instances.add(id);
567                int instancesSize = p.instances.size();
568                if (instancesSize == 1) {
569                    // tell the provider that it's ready
570                    sendEnableIntentLocked(p);
571                }
572
573                // send an update now -- We need this update now, and just for this appWidgetId.
574                // It's less critical when the next one happens, so when we schedule the next one,
575                // we add updatePeriodMillis to its start time. That time will have some slop,
576                // but that's okay.
577                sendUpdateIntentLocked(p, new int[] { appWidgetId });
578
579                // schedule the future updates
580                registerForBroadcastsLocked(p, getAppWidgetIds(p));
581                saveStateLocked();
582            }
583        } finally {
584            Binder.restoreCallingIdentity(ident);
585        }
586    }
587
588    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
589        mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
590            "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
591        bindAppWidgetIdImpl(appWidgetId, provider, options);
592    }
593
594    public boolean bindAppWidgetIdIfAllowed(
595            String packageName, int appWidgetId, ComponentName provider, Bundle options) {
596        try {
597            mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
598        } catch (SecurityException se) {
599            if (!callerHasBindAppWidgetPermission(packageName)) {
600                return false;
601            }
602        }
603        bindAppWidgetIdImpl(appWidgetId, provider, options);
604        return true;
605    }
606
607    private boolean callerHasBindAppWidgetPermission(String packageName) {
608        int callingUid = Binder.getCallingUid();
609        try {
610            if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
611                return false;
612            }
613        } catch (Exception e) {
614            return false;
615        }
616        synchronized (mAppWidgetIds) {
617            ensureStateLoadedLocked();
618            return mPackagesWithBindWidgetPermission.contains(packageName);
619        }
620    }
621
622    public boolean hasBindAppWidgetPermission(String packageName) {
623        mContext.enforceCallingPermission(
624                android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
625                "hasBindAppWidgetPermission packageName=" + packageName);
626
627        synchronized (mAppWidgetIds) {
628            ensureStateLoadedLocked();
629            return mPackagesWithBindWidgetPermission.contains(packageName);
630        }
631    }
632
633    public void setBindAppWidgetPermission(String packageName, boolean permission) {
634        mContext.enforceCallingPermission(
635                android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
636                "setBindAppWidgetPermission packageName=" + packageName);
637
638        synchronized (mAppWidgetIds) {
639            ensureStateLoadedLocked();
640            if (permission) {
641                mPackagesWithBindWidgetPermission.add(packageName);
642            } else {
643                mPackagesWithBindWidgetPermission.remove(packageName);
644            }
645        }
646        saveStateLocked();
647    }
648
649    // Binds to a specific RemoteViewsService
650    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
651        synchronized (mAppWidgetIds) {
652            ensureStateLoadedLocked();
653            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
654            if (id == null) {
655                throw new IllegalArgumentException("bad appWidgetId");
656            }
657            final ComponentName componentName = intent.getComponent();
658            try {
659                final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
660                        PackageManager.GET_PERMISSIONS);
661                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
662                    throw new SecurityException("Selected service does not require "
663                            + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
664                }
665            } catch (PackageManager.NameNotFoundException e) {
666                throw new IllegalArgumentException("Unknown component " + componentName);
667            }
668
669            // If there is already a connection made for this service intent, then disconnect from
670            // that first. (This does not allow multiple connections to the same service under
671            // the same key)
672            ServiceConnectionProxy conn = null;
673            FilterComparison fc = new FilterComparison(intent);
674            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
675            if (mBoundRemoteViewsServices.containsKey(key)) {
676                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
677                conn.disconnect();
678                mContext.unbindService(conn);
679                mBoundRemoteViewsServices.remove(key);
680            }
681
682            int userId = UserHandle.getUserId(id.provider.uid);
683            // Bind to the RemoteViewsService (which will trigger a callback to the
684            // RemoteViewsAdapter.onServiceConnected())
685            final long token = Binder.clearCallingIdentity();
686            try {
687                conn = new ServiceConnectionProxy(key, connection);
688                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
689                mBoundRemoteViewsServices.put(key, conn);
690            } finally {
691                Binder.restoreCallingIdentity(token);
692            }
693
694            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
695            // when we can call back to the RemoteViewsService later to destroy associated
696            // factories.
697            incrementAppWidgetServiceRefCount(appWidgetId, fc);
698        }
699    }
700
701    // Unbinds from a specific RemoteViewsService
702    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
703        synchronized (mAppWidgetIds) {
704            ensureStateLoadedLocked();
705            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
706            // RemoteViewsAdapter)
707            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
708                    intent));
709            if (mBoundRemoteViewsServices.containsKey(key)) {
710                // We don't need to use the appWidgetId until after we are sure there is something
711                // to unbind. Note that this may mask certain issues with apps calling unbind()
712                // more than necessary.
713                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
714                if (id == null) {
715                    throw new IllegalArgumentException("bad appWidgetId");
716                }
717
718                ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
719                        .get(key);
720                conn.disconnect();
721                mContext.unbindService(conn);
722                mBoundRemoteViewsServices.remove(key);
723            } else {
724                Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
725            }
726        }
727    }
728
729    // Unbinds from a RemoteViewsService when we delete an app widget
730    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
731        int appWidgetId = id.appWidgetId;
732        // Unbind all connections to Services bound to this AppWidgetId
733        Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
734                .iterator();
735        while (it.hasNext()) {
736            final Pair<Integer, Intent.FilterComparison> key = it.next();
737            if (key.first.intValue() == appWidgetId) {
738                final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
739                        .get(key);
740                conn.disconnect();
741                mContext.unbindService(conn);
742                it.remove();
743            }
744        }
745
746        // Check if we need to destroy any services (if no other app widgets are
747        // referencing the same service)
748        decrementAppWidgetServiceRefCount(id);
749    }
750
751    // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
752    private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
753        final ServiceConnection conn = new ServiceConnection() {
754            @Override
755            public void onServiceConnected(ComponentName name, IBinder service) {
756                final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
757                try {
758                    cb.onDestroy(intent);
759                } catch (RemoteException e) {
760                    e.printStackTrace();
761                } catch (RuntimeException e) {
762                    e.printStackTrace();
763                }
764                mContext.unbindService(this);
765            }
766
767            @Override
768            public void onServiceDisconnected(android.content.ComponentName name) {
769                // Do nothing
770            }
771        };
772
773        int userId = UserHandle.getUserId(id.provider.uid);
774        // Bind to the service and remove the static intent->factory mapping in the
775        // RemoteViewsService.
776        final long token = Binder.clearCallingIdentity();
777        try {
778            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
779        } finally {
780            Binder.restoreCallingIdentity(token);
781        }
782    }
783
784    // Adds to the ref-count for a given RemoteViewsService intent
785    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
786        HashSet<Integer> appWidgetIds = null;
787        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
788            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
789        } else {
790            appWidgetIds = new HashSet<Integer>();
791            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
792        }
793        appWidgetIds.add(appWidgetId);
794    }
795
796    // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
797    // the ref-count reaches zero.
798    private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
799        Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
800        while (it.hasNext()) {
801            final FilterComparison key = it.next();
802            final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
803            if (ids.remove(id.appWidgetId)) {
804                // If we have removed the last app widget referencing this service, then we
805                // should destroy it and remove it from this set
806                if (ids.isEmpty()) {
807                    destroyRemoteViewsService(key.getIntent(), id);
808                    it.remove();
809                }
810            }
811        }
812    }
813
814    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
815        synchronized (mAppWidgetIds) {
816            ensureStateLoadedLocked();
817            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
818            if (id != null && id.provider != null && !id.provider.zombie) {
819                return id.provider.info;
820            }
821            return null;
822        }
823    }
824
825    public RemoteViews getAppWidgetViews(int appWidgetId) {
826        synchronized (mAppWidgetIds) {
827            ensureStateLoadedLocked();
828            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
829            if (id != null) {
830                return id.views;
831            }
832            return null;
833        }
834    }
835
836    public List<AppWidgetProviderInfo> getInstalledProviders() {
837        synchronized (mAppWidgetIds) {
838            ensureStateLoadedLocked();
839            final int N = mInstalledProviders.size();
840            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
841            for (int i = 0; i < N; i++) {
842                Provider p = mInstalledProviders.get(i);
843                if (!p.zombie) {
844                    result.add(p.info);
845                }
846            }
847            return result;
848        }
849    }
850
851    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
852        if (appWidgetIds == null) {
853            return;
854        }
855
856        int bitmapMemoryUsage = 0;
857        if (views != null) {
858            bitmapMemoryUsage = views.estimateMemoryUsage();
859        }
860        if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
861            throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
862                    " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
863                    mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
864                    " fill the device's screen once.");
865        }
866
867        if (appWidgetIds.length == 0) {
868            return;
869        }
870        final int N = appWidgetIds.length;
871
872        synchronized (mAppWidgetIds) {
873            ensureStateLoadedLocked();
874            for (int i = 0; i < N; i++) {
875                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
876                updateAppWidgetInstanceLocked(id, views);
877            }
878        }
879    }
880
881    public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
882        synchronized (mAppWidgetIds) {
883            ensureStateLoadedLocked();
884            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
885
886            if (id == null) {
887                return;
888            }
889
890            Provider p = id.provider;
891            // Merge the options
892            id.options.putAll(options);
893
894            // send the broacast saying that this appWidgetId has been deleted
895            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
896            intent.setComponent(p.info.provider);
897            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
898            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
899            mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
900            saveStateLocked();
901        }
902    }
903
904    public Bundle getAppWidgetOptions(int appWidgetId) {
905        synchronized (mAppWidgetIds) {
906            ensureStateLoadedLocked();
907            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
908            if (id != null && id.options != null) {
909                return id.options;
910            } else {
911                return Bundle.EMPTY;
912            }
913        }
914    }
915
916    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
917        if (appWidgetIds == null) {
918            return;
919        }
920        if (appWidgetIds.length == 0) {
921            return;
922        }
923        final int N = appWidgetIds.length;
924
925        synchronized (mAppWidgetIds) {
926            ensureStateLoadedLocked();
927            for (int i = 0; i < N; i++) {
928                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
929                updateAppWidgetInstanceLocked(id, views, true);
930            }
931        }
932    }
933
934    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
935        if (appWidgetIds == null) {
936            return;
937        }
938        if (appWidgetIds.length == 0) {
939            return;
940        }
941        final int N = appWidgetIds.length;
942
943        synchronized (mAppWidgetIds) {
944            ensureStateLoadedLocked();
945            for (int i = 0; i < N; i++) {
946                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
947                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
948            }
949        }
950    }
951
952    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
953        synchronized (mAppWidgetIds) {
954            ensureStateLoadedLocked();
955            Provider p = lookupProviderLocked(provider);
956            if (p == null) {
957                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
958                return;
959            }
960            ArrayList<AppWidgetId> instances = p.instances;
961            final int callingUid = Binder.getCallingUid();
962            final int N = instances.size();
963            for (int i = 0; i < N; i++) {
964                AppWidgetId id = instances.get(i);
965                if (canAccessAppWidgetId(id, callingUid)) {
966                    updateAppWidgetInstanceLocked(id, views);
967                }
968            }
969        }
970    }
971
972    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
973        updateAppWidgetInstanceLocked(id, views, false);
974    }
975
976    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
977        // allow for stale appWidgetIds and other badness
978        // lookup also checks that the calling process can access the appWidgetId
979        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
980        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
981
982            // We do not want to save this RemoteViews
983            if (!isPartialUpdate)
984                id.views = views;
985
986            // is anyone listening?
987            if (id.host.callbacks != null) {
988                try {
989                    // the lock is held, but this is a oneway call
990                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
991                } catch (RemoteException e) {
992                    // It failed; remove the callback. No need to prune because
993                    // we know that this host is still referenced by this instance.
994                    id.host.callbacks = null;
995                }
996            }
997        }
998    }
999
1000    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1001        // allow for stale appWidgetIds and other badness
1002        // lookup also checks that the calling process can access the appWidgetId
1003        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1004        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1005            // is anyone listening?
1006            if (id.host.callbacks != null) {
1007                try {
1008                    // the lock is held, but this is a oneway call
1009                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1010                } catch (RemoteException e) {
1011                    // It failed; remove the callback. No need to prune because
1012                    // we know that this host is still referenced by this instance.
1013                    id.host.callbacks = null;
1014                }
1015            }
1016
1017            // If the host is unavailable, then we call the associated
1018            // RemoteViewsFactory.onDataSetChanged() directly
1019            if (id.host.callbacks == null) {
1020                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1021                for (FilterComparison key : keys) {
1022                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1023                        Intent intent = key.getIntent();
1024
1025                        final ServiceConnection conn = new ServiceConnection() {
1026                            @Override
1027                            public void onServiceConnected(ComponentName name, IBinder service) {
1028                                IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1029                                        .asInterface(service);
1030                                try {
1031                                    cb.onDataSetChangedAsync();
1032                                } catch (RemoteException e) {
1033                                    e.printStackTrace();
1034                                } catch (RuntimeException e) {
1035                                    e.printStackTrace();
1036                                }
1037                                mContext.unbindService(this);
1038                            }
1039
1040                            @Override
1041                            public void onServiceDisconnected(android.content.ComponentName name) {
1042                                // Do nothing
1043                            }
1044                        };
1045
1046                        int userId = UserHandle.getUserId(id.provider.uid);
1047                        // Bind to the service and call onDataSetChanged()
1048                        final long token = Binder.clearCallingIdentity();
1049                        try {
1050                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
1051                        } finally {
1052                            Binder.restoreCallingIdentity(token);
1053                        }
1054                    }
1055                }
1056            }
1057        }
1058    }
1059
1060    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1061            List<RemoteViews> updatedViews) {
1062        int callingUid = enforceCallingUid(packageName);
1063        synchronized (mAppWidgetIds) {
1064            ensureStateLoadedLocked();
1065            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1066            host.callbacks = callbacks;
1067
1068            updatedViews.clear();
1069
1070            ArrayList<AppWidgetId> instances = host.instances;
1071            int N = instances.size();
1072            int[] updatedIds = new int[N];
1073            for (int i = 0; i < N; i++) {
1074                AppWidgetId id = instances.get(i);
1075                updatedIds[i] = id.appWidgetId;
1076                updatedViews.add(id.views);
1077            }
1078            return updatedIds;
1079        }
1080    }
1081
1082    public void stopListening(int hostId) {
1083        synchronized (mAppWidgetIds) {
1084            ensureStateLoadedLocked();
1085            Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1086            if (host != null) {
1087                host.callbacks = null;
1088                pruneHostLocked(host);
1089            }
1090        }
1091    }
1092
1093    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1094        if (id.host.uid == callingUid) {
1095            // Apps hosting the AppWidget have access to it.
1096            return true;
1097        }
1098        if (id.provider != null && id.provider.uid == callingUid) {
1099            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1100            return true;
1101        }
1102        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1103            // Apps that can bind have access to all appWidgetIds.
1104            return true;
1105        }
1106        // Nobody else can access it.
1107        return false;
1108    }
1109
1110    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1111        int callingUid = Binder.getCallingUid();
1112        final int N = mAppWidgetIds.size();
1113        for (int i = 0; i < N; i++) {
1114            AppWidgetId id = mAppWidgetIds.get(i);
1115            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1116                return id;
1117            }
1118        }
1119        return null;
1120    }
1121
1122    Provider lookupProviderLocked(ComponentName provider) {
1123        final int N = mInstalledProviders.size();
1124        for (int i = 0; i < N; i++) {
1125            Provider p = mInstalledProviders.get(i);
1126            if (p.info.provider.equals(provider)) {
1127                return p;
1128            }
1129        }
1130        return null;
1131    }
1132
1133    Host lookupHostLocked(int uid, int hostId) {
1134        final int N = mHosts.size();
1135        for (int i = 0; i < N; i++) {
1136            Host h = mHosts.get(i);
1137            if (h.uid == uid && h.hostId == hostId) {
1138                return h;
1139            }
1140        }
1141        return null;
1142    }
1143
1144    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1145        final int N = mHosts.size();
1146        for (int i = 0; i < N; i++) {
1147            Host h = mHosts.get(i);
1148            if (h.hostId == hostId && h.packageName.equals(packageName)) {
1149                return h;
1150            }
1151        }
1152        Host host = new Host();
1153        host.packageName = packageName;
1154        host.uid = uid;
1155        host.hostId = hostId;
1156        mHosts.add(host);
1157        return host;
1158    }
1159
1160    void pruneHostLocked(Host host) {
1161        if (host.instances.size() == 0 && host.callbacks == null) {
1162            mHosts.remove(host);
1163        }
1164    }
1165
1166    void loadAppWidgetList() {
1167        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1168        try {
1169            List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1170                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1171                    PackageManager.GET_META_DATA, mUserId);
1172
1173            final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1174            for (int i = 0; i < N; i++) {
1175                ResolveInfo ri = broadcastReceivers.get(i);
1176                addProviderLocked(ri);
1177            }
1178        } catch (RemoteException re) {
1179            // Shouldn't happen, local call
1180        }
1181    }
1182
1183    boolean addProviderLocked(ResolveInfo ri) {
1184        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1185            return false;
1186        }
1187        if (!ri.activityInfo.isEnabled()) {
1188            return false;
1189        }
1190        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1191                ri.activityInfo.name), ri);
1192        if (p != null) {
1193            mInstalledProviders.add(p);
1194            return true;
1195        } else {
1196            return false;
1197        }
1198    }
1199
1200    void removeProviderLocked(int index, Provider p) {
1201        int N = p.instances.size();
1202        for (int i = 0; i < N; i++) {
1203            AppWidgetId id = p.instances.get(i);
1204            // Call back with empty RemoteViews
1205            updateAppWidgetInstanceLocked(id, null);
1206            // Stop telling the host about updates for this from now on
1207            cancelBroadcasts(p);
1208            // clear out references to this appWidgetId
1209            id.host.instances.remove(id);
1210            mAppWidgetIds.remove(id);
1211            id.provider = null;
1212            pruneHostLocked(id.host);
1213            id.host = null;
1214        }
1215        p.instances.clear();
1216        mInstalledProviders.remove(index);
1217        mDeletedProviders.add(p);
1218        // no need to send the DISABLE broadcast, since the receiver is gone anyway
1219        cancelBroadcasts(p);
1220    }
1221
1222    void sendEnableIntentLocked(Provider p) {
1223        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1224        intent.setComponent(p.info.provider);
1225        mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
1226    }
1227
1228    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1229        if (appWidgetIds != null && appWidgetIds.length > 0) {
1230            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1231            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1232            intent.setComponent(p.info.provider);
1233            mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
1234        }
1235    }
1236
1237    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1238        if (p.info.updatePeriodMillis > 0) {
1239            // if this is the first instance, set the alarm. otherwise,
1240            // rely on the fact that we've already set it and that
1241            // PendingIntent.getBroadcast will update the extras.
1242            boolean alreadyRegistered = p.broadcast != null;
1243            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1244            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1245            intent.setComponent(p.info.provider);
1246            long token = Binder.clearCallingIdentity();
1247            try {
1248                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1249                        PendingIntent.FLAG_UPDATE_CURRENT);
1250            } finally {
1251                Binder.restoreCallingIdentity(token);
1252            }
1253            if (!alreadyRegistered) {
1254                long period = p.info.updatePeriodMillis;
1255                if (period < MIN_UPDATE_PERIOD) {
1256                    period = MIN_UPDATE_PERIOD;
1257                }
1258                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1259                        .elapsedRealtime()
1260                        + period, period, p.broadcast);
1261            }
1262        }
1263    }
1264
1265    static int[] getAppWidgetIds(Provider p) {
1266        int instancesSize = p.instances.size();
1267        int appWidgetIds[] = new int[instancesSize];
1268        for (int i = 0; i < instancesSize; i++) {
1269            appWidgetIds[i] = p.instances.get(i).appWidgetId;
1270        }
1271        return appWidgetIds;
1272    }
1273
1274    public int[] getAppWidgetIds(ComponentName provider) {
1275        synchronized (mAppWidgetIds) {
1276            ensureStateLoadedLocked();
1277            Provider p = lookupProviderLocked(provider);
1278            if (p != null && Binder.getCallingUid() == p.uid) {
1279                return getAppWidgetIds(p);
1280            } else {
1281                return new int[0];
1282            }
1283        }
1284    }
1285
1286    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1287        Provider p = null;
1288
1289        ActivityInfo activityInfo = ri.activityInfo;
1290        XmlResourceParser parser = null;
1291        try {
1292            parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
1293                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1294            if (parser == null) {
1295                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1296                        + " meta-data for " + "AppWidget provider '" + component + '\'');
1297                return null;
1298            }
1299
1300            AttributeSet attrs = Xml.asAttributeSet(parser);
1301
1302            int type;
1303            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1304                    && type != XmlPullParser.START_TAG) {
1305                // drain whitespace, comments, etc.
1306            }
1307
1308            String nodeName = parser.getName();
1309            if (!"appwidget-provider".equals(nodeName)) {
1310                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1311                        + " AppWidget provider '" + component + '\'');
1312                return null;
1313            }
1314
1315            p = new Provider();
1316            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1317            info.provider = component;
1318            p.uid = activityInfo.applicationInfo.uid;
1319
1320            Resources res = mContext.getPackageManager()
1321                    .getResourcesForApplication(activityInfo.applicationInfo);
1322
1323            TypedArray sa = res.obtainAttributes(attrs,
1324                    com.android.internal.R.styleable.AppWidgetProviderInfo);
1325
1326            // These dimensions has to be resolved in the application's context.
1327            // We simply send back the raw complex data, which will be
1328            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1329            TypedValue value = sa
1330                    .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1331            info.minWidth = value != null ? value.data : 0;
1332            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1333            info.minHeight = value != null ? value.data : 0;
1334            value = sa.peekValue(
1335                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1336            info.minResizeWidth = value != null ? value.data : info.minWidth;
1337            value = sa.peekValue(
1338                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1339            info.minResizeHeight = value != null ? value.data : info.minHeight;
1340            info.updatePeriodMillis = sa.getInt(
1341                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1342            info.initialLayout = sa.getResourceId(
1343                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1344            info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1345                    AppWidgetProviderInfo_initialKeyguardLayout, 0);
1346            String className = sa
1347                    .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1348            if (className != null) {
1349                info.configure = new ComponentName(component.getPackageName(), className);
1350            }
1351            info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
1352            info.icon = ri.getIconResource();
1353            info.previewImage = sa.getResourceId(
1354                    com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1355            info.autoAdvanceViewId = sa.getResourceId(
1356                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1357            info.resizeMode = sa.getInt(
1358                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1359                    AppWidgetProviderInfo.RESIZE_NONE);
1360            info.widgetCategory = sa.getInt(
1361                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1362                    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
1363            info.widgetFeatures = sa.getInt(
1364                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1365                    AppWidgetProviderInfo.WIDGET_FEATURES_NONE);
1366
1367            sa.recycle();
1368        } catch (Exception e) {
1369            // Ok to catch Exception here, because anything going wrong because
1370            // of what a client process passes to us should not be fatal for the
1371            // system process.
1372            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1373            return null;
1374        } finally {
1375            if (parser != null)
1376                parser.close();
1377        }
1378        return p;
1379    }
1380
1381    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1382        PackageInfo pkgInfo = null;
1383        try {
1384            pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1385        } catch (RemoteException re) {
1386            // Shouldn't happen, local call
1387        }
1388        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1389            throw new PackageManager.NameNotFoundException();
1390        }
1391        return pkgInfo.applicationInfo.uid;
1392    }
1393
1394    int enforceCallingUid(String packageName) throws IllegalArgumentException {
1395        int callingUid = Binder.getCallingUid();
1396        int packageUid;
1397        try {
1398            packageUid = getUidForPackage(packageName);
1399        } catch (PackageManager.NameNotFoundException ex) {
1400            throw new IllegalArgumentException("packageName and uid don't match packageName="
1401                    + packageName);
1402        }
1403        if (!UserHandle.isSameApp(callingUid, packageUid)) {
1404            throw new IllegalArgumentException("packageName and uid don't match packageName="
1405                    + packageName);
1406        }
1407        return callingUid;
1408    }
1409
1410    void sendInitialBroadcasts() {
1411        synchronized (mAppWidgetIds) {
1412            ensureStateLoadedLocked();
1413            final int N = mInstalledProviders.size();
1414            for (int i = 0; i < N; i++) {
1415                Provider p = mInstalledProviders.get(i);
1416                if (p.instances.size() > 0) {
1417                    sendEnableIntentLocked(p);
1418                    int[] appWidgetIds = getAppWidgetIds(p);
1419                    sendUpdateIntentLocked(p, appWidgetIds);
1420                    registerForBroadcastsLocked(p, appWidgetIds);
1421                }
1422            }
1423        }
1424    }
1425
1426    // only call from initialization -- it assumes that the data structures are all empty
1427    void loadStateLocked() {
1428        AtomicFile file = savedStateFile();
1429        try {
1430            FileInputStream stream = file.openRead();
1431            readStateFromFileLocked(stream);
1432
1433            if (stream != null) {
1434                try {
1435                    stream.close();
1436                } catch (IOException e) {
1437                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
1438                }
1439            }
1440        } catch (FileNotFoundException e) {
1441            Slog.w(TAG, "Failed to read state: " + e);
1442        }
1443    }
1444
1445    void saveStateLocked() {
1446        AtomicFile file = savedStateFile();
1447        FileOutputStream stream;
1448        try {
1449            stream = file.startWrite();
1450            if (writeStateToFileLocked(stream)) {
1451                file.finishWrite(stream);
1452            } else {
1453                file.failWrite(stream);
1454                Slog.w(TAG, "Failed to save state, restoring backup.");
1455            }
1456        } catch (IOException e) {
1457            Slog.w(TAG, "Failed open state file for write: " + e);
1458        }
1459    }
1460
1461    boolean writeStateToFileLocked(FileOutputStream stream) {
1462        int N;
1463
1464        try {
1465            XmlSerializer out = new FastXmlSerializer();
1466            out.setOutput(stream, "utf-8");
1467            out.startDocument(null, true);
1468            out.startTag(null, "gs");
1469
1470            int providerIndex = 0;
1471            N = mInstalledProviders.size();
1472            for (int i = 0; i < N; i++) {
1473                Provider p = mInstalledProviders.get(i);
1474                if (p.instances.size() > 0) {
1475                    out.startTag(null, "p");
1476                    out.attribute(null, "pkg", p.info.provider.getPackageName());
1477                    out.attribute(null, "cl", p.info.provider.getClassName());
1478                    out.endTag(null, "p");
1479                    p.tag = providerIndex;
1480                    providerIndex++;
1481                }
1482            }
1483
1484            N = mHosts.size();
1485            for (int i = 0; i < N; i++) {
1486                Host host = mHosts.get(i);
1487                out.startTag(null, "h");
1488                out.attribute(null, "pkg", host.packageName);
1489                out.attribute(null, "id", Integer.toHexString(host.hostId));
1490                out.endTag(null, "h");
1491                host.tag = i;
1492            }
1493
1494            N = mAppWidgetIds.size();
1495            for (int i = 0; i < N; i++) {
1496                AppWidgetId id = mAppWidgetIds.get(i);
1497                out.startTag(null, "g");
1498                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1499                out.attribute(null, "h", Integer.toHexString(id.host.tag));
1500                if (id.provider != null) {
1501                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1502                }
1503                if (id.options != null) {
1504                    out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1505                            AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1506                    out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1507                            AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1508                    out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1509                            AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1510                    out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1511                            AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1512                    out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1513                            AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1514                }
1515                out.endTag(null, "g");
1516            }
1517
1518            Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1519            while (it.hasNext()) {
1520                out.startTag(null, "b");
1521                out.attribute(null, "packageName", it.next());
1522                out.endTag(null, "b");
1523            }
1524
1525            out.endTag(null, "gs");
1526
1527            out.endDocument();
1528            return true;
1529        } catch (IOException e) {
1530            Slog.w(TAG, "Failed to write state: " + e);
1531            return false;
1532        }
1533    }
1534
1535    @SuppressWarnings("unused")
1536    void readStateFromFileLocked(FileInputStream stream) {
1537        boolean success = false;
1538        try {
1539            XmlPullParser parser = Xml.newPullParser();
1540            parser.setInput(stream, null);
1541
1542            int type;
1543            int providerIndex = 0;
1544            HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1545            do {
1546                type = parser.next();
1547                if (type == XmlPullParser.START_TAG) {
1548                    String tag = parser.getName();
1549                    if ("p".equals(tag)) {
1550                        // TODO: do we need to check that this package has the same signature
1551                        // as before?
1552                        String pkg = parser.getAttributeValue(null, "pkg");
1553                        String cl = parser.getAttributeValue(null, "cl");
1554
1555                        final IPackageManager packageManager = AppGlobals.getPackageManager();
1556                        try {
1557                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0,
1558                                    UserHandle.getCallingUserId());
1559                        } catch (RemoteException e) {
1560                            String[] pkgs = mContext.getPackageManager()
1561                                    .currentToCanonicalPackageNames(new String[] { pkg });
1562                            pkg = pkgs[0];
1563                        }
1564
1565                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1566                        if (p == null && mSafeMode) {
1567                            // if we're in safe mode, make a temporary one
1568                            p = new Provider();
1569                            p.info = new AppWidgetProviderInfo();
1570                            p.info.provider = new ComponentName(pkg, cl);
1571                            p.zombie = true;
1572                            mInstalledProviders.add(p);
1573                        }
1574                        if (p != null) {
1575                            // if it wasn't uninstalled or something
1576                            loadedProviders.put(providerIndex, p);
1577                        }
1578                        providerIndex++;
1579                    } else if ("h".equals(tag)) {
1580                        Host host = new Host();
1581
1582                        // TODO: do we need to check that this package has the same signature
1583                        // as before?
1584                        host.packageName = parser.getAttributeValue(null, "pkg");
1585                        try {
1586                            host.uid = getUidForPackage(host.packageName);
1587                        } catch (PackageManager.NameNotFoundException ex) {
1588                            host.zombie = true;
1589                        }
1590                        if (!host.zombie || mSafeMode) {
1591                            // In safe mode, we don't discard the hosts we don't recognize
1592                            // so that they're not pruned from our list. Otherwise, we do.
1593                            host.hostId = Integer
1594                                    .parseInt(parser.getAttributeValue(null, "id"), 16);
1595                            mHosts.add(host);
1596                        }
1597                    } else if ("b".equals(tag)) {
1598                        String packageName = parser.getAttributeValue(null, "packageName");
1599                        if (packageName != null) {
1600                            mPackagesWithBindWidgetPermission.add(packageName);
1601                        }
1602                    } else if ("g".equals(tag)) {
1603                        AppWidgetId id = new AppWidgetId();
1604                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1605                        if (id.appWidgetId >= mNextAppWidgetId) {
1606                            mNextAppWidgetId = id.appWidgetId + 1;
1607                        }
1608
1609                        Bundle options = new Bundle();
1610                        String minWidthString = parser.getAttributeValue(null, "min_width");
1611                        if (minWidthString != null) {
1612                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1613                                    Integer.parseInt(minWidthString, 16));
1614                        }
1615                        String minHeightString = parser.getAttributeValue(null, "min_height");
1616                        if (minWidthString != null) {
1617                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1618                                    Integer.parseInt(minHeightString, 16));
1619                        }
1620                        String maxWidthString = parser.getAttributeValue(null, "max_height");
1621                        if (minWidthString != null) {
1622                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1623                                    Integer.parseInt(maxWidthString, 16));
1624                        }
1625                        String maxHeightString = parser.getAttributeValue(null, "max_height");
1626                        if (minWidthString != null) {
1627                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1628                                    Integer.parseInt(maxHeightString, 16));
1629                        }
1630                        String categoryString = parser.getAttributeValue(null, "host_category");
1631                        if (minWidthString != null) {
1632                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1633                                    Integer.parseInt(categoryString, 16));
1634                        }
1635                        id.options = options;
1636
1637                        String providerString = parser.getAttributeValue(null, "p");
1638                        if (providerString != null) {
1639                            // there's no provider if it hasn't been bound yet.
1640                            // maybe we don't have to save this, but it brings the system
1641                            // to the state it was in.
1642                            int pIndex = Integer.parseInt(providerString, 16);
1643                            id.provider = loadedProviders.get(pIndex);
1644                            if (false) {
1645                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1646                                        + pIndex + " which is " + id.provider);
1647                            }
1648                            if (id.provider == null) {
1649                                // This provider is gone. We just let the host figure out
1650                                // that this happened when it fails to load it.
1651                                continue;
1652                            }
1653                        }
1654
1655                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1656                        id.host = mHosts.get(hIndex);
1657                        if (id.host == null) {
1658                            // This host is gone.
1659                            continue;
1660                        }
1661
1662                        if (id.provider != null) {
1663                            id.provider.instances.add(id);
1664                        }
1665                        id.host.instances.add(id);
1666                        mAppWidgetIds.add(id);
1667                    }
1668                }
1669            } while (type != XmlPullParser.END_DOCUMENT);
1670            success = true;
1671        } catch (NullPointerException e) {
1672            Slog.w(TAG, "failed parsing " + e);
1673        } catch (NumberFormatException e) {
1674            Slog.w(TAG, "failed parsing " + e);
1675        } catch (XmlPullParserException e) {
1676            Slog.w(TAG, "failed parsing " + e);
1677        } catch (IOException e) {
1678            Slog.w(TAG, "failed parsing " + e);
1679        } catch (IndexOutOfBoundsException e) {
1680            Slog.w(TAG, "failed parsing " + e);
1681        }
1682
1683        if (success) {
1684            // delete any hosts that didn't manage to get connected (should happen)
1685            // if it matters, they'll be reconnected.
1686            for (int i = mHosts.size() - 1; i >= 0; i--) {
1687                pruneHostLocked(mHosts.get(i));
1688            }
1689        } else {
1690            // failed reading, clean up
1691            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1692
1693            mAppWidgetIds.clear();
1694            mHosts.clear();
1695            final int N = mInstalledProviders.size();
1696            for (int i = 0; i < N; i++) {
1697                mInstalledProviders.get(i).instances.clear();
1698            }
1699        }
1700    }
1701
1702    static File getSettingsFile(int userId) {
1703        return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
1704    }
1705
1706    AtomicFile savedStateFile() {
1707        File dir = Environment.getUserSystemDirectory(mUserId);
1708        File settingsFile = getSettingsFile(mUserId);
1709        if (!settingsFile.exists() && mUserId == 0) {
1710            if (!dir.exists()) {
1711                dir.mkdirs();
1712            }
1713            // Migrate old data
1714            File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1715            // Method doesn't throw an exception on failure. Ignore any errors
1716            // in moving the file (like non-existence)
1717            oldFile.renameTo(settingsFile);
1718        }
1719        return new AtomicFile(settingsFile);
1720    }
1721
1722    void onUserRemoved() {
1723        // prune the ones we don't want to keep
1724        int N = mInstalledProviders.size();
1725        for (int i = N - 1; i >= 0; i--) {
1726            Provider p = mInstalledProviders.get(i);
1727            cancelBroadcasts(p);
1728        }
1729        getSettingsFile(mUserId).delete();
1730    }
1731
1732    boolean addProvidersForPackageLocked(String pkgName) {
1733        boolean providersAdded = false;
1734        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1735        intent.setPackage(pkgName);
1736        List<ResolveInfo> broadcastReceivers;
1737        try {
1738            broadcastReceivers = mPm.queryIntentReceivers(intent,
1739                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1740                    PackageManager.GET_META_DATA, mUserId);
1741        } catch (RemoteException re) {
1742            // Shouldn't happen, local call
1743            return false;
1744        }
1745        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1746        for (int i = 0; i < N; i++) {
1747            ResolveInfo ri = broadcastReceivers.get(i);
1748            ActivityInfo ai = ri.activityInfo;
1749            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1750                continue;
1751            }
1752            if (pkgName.equals(ai.packageName)) {
1753                addProviderLocked(ri);
1754                providersAdded = true;
1755            }
1756        }
1757
1758        return providersAdded;
1759    }
1760
1761    /**
1762     * Updates all providers with the specified package names, and records any providers that were
1763     * pruned.
1764     *
1765     * @return whether any providers were updated
1766     */
1767    boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
1768        boolean providersUpdated = false;
1769        HashSet<String> keep = new HashSet<String>();
1770        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1771        intent.setPackage(pkgName);
1772        List<ResolveInfo> broadcastReceivers;
1773        try {
1774            broadcastReceivers = mPm.queryIntentReceivers(intent,
1775                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1776                PackageManager.GET_META_DATA, mUserId);
1777        } catch (RemoteException re) {
1778            // Shouldn't happen, local call
1779            return false;
1780        }
1781
1782        // add the missing ones and collect which ones to keep
1783        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1784        for (int i = 0; i < N; i++) {
1785            ResolveInfo ri = broadcastReceivers.get(i);
1786            ActivityInfo ai = ri.activityInfo;
1787            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1788                continue;
1789            }
1790            if (pkgName.equals(ai.packageName)) {
1791                ComponentName component = new ComponentName(ai.packageName, ai.name);
1792                Provider p = lookupProviderLocked(component);
1793                if (p == null) {
1794                    if (addProviderLocked(ri)) {
1795                        keep.add(ai.name);
1796                        providersUpdated = true;
1797                    }
1798                } else {
1799                    Provider parsed = parseProviderInfoXml(component, ri);
1800                    if (parsed != null) {
1801                        keep.add(ai.name);
1802                        // Use the new AppWidgetProviderInfo.
1803                        p.info = parsed.info;
1804                        // If it's enabled
1805                        final int M = p.instances.size();
1806                        if (M > 0) {
1807                            int[] appWidgetIds = getAppWidgetIds(p);
1808                            // Reschedule for the new updatePeriodMillis (don't worry about handling
1809                            // it specially if updatePeriodMillis didn't change because we just sent
1810                            // an update, and the next one will be updatePeriodMillis from now).
1811                            cancelBroadcasts(p);
1812                            registerForBroadcastsLocked(p, appWidgetIds);
1813                            // If it's currently showing, call back with the new
1814                            // AppWidgetProviderInfo.
1815                            for (int j = 0; j < M; j++) {
1816                                AppWidgetId id = p.instances.get(j);
1817                                id.views = null;
1818                                if (id.host != null && id.host.callbacks != null) {
1819                                    try {
1820                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1821                                    } catch (RemoteException ex) {
1822                                        // It failed; remove the callback. No need to prune because
1823                                        // we know that this host is still referenced by this
1824                                        // instance.
1825                                        id.host.callbacks = null;
1826                                    }
1827                                }
1828                            }
1829                            // Now that we've told the host, push out an update.
1830                            sendUpdateIntentLocked(p, appWidgetIds);
1831                            providersUpdated = true;
1832                        }
1833                    }
1834                }
1835            }
1836        }
1837
1838        // prune the ones we don't want to keep
1839        N = mInstalledProviders.size();
1840        for (int i = N - 1; i >= 0; i--) {
1841            Provider p = mInstalledProviders.get(i);
1842            if (pkgName.equals(p.info.provider.getPackageName())
1843                    && !keep.contains(p.info.provider.getClassName())) {
1844                if (removedProviders != null) {
1845                    removedProviders.add(p.info.provider);
1846                }
1847                removeProviderLocked(i, p);
1848                providersUpdated = true;
1849            }
1850        }
1851
1852        return providersUpdated;
1853    }
1854
1855    boolean removeProvidersForPackageLocked(String pkgName) {
1856        boolean providersRemoved = false;
1857        int N = mInstalledProviders.size();
1858        for (int i = N - 1; i >= 0; i--) {
1859            Provider p = mInstalledProviders.get(i);
1860            if (pkgName.equals(p.info.provider.getPackageName())) {
1861                removeProviderLocked(i, p);
1862                providersRemoved = true;
1863            }
1864        }
1865
1866        // Delete the hosts for this package too
1867        //
1868        // By now, we have removed any AppWidgets that were in any hosts here,
1869        // so we don't need to worry about sending DISABLE broadcasts to them.
1870        N = mHosts.size();
1871        for (int i = N - 1; i >= 0; i--) {
1872            Host host = mHosts.get(i);
1873            if (pkgName.equals(host.packageName)) {
1874                deleteHostLocked(host);
1875            }
1876        }
1877
1878        return providersRemoved;
1879    }
1880
1881    void notifyHostsForProvidersChangedLocked() {
1882        final int N = mHosts.size();
1883        for (int i = N - 1; i >= 0; i--) {
1884            Host host = mHosts.get(i);
1885            try {
1886                if (host.callbacks != null) {
1887                    host.callbacks.providersChanged();
1888                }
1889            } catch (RemoteException ex) {
1890                // It failed; remove the callback. No need to prune because
1891                // we know that this host is still referenced by this
1892                // instance.
1893                host.callbacks = null;
1894            }
1895        }
1896    }
1897}
1898