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