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