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