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