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