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