AppWidgetServiceImpl.java revision 3ff2d867d46067132890a5a6ad68be8a4314d7f6
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                options = cloneIfLocalBinder(options);
538                ensureStateLoadedLocked();
539                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
540                if (id == null) {
541                    throw new IllegalArgumentException("bad appWidgetId");
542                }
543                if (id.provider != null) {
544                    throw new IllegalArgumentException("appWidgetId " + appWidgetId
545                            + " already bound to " + id.provider.info.provider);
546                }
547                Provider p = lookupProviderLocked(provider);
548                if (p == null) {
549                    throw new IllegalArgumentException("not a appwidget provider: " + provider);
550                }
551                if (p.zombie) {
552                    throw new IllegalArgumentException("can't bind to a 3rd party provider in"
553                            + " safe mode: " + provider);
554                }
555
556                id.provider = p;
557                if (options == null) {
558                    options = new Bundle();
559                }
560                id.options = options;
561
562                // We need to provide a default value for the widget category if it is not specified
563                if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
564                    options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
565                            AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
566                }
567
568                p.instances.add(id);
569                int instancesSize = p.instances.size();
570                if (instancesSize == 1) {
571                    // tell the provider that it's ready
572                    sendEnableIntentLocked(p);
573                }
574
575                // send an update now -- We need this update now, and just for this appWidgetId.
576                // It's less critical when the next one happens, so when we schedule the next one,
577                // we add updatePeriodMillis to its start time. That time will have some slop,
578                // but that's okay.
579                sendUpdateIntentLocked(p, new int[] { appWidgetId });
580
581                // schedule the future updates
582                registerForBroadcastsLocked(p, getAppWidgetIds(p));
583                saveStateLocked();
584            }
585        } finally {
586            Binder.restoreCallingIdentity(ident);
587        }
588    }
589
590    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
591        mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
592            "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
593        bindAppWidgetIdImpl(appWidgetId, provider, options);
594    }
595
596    public boolean bindAppWidgetIdIfAllowed(
597            String packageName, int appWidgetId, ComponentName provider, Bundle options) {
598        try {
599            mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
600        } catch (SecurityException se) {
601            if (!callerHasBindAppWidgetPermission(packageName)) {
602                return false;
603            }
604        }
605        bindAppWidgetIdImpl(appWidgetId, provider, options);
606        return true;
607    }
608
609    private boolean callerHasBindAppWidgetPermission(String packageName) {
610        int callingUid = Binder.getCallingUid();
611        try {
612            if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
613                return false;
614            }
615        } catch (Exception e) {
616            return false;
617        }
618        synchronized (mAppWidgetIds) {
619            ensureStateLoadedLocked();
620            return mPackagesWithBindWidgetPermission.contains(packageName);
621        }
622    }
623
624    public boolean hasBindAppWidgetPermission(String packageName) {
625        mContext.enforceCallingPermission(
626                android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
627                "hasBindAppWidgetPermission packageName=" + packageName);
628
629        synchronized (mAppWidgetIds) {
630            ensureStateLoadedLocked();
631            return mPackagesWithBindWidgetPermission.contains(packageName);
632        }
633    }
634
635    public void setBindAppWidgetPermission(String packageName, boolean permission) {
636        mContext.enforceCallingPermission(
637                android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
638                "setBindAppWidgetPermission packageName=" + packageName);
639
640        synchronized (mAppWidgetIds) {
641            ensureStateLoadedLocked();
642            if (permission) {
643                mPackagesWithBindWidgetPermission.add(packageName);
644            } else {
645                mPackagesWithBindWidgetPermission.remove(packageName);
646            }
647        }
648        saveStateLocked();
649    }
650
651    // Binds to a specific RemoteViewsService
652    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
653        synchronized (mAppWidgetIds) {
654            ensureStateLoadedLocked();
655            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
656            if (id == null) {
657                throw new IllegalArgumentException("bad appWidgetId");
658            }
659            final ComponentName componentName = intent.getComponent();
660            try {
661                final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
662                        PackageManager.GET_PERMISSIONS, mUserId);
663                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
664                    throw new SecurityException("Selected service does not require "
665                            + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
666                }
667            } catch (RemoteException e) {
668                throw new IllegalArgumentException("Unknown component " + componentName);
669            }
670
671            // If there is already a connection made for this service intent, then disconnect from
672            // that first. (This does not allow multiple connections to the same service under
673            // the same key)
674            ServiceConnectionProxy conn = null;
675            FilterComparison fc = new FilterComparison(intent);
676            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
677            if (mBoundRemoteViewsServices.containsKey(key)) {
678                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
679                conn.disconnect();
680                mContext.unbindService(conn);
681                mBoundRemoteViewsServices.remove(key);
682            }
683
684            int userId = UserHandle.getUserId(id.provider.uid);
685            // Bind to the RemoteViewsService (which will trigger a callback to the
686            // RemoteViewsAdapter.onServiceConnected())
687            final long token = Binder.clearCallingIdentity();
688            try {
689                conn = new ServiceConnectionProxy(key, connection);
690                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
691                mBoundRemoteViewsServices.put(key, conn);
692            } finally {
693                Binder.restoreCallingIdentity(token);
694            }
695
696            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
697            // when we can call back to the RemoteViewsService later to destroy associated
698            // factories.
699            incrementAppWidgetServiceRefCount(appWidgetId, fc);
700        }
701    }
702
703    // Unbinds from a specific RemoteViewsService
704    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
705        synchronized (mAppWidgetIds) {
706            ensureStateLoadedLocked();
707            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
708            // RemoteViewsAdapter)
709            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
710                    intent));
711            if (mBoundRemoteViewsServices.containsKey(key)) {
712                // We don't need to use the appWidgetId until after we are sure there is something
713                // to unbind. Note that this may mask certain issues with apps calling unbind()
714                // more than necessary.
715                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
716                if (id == null) {
717                    throw new IllegalArgumentException("bad appWidgetId");
718                }
719
720                ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
721                        .get(key);
722                conn.disconnect();
723                mContext.unbindService(conn);
724                mBoundRemoteViewsServices.remove(key);
725            } else {
726                Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
727            }
728        }
729    }
730
731    // Unbinds from a RemoteViewsService when we delete an app widget
732    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
733        int appWidgetId = id.appWidgetId;
734        // Unbind all connections to Services bound to this AppWidgetId
735        Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
736                .iterator();
737        while (it.hasNext()) {
738            final Pair<Integer, Intent.FilterComparison> key = it.next();
739            if (key.first.intValue() == appWidgetId) {
740                final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
741                        .get(key);
742                conn.disconnect();
743                mContext.unbindService(conn);
744                it.remove();
745            }
746        }
747
748        // Check if we need to destroy any services (if no other app widgets are
749        // referencing the same service)
750        decrementAppWidgetServiceRefCount(id);
751    }
752
753    // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
754    private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
755        final ServiceConnection conn = new ServiceConnection() {
756            @Override
757            public void onServiceConnected(ComponentName name, IBinder service) {
758                final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
759                try {
760                    cb.onDestroy(intent);
761                } catch (RemoteException e) {
762                    e.printStackTrace();
763                } catch (RuntimeException e) {
764                    e.printStackTrace();
765                }
766                mContext.unbindService(this);
767            }
768
769            @Override
770            public void onServiceDisconnected(android.content.ComponentName name) {
771                // Do nothing
772            }
773        };
774
775        int userId = UserHandle.getUserId(id.provider.uid);
776        // Bind to the service and remove the static intent->factory mapping in the
777        // RemoteViewsService.
778        final long token = Binder.clearCallingIdentity();
779        try {
780            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
781        } finally {
782            Binder.restoreCallingIdentity(token);
783        }
784    }
785
786    // Adds to the ref-count for a given RemoteViewsService intent
787    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
788        HashSet<Integer> appWidgetIds = null;
789        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
790            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
791        } else {
792            appWidgetIds = new HashSet<Integer>();
793            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
794        }
795        appWidgetIds.add(appWidgetId);
796    }
797
798    // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
799    // the ref-count reaches zero.
800    private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
801        Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
802        while (it.hasNext()) {
803            final FilterComparison key = it.next();
804            final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
805            if (ids.remove(id.appWidgetId)) {
806                // If we have removed the last app widget referencing this service, then we
807                // should destroy it and remove it from this set
808                if (ids.isEmpty()) {
809                    destroyRemoteViewsService(key.getIntent(), id);
810                    it.remove();
811                }
812            }
813        }
814    }
815
816    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
817        synchronized (mAppWidgetIds) {
818            ensureStateLoadedLocked();
819            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
820            if (id != null && id.provider != null && !id.provider.zombie) {
821                return cloneIfLocalBinder(id.provider.info);
822            }
823            return null;
824        }
825    }
826
827    public RemoteViews getAppWidgetViews(int appWidgetId) {
828        synchronized (mAppWidgetIds) {
829            ensureStateLoadedLocked();
830            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
831            if (id != null) {
832                return cloneIfLocalBinder(id.views);
833            }
834            return null;
835        }
836    }
837
838    public List<AppWidgetProviderInfo> getInstalledProviders() {
839        synchronized (mAppWidgetIds) {
840            ensureStateLoadedLocked();
841            final int N = mInstalledProviders.size();
842            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
843            for (int i = 0; i < N; i++) {
844                Provider p = mInstalledProviders.get(i);
845                if (!p.zombie) {
846                    result.add(cloneIfLocalBinder(p.info));
847                }
848            }
849            return result;
850        }
851    }
852
853    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
854        if (appWidgetIds == null) {
855            return;
856        }
857
858        int bitmapMemoryUsage = 0;
859        if (views != null) {
860            bitmapMemoryUsage = views.estimateMemoryUsage();
861        }
862        if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
863            throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
864                    " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
865                    mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
866                    " fill the device's screen once.");
867        }
868
869        if (appWidgetIds.length == 0) {
870            return;
871        }
872        final int N = appWidgetIds.length;
873
874        synchronized (mAppWidgetIds) {
875            ensureStateLoadedLocked();
876            for (int i = 0; i < N; i++) {
877                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
878                updateAppWidgetInstanceLocked(id, views);
879            }
880        }
881    }
882
883    public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
884        synchronized (mAppWidgetIds) {
885            options = cloneIfLocalBinder(options);
886            ensureStateLoadedLocked();
887            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
888
889            if (id == null) {
890                return;
891            }
892
893            Provider p = id.provider;
894            // Merge the options
895            id.options.putAll(options);
896
897            // send the broacast saying that this appWidgetId has been deleted
898            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
899            intent.setComponent(p.info.provider);
900            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
901            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
902            mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
903            saveStateLocked();
904        }
905    }
906
907    public Bundle getAppWidgetOptions(int appWidgetId) {
908        synchronized (mAppWidgetIds) {
909            ensureStateLoadedLocked();
910            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
911            if (id != null && id.options != null) {
912                return cloneIfLocalBinder(id.options);
913            } else {
914                return Bundle.EMPTY;
915            }
916        }
917    }
918
919    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
920        if (appWidgetIds == null) {
921            return;
922        }
923        if (appWidgetIds.length == 0) {
924            return;
925        }
926        final int N = appWidgetIds.length;
927
928        synchronized (mAppWidgetIds) {
929            ensureStateLoadedLocked();
930            for (int i = 0; i < N; i++) {
931                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
932                updateAppWidgetInstanceLocked(id, views, true);
933            }
934        }
935    }
936
937    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
938        if (appWidgetIds == null) {
939            return;
940        }
941        if (appWidgetIds.length == 0) {
942            return;
943        }
944        final int N = appWidgetIds.length;
945
946        synchronized (mAppWidgetIds) {
947            ensureStateLoadedLocked();
948            for (int i = 0; i < N; i++) {
949                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
950                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
951            }
952        }
953    }
954
955    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
956        synchronized (mAppWidgetIds) {
957            ensureStateLoadedLocked();
958            Provider p = lookupProviderLocked(provider);
959            if (p == null) {
960                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
961                return;
962            }
963            ArrayList<AppWidgetId> instances = p.instances;
964            final int callingUid = Binder.getCallingUid();
965            final int N = instances.size();
966            for (int i = 0; i < N; i++) {
967                AppWidgetId id = instances.get(i);
968                if (canAccessAppWidgetId(id, callingUid)) {
969                    updateAppWidgetInstanceLocked(id, views);
970                }
971            }
972        }
973    }
974
975    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
976        updateAppWidgetInstanceLocked(id, views, false);
977    }
978
979    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
980        // allow for stale appWidgetIds and other badness
981        // lookup also checks that the calling process can access the appWidgetId
982        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
983        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
984
985            if (!isPartialUpdate) {
986                // For a full update we replace the RemoteViews completely.
987                id.views = views;
988            } else {
989                // For a partial update, we merge the new RemoteViews with the old.
990                id.views.mergeRemoteViews(views);
991            }
992
993            // is anyone listening?
994            if (id.host.callbacks != null) {
995                try {
996                    // the lock is held, but this is a oneway call
997                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
998                } catch (RemoteException e) {
999                    // It failed; remove the callback. No need to prune because
1000                    // we know that this host is still referenced by this instance.
1001                    id.host.callbacks = null;
1002                }
1003            }
1004        }
1005    }
1006
1007    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
1008        // allow for stale appWidgetIds and other badness
1009        // lookup also checks that the calling process can access the appWidgetId
1010        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
1011        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
1012            // is anyone listening?
1013            if (id.host.callbacks != null) {
1014                try {
1015                    // the lock is held, but this is a oneway call
1016                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
1017                } catch (RemoteException e) {
1018                    // It failed; remove the callback. No need to prune because
1019                    // we know that this host is still referenced by this instance.
1020                    id.host.callbacks = null;
1021                }
1022            }
1023
1024            // If the host is unavailable, then we call the associated
1025            // RemoteViewsFactory.onDataSetChanged() directly
1026            if (id.host.callbacks == null) {
1027                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
1028                for (FilterComparison key : keys) {
1029                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
1030                        Intent intent = key.getIntent();
1031
1032                        final ServiceConnection conn = new ServiceConnection() {
1033                            @Override
1034                            public void onServiceConnected(ComponentName name, IBinder service) {
1035                                IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1036                                        .asInterface(service);
1037                                try {
1038                                    cb.onDataSetChangedAsync();
1039                                } catch (RemoteException e) {
1040                                    e.printStackTrace();
1041                                } catch (RuntimeException e) {
1042                                    e.printStackTrace();
1043                                }
1044                                mContext.unbindService(this);
1045                            }
1046
1047                            @Override
1048                            public void onServiceDisconnected(android.content.ComponentName name) {
1049                                // Do nothing
1050                            }
1051                        };
1052
1053                        int userId = UserHandle.getUserId(id.provider.uid);
1054                        // Bind to the service and call onDataSetChanged()
1055                        final long token = Binder.clearCallingIdentity();
1056                        try {
1057                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
1058                        } finally {
1059                            Binder.restoreCallingIdentity(token);
1060                        }
1061                    }
1062                }
1063            }
1064        }
1065    }
1066
1067    private boolean isLocalBinder() {
1068        return Process.myPid() == Binder.getCallingPid();
1069    }
1070
1071    private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
1072        if (isLocalBinder() && rv != null) {
1073            return rv.clone();
1074        }
1075        return rv;
1076    }
1077
1078    private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
1079        if (isLocalBinder() && info != null) {
1080            return info.clone();
1081        }
1082        return info;
1083    }
1084
1085    private Bundle cloneIfLocalBinder(Bundle bundle) {
1086        // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
1087        // if we start adding objects to the options. Further, it would only be an issue if keyguard
1088        // used such options.
1089        if (isLocalBinder() && bundle != null) {
1090            return (Bundle) bundle.clone();
1091        }
1092        return bundle;
1093    }
1094
1095    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1096            List<RemoteViews> updatedViews) {
1097        int callingUid = enforceCallingUid(packageName);
1098        synchronized (mAppWidgetIds) {
1099            ensureStateLoadedLocked();
1100            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1101            host.callbacks = callbacks;
1102
1103            updatedViews.clear();
1104
1105            ArrayList<AppWidgetId> instances = host.instances;
1106            int N = instances.size();
1107            int[] updatedIds = new int[N];
1108            for (int i = 0; i < N; i++) {
1109                AppWidgetId id = instances.get(i);
1110                updatedIds[i] = id.appWidgetId;
1111                updatedViews.add(cloneIfLocalBinder(id.views));
1112            }
1113            return updatedIds;
1114        }
1115    }
1116
1117    public void stopListening(int hostId) {
1118        synchronized (mAppWidgetIds) {
1119            ensureStateLoadedLocked();
1120            Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1121            if (host != null) {
1122                host.callbacks = null;
1123                pruneHostLocked(host);
1124            }
1125        }
1126    }
1127
1128    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1129        if (id.host.uid == callingUid) {
1130            // Apps hosting the AppWidget have access to it.
1131            return true;
1132        }
1133        if (id.provider != null && id.provider.uid == callingUid) {
1134            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1135            return true;
1136        }
1137        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1138            // Apps that can bind have access to all appWidgetIds.
1139            return true;
1140        }
1141        // Nobody else can access it.
1142        return false;
1143    }
1144
1145    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1146        int callingUid = Binder.getCallingUid();
1147        final int N = mAppWidgetIds.size();
1148        for (int i = 0; i < N; i++) {
1149            AppWidgetId id = mAppWidgetIds.get(i);
1150            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1151                return id;
1152            }
1153        }
1154        return null;
1155    }
1156
1157    Provider lookupProviderLocked(ComponentName provider) {
1158        final int N = mInstalledProviders.size();
1159        for (int i = 0; i < N; i++) {
1160            Provider p = mInstalledProviders.get(i);
1161            if (p.info.provider.equals(provider)) {
1162                return p;
1163            }
1164        }
1165        return null;
1166    }
1167
1168    Host lookupHostLocked(int uid, int hostId) {
1169        final int N = mHosts.size();
1170        for (int i = 0; i < N; i++) {
1171            Host h = mHosts.get(i);
1172            if (h.uid == uid && h.hostId == hostId) {
1173                return h;
1174            }
1175        }
1176        return null;
1177    }
1178
1179    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1180        final int N = mHosts.size();
1181        for (int i = 0; i < N; i++) {
1182            Host h = mHosts.get(i);
1183            if (h.hostId == hostId && h.packageName.equals(packageName)) {
1184                return h;
1185            }
1186        }
1187        Host host = new Host();
1188        host.packageName = packageName;
1189        host.uid = uid;
1190        host.hostId = hostId;
1191        mHosts.add(host);
1192        return host;
1193    }
1194
1195    void pruneHostLocked(Host host) {
1196        if (host.instances.size() == 0 && host.callbacks == null) {
1197            mHosts.remove(host);
1198        }
1199    }
1200
1201    void loadAppWidgetList() {
1202        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1203        try {
1204            List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1205                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1206                    PackageManager.GET_META_DATA, mUserId);
1207
1208            final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1209            for (int i = 0; i < N; i++) {
1210                ResolveInfo ri = broadcastReceivers.get(i);
1211                addProviderLocked(ri);
1212            }
1213        } catch (RemoteException re) {
1214            // Shouldn't happen, local call
1215        }
1216    }
1217
1218    boolean addProviderLocked(ResolveInfo ri) {
1219        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1220            return false;
1221        }
1222        if (!ri.activityInfo.isEnabled()) {
1223            return false;
1224        }
1225        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1226                ri.activityInfo.name), ri);
1227        if (p != null) {
1228            mInstalledProviders.add(p);
1229            return true;
1230        } else {
1231            return false;
1232        }
1233    }
1234
1235    void removeProviderLocked(int index, Provider p) {
1236        int N = p.instances.size();
1237        for (int i = 0; i < N; i++) {
1238            AppWidgetId id = p.instances.get(i);
1239            // Call back with empty RemoteViews
1240            updateAppWidgetInstanceLocked(id, null);
1241            // Stop telling the host about updates for this from now on
1242            cancelBroadcasts(p);
1243            // clear out references to this appWidgetId
1244            id.host.instances.remove(id);
1245            mAppWidgetIds.remove(id);
1246            id.provider = null;
1247            pruneHostLocked(id.host);
1248            id.host = null;
1249        }
1250        p.instances.clear();
1251        mInstalledProviders.remove(index);
1252        mDeletedProviders.add(p);
1253        // no need to send the DISABLE broadcast, since the receiver is gone anyway
1254        cancelBroadcasts(p);
1255    }
1256
1257    void sendEnableIntentLocked(Provider p) {
1258        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1259        intent.setComponent(p.info.provider);
1260        mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
1261    }
1262
1263    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1264        if (appWidgetIds != null && appWidgetIds.length > 0) {
1265            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1266            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1267            intent.setComponent(p.info.provider);
1268            mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
1269        }
1270    }
1271
1272    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1273        if (p.info.updatePeriodMillis > 0) {
1274            // if this is the first instance, set the alarm. otherwise,
1275            // rely on the fact that we've already set it and that
1276            // PendingIntent.getBroadcast will update the extras.
1277            boolean alreadyRegistered = p.broadcast != null;
1278            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1279            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1280            intent.setComponent(p.info.provider);
1281            long token = Binder.clearCallingIdentity();
1282            try {
1283                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1284                        PendingIntent.FLAG_UPDATE_CURRENT);
1285            } finally {
1286                Binder.restoreCallingIdentity(token);
1287            }
1288            if (!alreadyRegistered) {
1289                long period = p.info.updatePeriodMillis;
1290                if (period < MIN_UPDATE_PERIOD) {
1291                    period = MIN_UPDATE_PERIOD;
1292                }
1293                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1294                        .elapsedRealtime()
1295                        + period, period, p.broadcast);
1296            }
1297        }
1298    }
1299
1300    static int[] getAppWidgetIds(Provider p) {
1301        int instancesSize = p.instances.size();
1302        int appWidgetIds[] = new int[instancesSize];
1303        for (int i = 0; i < instancesSize; i++) {
1304            appWidgetIds[i] = p.instances.get(i).appWidgetId;
1305        }
1306        return appWidgetIds;
1307    }
1308
1309    public int[] getAppWidgetIds(ComponentName provider) {
1310        synchronized (mAppWidgetIds) {
1311            ensureStateLoadedLocked();
1312            Provider p = lookupProviderLocked(provider);
1313            if (p != null && Binder.getCallingUid() == p.uid) {
1314                return getAppWidgetIds(p);
1315            } else {
1316                return new int[0];
1317            }
1318        }
1319    }
1320
1321    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1322        Provider p = null;
1323
1324        ActivityInfo activityInfo = ri.activityInfo;
1325        XmlResourceParser parser = null;
1326        try {
1327            parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
1328                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1329            if (parser == null) {
1330                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1331                        + " meta-data for " + "AppWidget provider '" + component + '\'');
1332                return null;
1333            }
1334
1335            AttributeSet attrs = Xml.asAttributeSet(parser);
1336
1337            int type;
1338            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1339                    && type != XmlPullParser.START_TAG) {
1340                // drain whitespace, comments, etc.
1341            }
1342
1343            String nodeName = parser.getName();
1344            if (!"appwidget-provider".equals(nodeName)) {
1345                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1346                        + " AppWidget provider '" + component + '\'');
1347                return null;
1348            }
1349
1350            p = new Provider();
1351            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1352            info.provider = component;
1353            p.uid = activityInfo.applicationInfo.uid;
1354
1355            Resources res = mContext.getPackageManager()
1356                    .getResourcesForApplication(activityInfo.applicationInfo);
1357
1358            TypedArray sa = res.obtainAttributes(attrs,
1359                    com.android.internal.R.styleable.AppWidgetProviderInfo);
1360
1361            // These dimensions has to be resolved in the application's context.
1362            // We simply send back the raw complex data, which will be
1363            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1364            TypedValue value = sa
1365                    .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1366            info.minWidth = value != null ? value.data : 0;
1367            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1368            info.minHeight = value != null ? value.data : 0;
1369            value = sa.peekValue(
1370                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1371            info.minResizeWidth = value != null ? value.data : info.minWidth;
1372            value = sa.peekValue(
1373                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1374            info.minResizeHeight = value != null ? value.data : info.minHeight;
1375            info.updatePeriodMillis = sa.getInt(
1376                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1377            info.initialLayout = sa.getResourceId(
1378                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1379            info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
1380                    AppWidgetProviderInfo_initialKeyguardLayout, 0);
1381            String className = sa
1382                    .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1383            if (className != null) {
1384                info.configure = new ComponentName(component.getPackageName(), className);
1385            }
1386            info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
1387            info.icon = ri.getIconResource();
1388            info.previewImage = sa.getResourceId(
1389                    com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1390            info.autoAdvanceViewId = sa.getResourceId(
1391                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1392            info.resizeMode = sa.getInt(
1393                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1394                    AppWidgetProviderInfo.RESIZE_NONE);
1395            info.widgetCategory = sa.getInt(
1396                    com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
1397                    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
1398            info.widgetFeatures = sa.getInt(
1399                    com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures,
1400                    AppWidgetProviderInfo.WIDGET_FEATURES_NONE);
1401
1402            sa.recycle();
1403        } catch (Exception e) {
1404            // Ok to catch Exception here, because anything going wrong because
1405            // of what a client process passes to us should not be fatal for the
1406            // system process.
1407            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1408            return null;
1409        } finally {
1410            if (parser != null)
1411                parser.close();
1412        }
1413        return p;
1414    }
1415
1416    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1417        PackageInfo pkgInfo = null;
1418        try {
1419            pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1420        } catch (RemoteException re) {
1421            // Shouldn't happen, local call
1422        }
1423        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1424            throw new PackageManager.NameNotFoundException();
1425        }
1426        return pkgInfo.applicationInfo.uid;
1427    }
1428
1429    int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
1430        int callingUid = Binder.getCallingUid();
1431        if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
1432            return callingUid;
1433        }
1434        return enforceCallingUid(packageName);
1435    }
1436
1437    int enforceCallingUid(String packageName) throws IllegalArgumentException {
1438        int callingUid = Binder.getCallingUid();
1439        int packageUid;
1440        try {
1441            packageUid = getUidForPackage(packageName);
1442        } catch (PackageManager.NameNotFoundException ex) {
1443            throw new IllegalArgumentException("packageName and uid don't match packageName="
1444                    + packageName);
1445        }
1446        if (!UserHandle.isSameApp(callingUid, packageUid)) {
1447            throw new IllegalArgumentException("packageName and uid don't match packageName="
1448                    + packageName);
1449        }
1450        return callingUid;
1451    }
1452
1453    void sendInitialBroadcasts() {
1454        synchronized (mAppWidgetIds) {
1455            ensureStateLoadedLocked();
1456            final int N = mInstalledProviders.size();
1457            for (int i = 0; i < N; i++) {
1458                Provider p = mInstalledProviders.get(i);
1459                if (p.instances.size() > 0) {
1460                    sendEnableIntentLocked(p);
1461                    int[] appWidgetIds = getAppWidgetIds(p);
1462                    sendUpdateIntentLocked(p, appWidgetIds);
1463                    registerForBroadcastsLocked(p, appWidgetIds);
1464                }
1465            }
1466        }
1467    }
1468
1469    // only call from initialization -- it assumes that the data structures are all empty
1470    void loadStateLocked() {
1471        AtomicFile file = savedStateFile();
1472        try {
1473            FileInputStream stream = file.openRead();
1474            readStateFromFileLocked(stream);
1475
1476            if (stream != null) {
1477                try {
1478                    stream.close();
1479                } catch (IOException e) {
1480                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
1481                }
1482            }
1483        } catch (FileNotFoundException e) {
1484            Slog.w(TAG, "Failed to read state: " + e);
1485        }
1486    }
1487
1488    void saveStateLocked() {
1489        AtomicFile file = savedStateFile();
1490        FileOutputStream stream;
1491        try {
1492            stream = file.startWrite();
1493            if (writeStateToFileLocked(stream)) {
1494                file.finishWrite(stream);
1495            } else {
1496                file.failWrite(stream);
1497                Slog.w(TAG, "Failed to save state, restoring backup.");
1498            }
1499        } catch (IOException e) {
1500            Slog.w(TAG, "Failed open state file for write: " + e);
1501        }
1502    }
1503
1504    boolean writeStateToFileLocked(FileOutputStream stream) {
1505        int N;
1506
1507        try {
1508            XmlSerializer out = new FastXmlSerializer();
1509            out.setOutput(stream, "utf-8");
1510            out.startDocument(null, true);
1511            out.startTag(null, "gs");
1512
1513            int providerIndex = 0;
1514            N = mInstalledProviders.size();
1515            for (int i = 0; i < N; i++) {
1516                Provider p = mInstalledProviders.get(i);
1517                if (p.instances.size() > 0) {
1518                    out.startTag(null, "p");
1519                    out.attribute(null, "pkg", p.info.provider.getPackageName());
1520                    out.attribute(null, "cl", p.info.provider.getClassName());
1521                    out.endTag(null, "p");
1522                    p.tag = providerIndex;
1523                    providerIndex++;
1524                }
1525            }
1526
1527            N = mHosts.size();
1528            for (int i = 0; i < N; i++) {
1529                Host host = mHosts.get(i);
1530                out.startTag(null, "h");
1531                out.attribute(null, "pkg", host.packageName);
1532                out.attribute(null, "id", Integer.toHexString(host.hostId));
1533                out.endTag(null, "h");
1534                host.tag = i;
1535            }
1536
1537            N = mAppWidgetIds.size();
1538            for (int i = 0; i < N; i++) {
1539                AppWidgetId id = mAppWidgetIds.get(i);
1540                out.startTag(null, "g");
1541                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1542                out.attribute(null, "h", Integer.toHexString(id.host.tag));
1543                if (id.provider != null) {
1544                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1545                }
1546                if (id.options != null) {
1547                    out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
1548                            AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
1549                    out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
1550                            AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
1551                    out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
1552                            AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
1553                    out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
1554                            AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
1555                    out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
1556                            AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
1557                }
1558                out.endTag(null, "g");
1559            }
1560
1561            Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1562            while (it.hasNext()) {
1563                out.startTag(null, "b");
1564                out.attribute(null, "packageName", it.next());
1565                out.endTag(null, "b");
1566            }
1567
1568            out.endTag(null, "gs");
1569
1570            out.endDocument();
1571            return true;
1572        } catch (IOException e) {
1573            Slog.w(TAG, "Failed to write state: " + e);
1574            return false;
1575        }
1576    }
1577
1578    @SuppressWarnings("unused")
1579    void readStateFromFileLocked(FileInputStream stream) {
1580        boolean success = false;
1581        try {
1582            XmlPullParser parser = Xml.newPullParser();
1583            parser.setInput(stream, null);
1584
1585            int type;
1586            int providerIndex = 0;
1587            HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1588            do {
1589                type = parser.next();
1590                if (type == XmlPullParser.START_TAG) {
1591                    String tag = parser.getName();
1592                    if ("p".equals(tag)) {
1593                        // TODO: do we need to check that this package has the same signature
1594                        // as before?
1595                        String pkg = parser.getAttributeValue(null, "pkg");
1596                        String cl = parser.getAttributeValue(null, "cl");
1597
1598                        final IPackageManager packageManager = AppGlobals.getPackageManager();
1599                        try {
1600                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0,
1601                                    UserHandle.getCallingUserId());
1602                        } catch (RemoteException e) {
1603                            String[] pkgs = mContext.getPackageManager()
1604                                    .currentToCanonicalPackageNames(new String[] { pkg });
1605                            pkg = pkgs[0];
1606                        }
1607
1608                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1609                        if (p == null && mSafeMode) {
1610                            // if we're in safe mode, make a temporary one
1611                            p = new Provider();
1612                            p.info = new AppWidgetProviderInfo();
1613                            p.info.provider = new ComponentName(pkg, cl);
1614                            p.zombie = true;
1615                            mInstalledProviders.add(p);
1616                        }
1617                        if (p != null) {
1618                            // if it wasn't uninstalled or something
1619                            loadedProviders.put(providerIndex, p);
1620                        }
1621                        providerIndex++;
1622                    } else if ("h".equals(tag)) {
1623                        Host host = new Host();
1624
1625                        // TODO: do we need to check that this package has the same signature
1626                        // as before?
1627                        host.packageName = parser.getAttributeValue(null, "pkg");
1628                        try {
1629                            host.uid = getUidForPackage(host.packageName);
1630                        } catch (PackageManager.NameNotFoundException ex) {
1631                            host.zombie = true;
1632                        }
1633                        if (!host.zombie || mSafeMode) {
1634                            // In safe mode, we don't discard the hosts we don't recognize
1635                            // so that they're not pruned from our list. Otherwise, we do.
1636                            host.hostId = Integer
1637                                    .parseInt(parser.getAttributeValue(null, "id"), 16);
1638                            mHosts.add(host);
1639                        }
1640                    } else if ("b".equals(tag)) {
1641                        String packageName = parser.getAttributeValue(null, "packageName");
1642                        if (packageName != null) {
1643                            mPackagesWithBindWidgetPermission.add(packageName);
1644                        }
1645                    } else if ("g".equals(tag)) {
1646                        AppWidgetId id = new AppWidgetId();
1647                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1648                        if (id.appWidgetId >= mNextAppWidgetId) {
1649                            mNextAppWidgetId = id.appWidgetId + 1;
1650                        }
1651
1652                        Bundle options = new Bundle();
1653                        String minWidthString = parser.getAttributeValue(null, "min_width");
1654                        if (minWidthString != null) {
1655                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
1656                                    Integer.parseInt(minWidthString, 16));
1657                        }
1658                        String minHeightString = parser.getAttributeValue(null, "min_height");
1659                        if (minHeightString != null) {
1660                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
1661                                    Integer.parseInt(minHeightString, 16));
1662                        }
1663                        String maxWidthString = parser.getAttributeValue(null, "max_width");
1664                        if (maxWidthString != null) {
1665                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
1666                                    Integer.parseInt(maxWidthString, 16));
1667                        }
1668                        String maxHeightString = parser.getAttributeValue(null, "max_height");
1669                        if (maxHeightString != null) {
1670                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
1671                                    Integer.parseInt(maxHeightString, 16));
1672                        }
1673                        String categoryString = parser.getAttributeValue(null, "host_category");
1674                        if (categoryString != null) {
1675                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
1676                                    Integer.parseInt(categoryString, 16));
1677                        }
1678                        id.options = options;
1679
1680                        String providerString = parser.getAttributeValue(null, "p");
1681                        if (providerString != null) {
1682                            // there's no provider if it hasn't been bound yet.
1683                            // maybe we don't have to save this, but it brings the system
1684                            // to the state it was in.
1685                            int pIndex = Integer.parseInt(providerString, 16);
1686                            id.provider = loadedProviders.get(pIndex);
1687                            if (false) {
1688                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1689                                        + pIndex + " which is " + id.provider);
1690                            }
1691                            if (id.provider == null) {
1692                                // This provider is gone. We just let the host figure out
1693                                // that this happened when it fails to load it.
1694                                continue;
1695                            }
1696                        }
1697
1698                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1699                        id.host = mHosts.get(hIndex);
1700                        if (id.host == null) {
1701                            // This host is gone.
1702                            continue;
1703                        }
1704
1705                        if (id.provider != null) {
1706                            id.provider.instances.add(id);
1707                        }
1708                        id.host.instances.add(id);
1709                        mAppWidgetIds.add(id);
1710                    }
1711                }
1712            } while (type != XmlPullParser.END_DOCUMENT);
1713            success = true;
1714        } catch (NullPointerException e) {
1715            Slog.w(TAG, "failed parsing " + e);
1716        } catch (NumberFormatException e) {
1717            Slog.w(TAG, "failed parsing " + e);
1718        } catch (XmlPullParserException e) {
1719            Slog.w(TAG, "failed parsing " + e);
1720        } catch (IOException e) {
1721            Slog.w(TAG, "failed parsing " + e);
1722        } catch (IndexOutOfBoundsException e) {
1723            Slog.w(TAG, "failed parsing " + e);
1724        }
1725
1726        if (success) {
1727            // delete any hosts that didn't manage to get connected (should happen)
1728            // if it matters, they'll be reconnected.
1729            for (int i = mHosts.size() - 1; i >= 0; i--) {
1730                pruneHostLocked(mHosts.get(i));
1731            }
1732        } else {
1733            // failed reading, clean up
1734            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1735
1736            mAppWidgetIds.clear();
1737            mHosts.clear();
1738            final int N = mInstalledProviders.size();
1739            for (int i = 0; i < N; i++) {
1740                mInstalledProviders.get(i).instances.clear();
1741            }
1742        }
1743    }
1744
1745    static File getSettingsFile(int userId) {
1746        return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
1747    }
1748
1749    AtomicFile savedStateFile() {
1750        File dir = Environment.getUserSystemDirectory(mUserId);
1751        File settingsFile = getSettingsFile(mUserId);
1752        if (!settingsFile.exists() && mUserId == 0) {
1753            if (!dir.exists()) {
1754                dir.mkdirs();
1755            }
1756            // Migrate old data
1757            File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1758            // Method doesn't throw an exception on failure. Ignore any errors
1759            // in moving the file (like non-existence)
1760            oldFile.renameTo(settingsFile);
1761        }
1762        return new AtomicFile(settingsFile);
1763    }
1764
1765    void onUserRemoved() {
1766        // prune the ones we don't want to keep
1767        int N = mInstalledProviders.size();
1768        for (int i = N - 1; i >= 0; i--) {
1769            Provider p = mInstalledProviders.get(i);
1770            cancelBroadcasts(p);
1771        }
1772        getSettingsFile(mUserId).delete();
1773    }
1774
1775    boolean addProvidersForPackageLocked(String pkgName) {
1776        boolean providersAdded = false;
1777        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1778        intent.setPackage(pkgName);
1779        List<ResolveInfo> broadcastReceivers;
1780        try {
1781            broadcastReceivers = mPm.queryIntentReceivers(intent,
1782                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1783                    PackageManager.GET_META_DATA, mUserId);
1784        } catch (RemoteException re) {
1785            // Shouldn't happen, local call
1786            return false;
1787        }
1788        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1789        for (int i = 0; i < N; i++) {
1790            ResolveInfo ri = broadcastReceivers.get(i);
1791            ActivityInfo ai = ri.activityInfo;
1792            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1793                continue;
1794            }
1795            if (pkgName.equals(ai.packageName)) {
1796                addProviderLocked(ri);
1797                providersAdded = true;
1798            }
1799        }
1800
1801        return providersAdded;
1802    }
1803
1804    /**
1805     * Updates all providers with the specified package names, and records any providers that were
1806     * pruned.
1807     *
1808     * @return whether any providers were updated
1809     */
1810    boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
1811        boolean providersUpdated = false;
1812        HashSet<String> keep = new HashSet<String>();
1813        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1814        intent.setPackage(pkgName);
1815        List<ResolveInfo> broadcastReceivers;
1816        try {
1817            broadcastReceivers = mPm.queryIntentReceivers(intent,
1818                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1819                PackageManager.GET_META_DATA, mUserId);
1820        } catch (RemoteException re) {
1821            // Shouldn't happen, local call
1822            return false;
1823        }
1824
1825        // add the missing ones and collect which ones to keep
1826        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1827        for (int i = 0; i < N; i++) {
1828            ResolveInfo ri = broadcastReceivers.get(i);
1829            ActivityInfo ai = ri.activityInfo;
1830            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1831                continue;
1832            }
1833            if (pkgName.equals(ai.packageName)) {
1834                ComponentName component = new ComponentName(ai.packageName, ai.name);
1835                Provider p = lookupProviderLocked(component);
1836                if (p == null) {
1837                    if (addProviderLocked(ri)) {
1838                        keep.add(ai.name);
1839                        providersUpdated = true;
1840                    }
1841                } else {
1842                    Provider parsed = parseProviderInfoXml(component, ri);
1843                    if (parsed != null) {
1844                        keep.add(ai.name);
1845                        // Use the new AppWidgetProviderInfo.
1846                        p.info = parsed.info;
1847                        // If it's enabled
1848                        final int M = p.instances.size();
1849                        if (M > 0) {
1850                            int[] appWidgetIds = getAppWidgetIds(p);
1851                            // Reschedule for the new updatePeriodMillis (don't worry about handling
1852                            // it specially if updatePeriodMillis didn't change because we just sent
1853                            // an update, and the next one will be updatePeriodMillis from now).
1854                            cancelBroadcasts(p);
1855                            registerForBroadcastsLocked(p, appWidgetIds);
1856                            // If it's currently showing, call back with the new
1857                            // AppWidgetProviderInfo.
1858                            for (int j = 0; j < M; j++) {
1859                                AppWidgetId id = p.instances.get(j);
1860                                id.views = null;
1861                                if (id.host != null && id.host.callbacks != null) {
1862                                    try {
1863                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1864                                    } catch (RemoteException ex) {
1865                                        // It failed; remove the callback. No need to prune because
1866                                        // we know that this host is still referenced by this
1867                                        // instance.
1868                                        id.host.callbacks = null;
1869                                    }
1870                                }
1871                            }
1872                            // Now that we've told the host, push out an update.
1873                            sendUpdateIntentLocked(p, appWidgetIds);
1874                            providersUpdated = true;
1875                        }
1876                    }
1877                }
1878            }
1879        }
1880
1881        // prune the ones we don't want to keep
1882        N = mInstalledProviders.size();
1883        for (int i = N - 1; i >= 0; i--) {
1884            Provider p = mInstalledProviders.get(i);
1885            if (pkgName.equals(p.info.provider.getPackageName())
1886                    && !keep.contains(p.info.provider.getClassName())) {
1887                if (removedProviders != null) {
1888                    removedProviders.add(p.info.provider);
1889                }
1890                removeProviderLocked(i, p);
1891                providersUpdated = true;
1892            }
1893        }
1894
1895        return providersUpdated;
1896    }
1897
1898    boolean removeProvidersForPackageLocked(String pkgName) {
1899        boolean providersRemoved = false;
1900        int N = mInstalledProviders.size();
1901        for (int i = N - 1; i >= 0; i--) {
1902            Provider p = mInstalledProviders.get(i);
1903            if (pkgName.equals(p.info.provider.getPackageName())) {
1904                removeProviderLocked(i, p);
1905                providersRemoved = true;
1906            }
1907        }
1908
1909        // Delete the hosts for this package too
1910        //
1911        // By now, we have removed any AppWidgets that were in any hosts here,
1912        // so we don't need to worry about sending DISABLE broadcasts to them.
1913        N = mHosts.size();
1914        for (int i = N - 1; i >= 0; i--) {
1915            Host host = mHosts.get(i);
1916            if (pkgName.equals(host.packageName)) {
1917                deleteHostLocked(host);
1918            }
1919        }
1920
1921        return providersRemoved;
1922    }
1923
1924    void notifyHostsForProvidersChangedLocked() {
1925        final int N = mHosts.size();
1926        for (int i = N - 1; i >= 0; i--) {
1927            Host host = mHosts.get(i);
1928            try {
1929                if (host.callbacks != null) {
1930                    host.callbacks.providersChanged();
1931                }
1932            } catch (RemoteException ex) {
1933                // It failed; remove the callback. No need to prune because
1934                // we know that this host is still referenced by this
1935                // instance.
1936                host.callbacks = null;
1937            }
1938        }
1939    }
1940}
1941