AppWidgetServiceImpl.java revision 98edc951712823dbf5db2b7e9c203a0e98fc616b
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 = AppGlobals.getPackageManager().getServiceInfo(componentName,
661                        PackageManager.GET_PERMISSIONS, mUserId);
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 (RemoteException 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            if (!isPartialUpdate) {
984                // For a full update we replace the RemoteViews completely.
985                id.views = views;
986            } else {
987                // For a partial update, we merge the new RemoteViews with the old.
988                id.views.mergeRemoteViews(views);
989            }
990
991            // is anyone listening?
992            if (id.host.callbacks != null) {
993                try {
994                    // the lock is held, but this is a oneway call
995                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
996                } catch (RemoteException e) {
997                    // It failed; remove the callback. No need to prune because
998                    // we know that this host is still referenced by this instance.
999                    id.host.callbacks = null;
1000                }
1001            }
1002        }
1003    }
1004
1005    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1006        // allow for stale appWidgetIds and other badness
1007        // lookup also checks that the calling process can access the appWidgetId
1008        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1009        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1010            // is anyone listening?
1011            if (id.host.callbacks != null) {
1012                try {
1013                    // the lock is held, but this is a oneway call
1014                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1015                } catch (RemoteException e) {
1016                    // It failed; remove the callback. No need to prune because
1017                    // we know that this host is still referenced by this instance.
1018                    id.host.callbacks = null;
1019                }
1020            }
1021
1022            // If the host is unavailable, then we call the associated
1023            // RemoteViewsFactory.onDataSetChanged() directly
1024            if (id.host.callbacks == null) {
1025                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1026                for (FilterComparison key : keys) {
1027                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1028                        Intent intent = key.getIntent();
1029
1030                        final ServiceConnection conn = new ServiceConnection() {
1031                            @Override
1032                            public void onServiceConnected(ComponentName name, IBinder service) {
1033                                IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1034                                        .asInterface(service);
1035                                try {
1036                                    cb.onDataSetChangedAsync();
1037                                } catch (RemoteException e) {
1038                                    e.printStackTrace();
1039                                } catch (RuntimeException e) {
1040                                    e.printStackTrace();
1041                                }
1042                                mContext.unbindService(this);
1043                            }
1044
1045                            @Override
1046                            public void onServiceDisconnected(android.content.ComponentName name) {
1047                                // Do nothing
1048                            }
1049                        };
1050
1051                        int userId = UserHandle.getUserId(id.provider.uid);
1052                        // Bind to the service and call onDataSetChanged()
1053                        final long token = Binder.clearCallingIdentity();
1054                        try {
1055                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
1056                        } finally {
1057                            Binder.restoreCallingIdentity(token);
1058                        }
1059                    }
1060                }
1061            }
1062        }
1063    }
1064
1065    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1066            List<RemoteViews> updatedViews) {
1067        int callingUid = enforceCallingUid(packageName);
1068        synchronized (mAppWidgetIds) {
1069            ensureStateLoadedLocked();
1070            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1071            host.callbacks = callbacks;
1072
1073            updatedViews.clear();
1074
1075            ArrayList<AppWidgetId> instances = host.instances;
1076            int N = instances.size();
1077            int[] updatedIds = new int[N];
1078            for (int i = 0; i < N; i++) {
1079                AppWidgetId id = instances.get(i);
1080                updatedIds[i] = id.appWidgetId;
1081                updatedViews.add(id.views);
1082            }
1083            return updatedIds;
1084        }
1085    }
1086
1087    public void stopListening(int hostId) {
1088        synchronized (mAppWidgetIds) {
1089            ensureStateLoadedLocked();
1090            Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1091            if (host != null) {
1092                host.callbacks = null;
1093                pruneHostLocked(host);
1094            }
1095        }
1096    }
1097
1098    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1099        if (id.host.uid == callingUid) {
1100            // Apps hosting the AppWidget have access to it.
1101            return true;
1102        }
1103        if (id.provider != null && id.provider.uid == callingUid) {
1104            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1105            return true;
1106        }
1107        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1108            // Apps that can bind have access to all appWidgetIds.
1109            return true;
1110        }
1111        // Nobody else can access it.
1112        return false;
1113    }
1114
1115    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1116        int callingUid = Binder.getCallingUid();
1117        final int N = mAppWidgetIds.size();
1118        for (int i = 0; i < N; i++) {
1119            AppWidgetId id = mAppWidgetIds.get(i);
1120            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1121                return id;
1122            }
1123        }
1124        return null;
1125    }
1126
1127    Provider lookupProviderLocked(ComponentName provider) {
1128        final int N = mInstalledProviders.size();
1129        for (int i = 0; i < N; i++) {
1130            Provider p = mInstalledProviders.get(i);
1131            if (p.info.provider.equals(provider)) {
1132                return p;
1133            }
1134        }
1135        return null;
1136    }
1137
1138    Host lookupHostLocked(int uid, int hostId) {
1139        final int N = mHosts.size();
1140        for (int i = 0; i < N; i++) {
1141            Host h = mHosts.get(i);
1142            if (h.uid == uid && h.hostId == hostId) {
1143                return h;
1144            }
1145        }
1146        return null;
1147    }
1148
1149    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1150        final int N = mHosts.size();
1151        for (int i = 0; i < N; i++) {
1152            Host h = mHosts.get(i);
1153            if (h.hostId == hostId && h.packageName.equals(packageName)) {
1154                return h;
1155            }
1156        }
1157        Host host = new Host();
1158        host.packageName = packageName;
1159        host.uid = uid;
1160        host.hostId = hostId;
1161        mHosts.add(host);
1162        return host;
1163    }
1164
1165    void pruneHostLocked(Host host) {
1166        if (host.instances.size() == 0 && host.callbacks == null) {
1167            mHosts.remove(host);
1168        }
1169    }
1170
1171    void loadAppWidgetList() {
1172        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1173        try {
1174            List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1175                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1176                    PackageManager.GET_META_DATA, mUserId);
1177
1178            final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1179            for (int i = 0; i < N; i++) {
1180                ResolveInfo ri = broadcastReceivers.get(i);
1181                addProviderLocked(ri);
1182            }
1183        } catch (RemoteException re) {
1184            // Shouldn't happen, local call
1185        }
1186    }
1187
1188    boolean addProviderLocked(ResolveInfo ri) {
1189        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1190            return false;
1191        }
1192        if (!ri.activityInfo.isEnabled()) {
1193            return false;
1194        }
1195        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1196                ri.activityInfo.name), ri);
1197        if (p != null) {
1198            mInstalledProviders.add(p);
1199            return true;
1200        } else {
1201            return false;
1202        }
1203    }
1204
1205    void removeProviderLocked(int index, Provider p) {
1206        int N = p.instances.size();
1207        for (int i = 0; i < N; i++) {
1208            AppWidgetId id = p.instances.get(i);
1209            // Call back with empty RemoteViews
1210            updateAppWidgetInstanceLocked(id, null);
1211            // Stop telling the host about updates for this from now on
1212            cancelBroadcasts(p);
1213            // clear out references to this appWidgetId
1214            id.host.instances.remove(id);
1215            mAppWidgetIds.remove(id);
1216            id.provider = null;
1217            pruneHostLocked(id.host);
1218            id.host = null;
1219        }
1220        p.instances.clear();
1221        mInstalledProviders.remove(index);
1222        mDeletedProviders.add(p);
1223        // no need to send the DISABLE broadcast, since the receiver is gone anyway
1224        cancelBroadcasts(p);
1225    }
1226
1227    void sendEnableIntentLocked(Provider p) {
1228        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1229        intent.setComponent(p.info.provider);
1230        mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
1231    }
1232
1233    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1234        if (appWidgetIds != null && appWidgetIds.length > 0) {
1235            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1236            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1237            intent.setComponent(p.info.provider);
1238            mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
1239        }
1240    }
1241
1242    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1243        if (p.info.updatePeriodMillis > 0) {
1244            // if this is the first instance, set the alarm. otherwise,
1245            // rely on the fact that we've already set it and that
1246            // PendingIntent.getBroadcast will update the extras.
1247            boolean alreadyRegistered = p.broadcast != null;
1248            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1249            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1250            intent.setComponent(p.info.provider);
1251            long token = Binder.clearCallingIdentity();
1252            try {
1253                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1254                        PendingIntent.FLAG_UPDATE_CURRENT);
1255            } finally {
1256                Binder.restoreCallingIdentity(token);
1257            }
1258            if (!alreadyRegistered) {
1259                long period = p.info.updatePeriodMillis;
1260                if (period < MIN_UPDATE_PERIOD) {
1261                    period = MIN_UPDATE_PERIOD;
1262                }
1263                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1264                        .elapsedRealtime()
1265                        + period, period, p.broadcast);
1266            }
1267        }
1268    }
1269
1270    static int[] getAppWidgetIds(Provider p) {
1271        int instancesSize = p.instances.size();
1272        int appWidgetIds[] = new int[instancesSize];
1273        for (int i = 0; i < instancesSize; i++) {
1274            appWidgetIds[i] = p.instances.get(i).appWidgetId;
1275        }
1276        return appWidgetIds;
1277    }
1278
1279    public int[] getAppWidgetIds(ComponentName provider) {
1280        synchronized (mAppWidgetIds) {
1281            ensureStateLoadedLocked();
1282            Provider p = lookupProviderLocked(provider);
1283            if (p != null && Binder.getCallingUid() == p.uid) {
1284                return getAppWidgetIds(p);
1285            } else {
1286                return new int[0];
1287            }
1288        }
1289    }
1290
1291    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1292        Provider p = null;
1293
1294        ActivityInfo activityInfo = ri.activityInfo;
1295        XmlResourceParser parser = null;
1296        try {
1297            parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
1298                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1299            if (parser == null) {
1300                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1301                        + " meta-data for " + "AppWidget provider '" + component + '\'');
1302                return null;
1303            }
1304
1305            AttributeSet attrs = Xml.asAttributeSet(parser);
1306
1307            int type;
1308            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1309                    && type != XmlPullParser.START_TAG) {
1310                // drain whitespace, comments, etc.
1311            }
1312
1313            String nodeName = parser.getName();
1314            if (!"appwidget-provider".equals(nodeName)) {
1315                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1316                        + " AppWidget provider '" + component + '\'');
1317                return null;
1318            }
1319
1320            p = new Provider();
1321            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1322            info.provider = component;
1323            p.uid = activityInfo.applicationInfo.uid;
1324
1325            Resources res = mContext.getPackageManager()
1326                    .getResourcesForApplication(activityInfo.applicationInfo);
1327
1328            TypedArray sa = res.obtainAttributes(attrs,
1329                    com.android.internal.R.styleable.AppWidgetProviderInfo);
1330
1331            // These dimensions has to be resolved in the application's context.
1332            // We simply send back the raw complex data, which will be
1333            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1334            TypedValue value = sa
1335                    .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1336            info.minWidth = value != null ? value.data : 0;
1337            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1338            info.minHeight = value != null ? value.data : 0;
1339            value = sa.peekValue(
1340                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1341            info.minResizeWidth = value != null ? value.data : info.minWidth;
1342            value = sa.peekValue(
1343                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1344            info.minResizeHeight = value != null ? value.data : info.minHeight;
1345            info.updatePeriodMillis = sa.getInt(
1346                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1347            info.initialLayout = sa.getResourceId(
1348                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1349            info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1350                    AppWidgetProviderInfo_initialKeyguardLayout, 0);
1351            String className = sa
1352                    .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1353            if (className != null) {
1354                info.configure = new ComponentName(component.getPackageName(), className);
1355            }
1356            info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
1357            info.icon = ri.getIconResource();
1358            info.previewImage = sa.getResourceId(
1359                    com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1360            info.autoAdvanceViewId = sa.getResourceId(
1361                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1362            info.resizeMode = sa.getInt(
1363                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1364                    AppWidgetProviderInfo.RESIZE_NONE);
1365            info.widgetCategory = sa.getInt(
1366                    com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
1367                    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
1368            info.widgetFeatures = sa.getInt(
1369                    com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures,
1370                    AppWidgetProviderInfo.WIDGET_FEATURES_NONE);
1371
1372            sa.recycle();
1373        } catch (Exception e) {
1374            // Ok to catch Exception here, because anything going wrong because
1375            // of what a client process passes to us should not be fatal for the
1376            // system process.
1377            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1378            return null;
1379        } finally {
1380            if (parser != null)
1381                parser.close();
1382        }
1383        return p;
1384    }
1385
1386    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1387        PackageInfo pkgInfo = null;
1388        try {
1389            pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1390        } catch (RemoteException re) {
1391            // Shouldn't happen, local call
1392        }
1393        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1394            throw new PackageManager.NameNotFoundException();
1395        }
1396        return pkgInfo.applicationInfo.uid;
1397    }
1398
1399    int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1400        int callingUid = Binder.getCallingUid();
1401        if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
1402            return callingUid;
1403        }
1404        return enforceCallingUid(packageName);
1405    }
1406
1407    int enforceCallingUid(String packageName) throws IllegalArgumentException {
1408        int callingUid = Binder.getCallingUid();
1409        int packageUid;
1410        try {
1411            packageUid = getUidForPackage(packageName);
1412        } catch (PackageManager.NameNotFoundException ex) {
1413            throw new IllegalArgumentException("packageName and uid don't match packageName="
1414                    + packageName);
1415        }
1416        if (!UserHandle.isSameApp(callingUid, packageUid)) {
1417            throw new IllegalArgumentException("packageName and uid don't match packageName="
1418                    + packageName);
1419        }
1420        return callingUid;
1421    }
1422
1423    void sendInitialBroadcasts() {
1424        synchronized (mAppWidgetIds) {
1425            ensureStateLoadedLocked();
1426            final int N = mInstalledProviders.size();
1427            for (int i = 0; i < N; i++) {
1428                Provider p = mInstalledProviders.get(i);
1429                if (p.instances.size() > 0) {
1430                    sendEnableIntentLocked(p);
1431                    int[] appWidgetIds = getAppWidgetIds(p);
1432                    sendUpdateIntentLocked(p, appWidgetIds);
1433                    registerForBroadcastsLocked(p, appWidgetIds);
1434                }
1435            }
1436        }
1437    }
1438
1439    // only call from initialization -- it assumes that the data structures are all empty
1440    void loadStateLocked() {
1441        AtomicFile file = savedStateFile();
1442        try {
1443            FileInputStream stream = file.openRead();
1444            readStateFromFileLocked(stream);
1445
1446            if (stream != null) {
1447                try {
1448                    stream.close();
1449                } catch (IOException e) {
1450                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
1451                }
1452            }
1453        } catch (FileNotFoundException e) {
1454            Slog.w(TAG, "Failed to read state: " + e);
1455        }
1456    }
1457
1458    void saveStateLocked() {
1459        AtomicFile file = savedStateFile();
1460        FileOutputStream stream;
1461        try {
1462            stream = file.startWrite();
1463            if (writeStateToFileLocked(stream)) {
1464                file.finishWrite(stream);
1465            } else {
1466                file.failWrite(stream);
1467                Slog.w(TAG, "Failed to save state, restoring backup.");
1468            }
1469        } catch (IOException e) {
1470            Slog.w(TAG, "Failed open state file for write: " + e);
1471        }
1472    }
1473
1474    boolean writeStateToFileLocked(FileOutputStream stream) {
1475        int N;
1476
1477        try {
1478            XmlSerializer out = new FastXmlSerializer();
1479            out.setOutput(stream, "utf-8");
1480            out.startDocument(null, true);
1481            out.startTag(null, "gs");
1482
1483            int providerIndex = 0;
1484            N = mInstalledProviders.size();
1485            for (int i = 0; i < N; i++) {
1486                Provider p = mInstalledProviders.get(i);
1487                if (p.instances.size() > 0) {
1488                    out.startTag(null, "p");
1489                    out.attribute(null, "pkg", p.info.provider.getPackageName());
1490                    out.attribute(null, "cl", p.info.provider.getClassName());
1491                    out.endTag(null, "p");
1492                    p.tag = providerIndex;
1493                    providerIndex++;
1494                }
1495            }
1496
1497            N = mHosts.size();
1498            for (int i = 0; i < N; i++) {
1499                Host host = mHosts.get(i);
1500                out.startTag(null, "h");
1501                out.attribute(null, "pkg", host.packageName);
1502                out.attribute(null, "id", Integer.toHexString(host.hostId));
1503                out.endTag(null, "h");
1504                host.tag = i;
1505            }
1506
1507            N = mAppWidgetIds.size();
1508            for (int i = 0; i < N; i++) {
1509                AppWidgetId id = mAppWidgetIds.get(i);
1510                out.startTag(null, "g");
1511                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1512                out.attribute(null, "h", Integer.toHexString(id.host.tag));
1513                if (id.provider != null) {
1514                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1515                }
1516                if (id.options != null) {
1517                    out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1518                            AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1519                    out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1520                            AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1521                    out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1522                            AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1523                    out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1524                            AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1525                    out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1526                            AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1527                }
1528                out.endTag(null, "g");
1529            }
1530
1531            Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1532            while (it.hasNext()) {
1533                out.startTag(null, "b");
1534                out.attribute(null, "packageName", it.next());
1535                out.endTag(null, "b");
1536            }
1537
1538            out.endTag(null, "gs");
1539
1540            out.endDocument();
1541            return true;
1542        } catch (IOException e) {
1543            Slog.w(TAG, "Failed to write state: " + e);
1544            return false;
1545        }
1546    }
1547
1548    @SuppressWarnings("unused")
1549    void readStateFromFileLocked(FileInputStream stream) {
1550        boolean success = false;
1551        try {
1552            XmlPullParser parser = Xml.newPullParser();
1553            parser.setInput(stream, null);
1554
1555            int type;
1556            int providerIndex = 0;
1557            HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1558            do {
1559                type = parser.next();
1560                if (type == XmlPullParser.START_TAG) {
1561                    String tag = parser.getName();
1562                    if ("p".equals(tag)) {
1563                        // TODO: do we need to check that this package has the same signature
1564                        // as before?
1565                        String pkg = parser.getAttributeValue(null, "pkg");
1566                        String cl = parser.getAttributeValue(null, "cl");
1567
1568                        final IPackageManager packageManager = AppGlobals.getPackageManager();
1569                        try {
1570                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0,
1571                                    UserHandle.getCallingUserId());
1572                        } catch (RemoteException e) {
1573                            String[] pkgs = mContext.getPackageManager()
1574                                    .currentToCanonicalPackageNames(new String[] { pkg });
1575                            pkg = pkgs[0];
1576                        }
1577
1578                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1579                        if (p == null && mSafeMode) {
1580                            // if we're in safe mode, make a temporary one
1581                            p = new Provider();
1582                            p.info = new AppWidgetProviderInfo();
1583                            p.info.provider = new ComponentName(pkg, cl);
1584                            p.zombie = true;
1585                            mInstalledProviders.add(p);
1586                        }
1587                        if (p != null) {
1588                            // if it wasn't uninstalled or something
1589                            loadedProviders.put(providerIndex, p);
1590                        }
1591                        providerIndex++;
1592                    } else if ("h".equals(tag)) {
1593                        Host host = new Host();
1594
1595                        // TODO: do we need to check that this package has the same signature
1596                        // as before?
1597                        host.packageName = parser.getAttributeValue(null, "pkg");
1598                        try {
1599                            host.uid = getUidForPackage(host.packageName);
1600                        } catch (PackageManager.NameNotFoundException ex) {
1601                            host.zombie = true;
1602                        }
1603                        if (!host.zombie || mSafeMode) {
1604                            // In safe mode, we don't discard the hosts we don't recognize
1605                            // so that they're not pruned from our list. Otherwise, we do.
1606                            host.hostId = Integer
1607                                    .parseInt(parser.getAttributeValue(null, "id"), 16);
1608                            mHosts.add(host);
1609                        }
1610                    } else if ("b".equals(tag)) {
1611                        String packageName = parser.getAttributeValue(null, "packageName");
1612                        if (packageName != null) {
1613                            mPackagesWithBindWidgetPermission.add(packageName);
1614                        }
1615                    } else if ("g".equals(tag)) {
1616                        AppWidgetId id = new AppWidgetId();
1617                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1618                        if (id.appWidgetId >= mNextAppWidgetId) {
1619                            mNextAppWidgetId = id.appWidgetId + 1;
1620                        }
1621
1622                        Bundle options = new Bundle();
1623                        String minWidthString = parser.getAttributeValue(null, "min_width");
1624                        if (minWidthString != null) {
1625                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1626                                    Integer.parseInt(minWidthString, 16));
1627                        }
1628                        String minHeightString = parser.getAttributeValue(null, "min_height");
1629                        if (minHeightString != null) {
1630                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1631                                    Integer.parseInt(minHeightString, 16));
1632                        }
1633                        String maxWidthString = parser.getAttributeValue(null, "max_width");
1634                        if (maxWidthString != null) {
1635                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1636                                    Integer.parseInt(maxWidthString, 16));
1637                        }
1638                        String maxHeightString = parser.getAttributeValue(null, "max_height");
1639                        if (maxHeightString != null) {
1640                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1641                                    Integer.parseInt(maxHeightString, 16));
1642                        }
1643                        String categoryString = parser.getAttributeValue(null, "host_category");
1644                        if (categoryString != null) {
1645                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1646                                    Integer.parseInt(categoryString, 16));
1647                        }
1648                        id.options = options;
1649
1650                        String providerString = parser.getAttributeValue(null, "p");
1651                        if (providerString != null) {
1652                            // there's no provider if it hasn't been bound yet.
1653                            // maybe we don't have to save this, but it brings the system
1654                            // to the state it was in.
1655                            int pIndex = Integer.parseInt(providerString, 16);
1656                            id.provider = loadedProviders.get(pIndex);
1657                            if (false) {
1658                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1659                                        + pIndex + " which is " + id.provider);
1660                            }
1661                            if (id.provider == null) {
1662                                // This provider is gone. We just let the host figure out
1663                                // that this happened when it fails to load it.
1664                                continue;
1665                            }
1666                        }
1667
1668                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1669                        id.host = mHosts.get(hIndex);
1670                        if (id.host == null) {
1671                            // This host is gone.
1672                            continue;
1673                        }
1674
1675                        if (id.provider != null) {
1676                            id.provider.instances.add(id);
1677                        }
1678                        id.host.instances.add(id);
1679                        mAppWidgetIds.add(id);
1680                    }
1681                }
1682            } while (type != XmlPullParser.END_DOCUMENT);
1683            success = true;
1684        } catch (NullPointerException e) {
1685            Slog.w(TAG, "failed parsing " + e);
1686        } catch (NumberFormatException e) {
1687            Slog.w(TAG, "failed parsing " + e);
1688        } catch (XmlPullParserException e) {
1689            Slog.w(TAG, "failed parsing " + e);
1690        } catch (IOException e) {
1691            Slog.w(TAG, "failed parsing " + e);
1692        } catch (IndexOutOfBoundsException e) {
1693            Slog.w(TAG, "failed parsing " + e);
1694        }
1695
1696        if (success) {
1697            // delete any hosts that didn't manage to get connected (should happen)
1698            // if it matters, they'll be reconnected.
1699            for (int i = mHosts.size() - 1; i >= 0; i--) {
1700                pruneHostLocked(mHosts.get(i));
1701            }
1702        } else {
1703            // failed reading, clean up
1704            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1705
1706            mAppWidgetIds.clear();
1707            mHosts.clear();
1708            final int N = mInstalledProviders.size();
1709            for (int i = 0; i < N; i++) {
1710                mInstalledProviders.get(i).instances.clear();
1711            }
1712        }
1713    }
1714
1715    static File getSettingsFile(int userId) {
1716        return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
1717    }
1718
1719    AtomicFile savedStateFile() {
1720        File dir = Environment.getUserSystemDirectory(mUserId);
1721        File settingsFile = getSettingsFile(mUserId);
1722        if (!settingsFile.exists() && mUserId == 0) {
1723            if (!dir.exists()) {
1724                dir.mkdirs();
1725            }
1726            // Migrate old data
1727            File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1728            // Method doesn't throw an exception on failure. Ignore any errors
1729            // in moving the file (like non-existence)
1730            oldFile.renameTo(settingsFile);
1731        }
1732        return new AtomicFile(settingsFile);
1733    }
1734
1735    void onUserRemoved() {
1736        // prune the ones we don't want to keep
1737        int N = mInstalledProviders.size();
1738        for (int i = N - 1; i >= 0; i--) {
1739            Provider p = mInstalledProviders.get(i);
1740            cancelBroadcasts(p);
1741        }
1742        getSettingsFile(mUserId).delete();
1743    }
1744
1745    boolean addProvidersForPackageLocked(String pkgName) {
1746        boolean providersAdded = false;
1747        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1748        intent.setPackage(pkgName);
1749        List<ResolveInfo> broadcastReceivers;
1750        try {
1751            broadcastReceivers = mPm.queryIntentReceivers(intent,
1752                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1753                    PackageManager.GET_META_DATA, mUserId);
1754        } catch (RemoteException re) {
1755            // Shouldn't happen, local call
1756            return false;
1757        }
1758        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1759        for (int i = 0; i < N; i++) {
1760            ResolveInfo ri = broadcastReceivers.get(i);
1761            ActivityInfo ai = ri.activityInfo;
1762            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1763                continue;
1764            }
1765            if (pkgName.equals(ai.packageName)) {
1766                addProviderLocked(ri);
1767                providersAdded = true;
1768            }
1769        }
1770
1771        return providersAdded;
1772    }
1773
1774    /**
1775     * Updates all providers with the specified package names, and records any providers that were
1776     * pruned.
1777     *
1778     * @return whether any providers were updated
1779     */
1780    boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
1781        boolean providersUpdated = false;
1782        HashSet<String> keep = new HashSet<String>();
1783        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1784        intent.setPackage(pkgName);
1785        List<ResolveInfo> broadcastReceivers;
1786        try {
1787            broadcastReceivers = mPm.queryIntentReceivers(intent,
1788                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1789                PackageManager.GET_META_DATA, mUserId);
1790        } catch (RemoteException re) {
1791            // Shouldn't happen, local call
1792            return false;
1793        }
1794
1795        // add the missing ones and collect which ones to keep
1796        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1797        for (int i = 0; i < N; i++) {
1798            ResolveInfo ri = broadcastReceivers.get(i);
1799            ActivityInfo ai = ri.activityInfo;
1800            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1801                continue;
1802            }
1803            if (pkgName.equals(ai.packageName)) {
1804                ComponentName component = new ComponentName(ai.packageName, ai.name);
1805                Provider p = lookupProviderLocked(component);
1806                if (p == null) {
1807                    if (addProviderLocked(ri)) {
1808                        keep.add(ai.name);
1809                        providersUpdated = true;
1810                    }
1811                } else {
1812                    Provider parsed = parseProviderInfoXml(component, ri);
1813                    if (parsed != null) {
1814                        keep.add(ai.name);
1815                        // Use the new AppWidgetProviderInfo.
1816                        p.info = parsed.info;
1817                        // If it's enabled
1818                        final int M = p.instances.size();
1819                        if (M > 0) {
1820                            int[] appWidgetIds = getAppWidgetIds(p);
1821                            // Reschedule for the new updatePeriodMillis (don't worry about handling
1822                            // it specially if updatePeriodMillis didn't change because we just sent
1823                            // an update, and the next one will be updatePeriodMillis from now).
1824                            cancelBroadcasts(p);
1825                            registerForBroadcastsLocked(p, appWidgetIds);
1826                            // If it's currently showing, call back with the new
1827                            // AppWidgetProviderInfo.
1828                            for (int j = 0; j < M; j++) {
1829                                AppWidgetId id = p.instances.get(j);
1830                                id.views = null;
1831                                if (id.host != null && id.host.callbacks != null) {
1832                                    try {
1833                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1834                                    } catch (RemoteException ex) {
1835                                        // It failed; remove the callback. No need to prune because
1836                                        // we know that this host is still referenced by this
1837                                        // instance.
1838                                        id.host.callbacks = null;
1839                                    }
1840                                }
1841                            }
1842                            // Now that we've told the host, push out an update.
1843                            sendUpdateIntentLocked(p, appWidgetIds);
1844                            providersUpdated = true;
1845                        }
1846                    }
1847                }
1848            }
1849        }
1850
1851        // prune the ones we don't want to keep
1852        N = mInstalledProviders.size();
1853        for (int i = N - 1; i >= 0; i--) {
1854            Provider p = mInstalledProviders.get(i);
1855            if (pkgName.equals(p.info.provider.getPackageName())
1856                    && !keep.contains(p.info.provider.getClassName())) {
1857                if (removedProviders != null) {
1858                    removedProviders.add(p.info.provider);
1859                }
1860                removeProviderLocked(i, p);
1861                providersUpdated = true;
1862            }
1863        }
1864
1865        return providersUpdated;
1866    }
1867
1868    boolean removeProvidersForPackageLocked(String pkgName) {
1869        boolean providersRemoved = false;
1870        int N = mInstalledProviders.size();
1871        for (int i = N - 1; i >= 0; i--) {
1872            Provider p = mInstalledProviders.get(i);
1873            if (pkgName.equals(p.info.provider.getPackageName())) {
1874                removeProviderLocked(i, p);
1875                providersRemoved = true;
1876            }
1877        }
1878
1879        // Delete the hosts for this package too
1880        //
1881        // By now, we have removed any AppWidgets that were in any hosts here,
1882        // so we don't need to worry about sending DISABLE broadcasts to them.
1883        N = mHosts.size();
1884        for (int i = N - 1; i >= 0; i--) {
1885            Host host = mHosts.get(i);
1886            if (pkgName.equals(host.packageName)) {
1887                deleteHostLocked(host);
1888            }
1889        }
1890
1891        return providersRemoved;
1892    }
1893
1894    void notifyHostsForProvidersChangedLocked() {
1895        final int N = mHosts.size();
1896        for (int i = N - 1; i >= 0; i--) {
1897            Host host = mHosts.get(i);
1898            try {
1899                if (host.callbacks != null) {
1900                    host.callbacks.providersChanged();
1901                }
1902            } catch (RemoteException ex) {
1903                // It failed; remove the callback. No need to prune because
1904                // we know that this host is still referenced by this
1905                // instance.
1906                host.callbacks = null;
1907            }
1908        }
1909    }
1910}
1911