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