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