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