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