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