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