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