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