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