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