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