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