AppWidgetService.java revision d2db2a579440608453994b64eb5b425840f5307a
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 updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
429        synchronized (mAppWidgetIds) {
430            Provider p = lookupProviderLocked(provider);
431            if (p == null) {
432                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
433                return;
434            }
435            ArrayList<AppWidgetId> instances = p.instances;
436            final int N = instances.size();
437            for (int i=0; i<N; i++) {
438                AppWidgetId id = instances.get(i);
439                updateAppWidgetInstanceLocked(id, views);
440            }
441        }
442    }
443
444    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
445        // allow for stale appWidgetIds and other badness
446        // lookup also checks that the calling process can access the appWidgetId
447        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
448        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
449            id.views = views;
450
451            // is anyone listening?
452            if (id.host.callbacks != null) {
453                try {
454                    // the lock is held, but this is a oneway call
455                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
456                } catch (RemoteException e) {
457                    // It failed; remove the callback. No need to prune because
458                    // we know that this host is still referenced by this instance.
459                    id.host.callbacks = null;
460                }
461            }
462        }
463    }
464
465    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
466            List<RemoteViews> updatedViews) {
467        int callingUid = enforceCallingUid(packageName);
468        synchronized (mAppWidgetIds) {
469            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
470            host.callbacks = callbacks;
471
472            updatedViews.clear();
473
474            ArrayList<AppWidgetId> instances = host.instances;
475            int N = instances.size();
476            int[] updatedIds = new int[N];
477            for (int i=0; i<N; i++) {
478                AppWidgetId id = instances.get(i);
479                updatedIds[i] = id.appWidgetId;
480                updatedViews.add(id.views);
481            }
482            return updatedIds;
483        }
484    }
485
486    public void stopListening(int hostId) {
487        synchronized (mAppWidgetIds) {
488            Host host = lookupHostLocked(getCallingUid(), hostId);
489            if (host != null) {
490                host.callbacks = null;
491                pruneHostLocked(host);
492            }
493        }
494    }
495
496    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
497        if (id.host.uid == callingUid) {
498            // Apps hosting the AppWidget have access to it.
499            return true;
500        }
501        if (id.provider != null && id.provider.uid == callingUid) {
502            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
503            return true;
504        }
505        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
506                == PackageManager.PERMISSION_GRANTED) {
507            // Apps that can bind have access to all appWidgetIds.
508            return true;
509        }
510        // Nobody else can access it.
511        return false;
512    }
513
514   AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
515        int callingUid = getCallingUid();
516        final int N = mAppWidgetIds.size();
517        for (int i=0; i<N; i++) {
518            AppWidgetId id = mAppWidgetIds.get(i);
519            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
520                return id;
521            }
522        }
523        return null;
524    }
525
526    Provider lookupProviderLocked(ComponentName provider) {
527        final String className = provider.getClassName();
528        final int N = mInstalledProviders.size();
529        for (int i=0; i<N; i++) {
530            Provider p = mInstalledProviders.get(i);
531            if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) {
532                return p;
533            }
534        }
535        return null;
536    }
537
538    Host lookupHostLocked(int uid, int hostId) {
539        final int N = mHosts.size();
540        for (int i=0; i<N; i++) {
541            Host h = mHosts.get(i);
542            if (h.uid == uid && h.hostId == hostId) {
543                return h;
544            }
545        }
546        return null;
547    }
548
549    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
550        final int N = mHosts.size();
551        for (int i=0; i<N; i++) {
552            Host h = mHosts.get(i);
553            if (h.hostId == hostId && h.packageName.equals(packageName)) {
554                return h;
555            }
556        }
557        Host host = new Host();
558        host.packageName = packageName;
559        host.uid = uid;
560        host.hostId = hostId;
561        mHosts.add(host);
562        return host;
563    }
564
565    void pruneHostLocked(Host host) {
566        if (host.instances.size() == 0 && host.callbacks == null) {
567            mHosts.remove(host);
568        }
569    }
570
571    void loadAppWidgetList() {
572        PackageManager pm = mPackageManager;
573
574        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
575        List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
576                PackageManager.GET_META_DATA);
577
578        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
579        for (int i=0; i<N; i++) {
580            ResolveInfo ri = broadcastReceivers.get(i);
581            addProviderLocked(ri);
582        }
583    }
584
585    boolean addProviderLocked(ResolveInfo ri) {
586        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
587                    ri.activityInfo.name), ri);
588        if (p != null) {
589            mInstalledProviders.add(p);
590            return true;
591        } else {
592            return false;
593        }
594    }
595
596    void removeProviderLocked(int index, Provider p) {
597        int N = p.instances.size();
598        for (int i=0; i<N; i++) {
599            AppWidgetId id = p.instances.get(i);
600            // Call back with empty RemoteViews
601            updateAppWidgetInstanceLocked(id, null);
602            // Stop telling the host about updates for this from now on
603            cancelBroadcasts(p);
604            // clear out references to this appWidgetId
605            id.host.instances.remove(id);
606            mAppWidgetIds.remove(id);
607            id.provider = null;
608            pruneHostLocked(id.host);
609            id.host = null;
610        }
611        p.instances.clear();
612        mInstalledProviders.remove(index);
613        // no need to send the DISABLE broadcast, since the receiver is gone anyway
614        cancelBroadcasts(p);
615    }
616
617    void sendEnableIntentLocked(Provider p) {
618        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
619        intent.setComponent(p.info.provider);
620        mContext.sendBroadcast(intent);
621    }
622
623    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
624        if (appWidgetIds != null && appWidgetIds.length > 0) {
625            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
626            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
627            intent.setComponent(p.info.provider);
628            mContext.sendBroadcast(intent);
629        }
630    }
631
632    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
633        if (p.info.updatePeriodMillis > 0) {
634            // if this is the first instance, set the alarm.  otherwise,
635            // rely on the fact that we've already set it and that
636            // PendingIntent.getBroadcast will update the extras.
637            boolean alreadyRegistered = p.broadcast != null;
638            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
639            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
640            intent.setComponent(p.info.provider);
641            long token = Binder.clearCallingIdentity();
642            try {
643                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
644                        PendingIntent.FLAG_UPDATE_CURRENT);
645            } finally {
646                Binder.restoreCallingIdentity(token);
647            }
648            if (!alreadyRegistered) {
649                long period = p.info.updatePeriodMillis;
650                if (period < MIN_UPDATE_PERIOD) {
651                    period = MIN_UPDATE_PERIOD;
652                }
653                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
654                        SystemClock.elapsedRealtime() + period, period, p.broadcast);
655            }
656        }
657    }
658
659    static int[] getAppWidgetIds(Provider p) {
660        int instancesSize = p.instances.size();
661        int appWidgetIds[] = new int[instancesSize];
662        for (int i=0; i<instancesSize; i++) {
663            appWidgetIds[i] = p.instances.get(i).appWidgetId;
664        }
665        return appWidgetIds;
666    }
667
668    public int[] getAppWidgetIds(ComponentName provider) {
669        synchronized (mAppWidgetIds) {
670            Provider p = lookupProviderLocked(provider);
671            if (p != null && getCallingUid() == p.uid) {
672                return getAppWidgetIds(p);
673            } else {
674                return new int[0];
675            }
676        }
677    }
678
679    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
680        Provider p = null;
681
682        ActivityInfo activityInfo = ri.activityInfo;
683        XmlResourceParser parser = null;
684        try {
685            parser = activityInfo.loadXmlMetaData(mPackageManager,
686                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
687            if (parser == null) {
688                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
689                        + "AppWidget provider '" + component + '\'');
690                return null;
691            }
692
693            AttributeSet attrs = Xml.asAttributeSet(parser);
694
695            int type;
696            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
697                    && type != XmlPullParser.START_TAG) {
698                // drain whitespace, comments, etc.
699            }
700
701            String nodeName = parser.getName();
702            if (!"appwidget-provider".equals(nodeName)) {
703                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
704                        + " AppWidget provider '" + component + '\'');
705                return null;
706            }
707
708            p = new Provider();
709            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
710            // If metaData was null, we would have returned earlier when getting
711            // the parser No need to do the check here
712            info.oldName = activityInfo.metaData.getString(
713                    AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME);
714
715            info.provider = component;
716            p.uid = activityInfo.applicationInfo.uid;
717
718            Resources res = mPackageManager.getResourcesForApplication(
719                    activityInfo.applicationInfo);
720
721            TypedArray sa = res.obtainAttributes(attrs,
722                    com.android.internal.R.styleable.AppWidgetProviderInfo);
723
724            // These dimensions has to be resolved in the application's context.
725            // We simply send back the raw complex data, which will be
726            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
727            TypedValue value = sa.peekValue(
728                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
729            info.minWidth = value != null ? value.data : 0;
730            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
731            info.minHeight = value != null ? value.data : 0;
732
733            info.updatePeriodMillis = sa.getInt(
734                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
735            info.initialLayout = sa.getResourceId(
736                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
737            String className = sa.getString(
738                    com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
739            if (className != null) {
740                info.configure = new ComponentName(component.getPackageName(), className);
741            }
742            info.label = activityInfo.loadLabel(mPackageManager).toString();
743            info.icon = ri.getIconResource();
744            info.previewImage = sa.getResourceId(
745            		com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
746
747            sa.recycle();
748        } catch (Exception e) {
749            // Ok to catch Exception here, because anything going wrong because
750            // of what a client process passes to us should not be fatal for the
751            // system process.
752            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
753            return null;
754        } finally {
755            if (parser != null) parser.close();
756        }
757        return p;
758    }
759
760    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
761        PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
762        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
763            throw new PackageManager.NameNotFoundException();
764        }
765        return pkgInfo.applicationInfo.uid;
766    }
767
768    int enforceCallingUid(String packageName) throws IllegalArgumentException {
769        int callingUid = getCallingUid();
770        int packageUid;
771        try {
772            packageUid = getUidForPackage(packageName);
773        } catch (PackageManager.NameNotFoundException ex) {
774            throw new IllegalArgumentException("packageName and uid don't match packageName="
775                    + packageName);
776        }
777        if (callingUid != packageUid && Process.supportsProcesses()) {
778            throw new IllegalArgumentException("packageName and uid don't match packageName="
779                    + packageName);
780        }
781        return callingUid;
782    }
783
784    void sendInitialBroadcasts() {
785        synchronized (mAppWidgetIds) {
786            final int N = mInstalledProviders.size();
787            for (int i=0; i<N; i++) {
788                Provider p = mInstalledProviders.get(i);
789                if (p.instances.size() > 0) {
790                    sendEnableIntentLocked(p);
791                    int[] appWidgetIds = getAppWidgetIds(p);
792                    sendUpdateIntentLocked(p, appWidgetIds);
793                    registerForBroadcastsLocked(p, appWidgetIds);
794                }
795            }
796        }
797    }
798
799    // only call from initialization -- it assumes that the data structures are all empty
800    void loadStateLocked() {
801        File temp = savedStateTempFile();
802        File real = savedStateRealFile();
803
804        // prefer the real file.  If it doesn't exist, use the temp one, and then copy it to the
805        // real one.  if there is both a real file and a temp one, assume that the temp one isn't
806        // fully written and delete it.
807        if (real.exists()) {
808            readStateFromFileLocked(real);
809            if (temp.exists()) {
810                //noinspection ResultOfMethodCallIgnored
811                temp.delete();
812            }
813        } else if (temp.exists()) {
814            readStateFromFileLocked(temp);
815            //noinspection ResultOfMethodCallIgnored
816            temp.renameTo(real);
817        }
818    }
819
820    void saveStateLocked() {
821        File temp = savedStateTempFile();
822        File real = savedStateRealFile();
823
824        if (!real.exists()) {
825            // If the real one doesn't exist, it's either because this is the first time
826            // or because something went wrong while copying them.  In this case, we can't
827            // trust anything that's in temp.  In order to have the loadState code not
828            // use the temporary one until it's fully written, create an empty file
829            // for real, which will we'll shortly delete.
830            try {
831                //noinspection ResultOfMethodCallIgnored
832                real.createNewFile();
833            } catch (IOException e) {
834                // Ignore
835            }
836        }
837
838        if (temp.exists()) {
839            //noinspection ResultOfMethodCallIgnored
840            temp.delete();
841        }
842
843        if (!writeStateToFileLocked(temp)) {
844            Slog.w(TAG, "Failed to persist new settings");
845            return;
846        }
847
848        //noinspection ResultOfMethodCallIgnored
849        real.delete();
850        //noinspection ResultOfMethodCallIgnored
851        temp.renameTo(real);
852    }
853
854    boolean writeStateToFileLocked(File file) {
855        FileOutputStream stream = null;
856        int N;
857
858        try {
859            stream = new FileOutputStream(file, false);
860            XmlSerializer out = new FastXmlSerializer();
861            out.setOutput(stream, "utf-8");
862            out.startDocument(null, true);
863
864
865            out.startTag(null, "gs");
866
867            int providerIndex = 0;
868            N = mInstalledProviders.size();
869            for (int i=0; i<N; i++) {
870                Provider p = mInstalledProviders.get(i);
871                if (p.instances.size() > 0) {
872                    out.startTag(null, "p");
873                    out.attribute(null, "pkg", p.info.provider.getPackageName());
874                    out.attribute(null, "cl", p.info.provider.getClassName());
875                    out.endTag(null, "p");
876                    p.tag = providerIndex;
877                    providerIndex++;
878                }
879            }
880
881            N = mHosts.size();
882            for (int i=0; i<N; i++) {
883                Host host = mHosts.get(i);
884                out.startTag(null, "h");
885                out.attribute(null, "pkg", host.packageName);
886                out.attribute(null, "id", Integer.toHexString(host.hostId));
887                out.endTag(null, "h");
888                host.tag = i;
889            }
890
891            N = mAppWidgetIds.size();
892            for (int i=0; i<N; i++) {
893                AppWidgetId id = mAppWidgetIds.get(i);
894                out.startTag(null, "g");
895                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
896                out.attribute(null, "h", Integer.toHexString(id.host.tag));
897                if (id.provider != null) {
898                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
899                }
900                out.endTag(null, "g");
901            }
902
903            out.endTag(null, "gs");
904
905            out.endDocument();
906            stream.close();
907            return true;
908        } catch (IOException e) {
909            try {
910                if (stream != null) {
911                    stream.close();
912                }
913            } catch (IOException ex) {
914                // Ignore
915            }
916            if (file.exists()) {
917                //noinspection ResultOfMethodCallIgnored
918                file.delete();
919            }
920            return false;
921        }
922    }
923
924    void readStateFromFileLocked(File file) {
925        FileInputStream stream = null;
926
927        boolean success = false;
928
929        try {
930            stream = new FileInputStream(file);
931            XmlPullParser parser = Xml.newPullParser();
932            parser.setInput(stream, null);
933
934            int type;
935            int providerIndex = 0;
936            HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
937            do {
938                type = parser.next();
939                if (type == XmlPullParser.START_TAG) {
940                    String tag = parser.getName();
941                    if ("p".equals(tag)) {
942                        // TODO: do we need to check that this package has the same signature
943                        // as before?
944                        String pkg = parser.getAttributeValue(null, "pkg");
945                        String cl = parser.getAttributeValue(null, "cl");
946
947                        final PackageManager packageManager = mContext.getPackageManager();
948                        try {
949                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
950                        } catch (PackageManager.NameNotFoundException e) {
951                            String[] pkgs = packageManager.currentToCanonicalPackageNames(
952                                    new String[] { pkg });
953                            pkg = pkgs[0];
954                        }
955
956                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
957                        if (p == null && mSafeMode) {
958                            // if we're in safe mode, make a temporary one
959                            p = new Provider();
960                            p.info = new AppWidgetProviderInfo();
961                            p.info.provider = new ComponentName(pkg, cl);
962                            p.zombie = true;
963                            mInstalledProviders.add(p);
964                        }
965                        if (p != null) {
966                            // if it wasn't uninstalled or something
967                            loadedProviders.put(providerIndex, p);
968                        }
969                        providerIndex++;
970                    }
971                    else if ("h".equals(tag)) {
972                        Host host = new Host();
973
974                        // TODO: do we need to check that this package has the same signature
975                        // as before?
976                        host.packageName = parser.getAttributeValue(null, "pkg");
977                        try {
978                            host.uid = getUidForPackage(host.packageName);
979                        } catch (PackageManager.NameNotFoundException ex) {
980                            host.zombie = true;
981                        }
982                        if (!host.zombie || mSafeMode) {
983                            // In safe mode, we don't discard the hosts we don't recognize
984                            // so that they're not pruned from our list.  Otherwise, we do.
985                            host.hostId = Integer.parseInt(
986                                    parser.getAttributeValue(null, "id"), 16);
987                            mHosts.add(host);
988                        }
989                    }
990                    else if ("g".equals(tag)) {
991                        AppWidgetId id = new AppWidgetId();
992                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
993                        if (id.appWidgetId >= mNextAppWidgetId) {
994                            mNextAppWidgetId = id.appWidgetId + 1;
995                        }
996
997                        String providerString = parser.getAttributeValue(null, "p");
998                        if (providerString != null) {
999                            // there's no provider if it hasn't been bound yet.
1000                            // maybe we don't have to save this, but it brings the system
1001                            // to the state it was in.
1002                            int pIndex = Integer.parseInt(providerString, 16);
1003                            id.provider = loadedProviders.get(pIndex);
1004                            if (false) {
1005                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1006                                        + pIndex + " which is " + id.provider);
1007                            }
1008                            if (id.provider == null) {
1009                                // This provider is gone.  We just let the host figure out
1010                                // that this happened when it fails to load it.
1011                                continue;
1012                            }
1013                        }
1014
1015                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1016                        id.host = mHosts.get(hIndex);
1017                        if (id.host == null) {
1018                            // This host is gone.
1019                            continue;
1020                        }
1021
1022                        if (id.provider != null) {
1023                            id.provider.instances.add(id);
1024                        }
1025                        id.host.instances.add(id);
1026                        mAppWidgetIds.add(id);
1027                    }
1028                }
1029            } while (type != XmlPullParser.END_DOCUMENT);
1030            success = true;
1031        } catch (NullPointerException e) {
1032            Slog.w(TAG, "failed parsing " + file, e);
1033        } catch (NumberFormatException e) {
1034            Slog.w(TAG, "failed parsing " + file, e);
1035        } catch (XmlPullParserException e) {
1036            Slog.w(TAG, "failed parsing " + file, e);
1037        } catch (IOException e) {
1038            Slog.w(TAG, "failed parsing " + file, e);
1039        } catch (IndexOutOfBoundsException e) {
1040            Slog.w(TAG, "failed parsing " + file, e);
1041        }
1042        try {
1043            if (stream != null) {
1044                stream.close();
1045            }
1046        } catch (IOException e) {
1047            // Ignore
1048        }
1049
1050        if (success) {
1051            // delete any hosts that didn't manage to get connected (should happen)
1052            // if it matters, they'll be reconnected.
1053            for (int i=mHosts.size()-1; i>=0; i--) {
1054                pruneHostLocked(mHosts.get(i));
1055            }
1056        } else {
1057            // failed reading, clean up
1058            mAppWidgetIds.clear();
1059            mHosts.clear();
1060            final int N = mInstalledProviders.size();
1061            for (int i=0; i<N; i++) {
1062                mInstalledProviders.get(i).instances.clear();
1063            }
1064        }
1065    }
1066
1067    File savedStateTempFile() {
1068        return new File("/data/system/" + SETTINGS_TMP_FILENAME);
1069        //return new File(mContext.getFilesDir(), SETTINGS_FILENAME);
1070    }
1071
1072    File savedStateRealFile() {
1073        return new File("/data/system/" + SETTINGS_FILENAME);
1074        //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME);
1075    }
1076
1077    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1078        public void onReceive(Context context, Intent intent) {
1079            String action = intent.getAction();
1080            //Slog.d(TAG, "received " + action);
1081            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
1082                sendInitialBroadcasts();
1083            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1084                Locale revised = Locale.getDefault();
1085                if (revised == null || mLocale == null ||
1086                    !(revised.equals(mLocale))) {
1087                    mLocale = revised;
1088
1089                    synchronized (mAppWidgetIds) {
1090                        int N = mInstalledProviders.size();
1091                        for (int i=N-1; i>=0; i--) {
1092                            Provider p = mInstalledProviders.get(i);
1093                            String pkgName = p.info.provider.getPackageName();
1094                            updateProvidersForPackageLocked(pkgName);
1095                        }
1096                        saveStateLocked();
1097                    }
1098                }
1099            } else {
1100                boolean added = false;
1101                String pkgList[] = null;
1102                if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
1103                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1104                    added = true;
1105                } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
1106                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1107                    added = false;
1108                } else  {
1109                    Uri uri = intent.getData();
1110                    if (uri == null) {
1111                        return;
1112                    }
1113                    String pkgName = uri.getSchemeSpecificPart();
1114                    if (pkgName == null) {
1115                        return;
1116                    }
1117                    pkgList = new String[] { pkgName };
1118                    added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1119                }
1120                if (pkgList == null || pkgList.length == 0) {
1121                    return;
1122                }
1123                if (added) {
1124                    synchronized (mAppWidgetIds) {
1125                        Bundle extras = intent.getExtras();
1126                        if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1127                            for (String pkgName : pkgList) {
1128                                // The package was just upgraded
1129                                updateProvidersForPackageLocked(pkgName);
1130                            }
1131                        } else {
1132                            // The package was just added
1133                            for (String pkgName : pkgList) {
1134                                addProvidersForPackageLocked(pkgName);
1135                            }
1136                        }
1137                        saveStateLocked();
1138                    }
1139                } else {
1140                    Bundle extras = intent.getExtras();
1141                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
1142                        // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
1143                    } else {
1144                        synchronized (mAppWidgetIds) {
1145                            for (String pkgName : pkgList) {
1146                                removeProvidersForPackageLocked(pkgName);
1147                                saveStateLocked();
1148                            }
1149                        }
1150                    }
1151                }
1152            }
1153        }
1154    };
1155
1156    void addProvidersForPackageLocked(String pkgName) {
1157        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1158        intent.setPackage(pkgName);
1159        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
1160                PackageManager.GET_META_DATA);
1161
1162        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1163        for (int i=0; i<N; i++) {
1164            ResolveInfo ri = broadcastReceivers.get(i);
1165            ActivityInfo ai = ri.activityInfo;
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 (pkgName.equals(ai.packageName)) {
1186                ComponentName component = new ComponentName(ai.packageName, ai.name);
1187                Provider p = lookupProviderLocked(component);
1188                if (p == null) {
1189                    if (addProviderLocked(ri)) {
1190                        keep.add(ai.name);
1191                    }
1192                } else {
1193                    Provider parsed = parseProviderInfoXml(component, ri);
1194                    if (parsed != null) {
1195                        keep.add(ai.name);
1196                        // Use the new AppWidgetProviderInfo.
1197                        p.info = parsed.info;
1198                        // If it's enabled
1199                        final int M = p.instances.size();
1200                        if (M > 0) {
1201                            int[] appWidgetIds = getAppWidgetIds(p);
1202                            // Reschedule for the new updatePeriodMillis (don't worry about handling
1203                            // it specially if updatePeriodMillis didn't change because we just sent
1204                            // an update, and the next one will be updatePeriodMillis from now).
1205                            cancelBroadcasts(p);
1206                            registerForBroadcastsLocked(p, appWidgetIds);
1207                            // If it's currently showing, call back with the new AppWidgetProviderInfo.
1208                            for (int j=0; j<M; j++) {
1209                                AppWidgetId id = p.instances.get(j);
1210                                id.views = null;
1211                                if (id.host != null && id.host.callbacks != null) {
1212                                    try {
1213                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1214                                    } catch (RemoteException ex) {
1215                                        // It failed; remove the callback. No need to prune because
1216                                        // we know that this host is still referenced by this
1217                                        // instance.
1218                                        id.host.callbacks = null;
1219                                    }
1220                                }
1221                            }
1222                            // Now that we've told the host, push out an update.
1223                            sendUpdateIntentLocked(p, appWidgetIds);
1224                        }
1225                    }
1226                }
1227            }
1228        }
1229
1230        // prune the ones we don't want to keep
1231        N = mInstalledProviders.size();
1232        for (int i=N-1; i>=0; i--) {
1233            Provider p = mInstalledProviders.get(i);
1234            if (pkgName.equals(p.info.provider.getPackageName())
1235                    && !keep.contains(p.info.provider.getClassName())) {
1236                removeProviderLocked(i, p);
1237            }
1238        }
1239    }
1240
1241    void removeProvidersForPackageLocked(String pkgName) {
1242        int N = mInstalledProviders.size();
1243        for (int i=N-1; i>=0; i--) {
1244            Provider p = mInstalledProviders.get(i);
1245            if (pkgName.equals(p.info.provider.getPackageName())) {
1246                removeProviderLocked(i, p);
1247            }
1248        }
1249
1250        // Delete the hosts for this package too
1251        //
1252        // By now, we have removed any AppWidgets that were in any hosts here,
1253        // so we don't need to worry about sending DISABLE broadcasts to them.
1254        N = mHosts.size();
1255        for (int i=N-1; i>=0; i--) {
1256            Host host = mHosts.get(i);
1257            if (pkgName.equals(host.packageName)) {
1258                deleteHostLocked(host);
1259            }
1260        }
1261    }
1262}
1263
1264