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