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