AppWidgetService.java revision 16c8d8a558f94ec14ef52bb5ac11044e2d0d902c
1/*
2 * Copyright (C) 2007 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 java.io.File;
20import java.io.FileDescriptor;
21import java.io.FileInputStream;
22import java.io.FileOutputStream;
23import java.io.IOException;
24import java.io.PrintWriter;
25import java.lang.ref.WeakReference;
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.Iterator;
30import java.util.List;
31import java.util.Locale;
32
33import org.xmlpull.v1.XmlPullParser;
34import org.xmlpull.v1.XmlPullParserException;
35import org.xmlpull.v1.XmlSerializer;
36
37import android.app.AlarmManager;
38import android.app.PendingIntent;
39import android.appwidget.AppWidgetManager;
40import android.appwidget.AppWidgetProviderInfo;
41import android.content.BroadcastReceiver;
42import android.content.ComponentName;
43import android.content.Context;
44import android.content.Intent;
45import android.content.Intent.FilterComparison;
46import android.content.IntentFilter;
47import android.content.ServiceConnection;
48import android.content.pm.ActivityInfo;
49import android.content.pm.ApplicationInfo;
50import android.content.pm.PackageInfo;
51import android.content.pm.PackageManager;
52import android.content.pm.ResolveInfo;
53import android.content.pm.ServiceInfo;
54import android.content.res.Resources;
55import android.content.res.TypedArray;
56import android.content.res.XmlResourceParser;
57import android.net.Uri;
58import android.os.Binder;
59import android.os.Bundle;
60import android.os.Handler;
61import android.os.IBinder;
62import android.os.Process;
63import android.os.RemoteException;
64import android.os.SystemClock;
65import android.util.AttributeSet;
66import android.util.Log;
67import android.util.Pair;
68import android.util.Slog;
69import android.util.TypedValue;
70import android.util.Xml;
71import android.widget.RemoteViews;
72
73import com.android.internal.appwidget.IAppWidgetHost;
74import com.android.internal.appwidget.IAppWidgetService;
75import com.android.internal.util.FastXmlSerializer;
76import com.android.internal.widget.IRemoteViewsAdapterConnection;
77
78class AppWidgetService extends IAppWidgetService.Stub
79{
80    private static final String TAG = "AppWidgetService";
81
82    private static final String SETTINGS_FILENAME = "appwidgets.xml";
83    private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
84    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
85
86    /*
87     * When identifying a Host or Provider based on the calling process, use the uid field.
88     * When identifying a Host or Provider based on a package manager broadcast, use the
89     * package given.
90     */
91
92    static class Provider {
93        int uid;
94        AppWidgetProviderInfo info;
95        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
96        PendingIntent broadcast;
97        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
98
99        int tag;    // for use while saving state (the index)
100    }
101
102    static class Host {
103        int uid;
104        int hostId;
105        String packageName;
106        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
107        IAppWidgetHost callbacks;
108        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
109
110        int tag;    // for use while saving state (the index)
111    }
112
113    static class AppWidgetId {
114        int appWidgetId;
115        Provider provider;
116        RemoteViews views;
117        Host host;
118    }
119
120    /**
121     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
122     * This needs to be a static inner class since a reference to the ServiceConnection is held
123     * globally and may lead us to leak AppWidgetService instances (if there were more than one).
124     */
125    static class ServiceConnectionProxy implements ServiceConnection {
126        private final Pair<Integer, Intent.FilterComparison> mKey;
127        private final IBinder mConnectionCb;
128
129        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
130            mKey = key;
131            mConnectionCb = connectionCb;
132        }
133        public void onServiceConnected(ComponentName name, IBinder service) {
134            final IRemoteViewsAdapterConnection cb =
135                IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
136            try {
137                cb.onServiceConnected(service);
138            } catch (RemoteException e) {
139                e.printStackTrace();
140            }
141        }
142        public void onServiceDisconnected(ComponentName name) {
143            disconnect();
144        }
145        public void disconnect() {
146            final IRemoteViewsAdapterConnection cb =
147                IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
148            try {
149                cb.onServiceDisconnected();
150            } catch (RemoteException e) {
151                e.printStackTrace();
152            }
153        }
154    }
155
156    // Manages connections to RemoteViewsServices
157    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
158        mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
159
160    Context mContext;
161    Locale mLocale;
162    PackageManager mPackageManager;
163    AlarmManager mAlarmManager;
164    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
165    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
166    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
167    ArrayList<Host> mHosts = new ArrayList<Host>();
168    boolean mSafeMode;
169
170    AppWidgetService(Context context) {
171        mContext = context;
172        mPackageManager = context.getPackageManager();
173        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
174    }
175
176    public void systemReady(boolean safeMode) {
177        mSafeMode = safeMode;
178
179        loadAppWidgetList();
180        loadStateLocked();
181
182        // Register for the boot completed broadcast, so we can send the
183        // ENABLE broacasts.  If we try to send them now, they time out,
184        // because the system isn't ready to handle them yet.
185        mContext.registerReceiver(mBroadcastReceiver,
186                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
187
188        // Register for configuration changes so we can update the names
189        // of the widgets when the locale changes.
190        mContext.registerReceiver(mBroadcastReceiver,
191                new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
192
193        // Register for broadcasts about package install, etc., so we can
194        // update the provider list.
195        IntentFilter filter = new IntentFilter();
196        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
197        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
198        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
199        filter.addDataScheme("package");
200        mContext.registerReceiver(mBroadcastReceiver, filter);
201        // Register for events related to sdcard installation.
202        IntentFilter sdFilter = new IntentFilter();
203        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
204        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
205        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
206    }
207
208    @Override
209    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
210        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
211                != PackageManager.PERMISSION_GRANTED) {
212            pw.println("Permission Denial: can't dump from from pid="
213                    + Binder.getCallingPid()
214                    + ", uid=" + Binder.getCallingUid());
215            return;
216        }
217
218        synchronized (mAppWidgetIds) {
219            int N = mInstalledProviders.size();
220            pw.println("Providers:");
221            for (int i=0; i<N; i++) {
222                Provider p = mInstalledProviders.get(i);
223                AppWidgetProviderInfo info = p.info;
224                pw.print("  ["); pw.print(i); pw.print("] provider ");
225                        pw.print(info.provider.flattenToShortString());
226                        pw.println(':');
227                pw.print("    min=("); pw.print(info.minWidth);
228                        pw.print("x"); pw.print(info.minHeight);
229                        pw.print(") updatePeriodMillis=");
230                        pw.print(info.updatePeriodMillis);
231                        pw.print(" initialLayout=#");
232                        pw.print(Integer.toHexString(info.initialLayout));
233                        pw.print(" zombie="); pw.println(p.zombie);
234            }
235
236            N = mAppWidgetIds.size();
237            pw.println(" ");
238            pw.println("AppWidgetIds:");
239            for (int i=0; i<N; i++) {
240                AppWidgetId id = mAppWidgetIds.get(i);
241                pw.print("  ["); pw.print(i); pw.print("] id=");
242                        pw.println(id.appWidgetId);
243                pw.print("    hostId=");
244                        pw.print(id.host.hostId); pw.print(' ');
245                        pw.print(id.host.packageName); pw.print('/');
246                        pw.println(id.host.uid);
247                if (id.provider != null) {
248                    pw.print("    provider=");
249                            pw.println(id.provider.info.provider.flattenToShortString());
250                }
251                if (id.host != null) {
252                    pw.print("    host.callbacks="); pw.println(id.host.callbacks);
253                }
254                if (id.views != null) {
255                    pw.print("    views="); pw.println(id.views);
256                }
257            }
258
259            N = mHosts.size();
260            pw.println(" ");
261            pw.println("Hosts:");
262            for (int i=0; i<N; i++) {
263                Host host = mHosts.get(i);
264                pw.print("  ["); pw.print(i); pw.print("] hostId=");
265                        pw.print(host.hostId); pw.print(' ');
266                        pw.print(host.packageName); pw.print('/');
267                        pw.print(host.uid); pw.println(':');
268                pw.print("    callbacks="); pw.println(host.callbacks);
269                pw.print("    instances.size="); pw.print(host.instances.size());
270                        pw.print(" zombie="); pw.println(host.zombie);
271            }
272        }
273    }
274
275    public int allocateAppWidgetId(String packageName, int hostId) {
276        int callingUid = enforceCallingUid(packageName);
277        synchronized (mAppWidgetIds) {
278            int appWidgetId = mNextAppWidgetId++;
279
280            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
281
282            AppWidgetId id = new AppWidgetId();
283            id.appWidgetId = appWidgetId;
284            id.host = host;
285
286            host.instances.add(id);
287            mAppWidgetIds.add(id);
288
289            saveStateLocked();
290
291            return appWidgetId;
292        }
293    }
294
295    public void deleteAppWidgetId(int appWidgetId) {
296        synchronized (mAppWidgetIds) {
297            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
298            if (id != null) {
299                deleteAppWidgetLocked(id);
300                saveStateLocked();
301            }
302        }
303    }
304
305    public void deleteHost(int hostId) {
306        synchronized (mAppWidgetIds) {
307            int callingUid = getCallingUid();
308            Host host = lookupHostLocked(callingUid, hostId);
309            if (host != null) {
310                deleteHostLocked(host);
311                saveStateLocked();
312            }
313        }
314    }
315
316    public void deleteAllHosts() {
317        synchronized (mAppWidgetIds) {
318            int callingUid = getCallingUid();
319            final int N = mHosts.size();
320            boolean changed = false;
321            for (int i=N-1; i>=0; i--) {
322                Host host = mHosts.get(i);
323                if (host.uid == callingUid) {
324                    deleteHostLocked(host);
325                    changed = true;
326                }
327            }
328            if (changed) {
329                saveStateLocked();
330            }
331        }
332    }
333
334    void deleteHostLocked(Host host) {
335        final int N = host.instances.size();
336        for (int i=N-1; i>=0; i--) {
337            AppWidgetId id = host.instances.get(i);
338            deleteAppWidgetLocked(id);
339        }
340        host.instances.clear();
341        mHosts.remove(host);
342        // it's gone or going away, abruptly drop the callback connection
343        host.callbacks = null;
344    }
345
346    void deleteAppWidgetLocked(AppWidgetId id) {
347        // We first unbind all services that are bound to this id
348        unbindAppWidgetRemoteViewsServicesLocked(id);
349
350        Host host = id.host;
351        host.instances.remove(id);
352        pruneHostLocked(host);
353
354        mAppWidgetIds.remove(id);
355
356        Provider p = id.provider;
357        if (p != null) {
358            p.instances.remove(id);
359            if (!p.zombie) {
360                // send the broacast saying that this appWidgetId has been deleted
361                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
362                intent.setComponent(p.info.provider);
363                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
364                mContext.sendBroadcast(intent);
365                if (p.instances.size() == 0) {
366                    // cancel the future updates
367                    cancelBroadcasts(p);
368
369                    // send the broacast saying that the provider is not in use any more
370                    intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
371                    intent.setComponent(p.info.provider);
372                    mContext.sendBroadcast(intent);
373                }
374            }
375        }
376    }
377
378    void cancelBroadcasts(Provider p) {
379        if (p.broadcast != null) {
380            mAlarmManager.cancel(p.broadcast);
381            long token = Binder.clearCallingIdentity();
382            try {
383                p.broadcast.cancel();
384            } finally {
385                Binder.restoreCallingIdentity(token);
386            }
387            p.broadcast = null;
388        }
389    }
390
391    public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
392        mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
393                "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
394        synchronized (mAppWidgetIds) {
395            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
396            if (id == null) {
397                throw new IllegalArgumentException("bad appWidgetId");
398            }
399            if (id.provider != null) {
400                throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
401                        + id.provider.info.provider);
402            }
403            Provider p = lookupProviderLocked(provider);
404            if (p == null) {
405                throw new IllegalArgumentException("not a appwidget provider: " + provider);
406            }
407            if (p.zombie) {
408                throw new IllegalArgumentException("can't bind to a 3rd party provider in"
409                        + " safe mode: " + provider);
410            }
411
412            id.provider = p;
413            p.instances.add(id);
414            int instancesSize = p.instances.size();
415            if (instancesSize == 1) {
416                // tell the provider that it's ready
417                sendEnableIntentLocked(p);
418            }
419
420            // send an update now -- We need this update now, and just for this appWidgetId.
421            // It's less critical when the next one happens, so when we schdule the next one,
422            // we add updatePeriodMillis to its start time.  That time will have some slop,
423            // but that's okay.
424            sendUpdateIntentLocked(p, new int[] { appWidgetId });
425
426            // schedule the future updates
427            registerForBroadcastsLocked(p, getAppWidgetIds(p));
428            saveStateLocked();
429        }
430    }
431
432    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
433        synchronized (mAppWidgetIds) {
434            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
435            if (id == null) {
436                throw new IllegalArgumentException("bad appWidgetId");
437            }
438            final ComponentName componentName = intent.getComponent();
439            try {
440                final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
441                        PackageManager.GET_PERMISSIONS);
442                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
443                    throw new SecurityException("Selected service does not require "
444                            + android.Manifest.permission.BIND_REMOTEVIEWS
445                            + ": " + componentName);
446                }
447            } catch (PackageManager.NameNotFoundException e) {
448                throw new IllegalArgumentException("Unknown component " + componentName);
449            }
450
451            // If there is already a connection made for this service intent, then disconnect from
452            // that first.  (This does not allow multiple connections to the same service under
453            // the same key)
454            ServiceConnectionProxy conn = null;
455            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
456                    new FilterComparison(intent));
457            if (mBoundRemoteViewsServices.containsKey(key)) {
458                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
459                conn.disconnect();
460                mContext.unbindService(conn);
461                mBoundRemoteViewsServices.remove(key);
462            }
463
464            // Bind to the RemoteViewsService (which will trigger a callback to the
465            // RemoteViewsAdapter.onServiceConnected())
466            final long token = Binder.clearCallingIdentity();
467            try {
468                conn = new ServiceConnectionProxy(key, connection);
469                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
470                mBoundRemoteViewsServices.put(key, conn);
471            } finally {
472                Binder.restoreCallingIdentity(token);
473            }
474        }
475    }
476
477    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
478        synchronized (mAppWidgetIds) {
479            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
480            // RemoteViewsAdapter)
481            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
482                    new FilterComparison(intent));
483            if (mBoundRemoteViewsServices.containsKey(key)) {
484                // We don't need to use the appWidgetId until after we are sure there is something
485                // to unbind.  Note that this may mask certain issues with apps calling unbind()
486                // more than necessary.
487                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
488                if (id == null) {
489                    throw new IllegalArgumentException("bad appWidgetId");
490                }
491
492                ServiceConnectionProxy conn =
493                    (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
494                conn.disconnect();
495                mContext.unbindService(conn);
496                mBoundRemoteViewsServices.remove(key);
497            } else {
498                Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
499            }
500        }
501    }
502
503    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
504        int appWidgetId = id.appWidgetId;
505        // Unbind all connections to Services bound to this AppWidgetId
506        Iterator<Pair<Integer, Intent.FilterComparison>> it =
507            mBoundRemoteViewsServices.keySet().iterator();
508        while (it.hasNext()) {
509            final Pair<Integer, Intent.FilterComparison> key = it.next();
510            if (key.first.intValue() == appWidgetId) {
511                final ServiceConnectionProxy conn = (ServiceConnectionProxy)
512                        mBoundRemoteViewsServices.get(key);
513                conn.disconnect();
514                mContext.unbindService(conn);
515                it.remove();
516            }
517        }
518    }
519
520    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
521        synchronized (mAppWidgetIds) {
522            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
523            if (id != null && id.provider != null && !id.provider.zombie) {
524                return id.provider.info;
525            }
526            return null;
527        }
528    }
529
530    public RemoteViews getAppWidgetViews(int appWidgetId) {
531        synchronized (mAppWidgetIds) {
532            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
533            if (id != null) {
534                return id.views;
535            }
536            return null;
537        }
538    }
539
540    public List<AppWidgetProviderInfo> getInstalledProviders() {
541        synchronized (mAppWidgetIds) {
542            final int N = mInstalledProviders.size();
543            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
544            for (int i=0; i<N; i++) {
545                Provider p = mInstalledProviders.get(i);
546                if (!p.zombie) {
547                    result.add(p.info);
548                }
549            }
550            return result;
551        }
552    }
553
554    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
555        if (appWidgetIds == null) {
556            return;
557        }
558        if (appWidgetIds.length == 0) {
559            return;
560        }
561        final int N = appWidgetIds.length;
562
563        synchronized (mAppWidgetIds) {
564            for (int i=0; i<N; i++) {
565                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
566                updateAppWidgetInstanceLocked(id, views);
567            }
568        }
569    }
570
571    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
572        if (appWidgetIds == null) {
573            return;
574        }
575        if (appWidgetIds.length == 0) {
576            return;
577        }
578        final int N = appWidgetIds.length;
579
580        synchronized (mAppWidgetIds) {
581            for (int i=0; i<N; i++) {
582                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
583                updateAppWidgetInstanceLocked(id, views, true);
584            }
585        }
586    }
587
588    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
589        if (appWidgetIds == null) {
590            return;
591        }
592        if (appWidgetIds.length == 0) {
593            return;
594        }
595        final int N = appWidgetIds.length;
596
597        synchronized (mAppWidgetIds) {
598            for (int i=0; i<N; i++) {
599                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
600                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
601            }
602        }
603    }
604
605    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
606        synchronized (mAppWidgetIds) {
607            Provider p = lookupProviderLocked(provider);
608            if (p == null) {
609                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
610                return;
611            }
612            ArrayList<AppWidgetId> instances = p.instances;
613            final int N = instances.size();
614            for (int i=0; i<N; i++) {
615                AppWidgetId id = instances.get(i);
616                updateAppWidgetInstanceLocked(id, views);
617            }
618        }
619    }
620
621    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
622        updateAppWidgetInstanceLocked(id, views, false);
623    }
624
625    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
626        // allow for stale appWidgetIds and other badness
627        // lookup also checks that the calling process can access the appWidgetId
628        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
629        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
630
631            // We do not want to save this RemoteViews
632            if (!isPartialUpdate) id.views = views;
633
634            // is anyone listening?
635            if (id.host.callbacks != null) {
636                try {
637                    // the lock is held, but this is a oneway call
638                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
639                } catch (RemoteException e) {
640                    // It failed; remove the callback. No need to prune because
641                    // we know that this host is still referenced by this instance.
642                    id.host.callbacks = null;
643                }
644            }
645        }
646    }
647
648    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
649        // allow for stale appWidgetIds and other badness
650        // lookup also checks that the calling process can access the appWidgetId
651        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
652        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
653            // is anyone listening?
654            if (id.host.callbacks != null) {
655                try {
656                    // the lock is held, but this is a oneway call
657                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
658                } catch (RemoteException e) {
659                    // It failed; remove the callback. No need to prune because
660                    // we know that this host is still referenced by this instance.
661                    id.host.callbacks = null;
662                }
663            }
664        }
665    }
666
667    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
668            List<RemoteViews> updatedViews) {
669        int callingUid = enforceCallingUid(packageName);
670        synchronized (mAppWidgetIds) {
671            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
672            host.callbacks = callbacks;
673
674            updatedViews.clear();
675
676            ArrayList<AppWidgetId> instances = host.instances;
677            int N = instances.size();
678            int[] updatedIds = new int[N];
679            for (int i=0; i<N; i++) {
680                AppWidgetId id = instances.get(i);
681                updatedIds[i] = id.appWidgetId;
682                updatedViews.add(id.views);
683            }
684            return updatedIds;
685        }
686    }
687
688    public void stopListening(int hostId) {
689        synchronized (mAppWidgetIds) {
690            Host host = lookupHostLocked(getCallingUid(), hostId);
691            if (host != null) {
692                host.callbacks = null;
693                pruneHostLocked(host);
694            }
695        }
696    }
697
698    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
699        if (id.host.uid == callingUid) {
700            // Apps hosting the AppWidget have access to it.
701            return true;
702        }
703        if (id.provider != null && id.provider.uid == callingUid) {
704            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
705            return true;
706        }
707        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
708                == PackageManager.PERMISSION_GRANTED) {
709            // Apps that can bind have access to all appWidgetIds.
710            return true;
711        }
712        // Nobody else can access it.
713        return false;
714    }
715
716   AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
717        int callingUid = getCallingUid();
718        final int N = mAppWidgetIds.size();
719        for (int i=0; i<N; i++) {
720            AppWidgetId id = mAppWidgetIds.get(i);
721            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
722                return id;
723            }
724        }
725        return null;
726    }
727
728    Provider lookupProviderLocked(ComponentName provider) {
729        final String className = provider.getClassName();
730        final int N = mInstalledProviders.size();
731        for (int i=0; i<N; i++) {
732            Provider p = mInstalledProviders.get(i);
733            if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
734                return p;
735            }
736        }
737        return null;
738    }
739
740    Host lookupHostLocked(int uid, int hostId) {
741        final int N = mHosts.size();
742        for (int i=0; i<N; i++) {
743            Host h = mHosts.get(i);
744            if (h.uid == uid && h.hostId == hostId) {
745                return h;
746            }
747        }
748        return null;
749    }
750
751    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
752        final int N = mHosts.size();
753        for (int i=0; i<N; i++) {
754            Host h = mHosts.get(i);
755            if (h.hostId == hostId && h.packageName.equals(packageName)) {
756                return h;
757            }
758        }
759        Host host = new Host();
760        host.packageName = packageName;
761        host.uid = uid;
762        host.hostId = hostId;
763        mHosts.add(host);
764        return host;
765    }
766
767    void pruneHostLocked(Host host) {
768        if (host.instances.size() == 0 && host.callbacks == null) {
769            mHosts.remove(host);
770        }
771    }
772
773    void loadAppWidgetList() {
774        PackageManager pm = mPackageManager;
775
776        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
777        List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
778                PackageManager.GET_META_DATA);
779
780        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
781        for (int i=0; i<N; i++) {
782            ResolveInfo ri = broadcastReceivers.get(i);
783            addProviderLocked(ri);
784        }
785    }
786
787    boolean addProviderLocked(ResolveInfo ri) {
788        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
789            return false;
790        }
791        if (!ri.activityInfo.isEnabled()) {
792            return false;
793        }
794        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
795                    ri.activityInfo.name), ri);
796        if (p != null) {
797            mInstalledProviders.add(p);
798            return true;
799        } else {
800            return false;
801        }
802    }
803
804    void removeProviderLocked(int index, Provider p) {
805        int N = p.instances.size();
806        for (int i=0; i<N; i++) {
807            AppWidgetId id = p.instances.get(i);
808            // Call back with empty RemoteViews
809            updateAppWidgetInstanceLocked(id, null);
810            // Stop telling the host about updates for this from now on
811            cancelBroadcasts(p);
812            // clear out references to this appWidgetId
813            id.host.instances.remove(id);
814            mAppWidgetIds.remove(id);
815            id.provider = null;
816            pruneHostLocked(id.host);
817            id.host = null;
818        }
819        p.instances.clear();
820        mInstalledProviders.remove(index);
821        // no need to send the DISABLE broadcast, since the receiver is gone anyway
822        cancelBroadcasts(p);
823    }
824
825    void sendEnableIntentLocked(Provider p) {
826        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
827        intent.setComponent(p.info.provider);
828        mContext.sendBroadcast(intent);
829    }
830
831    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
832        if (appWidgetIds != null && appWidgetIds.length > 0) {
833            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
834            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
835            intent.setComponent(p.info.provider);
836            mContext.sendBroadcast(intent);
837        }
838    }
839
840    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
841        if (p.info.updatePeriodMillis > 0) {
842            // if this is the first instance, set the alarm.  otherwise,
843            // rely on the fact that we've already set it and that
844            // PendingIntent.getBroadcast will update the extras.
845            boolean alreadyRegistered = p.broadcast != null;
846            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
847            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
848            intent.setComponent(p.info.provider);
849            long token = Binder.clearCallingIdentity();
850            try {
851                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
852                        PendingIntent.FLAG_UPDATE_CURRENT);
853            } finally {
854                Binder.restoreCallingIdentity(token);
855            }
856            if (!alreadyRegistered) {
857                long period = p.info.updatePeriodMillis;
858                if (period < MIN_UPDATE_PERIOD) {
859                    period = MIN_UPDATE_PERIOD;
860                }
861                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
862                        SystemClock.elapsedRealtime() + period, period, p.broadcast);
863            }
864        }
865    }
866
867    static int[] getAppWidgetIds(Provider p) {
868        int instancesSize = p.instances.size();
869        int appWidgetIds[] = new int[instancesSize];
870        for (int i=0; i<instancesSize; i++) {
871            appWidgetIds[i] = p.instances.get(i).appWidgetId;
872        }
873        return appWidgetIds;
874    }
875
876    public int[] getAppWidgetIds(ComponentName provider) {
877        synchronized (mAppWidgetIds) {
878            Provider p = lookupProviderLocked(provider);
879            if (p != null && getCallingUid() == p.uid) {
880                return getAppWidgetIds(p);
881            } else {
882                return new int[0];
883            }
884        }
885    }
886
887    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
888        Provider p = null;
889
890        ActivityInfo activityInfo = ri.activityInfo;
891        XmlResourceParser parser = null;
892        try {
893            parser = activityInfo.loadXmlMetaData(mPackageManager,
894                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
895            if (parser == null) {
896                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
897                        + "AppWidget provider '" + component + '\'');
898                return null;
899            }
900
901            AttributeSet attrs = Xml.asAttributeSet(parser);
902
903            int type;
904            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
905                    && type != XmlPullParser.START_TAG) {
906                // drain whitespace, comments, etc.
907            }
908
909            String nodeName = parser.getName();
910            if (!"appwidget-provider".equals(nodeName)) {
911                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
912                        + " AppWidget provider '" + component + '\'');
913                return null;
914            }
915
916            p = new Provider();
917            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
918            // If metaData was null, we would have returned earlier when getting
919            // the parser No need to do the check here
920            info.oldName = activityInfo.metaData.getString(
921                    AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
922
923            info.provider = component;
924            p.uid = activityInfo.applicationInfo.uid;
925
926            Resources res = mPackageManager.getResourcesForApplication(
927                    activityInfo.applicationInfo);
928
929            TypedArray sa = res.obtainAttributes(attrs,
930                    com.android.internal.R.styleable.AppWidgetProviderInfo);
931
932            // These dimensions has to be resolved in the application's context.
933            // We simply send back the raw complex data, which will be
934            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
935            TypedValue value = sa.peekValue(
936                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
937            info.minWidth = value != null ? value.data : 0;
938            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
939            info.minHeight = value != null ? value.data : 0;
940
941            info.updatePeriodMillis = sa.getInt(
942                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
943            info.initialLayout = sa.getResourceId(
944                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
945            String className = sa.getString(
946                    com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
947            if (className != null) {
948                info.configure = new ComponentName(component.getPackageName(), className);
949            }
950            info.label = activityInfo.loadLabel(mPackageManager).toString();
951            info.icon = ri.getIconResource();
952            info.previewImage = sa.getResourceId(
953            		com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
954            info.autoAdvanceViewId = sa.getResourceId(
955                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
956
957            sa.recycle();
958        } catch (Exception e) {
959            // Ok to catch Exception here, because anything going wrong because
960            // of what a client process passes to us should not be fatal for the
961            // system process.
962            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
963            return null;
964        } finally {
965            if (parser != null) parser.close();
966        }
967        return p;
968    }
969
970    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
971        PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
972        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
973            throw new PackageManager.NameNotFoundException();
974        }
975        return pkgInfo.applicationInfo.uid;
976    }
977
978    int enforceCallingUid(String packageName) throws IllegalArgumentException {
979        int callingUid = getCallingUid();
980        int packageUid;
981        try {
982            packageUid = getUidForPackage(packageName);
983        } catch (PackageManager.NameNotFoundException ex) {
984            throw new IllegalArgumentException("packageName and uid don't match packageName="
985                    + packageName);
986        }
987        if (callingUid != packageUid && Process.supportsProcesses()) {
988            throw new IllegalArgumentException("packageName and uid don't match packageName="
989                    + packageName);
990        }
991        return callingUid;
992    }
993
994    void sendInitialBroadcasts() {
995        synchronized (mAppWidgetIds) {
996            final int N = mInstalledProviders.size();
997            for (int i=0; i<N; i++) {
998                Provider p = mInstalledProviders.get(i);
999                if (p.instances.size() > 0) {
1000                    sendEnableIntentLocked(p);
1001                    int[] appWidgetIds = getAppWidgetIds(p);
1002                    sendUpdateIntentLocked(p, appWidgetIds);
1003                    registerForBroadcastsLocked(p, appWidgetIds);
1004                }
1005            }
1006        }
1007    }
1008
1009    // only call from initialization -- it assumes that the data structures are all empty
1010    void loadStateLocked() {
1011        File temp = savedStateTempFile();
1012        File real = savedStateRealFile();
1013
1014        // prefer the real file.  If it doesn't exist, use the temp one, and then copy it to the
1015        // real one.  if there is both a real file and a temp one, assume that the temp one isn't
1016        // fully written and delete it.
1017        if (real.exists()) {
1018            readStateFromFileLocked(real);
1019            if (temp.exists()) {
1020                //noinspection ResultOfMethodCallIgnored
1021                temp.delete();
1022            }
1023        } else if (temp.exists()) {
1024            readStateFromFileLocked(temp);
1025            //noinspection ResultOfMethodCallIgnored
1026            temp.renameTo(real);
1027        }
1028    }
1029
1030    void saveStateLocked() {
1031        File temp = savedStateTempFile();
1032        File real = savedStateRealFile();
1033
1034        if (!real.exists()) {
1035            // If the real one doesn't exist, it's either because this is the first time
1036            // or because something went wrong while copying them.  In this case, we can't
1037            // trust anything that's in temp.  In order to have the loadState code not
1038            // use the temporary one until it's fully written, create an empty file
1039            // for real, which will we'll shortly delete.
1040            try {
1041                //noinspection ResultOfMethodCallIgnored
1042                real.createNewFile();
1043            } catch (IOException e) {
1044                // Ignore
1045            }
1046        }
1047
1048        if (temp.exists()) {
1049            //noinspection ResultOfMethodCallIgnored
1050            temp.delete();
1051        }
1052
1053        if (!writeStateToFileLocked(temp)) {
1054            Slog.w(TAG, "Failed to persist new settings");
1055            return;
1056        }
1057
1058        //noinspection ResultOfMethodCallIgnored
1059        real.delete();
1060        //noinspection ResultOfMethodCallIgnored
1061        temp.renameTo(real);
1062    }
1063
1064    boolean writeStateToFileLocked(File file) {
1065        FileOutputStream stream = null;
1066        int N;
1067
1068        try {
1069            stream = new FileOutputStream(file, false);
1070            XmlSerializer out = new FastXmlSerializer();
1071            out.setOutput(stream, "utf-8");
1072            out.startDocument(null, true);
1073
1074
1075            out.startTag(null, "gs");
1076
1077            int providerIndex = 0;
1078            N = mInstalledProviders.size();
1079            for (int i=0; i<N; i++) {
1080                Provider p = mInstalledProviders.get(i);
1081                if (p.instances.size() > 0) {
1082                    out.startTag(null, "p");
1083                    out.attribute(null, "pkg", p.info.provider.getPackageName());
1084                    out.attribute(null, "cl", p.info.provider.getClassName());
1085                    out.endTag(null, "p");
1086                    p.tag = providerIndex;
1087                    providerIndex++;
1088                }
1089            }
1090
1091            N = mHosts.size();
1092            for (int i=0; i<N; i++) {
1093                Host host = mHosts.get(i);
1094                out.startTag(null, "h");
1095                out.attribute(null, "pkg", host.packageName);
1096                out.attribute(null, "id", Integer.toHexString(host.hostId));
1097                out.endTag(null, "h");
1098                host.tag = i;
1099            }
1100
1101            N = mAppWidgetIds.size();
1102            for (int i=0; i<N; i++) {
1103                AppWidgetId id = mAppWidgetIds.get(i);
1104                out.startTag(null, "g");
1105                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1106                out.attribute(null, "h", Integer.toHexString(id.host.tag));
1107                if (id.provider != null) {
1108                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1109                }
1110                out.endTag(null, "g");
1111            }
1112
1113            out.endTag(null, "gs");
1114
1115            out.endDocument();
1116            stream.close();
1117            return true;
1118        } catch (IOException e) {
1119            try {
1120                if (stream != null) {
1121                    stream.close();
1122                }
1123            } catch (IOException ex) {
1124                // Ignore
1125            }
1126            if (file.exists()) {
1127                //noinspection ResultOfMethodCallIgnored
1128                file.delete();
1129            }
1130            return false;
1131        }
1132    }
1133
1134    void readStateFromFileLocked(File file) {
1135        FileInputStream stream = null;
1136
1137        boolean success = false;
1138
1139        try {
1140            stream = new FileInputStream(file);
1141            XmlPullParser parser = Xml.newPullParser();
1142            parser.setInput(stream, null);
1143
1144            int type;
1145            int providerIndex = 0;
1146            HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
1147            do {
1148                type = parser.next();
1149                if (type == XmlPullParser.START_TAG) {
1150                    String tag = parser.getName();
1151                    if ("p".equals(tag)) {
1152                        // TODO: do we need to check that this package has the same signature
1153                        // as before?
1154                        String pkg = parser.getAttributeValue(null, "pkg");
1155                        String cl = parser.getAttributeValue(null, "cl");
1156
1157                        final PackageManager packageManager = mContext.getPackageManager();
1158                        try {
1159                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1160                        } catch (PackageManager.NameNotFoundException e) {
1161                            String[] pkgs = packageManager.currentToCanonicalPackageNames(
1162                                    new String[] { pkg });
1163                            pkg = pkgs[0];
1164                        }
1165
1166                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1167                        if (p == null && mSafeMode) {
1168                            // if we're in safe mode, make a temporary one
1169                            p = new Provider();
1170                            p.info = new AppWidgetProviderInfo();
1171                            p.info.provider = new ComponentName(pkg, cl);
1172                            p.zombie = true;
1173                            mInstalledProviders.add(p);
1174                        }
1175                        if (p != null) {
1176                            // if it wasn't uninstalled or something
1177                            loadedProviders.put(providerIndex, p);
1178                        }
1179                        providerIndex++;
1180                    }
1181                    else if ("h".equals(tag)) {
1182                        Host host = new Host();
1183
1184                        // TODO: do we need to check that this package has the same signature
1185                        // as before?
1186                        host.packageName = parser.getAttributeValue(null, "pkg");
1187                        try {
1188                            host.uid = getUidForPackage(host.packageName);
1189                        } catch (PackageManager.NameNotFoundException ex) {
1190                            host.zombie = true;
1191                        }
1192                        if (!host.zombie || mSafeMode) {
1193                            // In safe mode, we don't discard the hosts we don't recognize
1194                            // so that they're not pruned from our list.  Otherwise, we do.
1195                            host.hostId = Integer.parseInt(
1196                                    parser.getAttributeValue(null, "id"), 16);
1197                            mHosts.add(host);
1198                        }
1199                    }
1200                    else if ("g".equals(tag)) {
1201                        AppWidgetId id = new AppWidgetId();
1202                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1203                        if (id.appWidgetId >= mNextAppWidgetId) {
1204                            mNextAppWidgetId = id.appWidgetId + 1;
1205                        }
1206
1207                        String providerString = parser.getAttributeValue(null, "p");
1208                        if (providerString != null) {
1209                            // there's no provider if it hasn't been bound yet.
1210                            // maybe we don't have to save this, but it brings the system
1211                            // to the state it was in.
1212                            int pIndex = Integer.parseInt(providerString, 16);
1213                            id.provider = loadedProviders.get(pIndex);
1214                            if (false) {
1215                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1216                                        + pIndex + " which is " + id.provider);
1217                            }
1218                            if (id.provider == null) {
1219                                // This provider is gone.  We just let the host figure out
1220                                // that this happened when it fails to load it.
1221                                continue;
1222                            }
1223                        }
1224
1225                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1226                        id.host = mHosts.get(hIndex);
1227                        if (id.host == null) {
1228                            // This host is gone.
1229                            continue;
1230                        }
1231
1232                        if (id.provider != null) {
1233                            id.provider.instances.add(id);
1234                        }
1235                        id.host.instances.add(id);
1236                        mAppWidgetIds.add(id);
1237                    }
1238                }
1239            } while (type != XmlPullParser.END_DOCUMENT);
1240            success = true;
1241        } catch (NullPointerException e) {
1242            Slog.w(TAG, "failed parsing " + file, e);
1243        } catch (NumberFormatException e) {
1244            Slog.w(TAG, "failed parsing " + file, e);
1245        } catch (XmlPullParserException e) {
1246            Slog.w(TAG, "failed parsing " + file, e);
1247        } catch (IOException e) {
1248            Slog.w(TAG, "failed parsing " + file, e);
1249        } catch (IndexOutOfBoundsException e) {
1250            Slog.w(TAG, "failed parsing " + file, e);
1251        }
1252        try {
1253            if (stream != null) {
1254                stream.close();
1255            }
1256        } catch (IOException e) {
1257            // Ignore
1258        }
1259
1260        if (success) {
1261            // delete any hosts that didn't manage to get connected (should happen)
1262            // if it matters, they'll be reconnected.
1263            for (int i=mHosts.size()-1; i>=0; i--) {
1264                pruneHostLocked(mHosts.get(i));
1265            }
1266        } else {
1267            // failed reading, clean up
1268            mAppWidgetIds.clear();
1269            mHosts.clear();
1270            final int N = mInstalledProviders.size();
1271            for (int i=0; i<N; i++) {
1272                mInstalledProviders.get(i).instances.clear();
1273            }
1274        }
1275    }
1276
1277    File savedStateTempFile() {
1278        return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1279        //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1280    }
1281
1282    File savedStateRealFile() {
1283        return new File("/data/system/" + SETTINGS_FILENAME);
1284        //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1285    }
1286
1287    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1288        public void onReceive(Context context, Intent intent) {
1289            String action = intent.getAction();
1290            //Slog.d(TAG, "received " + action);
1291            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1292                sendInitialBroadcasts();
1293            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1294                Locale revised = Locale.getDefault();
1295                if (revised == null || mLocale == null ||
1296                    !(revised.equals(mLocale))) {
1297                    mLocale = revised;
1298
1299                    synchronized (mAppWidgetIds) {
1300                        int N = mInstalledProviders.size();
1301                        for (int i=N-1; i>=0; i--) {
1302                            Provider p = mInstalledProviders.get(i);
1303                            String pkgName = p.info.provider.getPackageName();
1304                            updateProvidersForPackageLocked(pkgName);
1305                        }
1306                        saveStateLocked();
1307                    }
1308                }
1309            } else {
1310                boolean added = false;
1311                boolean changed = false;
1312                String pkgList[] = null;
1313                if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
1314                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1315                    added = true;
1316                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
1317                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1318                    added = false;
1319                } else  {
1320                    Uri uri = intent.getData();
1321                    if (uri == null) {
1322                        return;
1323                    }
1324                    String pkgName = uri.getSchemeSpecificPart();
1325                    if (pkgName == null) {
1326                        return;
1327                    }
1328                    pkgList = new String[] { pkgName };
1329                    added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1330                    changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
1331                }
1332                if (pkgList == null || pkgList.length == 0) {
1333                    return;
1334                }
1335                if (added || changed) {
1336                    synchronized (mAppWidgetIds) {
1337                        Bundle extras = intent.getExtras();
1338                        if (changed || (extras != null &&
1339                                    extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
1340                            for (String pkgName : pkgList) {
1341                                // The package was just upgraded
1342                                updateProvidersForPackageLocked(pkgName);
1343                            }
1344                        } else {
1345                            // The package was just added
1346                            for (String pkgName : pkgList) {
1347                                addProvidersForPackageLocked(pkgName);
1348                            }
1349                        }
1350                        saveStateLocked();
1351                    }
1352                } else {
1353                    Bundle extras = intent.getExtras();
1354                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1355                        // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
1356                    } else {
1357                        synchronized (mAppWidgetIds) {
1358                            for (String pkgName : pkgList) {
1359                                removeProvidersForPackageLocked(pkgName);
1360                                saveStateLocked();
1361                            }
1362                        }
1363                    }
1364                }
1365            }
1366        }
1367    };
1368
1369    void addProvidersForPackageLocked(String pkgName) {
1370        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1371        intent.setPackage(pkgName);
1372        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1373                PackageManager.GET_META_DATA);
1374
1375        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1376        for (int i=0; i<N; i++) {
1377            ResolveInfo ri = broadcastReceivers.get(i);
1378            ActivityInfo ai = ri.activityInfo;
1379            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1380                continue;
1381            }
1382            if (pkgName.equals(ai.packageName)) {
1383                addProviderLocked(ri);
1384            }
1385        }
1386    }
1387
1388    void updateProvidersForPackageLocked(String pkgName) {
1389        HashSet<String> keep = new HashSet<String>();
1390        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1391        intent.setPackage(pkgName);
1392        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1393                PackageManager.GET_META_DATA);
1394
1395        // add the missing ones and collect which ones to keep
1396        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1397        for (int i=0; i<N; i++) {
1398            ResolveInfo ri = broadcastReceivers.get(i);
1399            ActivityInfo ai = ri.activityInfo;
1400            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1401                continue;
1402            }
1403            if (pkgName.equals(ai.packageName)) {
1404                ComponentName component = new ComponentName(ai.packageName, ai.name);
1405                Provider p = lookupProviderLocked(component);
1406                if (p == null) {
1407                    if (addProviderLocked(ri)) {
1408                        keep.add(ai.name);
1409                    }
1410                } else {
1411                    Provider parsed = parseProviderInfoXml(component, ri);
1412                    if (parsed != null) {
1413                        keep.add(ai.name);
1414                        // Use the new AppWidgetProviderInfo.
1415                        p.info = parsed.info;
1416                        // If it's enabled
1417                        final int M = p.instances.size();
1418                        if (M > 0) {
1419                            int[] appWidgetIds = getAppWidgetIds(p);
1420                            // Reschedule for the new updatePeriodMillis (don't worry about handling
1421                            // it specially if updatePeriodMillis didn't change because we just sent
1422                            // an update, and the next one will be updatePeriodMillis from now).
1423                            cancelBroadcasts(p);
1424                            registerForBroadcastsLocked(p, appWidgetIds);
1425                            // If it's currently showing, call back with the new AppWidgetProviderInfo.
1426                            for (int j=0; j<M; j++) {
1427                                AppWidgetId id = p.instances.get(j);
1428                                id.views = null;
1429                                if (id.host != null && id.host.callbacks != null) {
1430                                    try {
1431                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1432                                    } catch (RemoteException ex) {
1433                                        // It failed; remove the callback. No need to prune because
1434                                        // we know that this host is still referenced by this
1435                                        // instance.
1436                                        id.host.callbacks = null;
1437                                    }
1438                                }
1439                            }
1440                            // Now that we've told the host, push out an update.
1441                            sendUpdateIntentLocked(p, appWidgetIds);
1442                        }
1443                    }
1444                }
1445            }
1446        }
1447
1448        // prune the ones we don't want to keep
1449        N = mInstalledProviders.size();
1450        for (int i=N-1; i>=0; i--) {
1451            Provider p = mInstalledProviders.get(i);
1452            if (pkgName.equals(p.info.provider.getPackageName())
1453                    && !keep.contains(p.info.provider.getClassName())) {
1454                removeProviderLocked(i, p);
1455            }
1456        }
1457    }
1458
1459    void removeProvidersForPackageLocked(String pkgName) {
1460        int N = mInstalledProviders.size();
1461        for (int i=N-1; i>=0; i--) {
1462            Provider p = mInstalledProviders.get(i);
1463            if (pkgName.equals(p.info.provider.getPackageName())) {
1464                removeProviderLocked(i, p);
1465            }
1466        }
1467
1468        // Delete the hosts for this package too
1469        //
1470        // By now, we have removed any AppWidgets that were in any hosts here,
1471        // so we don't need to worry about sending DISABLE broadcasts to them.
1472        N = mHosts.size();
1473        for (int i=N-1; i>=0; i--) {
1474            Host host = mHosts.get(i);
1475            if (pkgName.equals(host.packageName)) {
1476                deleteHostLocked(host);
1477            }
1478        }
1479    }
1480}
1481
1482