AppWidgetServiceImpl.java revision b6f83374cc706e5da95a137e9d0f6bd6bbf3220e
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 if (id.views != null) { 947 // Only trigger a partial update for a widget if it has received a full update 948 updateAppWidgetInstanceLocked(id, views, true); 949 } 950 } 951 } 952 } 953 954 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) { 955 if (appWidgetIds == null) { 956 return; 957 } 958 if (appWidgetIds.length == 0) { 959 return; 960 } 961 final int N = appWidgetIds.length; 962 963 synchronized (mAppWidgetIds) { 964 ensureStateLoadedLocked(); 965 for (int i = 0; i < N; i++) { 966 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]); 967 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId); 968 } 969 } 970 } 971 972 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) { 973 synchronized (mAppWidgetIds) { 974 ensureStateLoadedLocked(); 975 Provider p = lookupProviderLocked(provider); 976 if (p == null) { 977 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider); 978 return; 979 } 980 ArrayList<AppWidgetId> instances = p.instances; 981 final int callingUid = Binder.getCallingUid(); 982 final int N = instances.size(); 983 for (int i = 0; i < N; i++) { 984 AppWidgetId id = instances.get(i); 985 if (canAccessAppWidgetId(id, callingUid)) { 986 updateAppWidgetInstanceLocked(id, views); 987 } 988 } 989 } 990 } 991 992 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) { 993 updateAppWidgetInstanceLocked(id, views, false); 994 } 995 996 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) { 997 // allow for stale appWidgetIds and other badness 998 // lookup also checks that the calling process can access the appWidgetId 999 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances) 1000 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) { 1001 1002 if (!isPartialUpdate) { 1003 // For a full update we replace the RemoteViews completely. 1004 id.views = views; 1005 } else { 1006 // For a partial update, we merge the new RemoteViews with the old. 1007 id.views.mergeRemoteViews(views); 1008 } 1009 1010 // is anyone listening? 1011 if (id.host.callbacks != null) { 1012 try { 1013 // the lock is held, but this is a oneway call 1014 id.host.callbacks.updateAppWidget(id.appWidgetId, views); 1015 } catch (RemoteException e) { 1016 // It failed; remove the callback. No need to prune because 1017 // we know that this host is still referenced by this instance. 1018 id.host.callbacks = null; 1019 } 1020 } 1021 } 1022 } 1023 1024 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) { 1025 // allow for stale appWidgetIds and other badness 1026 // lookup also checks that the calling process can access the appWidgetId 1027 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances) 1028 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) { 1029 // is anyone listening? 1030 if (id.host.callbacks != null) { 1031 try { 1032 // the lock is held, but this is a oneway call 1033 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId); 1034 } catch (RemoteException e) { 1035 // It failed; remove the callback. No need to prune because 1036 // we know that this host is still referenced by this instance. 1037 id.host.callbacks = null; 1038 } 1039 } 1040 1041 // If the host is unavailable, then we call the associated 1042 // RemoteViewsFactory.onDataSetChanged() directly 1043 if (id.host.callbacks == null) { 1044 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet(); 1045 for (FilterComparison key : keys) { 1046 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) { 1047 Intent intent = key.getIntent(); 1048 1049 final ServiceConnection conn = new ServiceConnection() { 1050 @Override 1051 public void onServiceConnected(ComponentName name, IBinder service) { 1052 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub 1053 .asInterface(service); 1054 try { 1055 cb.onDataSetChangedAsync(); 1056 } catch (RemoteException e) { 1057 e.printStackTrace(); 1058 } catch (RuntimeException e) { 1059 e.printStackTrace(); 1060 } 1061 mContext.unbindService(this); 1062 } 1063 1064 @Override 1065 public void onServiceDisconnected(android.content.ComponentName name) { 1066 // Do nothing 1067 } 1068 }; 1069 1070 int userId = UserHandle.getUserId(id.provider.uid); 1071 // Bind to the service and call onDataSetChanged() 1072 final long token = Binder.clearCallingIdentity(); 1073 try { 1074 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId); 1075 } finally { 1076 Binder.restoreCallingIdentity(token); 1077 } 1078 } 1079 } 1080 } 1081 } 1082 } 1083 1084 private boolean isLocalBinder() { 1085 return Process.myPid() == Binder.getCallingPid(); 1086 } 1087 1088 private RemoteViews cloneIfLocalBinder(RemoteViews rv) { 1089 if (isLocalBinder() && rv != null) { 1090 return rv.clone(); 1091 } 1092 return rv; 1093 } 1094 1095 private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) { 1096 if (isLocalBinder() && info != null) { 1097 return info.clone(); 1098 } 1099 return info; 1100 } 1101 1102 private Bundle cloneIfLocalBinder(Bundle bundle) { 1103 // Note: this is only a shallow copy. For now this will be fine, but it could be problematic 1104 // if we start adding objects to the options. Further, it would only be an issue if keyguard 1105 // used such options. 1106 if (isLocalBinder() && bundle != null) { 1107 return (Bundle) bundle.clone(); 1108 } 1109 return bundle; 1110 } 1111 1112 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId, 1113 List<RemoteViews> updatedViews) { 1114 int callingUid = enforceCallingUid(packageName); 1115 synchronized (mAppWidgetIds) { 1116 ensureStateLoadedLocked(); 1117 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId); 1118 host.callbacks = callbacks; 1119 1120 updatedViews.clear(); 1121 1122 ArrayList<AppWidgetId> instances = host.instances; 1123 int N = instances.size(); 1124 int[] updatedIds = new int[N]; 1125 for (int i = 0; i < N; i++) { 1126 AppWidgetId id = instances.get(i); 1127 updatedIds[i] = id.appWidgetId; 1128 updatedViews.add(cloneIfLocalBinder(id.views)); 1129 } 1130 return updatedIds; 1131 } 1132 } 1133 1134 public void stopListening(int hostId) { 1135 synchronized (mAppWidgetIds) { 1136 ensureStateLoadedLocked(); 1137 Host host = lookupHostLocked(Binder.getCallingUid(), hostId); 1138 if (host != null) { 1139 host.callbacks = null; 1140 pruneHostLocked(host); 1141 } 1142 } 1143 } 1144 1145 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) { 1146 if (id.host.uid == callingUid) { 1147 // Apps hosting the AppWidget have access to it. 1148 return true; 1149 } 1150 if (id.provider != null && id.provider.uid == callingUid) { 1151 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound) 1152 return true; 1153 } 1154 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) { 1155 // Apps that can bind have access to all appWidgetIds. 1156 return true; 1157 } 1158 // Nobody else can access it. 1159 return false; 1160 } 1161 1162 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) { 1163 int callingUid = Binder.getCallingUid(); 1164 final int N = mAppWidgetIds.size(); 1165 for (int i = 0; i < N; i++) { 1166 AppWidgetId id = mAppWidgetIds.get(i); 1167 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) { 1168 return id; 1169 } 1170 } 1171 return null; 1172 } 1173 1174 Provider lookupProviderLocked(ComponentName provider) { 1175 final int N = mInstalledProviders.size(); 1176 for (int i = 0; i < N; i++) { 1177 Provider p = mInstalledProviders.get(i); 1178 if (p.info.provider.equals(provider)) { 1179 return p; 1180 } 1181 } 1182 return null; 1183 } 1184 1185 Host lookupHostLocked(int uid, int hostId) { 1186 final int N = mHosts.size(); 1187 for (int i = 0; i < N; i++) { 1188 Host h = mHosts.get(i); 1189 if (h.uid == uid && h.hostId == hostId) { 1190 return h; 1191 } 1192 } 1193 return null; 1194 } 1195 1196 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) { 1197 final int N = mHosts.size(); 1198 for (int i = 0; i < N; i++) { 1199 Host h = mHosts.get(i); 1200 if (h.hostId == hostId && h.packageName.equals(packageName)) { 1201 return h; 1202 } 1203 } 1204 Host host = new Host(); 1205 host.packageName = packageName; 1206 host.uid = uid; 1207 host.hostId = hostId; 1208 mHosts.add(host); 1209 return host; 1210 } 1211 1212 void pruneHostLocked(Host host) { 1213 if (host.instances.size() == 0 && host.callbacks == null) { 1214 mHosts.remove(host); 1215 } 1216 } 1217 1218 void loadAppWidgetList() { 1219 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1220 try { 1221 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent, 1222 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 1223 PackageManager.GET_META_DATA, mUserId); 1224 1225 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1226 for (int i = 0; i < N; i++) { 1227 ResolveInfo ri = broadcastReceivers.get(i); 1228 addProviderLocked(ri); 1229 } 1230 } catch (RemoteException re) { 1231 // Shouldn't happen, local call 1232 } 1233 } 1234 1235 boolean addProviderLocked(ResolveInfo ri) { 1236 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 1237 return false; 1238 } 1239 if (!ri.activityInfo.isEnabled()) { 1240 return false; 1241 } 1242 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName, 1243 ri.activityInfo.name), ri); 1244 if (p != null) { 1245 mInstalledProviders.add(p); 1246 return true; 1247 } else { 1248 return false; 1249 } 1250 } 1251 1252 void removeProviderLocked(int index, Provider p) { 1253 int N = p.instances.size(); 1254 for (int i = 0; i < N; i++) { 1255 AppWidgetId id = p.instances.get(i); 1256 // Call back with empty RemoteViews 1257 updateAppWidgetInstanceLocked(id, null); 1258 // Stop telling the host about updates for this from now on 1259 cancelBroadcasts(p); 1260 // clear out references to this appWidgetId 1261 id.host.instances.remove(id); 1262 mAppWidgetIds.remove(id); 1263 id.provider = null; 1264 pruneHostLocked(id.host); 1265 id.host = null; 1266 } 1267 p.instances.clear(); 1268 mInstalledProviders.remove(index); 1269 mDeletedProviders.add(p); 1270 // no need to send the DISABLE broadcast, since the receiver is gone anyway 1271 cancelBroadcasts(p); 1272 } 1273 1274 void sendEnableIntentLocked(Provider p) { 1275 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED); 1276 intent.setComponent(p.info.provider); 1277 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId)); 1278 } 1279 1280 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) { 1281 if (appWidgetIds != null && appWidgetIds.length > 0) { 1282 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1283 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 1284 intent.setComponent(p.info.provider); 1285 mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId)); 1286 } 1287 } 1288 1289 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) { 1290 if (p.info.updatePeriodMillis > 0) { 1291 // if this is the first instance, set the alarm. otherwise, 1292 // rely on the fact that we've already set it and that 1293 // PendingIntent.getBroadcast will update the extras. 1294 boolean alreadyRegistered = p.broadcast != null; 1295 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1296 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 1297 intent.setComponent(p.info.provider); 1298 long token = Binder.clearCallingIdentity(); 1299 try { 1300 p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent, 1301 PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId)); 1302 } finally { 1303 Binder.restoreCallingIdentity(token); 1304 } 1305 if (!alreadyRegistered) { 1306 long period = p.info.updatePeriodMillis; 1307 if (period < MIN_UPDATE_PERIOD) { 1308 period = MIN_UPDATE_PERIOD; 1309 } 1310 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock 1311 .elapsedRealtime() 1312 + period, period, p.broadcast); 1313 } 1314 } 1315 } 1316 1317 static int[] getAppWidgetIds(Provider p) { 1318 int instancesSize = p.instances.size(); 1319 int appWidgetIds[] = new int[instancesSize]; 1320 for (int i = 0; i < instancesSize; i++) { 1321 appWidgetIds[i] = p.instances.get(i).appWidgetId; 1322 } 1323 return appWidgetIds; 1324 } 1325 1326 public int[] getAppWidgetIds(ComponentName provider) { 1327 synchronized (mAppWidgetIds) { 1328 ensureStateLoadedLocked(); 1329 Provider p = lookupProviderLocked(provider); 1330 if (p != null && Binder.getCallingUid() == p.uid) { 1331 return getAppWidgetIds(p); 1332 } else { 1333 return new int[0]; 1334 } 1335 } 1336 } 1337 1338 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) { 1339 Provider p = null; 1340 1341 ActivityInfo activityInfo = ri.activityInfo; 1342 XmlResourceParser parser = null; 1343 try { 1344 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(), 1345 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER); 1346 if (parser == null) { 1347 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER 1348 + " meta-data for " + "AppWidget provider '" + component + '\''); 1349 return null; 1350 } 1351 1352 AttributeSet attrs = Xml.asAttributeSet(parser); 1353 1354 int type; 1355 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1356 && type != XmlPullParser.START_TAG) { 1357 // drain whitespace, comments, etc. 1358 } 1359 1360 String nodeName = parser.getName(); 1361 if (!"appwidget-provider".equals(nodeName)) { 1362 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for" 1363 + " AppWidget provider '" + component + '\''); 1364 return null; 1365 } 1366 1367 p = new Provider(); 1368 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo(); 1369 info.provider = component; 1370 p.uid = activityInfo.applicationInfo.uid; 1371 1372 Resources res = mContext.getPackageManager() 1373 .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId); 1374 1375 TypedArray sa = res.obtainAttributes(attrs, 1376 com.android.internal.R.styleable.AppWidgetProviderInfo); 1377 1378 // These dimensions has to be resolved in the application's context. 1379 // We simply send back the raw complex data, which will be 1380 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}. 1381 TypedValue value = sa 1382 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth); 1383 info.minWidth = value != null ? value.data : 0; 1384 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight); 1385 info.minHeight = value != null ? value.data : 0; 1386 value = sa.peekValue( 1387 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth); 1388 info.minResizeWidth = value != null ? value.data : info.minWidth; 1389 value = sa.peekValue( 1390 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight); 1391 info.minResizeHeight = value != null ? value.data : info.minHeight; 1392 info.updatePeriodMillis = sa.getInt( 1393 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); 1394 info.initialLayout = sa.getResourceId( 1395 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0); 1396 info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable. 1397 AppWidgetProviderInfo_initialKeyguardLayout, 0); 1398 String className = sa 1399 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure); 1400 if (className != null) { 1401 info.configure = new ComponentName(component.getPackageName(), className); 1402 } 1403 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString(); 1404 info.icon = ri.getIconResource(); 1405 info.previewImage = sa.getResourceId( 1406 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0); 1407 info.autoAdvanceViewId = sa.getResourceId( 1408 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1); 1409 info.resizeMode = sa.getInt( 1410 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode, 1411 AppWidgetProviderInfo.RESIZE_NONE); 1412 info.widgetCategory = sa.getInt( 1413 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory, 1414 AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); 1415 info.widgetFeatures = sa.getInt( 1416 com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 1417 AppWidgetProviderInfo.WIDGET_FEATURES_NONE); 1418 1419 sa.recycle(); 1420 } catch (Exception e) { 1421 // Ok to catch Exception here, because anything going wrong because 1422 // of what a client process passes to us should not be fatal for the 1423 // system process. 1424 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e); 1425 return null; 1426 } finally { 1427 if (parser != null) 1428 parser.close(); 1429 } 1430 return p; 1431 } 1432 1433 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException { 1434 PackageInfo pkgInfo = null; 1435 try { 1436 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId); 1437 } catch (RemoteException re) { 1438 // Shouldn't happen, local call 1439 } 1440 if (pkgInfo == null || pkgInfo.applicationInfo == null) { 1441 throw new PackageManager.NameNotFoundException(); 1442 } 1443 return pkgInfo.applicationInfo.uid; 1444 } 1445 1446 int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException { 1447 int callingUid = Binder.getCallingUid(); 1448 if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) { 1449 return callingUid; 1450 } 1451 return enforceCallingUid(packageName); 1452 } 1453 1454 int enforceCallingUid(String packageName) throws IllegalArgumentException { 1455 int callingUid = Binder.getCallingUid(); 1456 int packageUid; 1457 try { 1458 packageUid = getUidForPackage(packageName); 1459 } catch (PackageManager.NameNotFoundException ex) { 1460 throw new IllegalArgumentException("packageName and uid don't match packageName=" 1461 + packageName); 1462 } 1463 if (!UserHandle.isSameApp(callingUid, packageUid)) { 1464 throw new IllegalArgumentException("packageName and uid don't match packageName=" 1465 + packageName); 1466 } 1467 return callingUid; 1468 } 1469 1470 void sendInitialBroadcasts() { 1471 synchronized (mAppWidgetIds) { 1472 ensureStateLoadedLocked(); 1473 final int N = mInstalledProviders.size(); 1474 for (int i = 0; i < N; i++) { 1475 Provider p = mInstalledProviders.get(i); 1476 if (p.instances.size() > 0) { 1477 sendEnableIntentLocked(p); 1478 int[] appWidgetIds = getAppWidgetIds(p); 1479 sendUpdateIntentLocked(p, appWidgetIds); 1480 registerForBroadcastsLocked(p, appWidgetIds); 1481 } 1482 } 1483 } 1484 } 1485 1486 // only call from initialization -- it assumes that the data structures are all empty 1487 void loadStateLocked() { 1488 AtomicFile file = savedStateFile(); 1489 try { 1490 FileInputStream stream = file.openRead(); 1491 readStateFromFileLocked(stream); 1492 1493 if (stream != null) { 1494 try { 1495 stream.close(); 1496 } catch (IOException e) { 1497 Slog.w(TAG, "Failed to close state FileInputStream " + e); 1498 } 1499 } 1500 } catch (FileNotFoundException e) { 1501 Slog.w(TAG, "Failed to read state: " + e); 1502 } 1503 } 1504 1505 void saveStateLocked() { 1506 AtomicFile file = savedStateFile(); 1507 FileOutputStream stream; 1508 try { 1509 stream = file.startWrite(); 1510 if (writeStateToFileLocked(stream)) { 1511 file.finishWrite(stream); 1512 } else { 1513 file.failWrite(stream); 1514 Slog.w(TAG, "Failed to save state, restoring backup."); 1515 } 1516 } catch (IOException e) { 1517 Slog.w(TAG, "Failed open state file for write: " + e); 1518 } 1519 } 1520 1521 boolean writeStateToFileLocked(FileOutputStream stream) { 1522 int N; 1523 1524 try { 1525 XmlSerializer out = new FastXmlSerializer(); 1526 out.setOutput(stream, "utf-8"); 1527 out.startDocument(null, true); 1528 out.startTag(null, "gs"); 1529 1530 int providerIndex = 0; 1531 N = mInstalledProviders.size(); 1532 for (int i = 0; i < N; i++) { 1533 Provider p = mInstalledProviders.get(i); 1534 if (p.instances.size() > 0) { 1535 out.startTag(null, "p"); 1536 out.attribute(null, "pkg", p.info.provider.getPackageName()); 1537 out.attribute(null, "cl", p.info.provider.getClassName()); 1538 out.endTag(null, "p"); 1539 p.tag = providerIndex; 1540 providerIndex++; 1541 } 1542 } 1543 1544 N = mHosts.size(); 1545 for (int i = 0; i < N; i++) { 1546 Host host = mHosts.get(i); 1547 out.startTag(null, "h"); 1548 out.attribute(null, "pkg", host.packageName); 1549 out.attribute(null, "id", Integer.toHexString(host.hostId)); 1550 out.endTag(null, "h"); 1551 host.tag = i; 1552 } 1553 1554 N = mAppWidgetIds.size(); 1555 for (int i = 0; i < N; i++) { 1556 AppWidgetId id = mAppWidgetIds.get(i); 1557 out.startTag(null, "g"); 1558 out.attribute(null, "id", Integer.toHexString(id.appWidgetId)); 1559 out.attribute(null, "h", Integer.toHexString(id.host.tag)); 1560 if (id.provider != null) { 1561 out.attribute(null, "p", Integer.toHexString(id.provider.tag)); 1562 } 1563 if (id.options != null) { 1564 out.attribute(null, "min_width", Integer.toHexString(id.options.getInt( 1565 AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH))); 1566 out.attribute(null, "min_height", Integer.toHexString(id.options.getInt( 1567 AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT))); 1568 out.attribute(null, "max_width", Integer.toHexString(id.options.getInt( 1569 AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH))); 1570 out.attribute(null, "max_height", Integer.toHexString(id.options.getInt( 1571 AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT))); 1572 out.attribute(null, "host_category", Integer.toHexString(id.options.getInt( 1573 AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY))); 1574 } 1575 out.endTag(null, "g"); 1576 } 1577 1578 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator(); 1579 while (it.hasNext()) { 1580 out.startTag(null, "b"); 1581 out.attribute(null, "packageName", it.next()); 1582 out.endTag(null, "b"); 1583 } 1584 1585 out.endTag(null, "gs"); 1586 1587 out.endDocument(); 1588 return true; 1589 } catch (IOException e) { 1590 Slog.w(TAG, "Failed to write state: " + e); 1591 return false; 1592 } 1593 } 1594 1595 @SuppressWarnings("unused") 1596 void readStateFromFileLocked(FileInputStream stream) { 1597 boolean success = false; 1598 try { 1599 XmlPullParser parser = Xml.newPullParser(); 1600 parser.setInput(stream, null); 1601 1602 int type; 1603 int providerIndex = 0; 1604 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>(); 1605 do { 1606 type = parser.next(); 1607 if (type == XmlPullParser.START_TAG) { 1608 String tag = parser.getName(); 1609 if ("p".equals(tag)) { 1610 // TODO: do we need to check that this package has the same signature 1611 // as before? 1612 String pkg = parser.getAttributeValue(null, "pkg"); 1613 String cl = parser.getAttributeValue(null, "cl"); 1614 1615 final IPackageManager packageManager = AppGlobals.getPackageManager(); 1616 try { 1617 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId); 1618 } catch (RemoteException e) { 1619 String[] pkgs = mContext.getPackageManager() 1620 .currentToCanonicalPackageNames(new String[] { pkg }); 1621 pkg = pkgs[0]; 1622 } 1623 1624 Provider p = lookupProviderLocked(new ComponentName(pkg, cl)); 1625 if (p == null && mSafeMode) { 1626 // if we're in safe mode, make a temporary one 1627 p = new Provider(); 1628 p.info = new AppWidgetProviderInfo(); 1629 p.info.provider = new ComponentName(pkg, cl); 1630 p.zombie = true; 1631 mInstalledProviders.add(p); 1632 } 1633 if (p != null) { 1634 // if it wasn't uninstalled or something 1635 loadedProviders.put(providerIndex, p); 1636 } 1637 providerIndex++; 1638 } else if ("h".equals(tag)) { 1639 Host host = new Host(); 1640 1641 // TODO: do we need to check that this package has the same signature 1642 // as before? 1643 host.packageName = parser.getAttributeValue(null, "pkg"); 1644 try { 1645 host.uid = getUidForPackage(host.packageName); 1646 } catch (PackageManager.NameNotFoundException ex) { 1647 host.zombie = true; 1648 } 1649 if (!host.zombie || mSafeMode) { 1650 // In safe mode, we don't discard the hosts we don't recognize 1651 // so that they're not pruned from our list. Otherwise, we do. 1652 host.hostId = Integer 1653 .parseInt(parser.getAttributeValue(null, "id"), 16); 1654 mHosts.add(host); 1655 } 1656 } else if ("b".equals(tag)) { 1657 String packageName = parser.getAttributeValue(null, "packageName"); 1658 if (packageName != null) { 1659 mPackagesWithBindWidgetPermission.add(packageName); 1660 } 1661 } else if ("g".equals(tag)) { 1662 AppWidgetId id = new AppWidgetId(); 1663 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16); 1664 if (id.appWidgetId >= mNextAppWidgetId) { 1665 mNextAppWidgetId = id.appWidgetId + 1; 1666 } 1667 1668 Bundle options = new Bundle(); 1669 String minWidthString = parser.getAttributeValue(null, "min_width"); 1670 if (minWidthString != null) { 1671 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1672 Integer.parseInt(minWidthString, 16)); 1673 } 1674 String minHeightString = parser.getAttributeValue(null, "min_height"); 1675 if (minHeightString != null) { 1676 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 1677 Integer.parseInt(minHeightString, 16)); 1678 } 1679 String maxWidthString = parser.getAttributeValue(null, "max_width"); 1680 if (maxWidthString != null) { 1681 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 1682 Integer.parseInt(maxWidthString, 16)); 1683 } 1684 String maxHeightString = parser.getAttributeValue(null, "max_height"); 1685 if (maxHeightString != null) { 1686 options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 1687 Integer.parseInt(maxHeightString, 16)); 1688 } 1689 String categoryString = parser.getAttributeValue(null, "host_category"); 1690 if (categoryString != null) { 1691 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 1692 Integer.parseInt(categoryString, 16)); 1693 } 1694 id.options = options; 1695 1696 String providerString = parser.getAttributeValue(null, "p"); 1697 if (providerString != null) { 1698 // there's no provider if it hasn't been bound yet. 1699 // maybe we don't have to save this, but it brings the system 1700 // to the state it was in. 1701 int pIndex = Integer.parseInt(providerString, 16); 1702 id.provider = loadedProviders.get(pIndex); 1703 if (false) { 1704 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider " 1705 + pIndex + " which is " + id.provider); 1706 } 1707 if (id.provider == null) { 1708 // This provider is gone. We just let the host figure out 1709 // that this happened when it fails to load it. 1710 continue; 1711 } 1712 } 1713 1714 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16); 1715 id.host = mHosts.get(hIndex); 1716 if (id.host == null) { 1717 // This host is gone. 1718 continue; 1719 } 1720 1721 if (id.provider != null) { 1722 id.provider.instances.add(id); 1723 } 1724 id.host.instances.add(id); 1725 mAppWidgetIds.add(id); 1726 } 1727 } 1728 } while (type != XmlPullParser.END_DOCUMENT); 1729 success = true; 1730 } catch (NullPointerException e) { 1731 Slog.w(TAG, "failed parsing " + e); 1732 } catch (NumberFormatException e) { 1733 Slog.w(TAG, "failed parsing " + e); 1734 } catch (XmlPullParserException e) { 1735 Slog.w(TAG, "failed parsing " + e); 1736 } catch (IOException e) { 1737 Slog.w(TAG, "failed parsing " + e); 1738 } catch (IndexOutOfBoundsException e) { 1739 Slog.w(TAG, "failed parsing " + e); 1740 } 1741 1742 if (success) { 1743 // delete any hosts that didn't manage to get connected (should happen) 1744 // if it matters, they'll be reconnected. 1745 for (int i = mHosts.size() - 1; i >= 0; i--) { 1746 pruneHostLocked(mHosts.get(i)); 1747 } 1748 } else { 1749 // failed reading, clean up 1750 Slog.w(TAG, "Failed to read state, clearing widgets and hosts."); 1751 1752 mAppWidgetIds.clear(); 1753 mHosts.clear(); 1754 final int N = mInstalledProviders.size(); 1755 for (int i = 0; i < N; i++) { 1756 mInstalledProviders.get(i).instances.clear(); 1757 } 1758 } 1759 } 1760 1761 static File getSettingsFile(int userId) { 1762 return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME); 1763 } 1764 1765 AtomicFile savedStateFile() { 1766 File dir = Environment.getUserSystemDirectory(mUserId); 1767 File settingsFile = getSettingsFile(mUserId); 1768 if (!settingsFile.exists() && mUserId == 0) { 1769 if (!dir.exists()) { 1770 dir.mkdirs(); 1771 } 1772 // Migrate old data 1773 File oldFile = new File("/data/system/" + SETTINGS_FILENAME); 1774 // Method doesn't throw an exception on failure. Ignore any errors 1775 // in moving the file (like non-existence) 1776 oldFile.renameTo(settingsFile); 1777 } 1778 return new AtomicFile(settingsFile); 1779 } 1780 1781 void onUserStopping() { 1782 // prune the ones we don't want to keep 1783 int N = mInstalledProviders.size(); 1784 for (int i = N - 1; i >= 0; i--) { 1785 Provider p = mInstalledProviders.get(i); 1786 cancelBroadcasts(p); 1787 } 1788 } 1789 1790 void onUserRemoved() { 1791 getSettingsFile(mUserId).delete(); 1792 } 1793 1794 boolean addProvidersForPackageLocked(String pkgName) { 1795 boolean providersAdded = false; 1796 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1797 intent.setPackage(pkgName); 1798 List<ResolveInfo> broadcastReceivers; 1799 try { 1800 broadcastReceivers = mPm.queryIntentReceivers(intent, 1801 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 1802 PackageManager.GET_META_DATA, mUserId); 1803 } catch (RemoteException re) { 1804 // Shouldn't happen, local call 1805 return false; 1806 } 1807 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1808 for (int i = 0; i < N; i++) { 1809 ResolveInfo ri = broadcastReceivers.get(i); 1810 ActivityInfo ai = ri.activityInfo; 1811 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 1812 continue; 1813 } 1814 if (pkgName.equals(ai.packageName)) { 1815 addProviderLocked(ri); 1816 providersAdded = true; 1817 } 1818 } 1819 1820 return providersAdded; 1821 } 1822 1823 /** 1824 * Updates all providers with the specified package names, and records any providers that were 1825 * pruned. 1826 * 1827 * @return whether any providers were updated 1828 */ 1829 boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) { 1830 boolean providersUpdated = false; 1831 HashSet<String> keep = new HashSet<String>(); 1832 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1833 intent.setPackage(pkgName); 1834 List<ResolveInfo> broadcastReceivers; 1835 try { 1836 broadcastReceivers = mPm.queryIntentReceivers(intent, 1837 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 1838 PackageManager.GET_META_DATA, mUserId); 1839 } catch (RemoteException re) { 1840 // Shouldn't happen, local call 1841 return false; 1842 } 1843 1844 // add the missing ones and collect which ones to keep 1845 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1846 for (int i = 0; i < N; i++) { 1847 ResolveInfo ri = broadcastReceivers.get(i); 1848 ActivityInfo ai = ri.activityInfo; 1849 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 1850 continue; 1851 } 1852 if (pkgName.equals(ai.packageName)) { 1853 ComponentName component = new ComponentName(ai.packageName, ai.name); 1854 Provider p = lookupProviderLocked(component); 1855 if (p == null) { 1856 if (addProviderLocked(ri)) { 1857 keep.add(ai.name); 1858 providersUpdated = true; 1859 } 1860 } else { 1861 Provider parsed = parseProviderInfoXml(component, ri); 1862 if (parsed != null) { 1863 keep.add(ai.name); 1864 // Use the new AppWidgetProviderInfo. 1865 p.info = parsed.info; 1866 // If it's enabled 1867 final int M = p.instances.size(); 1868 if (M > 0) { 1869 int[] appWidgetIds = getAppWidgetIds(p); 1870 // Reschedule for the new updatePeriodMillis (don't worry about handling 1871 // it specially if updatePeriodMillis didn't change because we just sent 1872 // an update, and the next one will be updatePeriodMillis from now). 1873 cancelBroadcasts(p); 1874 registerForBroadcastsLocked(p, appWidgetIds); 1875 // If it's currently showing, call back with the new 1876 // AppWidgetProviderInfo. 1877 for (int j = 0; j < M; j++) { 1878 AppWidgetId id = p.instances.get(j); 1879 id.views = null; 1880 if (id.host != null && id.host.callbacks != null) { 1881 try { 1882 id.host.callbacks.providerChanged(id.appWidgetId, p.info); 1883 } catch (RemoteException ex) { 1884 // It failed; remove the callback. No need to prune because 1885 // we know that this host is still referenced by this 1886 // instance. 1887 id.host.callbacks = null; 1888 } 1889 } 1890 } 1891 // Now that we've told the host, push out an update. 1892 sendUpdateIntentLocked(p, appWidgetIds); 1893 providersUpdated = true; 1894 } 1895 } 1896 } 1897 } 1898 } 1899 1900 // prune the ones we don't want to keep 1901 N = mInstalledProviders.size(); 1902 for (int i = N - 1; i >= 0; i--) { 1903 Provider p = mInstalledProviders.get(i); 1904 if (pkgName.equals(p.info.provider.getPackageName()) 1905 && !keep.contains(p.info.provider.getClassName())) { 1906 if (removedProviders != null) { 1907 removedProviders.add(p.info.provider); 1908 } 1909 removeProviderLocked(i, p); 1910 providersUpdated = true; 1911 } 1912 } 1913 1914 return providersUpdated; 1915 } 1916 1917 boolean removeProvidersForPackageLocked(String pkgName) { 1918 boolean providersRemoved = false; 1919 int N = mInstalledProviders.size(); 1920 for (int i = N - 1; i >= 0; i--) { 1921 Provider p = mInstalledProviders.get(i); 1922 if (pkgName.equals(p.info.provider.getPackageName())) { 1923 removeProviderLocked(i, p); 1924 providersRemoved = true; 1925 } 1926 } 1927 1928 // Delete the hosts for this package too 1929 // 1930 // By now, we have removed any AppWidgets that were in any hosts here, 1931 // so we don't need to worry about sending DISABLE broadcasts to them. 1932 N = mHosts.size(); 1933 for (int i = N - 1; i >= 0; i--) { 1934 Host host = mHosts.get(i); 1935 if (pkgName.equals(host.packageName)) { 1936 deleteHostLocked(host); 1937 } 1938 } 1939 1940 return providersRemoved; 1941 } 1942 1943 void notifyHostsForProvidersChangedLocked() { 1944 final int N = mHosts.size(); 1945 for (int i = N - 1; i >= 0; i--) { 1946 Host host = mHosts.get(i); 1947 try { 1948 if (host.callbacks != null) { 1949 host.callbacks.providersChanged(); 1950 } 1951 } catch (RemoteException ex) { 1952 // It failed; remove the callback. No need to prune because 1953 // we know that this host is still referenced by this 1954 // instance. 1955 host.callbacks = null; 1956 } 1957 } 1958 } 1959} 1960