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