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