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