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