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