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