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