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