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