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