AppWidgetServiceImpl.java revision a3195057fc5874d158cc8ea053aa75d5e016bdcf
1/*
2 * Copyright (C) 2011 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.AppGlobals;
21import android.app.PendingIntent;
22import android.appwidget.AppWidgetManager;
23import android.appwidget.AppWidgetProviderInfo;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.Intent.FilterComparison;
28import android.content.ServiceConnection;
29import android.content.pm.ActivityInfo;
30import android.content.pm.ApplicationInfo;
31import android.content.pm.IPackageManager;
32import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
35import android.content.pm.ServiceInfo;
36import android.content.res.Resources;
37import android.content.res.TypedArray;
38import android.content.res.XmlResourceParser;
39import android.net.Uri;
40import android.os.Binder;
41import android.os.Bundle;
42import android.os.IBinder;
43import android.os.RemoteException;
44import android.os.SystemClock;
45import android.os.UserId;
46import android.util.AttributeSet;
47import android.util.Log;
48import android.util.Pair;
49import android.util.Slog;
50import android.util.TypedValue;
51import android.util.Xml;
52import android.view.WindowManager;
53import android.widget.RemoteViews;
54
55import com.android.internal.appwidget.IAppWidgetHost;
56import com.android.internal.os.AtomicFile;
57import com.android.internal.util.FastXmlSerializer;
58import com.android.internal.widget.IRemoteViewsAdapterConnection;
59import com.android.internal.widget.IRemoteViewsFactory;
60
61import org.xmlpull.v1.XmlPullParser;
62import org.xmlpull.v1.XmlPullParserException;
63import org.xmlpull.v1.XmlSerializer;
64
65import java.io.File;
66import java.io.FileDescriptor;
67import java.io.FileInputStream;
68import java.io.FileNotFoundException;
69import java.io.FileOutputStream;
70import java.io.IOException;
71import java.io.PrintWriter;
72import java.util.ArrayList;
73import java.util.HashMap;
74import java.util.HashSet;
75import java.util.Iterator;
76import java.util.List;
77import java.util.Locale;
78import java.util.Set;
79
80class AppWidgetServiceImpl {
81
82    private static final String TAG = "AppWidgetServiceImpl";
83    private static final String SETTINGS_FILENAME = "appwidgets.xml";
84    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
85
86    /*
87     * When identifying a Host or Provider based on the calling process, use the uid field. When
88     * identifying a Host or Provider based on a package manager broadcast, use the package given.
89     */
90
91    static class Provider {
92        int uid;
93        AppWidgetProviderInfo info;
94        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
95        PendingIntent broadcast;
96        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
97
98        int tag; // for use while saving state (the index)
99    }
100
101    static class Host {
102        int uid;
103        int hostId;
104        String packageName;
105        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
106        IAppWidgetHost callbacks;
107        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
108
109        int tag; // for use while saving state (the index)
110    }
111
112    static class AppWidgetId {
113        int appWidgetId;
114        Provider provider;
115        RemoteViews views;
116        Bundle options;
117        Host host;
118    }
119
120    /**
121     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
122     * needs to be a static inner class since a reference to the ServiceConnection is held globally
123     * and may lead us to leak AppWidgetService instances (if there were more than one).
124     */
125    static class ServiceConnectionProxy implements ServiceConnection {
126        private final IBinder mConnectionCb;
127
128        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
129            mConnectionCb = connectionCb;
130        }
131
132        public void onServiceConnected(ComponentName name, IBinder service) {
133            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
134                    .asInterface(mConnectionCb);
135            try {
136                cb.onServiceConnected(service);
137            } catch (Exception e) {
138                e.printStackTrace();
139            }
140        }
141
142        public void onServiceDisconnected(ComponentName name) {
143            disconnect();
144        }
145
146        public void disconnect() {
147            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
148                    .asInterface(mConnectionCb);
149            try {
150                cb.onServiceDisconnected();
151            } catch (Exception e) {
152                e.printStackTrace();
153            }
154        }
155    }
156
157    // Manages active connections to RemoteViewsServices
158    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
159    // Manages persistent references to RemoteViewsServices from different App Widgets
160    private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
161
162    Context mContext;
163    Locale mLocale;
164    IPackageManager mPm;
165    AlarmManager mAlarmManager;
166    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
167    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
168    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
169    ArrayList<Host> mHosts = new ArrayList<Host>();
170    // set of package names
171    HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
172    boolean mSafeMode;
173    int mUserId;
174    boolean mStateLoaded;
175    int mMaxWidgetBitmapMemory;
176
177    // These are for debugging only -- widgets are going missing in some rare instances
178    ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
179    ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
180
181    AppWidgetServiceImpl(Context context, int userId) {
182        mContext = context;
183        mPm = AppGlobals.getPackageManager();
184        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
185        mUserId = userId;
186        computeMaximumWidgetBitmapMemory();
187    }
188
189    void computeMaximumWidgetBitmapMemory() {
190        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
191        int height = wm.getDefaultDisplay().getRawHeight();
192        int width = wm.getDefaultDisplay().getRawWidth();
193        mMaxWidgetBitmapMemory = 4 * width * height;
194    }
195
196    public void systemReady(boolean safeMode) {
197        mSafeMode = safeMode;
198
199        synchronized (mAppWidgetIds) {
200            ensureStateLoadedLocked();
201        }
202    }
203
204    void onConfigurationChanged() {
205        Locale revised = Locale.getDefault();
206        if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
207            mLocale = revised;
208
209            synchronized (mAppWidgetIds) {
210                ensureStateLoadedLocked();
211                // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
212                // list of installed providers and skip providers that we don't need to update.
213                // Also note that remove the provider does not clear the Provider component data.
214                ArrayList<Provider> installedProviders =
215                        new ArrayList<Provider>(mInstalledProviders);
216                HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
217                int N = installedProviders.size();
218                for (int i = N - 1; i >= 0; i--) {
219                    Provider p = installedProviders.get(i);
220                    ComponentName cn = p.info.provider;
221                    if (!removedProviders.contains(cn)) {
222                        updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
223                    }
224                }
225                saveStateLocked();
226            }
227        }
228    }
229
230    void onBroadcastReceived(Intent intent) {
231        final String action = intent.getAction();
232        boolean added = false;
233        boolean changed = false;
234        boolean providersModified = false;
235        String pkgList[] = null;
236        if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
237            pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
238            added = true;
239        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
240            pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
241            added = false;
242        } else {
243            Uri uri = intent.getData();
244            if (uri == null) {
245                return;
246            }
247            String pkgName = uri.getSchemeSpecificPart();
248            if (pkgName == null) {
249                return;
250            }
251            pkgList = new String[] { pkgName };
252            added = Intent.ACTION_PACKAGE_ADDED.equals(action);
253            changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
254        }
255        if (pkgList == null || pkgList.length == 0) {
256            return;
257        }
258        if (added || changed) {
259            synchronized (mAppWidgetIds) {
260                ensureStateLoadedLocked();
261                Bundle extras = intent.getExtras();
262                if (changed
263                        || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
264                    for (String pkgName : pkgList) {
265                        // The package was just upgraded
266                        providersModified |= updateProvidersForPackageLocked(pkgName, null);
267                    }
268                } else {
269                    // The package was just added
270                    for (String pkgName : pkgList) {
271                        providersModified |= addProvidersForPackageLocked(pkgName);
272                    }
273                }
274                saveStateLocked();
275            }
276        } else {
277            Bundle extras = intent.getExtras();
278            if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
279                // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
280            } else {
281                synchronized (mAppWidgetIds) {
282                    ensureStateLoadedLocked();
283                    for (String pkgName : pkgList) {
284                        providersModified |= removeProvidersForPackageLocked(pkgName);
285                        saveStateLocked();
286                    }
287                }
288            }
289        }
290
291        if (providersModified) {
292            // If the set of providers has been modified, notify each active AppWidgetHost
293            synchronized (mAppWidgetIds) {
294                ensureStateLoadedLocked();
295                notifyHostsForProvidersChangedLocked();
296            }
297        }
298    }
299
300    private void dumpProvider(Provider p, int index, PrintWriter pw) {
301        AppWidgetProviderInfo info = p.info;
302        pw.print("  ["); pw.print(index); pw.print("] provider ");
303                pw.print(info.provider.flattenToShortString());
304                pw.println(':');
305        pw.print("    min=("); pw.print(info.minWidth);
306                pw.print("x"); pw.print(info.minHeight);
307        pw.print(")   minResize=("); pw.print(info.minResizeWidth);
308                pw.print("x"); pw.print(info.minResizeHeight);
309                pw.print(") updatePeriodMillis=");
310                pw.print(info.updatePeriodMillis);
311                pw.print(" resizeMode=");
312                pw.print(info.resizeMode);
313                pw.print(" autoAdvanceViewId=");
314                pw.print(info.autoAdvanceViewId);
315                pw.print(" initialLayout=#");
316                pw.print(Integer.toHexString(info.initialLayout));
317                pw.print(" zombie="); pw.println(p.zombie);
318    }
319
320    private void dumpHost(Host host, int index, PrintWriter pw) {
321        pw.print("  ["); pw.print(index); pw.print("] hostId=");
322                pw.print(host.hostId); pw.print(' ');
323                pw.print(host.packageName); pw.print('/');
324        pw.print(host.uid); pw.println(':');
325        pw.print("    callbacks="); pw.println(host.callbacks);
326        pw.print("    instances.size="); pw.print(host.instances.size());
327                pw.print(" zombie="); pw.println(host.zombie);
328    }
329
330    private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
331        pw.print("  ["); pw.print(index); pw.print("] id=");
332                pw.println(id.appWidgetId);
333        pw.print("    hostId=");
334                pw.print(id.host.hostId); pw.print(' ');
335                pw.print(id.host.packageName); pw.print('/');
336                pw.println(id.host.uid);
337        if (id.provider != null) {
338            pw.print("    provider=");
339                    pw.println(id.provider.info.provider.flattenToShortString());
340        }
341        if (id.host != null) {
342            pw.print("    host.callbacks="); pw.println(id.host.callbacks);
343        }
344        if (id.views != null) {
345            pw.print("    views="); pw.println(id.views);
346        }
347    }
348
349    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
350        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
351                != PackageManager.PERMISSION_GRANTED) {
352            pw.println("Permission Denial: can't dump from from pid="
353                    + Binder.getCallingPid()
354                    + ", uid=" + Binder.getCallingUid());
355            return;
356        }
357
358        synchronized (mAppWidgetIds) {
359            int N = mInstalledProviders.size();
360            pw.println("Providers:");
361            for (int i=0; i<N; i++) {
362                dumpProvider(mInstalledProviders.get(i), i, pw);
363            }
364
365            N = mAppWidgetIds.size();
366            pw.println(" ");
367            pw.println("AppWidgetIds:");
368            for (int i=0; i<N; i++) {
369                dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
370            }
371
372            N = mHosts.size();
373            pw.println(" ");
374            pw.println("Hosts:");
375            for (int i=0; i<N; i++) {
376                dumpHost(mHosts.get(i), i, pw);
377            }
378
379            N = mDeletedProviders.size();
380            pw.println(" ");
381            pw.println("Deleted Providers:");
382            for (int i=0; i<N; i++) {
383                dumpProvider(mDeletedProviders.get(i), i, pw);
384            }
385
386            N = mDeletedHosts.size();
387            pw.println(" ");
388            pw.println("Deleted Hosts:");
389            for (int i=0; i<N; i++) {
390                dumpHost(mDeletedHosts.get(i), i, pw);
391            }
392        }
393    }
394
395    private void ensureStateLoadedLocked() {
396        if (!mStateLoaded) {
397            loadAppWidgetList();
398            loadStateLocked();
399            mStateLoaded = true;
400        }
401    }
402
403    public int allocateAppWidgetId(String packageName, int hostId) {
404        int callingUid = enforceCallingUid(packageName);
405        synchronized (mAppWidgetIds) {
406            ensureStateLoadedLocked();
407            int appWidgetId = mNextAppWidgetId++;
408
409            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
410
411            AppWidgetId id = new AppWidgetId();
412            id.appWidgetId = appWidgetId;
413            id.host = host;
414
415            host.instances.add(id);
416            mAppWidgetIds.add(id);
417
418            saveStateLocked();
419
420            return appWidgetId;
421        }
422    }
423
424    public void deleteAppWidgetId(int appWidgetId) {
425        synchronized (mAppWidgetIds) {
426            ensureStateLoadedLocked();
427            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
428            if (id != null) {
429                deleteAppWidgetLocked(id);
430                saveStateLocked();
431            }
432        }
433    }
434
435    public void deleteHost(int hostId) {
436        synchronized (mAppWidgetIds) {
437            ensureStateLoadedLocked();
438            int callingUid = Binder.getCallingUid();
439            Host host = lookupHostLocked(callingUid, hostId);
440            if (host != null) {
441                deleteHostLocked(host);
442                saveStateLocked();
443            }
444        }
445    }
446
447    public void deleteAllHosts() {
448        synchronized (mAppWidgetIds) {
449            ensureStateLoadedLocked();
450            int callingUid = Binder.getCallingUid();
451            final int N = mHosts.size();
452            boolean changed = false;
453            for (int i = N - 1; i >= 0; i--) {
454                Host host = mHosts.get(i);
455                if (host.uid == callingUid) {
456                    deleteHostLocked(host);
457                    changed = true;
458                }
459            }
460            if (changed) {
461                saveStateLocked();
462            }
463        }
464    }
465
466    void deleteHostLocked(Host host) {
467        final int N = host.instances.size();
468        for (int i = N - 1; i >= 0; i--) {
469            AppWidgetId id = host.instances.get(i);
470            deleteAppWidgetLocked(id);
471        }
472        host.instances.clear();
473        mHosts.remove(host);
474        mDeletedHosts.add(host);
475        // it's gone or going away, abruptly drop the callback connection
476        host.callbacks = null;
477    }
478
479    void deleteAppWidgetLocked(AppWidgetId id) {
480        // We first unbind all services that are bound to this id
481        unbindAppWidgetRemoteViewsServicesLocked(id);
482
483        Host host = id.host;
484        host.instances.remove(id);
485        pruneHostLocked(host);
486
487        mAppWidgetIds.remove(id);
488
489        Provider p = id.provider;
490        if (p != null) {
491            p.instances.remove(id);
492            if (!p.zombie) {
493                // send the broacast saying that this appWidgetId has been deleted
494                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
495                intent.setComponent(p.info.provider);
496                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
497                mContext.sendBroadcast(intent, mUserId);
498                if (p.instances.size() == 0) {
499                    // cancel the future updates
500                    cancelBroadcasts(p);
501
502                    // send the broacast saying that the provider is not in use any more
503                    intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
504                    intent.setComponent(p.info.provider);
505                    mContext.sendBroadcast(intent, mUserId);
506                }
507            }
508        }
509    }
510
511    void cancelBroadcasts(Provider p) {
512        if (p.broadcast != null) {
513            mAlarmManager.cancel(p.broadcast);
514            long token = Binder.clearCallingIdentity();
515            try {
516                p.broadcast.cancel();
517            } finally {
518                Binder.restoreCallingIdentity(token);
519            }
520            p.broadcast = null;
521        }
522    }
523
524    private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider) {
525        final long ident = Binder.clearCallingIdentity();
526        try {
527            synchronized (mAppWidgetIds) {
528                ensureStateLoadedLocked();
529                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
530                if (id == null) {
531                    throw new IllegalArgumentException("bad appWidgetId");
532                }
533                if (id.provider != null) {
534                    throw new IllegalArgumentException("appWidgetId " + appWidgetId
535                            + " already bound to " + id.provider.info.provider);
536                }
537                Provider p = lookupProviderLocked(provider);
538                if (p == null) {
539                    throw new IllegalArgumentException("not a appwidget provider: " + provider);
540                }
541                if (p.zombie) {
542                    throw new IllegalArgumentException("can't bind to a 3rd party provider in"
543                            + " safe mode: " + provider);
544                }
545
546                id.provider = p;
547                p.instances.add(id);
548                int instancesSize = p.instances.size();
549                if (instancesSize == 1) {
550                    // tell the provider that it's ready
551                    sendEnableIntentLocked(p);
552                }
553
554                // send an update now -- We need this update now, and just for this appWidgetId.
555                // It's less critical when the next one happens, so when we schedule the next one,
556                // we add updatePeriodMillis to its start time. That time will have some slop,
557                // but that's okay.
558                sendUpdateIntentLocked(p, new int[] { appWidgetId });
559
560                // schedule the future updates
561                registerForBroadcastsLocked(p, getAppWidgetIds(p));
562                saveStateLocked();
563            }
564        } finally {
565            Binder.restoreCallingIdentity(ident);
566        }
567    }
568
569    public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
570        mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
571            "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
572        bindAppWidgetIdImpl(appWidgetId, provider);
573    }
574
575    public boolean bindAppWidgetIdIfAllowed(
576            String packageName, int appWidgetId, ComponentName provider) {
577        try {
578            mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
579        } catch (SecurityException se) {
580            if (!callerHasBindAppWidgetPermission(packageName)) {
581                return false;
582            }
583        }
584        bindAppWidgetIdImpl(appWidgetId, provider);
585        return true;
586    }
587
588    private boolean callerHasBindAppWidgetPermission(String packageName) {
589        int callingUid = Binder.getCallingUid();
590        try {
591            if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) {
592                return false;
593            }
594        } catch (Exception e) {
595            return false;
596        }
597        synchronized (mAppWidgetIds) {
598            ensureStateLoadedLocked();
599            return mPackagesWithBindWidgetPermission.contains(packageName);
600        }
601    }
602
603    public boolean hasBindAppWidgetPermission(String packageName) {
604        mContext.enforceCallingPermission(
605                android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
606                "hasBindAppWidgetPermission packageName=" + packageName);
607
608        synchronized (mAppWidgetIds) {
609            ensureStateLoadedLocked();
610            return mPackagesWithBindWidgetPermission.contains(packageName);
611        }
612    }
613
614    public void setBindAppWidgetPermission(String packageName, boolean permission) {
615        mContext.enforceCallingPermission(
616                android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
617                "setBindAppWidgetPermission packageName=" + packageName);
618
619        synchronized (mAppWidgetIds) {
620            ensureStateLoadedLocked();
621            if (permission) {
622                mPackagesWithBindWidgetPermission.add(packageName);
623            } else {
624                mPackagesWithBindWidgetPermission.remove(packageName);
625            }
626        }
627        saveStateLocked();
628    }
629
630    // Binds to a specific RemoteViewsService
631    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
632        synchronized (mAppWidgetIds) {
633            ensureStateLoadedLocked();
634            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
635            if (id == null) {
636                throw new IllegalArgumentException("bad appWidgetId");
637            }
638            final ComponentName componentName = intent.getComponent();
639            try {
640                final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
641                        PackageManager.GET_PERMISSIONS);
642                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
643                    throw new SecurityException("Selected service does not require "
644                            + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
645                }
646            } catch (PackageManager.NameNotFoundException e) {
647                throw new IllegalArgumentException("Unknown component " + componentName);
648            }
649
650            // If there is already a connection made for this service intent, then disconnect from
651            // that first. (This does not allow multiple connections to the same service under
652            // the same key)
653            ServiceConnectionProxy conn = null;
654            FilterComparison fc = new FilterComparison(intent);
655            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
656            if (mBoundRemoteViewsServices.containsKey(key)) {
657                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
658                conn.disconnect();
659                mContext.unbindService(conn);
660                mBoundRemoteViewsServices.remove(key);
661            }
662
663            int userId = UserId.getUserId(id.provider.uid);
664            // Bind to the RemoteViewsService (which will trigger a callback to the
665            // RemoteViewsAdapter.onServiceConnected())
666            final long token = Binder.clearCallingIdentity();
667            try {
668                conn = new ServiceConnectionProxy(key, connection);
669                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
670                mBoundRemoteViewsServices.put(key, conn);
671            } finally {
672                Binder.restoreCallingIdentity(token);
673            }
674
675            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
676            // when we can call back to the RemoteViewsService later to destroy associated
677            // factories.
678            incrementAppWidgetServiceRefCount(appWidgetId, fc);
679        }
680    }
681
682    // Unbinds from a specific RemoteViewsService
683    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
684        synchronized (mAppWidgetIds) {
685            ensureStateLoadedLocked();
686            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
687            // RemoteViewsAdapter)
688            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
689                    intent));
690            if (mBoundRemoteViewsServices.containsKey(key)) {
691                // We don't need to use the appWidgetId until after we are sure there is something
692                // to unbind. Note that this may mask certain issues with apps calling unbind()
693                // more than necessary.
694                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
695                if (id == null) {
696                    throw new IllegalArgumentException("bad appWidgetId");
697                }
698
699                ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
700                        .get(key);
701                conn.disconnect();
702                mContext.unbindService(conn);
703                mBoundRemoteViewsServices.remove(key);
704            } else {
705                Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
706            }
707        }
708    }
709
710    // Unbinds from a RemoteViewsService when we delete an app widget
711    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
712        int appWidgetId = id.appWidgetId;
713        // Unbind all connections to Services bound to this AppWidgetId
714        Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
715                .iterator();
716        while (it.hasNext()) {
717            final Pair<Integer, Intent.FilterComparison> key = it.next();
718            if (key.first.intValue() == appWidgetId) {
719                final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
720                        .get(key);
721                conn.disconnect();
722                mContext.unbindService(conn);
723                it.remove();
724            }
725        }
726
727        // Check if we need to destroy any services (if no other app widgets are
728        // referencing the same service)
729        decrementAppWidgetServiceRefCount(id);
730    }
731
732    // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
733    private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
734        final ServiceConnection conn = new ServiceConnection() {
735            @Override
736            public void onServiceConnected(ComponentName name, IBinder service) {
737                final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
738                try {
739                    cb.onDestroy(intent);
740                } catch (RemoteException e) {
741                    e.printStackTrace();
742                } catch (RuntimeException e) {
743                    e.printStackTrace();
744                }
745                mContext.unbindService(this);
746            }
747
748            @Override
749            public void onServiceDisconnected(android.content.ComponentName name) {
750                // Do nothing
751            }
752        };
753
754        int userId = UserId.getUserId(id.provider.uid);
755        // Bind to the service and remove the static intent->factory mapping in the
756        // RemoteViewsService.
757        final long token = Binder.clearCallingIdentity();
758        try {
759            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
760        } finally {
761            Binder.restoreCallingIdentity(token);
762        }
763    }
764
765    // Adds to the ref-count for a given RemoteViewsService intent
766    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
767        HashSet<Integer> appWidgetIds = null;
768        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
769            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
770        } else {
771            appWidgetIds = new HashSet<Integer>();
772            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
773        }
774        appWidgetIds.add(appWidgetId);
775    }
776
777    // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
778    // the ref-count reaches zero.
779    private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
780        Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
781        while (it.hasNext()) {
782            final FilterComparison key = it.next();
783            final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
784            if (ids.remove(id.appWidgetId)) {
785                // If we have removed the last app widget referencing this service, then we
786                // should destroy it and remove it from this set
787                if (ids.isEmpty()) {
788                    destroyRemoteViewsService(key.getIntent(), id);
789                    it.remove();
790                }
791            }
792        }
793    }
794
795    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
796        synchronized (mAppWidgetIds) {
797            ensureStateLoadedLocked();
798            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
799            if (id != null && id.provider != null && !id.provider.zombie) {
800                return id.provider.info;
801            }
802            return null;
803        }
804    }
805
806    public RemoteViews getAppWidgetViews(int appWidgetId) {
807        synchronized (mAppWidgetIds) {
808            ensureStateLoadedLocked();
809            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
810            if (id != null) {
811                return id.views;
812            }
813            return null;
814        }
815    }
816
817    public List<AppWidgetProviderInfo> getInstalledProviders() {
818        synchronized (mAppWidgetIds) {
819            ensureStateLoadedLocked();
820            final int N = mInstalledProviders.size();
821            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
822            for (int i = 0; i < N; i++) {
823                Provider p = mInstalledProviders.get(i);
824                if (!p.zombie) {
825                    result.add(p.info);
826                }
827            }
828            return result;
829        }
830    }
831
832    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
833        if (appWidgetIds == null) {
834            return;
835        }
836
837        int bitmapMemoryUsage = views.estimateMemoryUsage();
838        if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
839            throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
840                    " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
841                    mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
842                    " fill the device's screen once.");
843        }
844
845        if (appWidgetIds.length == 0) {
846            return;
847        }
848        final int N = appWidgetIds.length;
849
850        synchronized (mAppWidgetIds) {
851            ensureStateLoadedLocked();
852            for (int i = 0; i < N; i++) {
853                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
854                updateAppWidgetInstanceLocked(id, views);
855            }
856        }
857    }
858
859    public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
860        synchronized (mAppWidgetIds) {
861            ensureStateLoadedLocked();
862            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
863
864            if (id == null) {
865                return;
866            }
867            Provider p = id.provider;
868            id.options = options;
869
870            // send the broacast saying that this appWidgetId has been deleted
871            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
872            intent.setComponent(p.info.provider);
873            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
874            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
875            mContext.sendBroadcast(intent, mUserId);
876        }
877    }
878
879    public Bundle getAppWidgetOptions(int appWidgetId) {
880        synchronized (mAppWidgetIds) {
881            ensureStateLoadedLocked();
882            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
883            if (id != null && id.options != null) {
884                return id.options;
885            } else {
886                return Bundle.EMPTY;
887            }
888        }
889    }
890
891    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
892        if (appWidgetIds == null) {
893            return;
894        }
895        if (appWidgetIds.length == 0) {
896            return;
897        }
898        final int N = appWidgetIds.length;
899
900        synchronized (mAppWidgetIds) {
901            ensureStateLoadedLocked();
902            for (int i = 0; i < N; i++) {
903                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
904                updateAppWidgetInstanceLocked(id, views, true);
905            }
906        }
907    }
908
909    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
910        if (appWidgetIds == null) {
911            return;
912        }
913        if (appWidgetIds.length == 0) {
914            return;
915        }
916        final int N = appWidgetIds.length;
917
918        synchronized (mAppWidgetIds) {
919            ensureStateLoadedLocked();
920            for (int i = 0; i < N; i++) {
921                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
922                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
923            }
924        }
925    }
926
927    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
928        synchronized (mAppWidgetIds) {
929            ensureStateLoadedLocked();
930            Provider p = lookupProviderLocked(provider);
931            if (p == null) {
932                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
933                return;
934            }
935            ArrayList<AppWidgetId> instances = p.instances;
936            final int callingUid = Binder.getCallingUid();
937            final int N = instances.size();
938            for (int i = 0; i < N; i++) {
939                AppWidgetId id = instances.get(i);
940                if (canAccessAppWidgetId(id, callingUid)) {
941                    updateAppWidgetInstanceLocked(id, views);
942                }
943            }
944        }
945    }
946
947    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
948        updateAppWidgetInstanceLocked(id, views, false);
949    }
950
951    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
952        // allow for stale appWidgetIds and other badness
953        // lookup also checks that the calling process can access the appWidgetId
954        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
955        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
956
957            // We do not want to save this RemoteViews
958            if (!isPartialUpdate)
959                id.views = views;
960
961            // is anyone listening?
962            if (id.host.callbacks != null) {
963                try {
964                    // the lock is held, but this is a oneway call
965                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
966                } catch (RemoteException e) {
967                    // It failed; remove the callback. No need to prune because
968                    // we know that this host is still referenced by this instance.
969                    id.host.callbacks = null;
970                }
971            }
972        }
973    }
974
975    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
976        // allow for stale appWidgetIds and other badness
977        // lookup also checks that the calling process can access the appWidgetId
978        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
979        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
980            // is anyone listening?
981            if (id.host.callbacks != null) {
982                try {
983                    // the lock is held, but this is a oneway call
984                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
985                } catch (RemoteException e) {
986                    // It failed; remove the callback. No need to prune because
987                    // we know that this host is still referenced by this instance.
988                    id.host.callbacks = null;
989                }
990            }
991
992            // If the host is unavailable, then we call the associated
993            // RemoteViewsFactory.onDataSetChanged() directly
994            if (id.host.callbacks == null) {
995                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
996                for (FilterComparison key : keys) {
997                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
998                        Intent intent = key.getIntent();
999
1000                        final ServiceConnection conn = new ServiceConnection() {
1001                            @Override
1002                            public void onServiceConnected(ComponentName name, IBinder service) {
1003                                IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
1004                                        .asInterface(service);
1005                                try {
1006                                    cb.onDataSetChangedAsync();
1007                                } catch (RemoteException e) {
1008                                    e.printStackTrace();
1009                                } catch (RuntimeException e) {
1010                                    e.printStackTrace();
1011                                }
1012                                mContext.unbindService(this);
1013                            }
1014
1015                            @Override
1016                            public void onServiceDisconnected(android.content.ComponentName name) {
1017                                // Do nothing
1018                            }
1019                        };
1020
1021                        int userId = UserId.getUserId(id.provider.uid);
1022                        // Bind to the service and call onDataSetChanged()
1023                        final long token = Binder.clearCallingIdentity();
1024                        try {
1025                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId);
1026                        } finally {
1027                            Binder.restoreCallingIdentity(token);
1028                        }
1029                    }
1030                }
1031            }
1032        }
1033    }
1034
1035    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
1036            List<RemoteViews> updatedViews) {
1037        int callingUid = enforceCallingUid(packageName);
1038        synchronized (mAppWidgetIds) {
1039            ensureStateLoadedLocked();
1040            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
1041            host.callbacks = callbacks;
1042
1043            updatedViews.clear();
1044
1045            ArrayList<AppWidgetId> instances = host.instances;
1046            int N = instances.size();
1047            int[] updatedIds = new int[N];
1048            for (int i = 0; i < N; i++) {
1049                AppWidgetId id = instances.get(i);
1050                updatedIds[i] = id.appWidgetId;
1051                updatedViews.add(id.views);
1052            }
1053            return updatedIds;
1054        }
1055    }
1056
1057    public void stopListening(int hostId) {
1058        synchronized (mAppWidgetIds) {
1059            ensureStateLoadedLocked();
1060            Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
1061            if (host != null) {
1062                host.callbacks = null;
1063                pruneHostLocked(host);
1064            }
1065        }
1066    }
1067
1068    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
1069        if (id.host.uid == callingUid) {
1070            // Apps hosting the AppWidget have access to it.
1071            return true;
1072        }
1073        if (id.provider != null && id.provider.uid == callingUid) {
1074            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
1075            return true;
1076        }
1077        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
1078            // Apps that can bind have access to all appWidgetIds.
1079            return true;
1080        }
1081        // Nobody else can access it.
1082        return false;
1083    }
1084
1085    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
1086        int callingUid = Binder.getCallingUid();
1087        final int N = mAppWidgetIds.size();
1088        for (int i = 0; i < N; i++) {
1089            AppWidgetId id = mAppWidgetIds.get(i);
1090            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
1091                return id;
1092            }
1093        }
1094        return null;
1095    }
1096
1097    Provider lookupProviderLocked(ComponentName provider) {
1098        final int N = mInstalledProviders.size();
1099        for (int i = 0; i < N; i++) {
1100            Provider p = mInstalledProviders.get(i);
1101            if (p.info.provider.equals(provider)) {
1102                return p;
1103            }
1104        }
1105        return null;
1106    }
1107
1108    Host lookupHostLocked(int uid, int hostId) {
1109        final int N = mHosts.size();
1110        for (int i = 0; i < N; i++) {
1111            Host h = mHosts.get(i);
1112            if (h.uid == uid && h.hostId == hostId) {
1113                return h;
1114            }
1115        }
1116        return null;
1117    }
1118
1119    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
1120        final int N = mHosts.size();
1121        for (int i = 0; i < N; i++) {
1122            Host h = mHosts.get(i);
1123            if (h.hostId == hostId && h.packageName.equals(packageName)) {
1124                return h;
1125            }
1126        }
1127        Host host = new Host();
1128        host.packageName = packageName;
1129        host.uid = uid;
1130        host.hostId = hostId;
1131        mHosts.add(host);
1132        return host;
1133    }
1134
1135    void pruneHostLocked(Host host) {
1136        if (host.instances.size() == 0 && host.callbacks == null) {
1137            mHosts.remove(host);
1138        }
1139    }
1140
1141    void loadAppWidgetList() {
1142        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1143        try {
1144            List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
1145                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1146                    PackageManager.GET_META_DATA, mUserId);
1147
1148            final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1149            for (int i = 0; i < N; i++) {
1150                ResolveInfo ri = broadcastReceivers.get(i);
1151                addProviderLocked(ri);
1152            }
1153        } catch (RemoteException re) {
1154            // Shouldn't happen, local call
1155        }
1156    }
1157
1158    boolean addProviderLocked(ResolveInfo ri) {
1159        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1160            return false;
1161        }
1162        if (!ri.activityInfo.isEnabled()) {
1163            return false;
1164        }
1165        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
1166                ri.activityInfo.name), ri);
1167        if (p != null) {
1168            mInstalledProviders.add(p);
1169            return true;
1170        } else {
1171            return false;
1172        }
1173    }
1174
1175    void removeProviderLocked(int index, Provider p) {
1176        int N = p.instances.size();
1177        for (int i = 0; i < N; i++) {
1178            AppWidgetId id = p.instances.get(i);
1179            // Call back with empty RemoteViews
1180            updateAppWidgetInstanceLocked(id, null);
1181            // Stop telling the host about updates for this from now on
1182            cancelBroadcasts(p);
1183            // clear out references to this appWidgetId
1184            id.host.instances.remove(id);
1185            mAppWidgetIds.remove(id);
1186            id.provider = null;
1187            pruneHostLocked(id.host);
1188            id.host = null;
1189        }
1190        p.instances.clear();
1191        mInstalledProviders.remove(index);
1192        mDeletedProviders.add(p);
1193        // no need to send the DISABLE broadcast, since the receiver is gone anyway
1194        cancelBroadcasts(p);
1195    }
1196
1197    void sendEnableIntentLocked(Provider p) {
1198        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
1199        intent.setComponent(p.info.provider);
1200        mContext.sendBroadcast(intent, mUserId);
1201    }
1202
1203    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
1204        if (appWidgetIds != null && appWidgetIds.length > 0) {
1205            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1206            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1207            intent.setComponent(p.info.provider);
1208            mContext.sendBroadcast(intent, mUserId);
1209        }
1210    }
1211
1212    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
1213        if (p.info.updatePeriodMillis > 0) {
1214            // if this is the first instance, set the alarm. otherwise,
1215            // rely on the fact that we've already set it and that
1216            // PendingIntent.getBroadcast will update the extras.
1217            boolean alreadyRegistered = p.broadcast != null;
1218            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1219            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
1220            intent.setComponent(p.info.provider);
1221            long token = Binder.clearCallingIdentity();
1222            try {
1223                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
1224                        PendingIntent.FLAG_UPDATE_CURRENT);
1225            } finally {
1226                Binder.restoreCallingIdentity(token);
1227            }
1228            if (!alreadyRegistered) {
1229                long period = p.info.updatePeriodMillis;
1230                if (period < MIN_UPDATE_PERIOD) {
1231                    period = MIN_UPDATE_PERIOD;
1232                }
1233                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
1234                        .elapsedRealtime()
1235                        + period, period, p.broadcast);
1236            }
1237        }
1238    }
1239
1240    static int[] getAppWidgetIds(Provider p) {
1241        int instancesSize = p.instances.size();
1242        int appWidgetIds[] = new int[instancesSize];
1243        for (int i = 0; i < instancesSize; i++) {
1244            appWidgetIds[i] = p.instances.get(i).appWidgetId;
1245        }
1246        return appWidgetIds;
1247    }
1248
1249    public int[] getAppWidgetIds(ComponentName provider) {
1250        synchronized (mAppWidgetIds) {
1251            ensureStateLoadedLocked();
1252            Provider p = lookupProviderLocked(provider);
1253            if (p != null && Binder.getCallingUid() == p.uid) {
1254                return getAppWidgetIds(p);
1255            } else {
1256                return new int[0];
1257            }
1258        }
1259    }
1260
1261    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
1262        Provider p = null;
1263
1264        ActivityInfo activityInfo = ri.activityInfo;
1265        XmlResourceParser parser = null;
1266        try {
1267            parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(),
1268                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
1269            if (parser == null) {
1270                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
1271                        + " meta-data for " + "AppWidget provider '" + component + '\'');
1272                return null;
1273            }
1274
1275            AttributeSet attrs = Xml.asAttributeSet(parser);
1276
1277            int type;
1278            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1279                    && type != XmlPullParser.START_TAG) {
1280                // drain whitespace, comments, etc.
1281            }
1282
1283            String nodeName = parser.getName();
1284            if (!"appwidget-provider".equals(nodeName)) {
1285                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
1286                        + " AppWidget provider '" + component + '\'');
1287                return null;
1288            }
1289
1290            p = new Provider();
1291            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
1292            info.provider = component;
1293            p.uid = activityInfo.applicationInfo.uid;
1294
1295            Resources res = mContext.getPackageManager()
1296                    .getResourcesForApplication(activityInfo.applicationInfo);
1297
1298            TypedArray sa = res.obtainAttributes(attrs,
1299                    com.android.internal.R.styleable.AppWidgetProviderInfo);
1300
1301            // These dimensions has to be resolved in the application's context.
1302            // We simply send back the raw complex data, which will be
1303            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
1304            TypedValue value = sa
1305                    .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
1306            info.minWidth = value != null ? value.data : 0;
1307            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
1308            info.minHeight = value != null ? value.data : 0;
1309            value = sa.peekValue(
1310                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
1311            info.minResizeWidth = value != null ? value.data : info.minWidth;
1312            value = sa.peekValue(
1313                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
1314            info.minResizeHeight = value != null ? value.data : info.minHeight;
1315            info.updatePeriodMillis = sa.getInt(
1316                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
1317            info.initialLayout = sa.getResourceId(
1318                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
1319            String className = sa
1320                    .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
1321            if (className != null) {
1322                info.configure = new ComponentName(component.getPackageName(), className);
1323            }
1324            info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
1325            info.icon = ri.getIconResource();
1326            info.previewImage = sa.getResourceId(
1327                    com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
1328            info.autoAdvanceViewId = sa.getResourceId(
1329                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
1330            info.resizeMode = sa.getInt(
1331                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
1332                    AppWidgetProviderInfo.RESIZE_NONE);
1333
1334            sa.recycle();
1335        } catch (Exception e) {
1336            // Ok to catch Exception here, because anything going wrong because
1337            // of what a client process passes to us should not be fatal for the
1338            // system process.
1339            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
1340            return null;
1341        } finally {
1342            if (parser != null)
1343                parser.close();
1344        }
1345        return p;
1346    }
1347
1348    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
1349        PackageInfo pkgInfo = null;
1350        try {
1351            pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
1352        } catch (RemoteException re) {
1353            // Shouldn't happen, local call
1354        }
1355        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1356            throw new PackageManager.NameNotFoundException();
1357        }
1358        return pkgInfo.applicationInfo.uid;
1359    }
1360
1361    int enforceCallingUid(String packageName) throws IllegalArgumentException {
1362        int callingUid = Binder.getCallingUid();
1363        int packageUid;
1364        try {
1365            packageUid = getUidForPackage(packageName);
1366        } catch (PackageManager.NameNotFoundException ex) {
1367            throw new IllegalArgumentException("packageName and uid don't match packageName="
1368                    + packageName);
1369        }
1370        if (!UserId.isSameApp(callingUid, packageUid)) {
1371            throw new IllegalArgumentException("packageName and uid don't match packageName="
1372                    + packageName);
1373        }
1374        return callingUid;
1375    }
1376
1377    void sendInitialBroadcasts() {
1378        synchronized (mAppWidgetIds) {
1379            ensureStateLoadedLocked();
1380            final int N = mInstalledProviders.size();
1381            for (int i = 0; i < N; i++) {
1382                Provider p = mInstalledProviders.get(i);
1383                if (p.instances.size() > 0) {
1384                    sendEnableIntentLocked(p);
1385                    int[] appWidgetIds = getAppWidgetIds(p);
1386                    sendUpdateIntentLocked(p, appWidgetIds);
1387                    registerForBroadcastsLocked(p, appWidgetIds);
1388                }
1389            }
1390        }
1391    }
1392
1393    // only call from initialization -- it assumes that the data structures are all empty
1394    void loadStateLocked() {
1395        AtomicFile file = savedStateFile();
1396        try {
1397            FileInputStream stream = file.openRead();
1398            readStateFromFileLocked(stream);
1399
1400            if (stream != null) {
1401                try {
1402                    stream.close();
1403                } catch (IOException e) {
1404                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
1405                }
1406            }
1407        } catch (FileNotFoundException e) {
1408            Slog.w(TAG, "Failed to read state: " + e);
1409        }
1410    }
1411
1412    void saveStateLocked() {
1413        AtomicFile file = savedStateFile();
1414        FileOutputStream stream;
1415        try {
1416            stream = file.startWrite();
1417            if (writeStateToFileLocked(stream)) {
1418                file.finishWrite(stream);
1419            } else {
1420                file.failWrite(stream);
1421                Slog.w(TAG, "Failed to save state, restoring backup.");
1422            }
1423        } catch (IOException e) {
1424            Slog.w(TAG, "Failed open state file for write: " + e);
1425        }
1426    }
1427
1428    boolean writeStateToFileLocked(FileOutputStream stream) {
1429        int N;
1430
1431        try {
1432            XmlSerializer out = new FastXmlSerializer();
1433            out.setOutput(stream, "utf-8");
1434            out.startDocument(null, true);
1435            out.startTag(null, "gs");
1436
1437            int providerIndex = 0;
1438            N = mInstalledProviders.size();
1439            for (int i = 0; i < N; i++) {
1440                Provider p = mInstalledProviders.get(i);
1441                if (p.instances.size() > 0) {
1442                    out.startTag(null, "p");
1443                    out.attribute(null, "pkg", p.info.provider.getPackageName());
1444                    out.attribute(null, "cl", p.info.provider.getClassName());
1445                    out.endTag(null, "p");
1446                    p.tag = providerIndex;
1447                    providerIndex++;
1448                }
1449            }
1450
1451            N = mHosts.size();
1452            for (int i = 0; i < N; i++) {
1453                Host host = mHosts.get(i);
1454                out.startTag(null, "h");
1455                out.attribute(null, "pkg", host.packageName);
1456                out.attribute(null, "id", Integer.toHexString(host.hostId));
1457                out.endTag(null, "h");
1458                host.tag = i;
1459            }
1460
1461            N = mAppWidgetIds.size();
1462            for (int i = 0; i < N; i++) {
1463                AppWidgetId id = mAppWidgetIds.get(i);
1464                out.startTag(null, "g");
1465                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
1466                out.attribute(null, "h", Integer.toHexString(id.host.tag));
1467                if (id.provider != null) {
1468                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
1469                }
1470                out.endTag(null, "g");
1471            }
1472
1473            Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1474            while (it.hasNext()) {
1475                out.startTag(null, "b");
1476                out.attribute(null, "packageName", it.next());
1477                out.endTag(null, "b");
1478            }
1479
1480            out.endTag(null, "gs");
1481
1482            out.endDocument();
1483            return true;
1484        } catch (IOException e) {
1485            Slog.w(TAG, "Failed to write state: " + e);
1486            return false;
1487        }
1488    }
1489
1490    void readStateFromFileLocked(FileInputStream stream) {
1491        boolean success = false;
1492        try {
1493            XmlPullParser parser = Xml.newPullParser();
1494            parser.setInput(stream, null);
1495
1496            int type;
1497            int providerIndex = 0;
1498            HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
1499            do {
1500                type = parser.next();
1501                if (type == XmlPullParser.START_TAG) {
1502                    String tag = parser.getName();
1503                    if ("p".equals(tag)) {
1504                        // TODO: do we need to check that this package has the same signature
1505                        // as before?
1506                        String pkg = parser.getAttributeValue(null, "pkg");
1507                        String cl = parser.getAttributeValue(null, "cl");
1508
1509                        final PackageManager packageManager = mContext.getPackageManager();
1510                        try {
1511                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
1512                        } catch (PackageManager.NameNotFoundException e) {
1513                            String[] pkgs = packageManager
1514                                    .currentToCanonicalPackageNames(new String[] { pkg });
1515                            pkg = pkgs[0];
1516                        }
1517
1518                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
1519                        if (p == null && mSafeMode) {
1520                            // if we're in safe mode, make a temporary one
1521                            p = new Provider();
1522                            p.info = new AppWidgetProviderInfo();
1523                            p.info.provider = new ComponentName(pkg, cl);
1524                            p.zombie = true;
1525                            mInstalledProviders.add(p);
1526                        }
1527                        if (p != null) {
1528                            // if it wasn't uninstalled or something
1529                            loadedProviders.put(providerIndex, p);
1530                        }
1531                        providerIndex++;
1532                    } else if ("h".equals(tag)) {
1533                        Host host = new Host();
1534
1535                        // TODO: do we need to check that this package has the same signature
1536                        // as before?
1537                        host.packageName = parser.getAttributeValue(null, "pkg");
1538                        try {
1539                            host.uid = getUidForPackage(host.packageName);
1540                        } catch (PackageManager.NameNotFoundException ex) {
1541                            host.zombie = true;
1542                        }
1543                        if (!host.zombie || mSafeMode) {
1544                            // In safe mode, we don't discard the hosts we don't recognize
1545                            // so that they're not pruned from our list. Otherwise, we do.
1546                            host.hostId = Integer
1547                                    .parseInt(parser.getAttributeValue(null, "id"), 16);
1548                            mHosts.add(host);
1549                        }
1550                    } else if ("b".equals(tag)) {
1551                        String packageName = parser.getAttributeValue(null, "packageName");
1552                        if (packageName != null) {
1553                            mPackagesWithBindWidgetPermission.add(packageName);
1554                        }
1555                    } else if ("g".equals(tag)) {
1556                        AppWidgetId id = new AppWidgetId();
1557                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
1558                        if (id.appWidgetId >= mNextAppWidgetId) {
1559                            mNextAppWidgetId = id.appWidgetId + 1;
1560                        }
1561
1562                        String providerString = parser.getAttributeValue(null, "p");
1563                        if (providerString != null) {
1564                            // there's no provider if it hasn't been bound yet.
1565                            // maybe we don't have to save this, but it brings the system
1566                            // to the state it was in.
1567                            int pIndex = Integer.parseInt(providerString, 16);
1568                            id.provider = loadedProviders.get(pIndex);
1569                            if (false) {
1570                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
1571                                        + pIndex + " which is " + id.provider);
1572                            }
1573                            if (id.provider == null) {
1574                                // This provider is gone. We just let the host figure out
1575                                // that this happened when it fails to load it.
1576                                continue;
1577                            }
1578                        }
1579
1580                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
1581                        id.host = mHosts.get(hIndex);
1582                        if (id.host == null) {
1583                            // This host is gone.
1584                            continue;
1585                        }
1586
1587                        if (id.provider != null) {
1588                            id.provider.instances.add(id);
1589                        }
1590                        id.host.instances.add(id);
1591                        mAppWidgetIds.add(id);
1592                    }
1593                }
1594            } while (type != XmlPullParser.END_DOCUMENT);
1595            success = true;
1596        } catch (NullPointerException e) {
1597            Slog.w(TAG, "failed parsing " + e);
1598        } catch (NumberFormatException e) {
1599            Slog.w(TAG, "failed parsing " + e);
1600        } catch (XmlPullParserException e) {
1601            Slog.w(TAG, "failed parsing " + e);
1602        } catch (IOException e) {
1603            Slog.w(TAG, "failed parsing " + e);
1604        } catch (IndexOutOfBoundsException e) {
1605            Slog.w(TAG, "failed parsing " + e);
1606        }
1607
1608        if (success) {
1609            // delete any hosts that didn't manage to get connected (should happen)
1610            // if it matters, they'll be reconnected.
1611            for (int i = mHosts.size() - 1; i >= 0; i--) {
1612                pruneHostLocked(mHosts.get(i));
1613            }
1614        } else {
1615            // failed reading, clean up
1616            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
1617
1618            mAppWidgetIds.clear();
1619            mHosts.clear();
1620            final int N = mInstalledProviders.size();
1621            for (int i = 0; i < N; i++) {
1622                mInstalledProviders.get(i).instances.clear();
1623            }
1624        }
1625    }
1626
1627    static File getSettingsFile(int userId) {
1628        return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME);
1629    }
1630
1631    AtomicFile savedStateFile() {
1632        File dir = new File("/data/system/users/" + mUserId);
1633        File settingsFile = getSettingsFile(mUserId);
1634        if (!settingsFile.exists() && mUserId == 0) {
1635            if (!dir.exists()) {
1636                dir.mkdirs();
1637            }
1638            // Migrate old data
1639            File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
1640            // Method doesn't throw an exception on failure. Ignore any errors
1641            // in moving the file (like non-existence)
1642            oldFile.renameTo(settingsFile);
1643        }
1644        return new AtomicFile(settingsFile);
1645    }
1646
1647    void onUserRemoved() {
1648        // prune the ones we don't want to keep
1649        int N = mInstalledProviders.size();
1650        for (int i = N - 1; i >= 0; i--) {
1651            Provider p = mInstalledProviders.get(i);
1652            cancelBroadcasts(p);
1653        }
1654        getSettingsFile(mUserId).delete();
1655    }
1656
1657    boolean addProvidersForPackageLocked(String pkgName) {
1658        boolean providersAdded = false;
1659        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1660        intent.setPackage(pkgName);
1661        List<ResolveInfo> broadcastReceivers;
1662        try {
1663            broadcastReceivers = mPm.queryIntentReceivers(intent,
1664                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1665                    PackageManager.GET_META_DATA, mUserId);
1666        } catch (RemoteException re) {
1667            // Shouldn't happen, local call
1668            return false;
1669        }
1670        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1671        for (int i = 0; i < N; i++) {
1672            ResolveInfo ri = broadcastReceivers.get(i);
1673            ActivityInfo ai = ri.activityInfo;
1674            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1675                continue;
1676            }
1677            if (pkgName.equals(ai.packageName)) {
1678                addProviderLocked(ri);
1679                providersAdded = true;
1680            }
1681        }
1682
1683        return providersAdded;
1684    }
1685
1686    /**
1687     * Updates all providers with the specified package names, and records any providers that were
1688     * pruned.
1689     *
1690     * @return whether any providers were updated
1691     */
1692    boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
1693        boolean providersUpdated = false;
1694        HashSet<String> keep = new HashSet<String>();
1695        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
1696        intent.setPackage(pkgName);
1697        List<ResolveInfo> broadcastReceivers;
1698        try {
1699            broadcastReceivers = mPm.queryIntentReceivers(intent,
1700                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
1701                PackageManager.GET_META_DATA, mUserId);
1702        } catch (RemoteException re) {
1703            // Shouldn't happen, local call
1704            return false;
1705        }
1706
1707        // add the missing ones and collect which ones to keep
1708        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
1709        for (int i = 0; i < N; i++) {
1710            ResolveInfo ri = broadcastReceivers.get(i);
1711            ActivityInfo ai = ri.activityInfo;
1712            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
1713                continue;
1714            }
1715            if (pkgName.equals(ai.packageName)) {
1716                ComponentName component = new ComponentName(ai.packageName, ai.name);
1717                Provider p = lookupProviderLocked(component);
1718                if (p == null) {
1719                    if (addProviderLocked(ri)) {
1720                        keep.add(ai.name);
1721                        providersUpdated = true;
1722                    }
1723                } else {
1724                    Provider parsed = parseProviderInfoXml(component, ri);
1725                    if (parsed != null) {
1726                        keep.add(ai.name);
1727                        // Use the new AppWidgetProviderInfo.
1728                        p.info = parsed.info;
1729                        // If it's enabled
1730                        final int M = p.instances.size();
1731                        if (M > 0) {
1732                            int[] appWidgetIds = getAppWidgetIds(p);
1733                            // Reschedule for the new updatePeriodMillis (don't worry about handling
1734                            // it specially if updatePeriodMillis didn't change because we just sent
1735                            // an update, and the next one will be updatePeriodMillis from now).
1736                            cancelBroadcasts(p);
1737                            registerForBroadcastsLocked(p, appWidgetIds);
1738                            // If it's currently showing, call back with the new
1739                            // AppWidgetProviderInfo.
1740                            for (int j = 0; j < M; j++) {
1741                                AppWidgetId id = p.instances.get(j);
1742                                id.views = null;
1743                                if (id.host != null && id.host.callbacks != null) {
1744                                    try {
1745                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
1746                                    } catch (RemoteException ex) {
1747                                        // It failed; remove the callback. No need to prune because
1748                                        // we know that this host is still referenced by this
1749                                        // instance.
1750                                        id.host.callbacks = null;
1751                                    }
1752                                }
1753                            }
1754                            // Now that we've told the host, push out an update.
1755                            sendUpdateIntentLocked(p, appWidgetIds);
1756                            providersUpdated = true;
1757                        }
1758                    }
1759                }
1760            }
1761        }
1762
1763        // prune the ones we don't want to keep
1764        N = mInstalledProviders.size();
1765        for (int i = N - 1; i >= 0; i--) {
1766            Provider p = mInstalledProviders.get(i);
1767            if (pkgName.equals(p.info.provider.getPackageName())
1768                    && !keep.contains(p.info.provider.getClassName())) {
1769                if (removedProviders != null) {
1770                    removedProviders.add(p.info.provider);
1771                }
1772                removeProviderLocked(i, p);
1773                providersUpdated = true;
1774            }
1775        }
1776
1777        return providersUpdated;
1778    }
1779
1780    boolean removeProvidersForPackageLocked(String pkgName) {
1781        boolean providersRemoved = false;
1782        int N = mInstalledProviders.size();
1783        for (int i = N - 1; i >= 0; i--) {
1784            Provider p = mInstalledProviders.get(i);
1785            if (pkgName.equals(p.info.provider.getPackageName())) {
1786                removeProviderLocked(i, p);
1787                providersRemoved = true;
1788            }
1789        }
1790
1791        // Delete the hosts for this package too
1792        //
1793        // By now, we have removed any AppWidgets that were in any hosts here,
1794        // so we don't need to worry about sending DISABLE broadcasts to them.
1795        N = mHosts.size();
1796        for (int i = N - 1; i >= 0; i--) {
1797            Host host = mHosts.get(i);
1798            if (pkgName.equals(host.packageName)) {
1799                deleteHostLocked(host);
1800            }
1801        }
1802
1803        return providersRemoved;
1804    }
1805
1806    void notifyHostsForProvidersChangedLocked() {
1807        final int N = mHosts.size();
1808        for (int i = N - 1; i >= 0; i--) {
1809            Host host = mHosts.get(i);
1810            try {
1811                if (host.callbacks != null) {
1812                    host.callbacks.providersChanged();
1813                }
1814            } catch (RemoteException ex) {
1815                // It failed; remove the callback. No need to prune because
1816                // we know that this host is still referenced by this
1817                // instance.
1818                host.callbacks = null;
1819            }
1820        }
1821    }
1822}
1823