AppWidgetServiceImpl.java revision 311c79c3e93589c6fc720fe6c58ed522af591376
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 = views.estimateMemoryUsage(); 821 if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) { 822 throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" + 823 " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " + 824 mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" + 825 " fill the device's screen once."); 826 } 827 828 if (appWidgetIds.length == 0) { 829 return; 830 } 831 final int N = appWidgetIds.length; 832 833 synchronized (mAppWidgetIds) { 834 ensureStateLoadedLocked(); 835 for (int i = 0; i < N; i++) { 836 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]); 837 updateAppWidgetInstanceLocked(id, views); 838 } 839 } 840 } 841 842 public void updateAppWidgetOptions(int appWidgetId, Bundle options) { 843 synchronized (mAppWidgetIds) { 844 ensureStateLoadedLocked(); 845 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); 846 847 if (id == null) { 848 return; 849 } 850 Provider p = id.provider; 851 id.options = options; 852 853 // send the broacast saying that this appWidgetId has been deleted 854 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED); 855 intent.setComponent(p.info.provider); 856 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId); 857 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); 858 mContext.sendBroadcast(intent, mUserId); 859 } 860 } 861 862 public Bundle getAppWidgetOptions(int appWidgetId) { 863 synchronized (mAppWidgetIds) { 864 ensureStateLoadedLocked(); 865 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); 866 if (id != null && id.options != null) { 867 return id.options; 868 } else { 869 return Bundle.EMPTY; 870 } 871 } 872 } 873 874 public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) { 875 if (appWidgetIds == null) { 876 return; 877 } 878 if (appWidgetIds.length == 0) { 879 return; 880 } 881 final int N = appWidgetIds.length; 882 883 synchronized (mAppWidgetIds) { 884 ensureStateLoadedLocked(); 885 for (int i = 0; i < N; i++) { 886 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]); 887 updateAppWidgetInstanceLocked(id, views, true); 888 } 889 } 890 } 891 892 public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) { 893 if (appWidgetIds == null) { 894 return; 895 } 896 if (appWidgetIds.length == 0) { 897 return; 898 } 899 final int N = appWidgetIds.length; 900 901 synchronized (mAppWidgetIds) { 902 ensureStateLoadedLocked(); 903 for (int i = 0; i < N; i++) { 904 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]); 905 notifyAppWidgetViewDataChangedInstanceLocked(id, viewId); 906 } 907 } 908 } 909 910 public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) { 911 synchronized (mAppWidgetIds) { 912 ensureStateLoadedLocked(); 913 Provider p = lookupProviderLocked(provider); 914 if (p == null) { 915 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider); 916 return; 917 } 918 ArrayList<AppWidgetId> instances = p.instances; 919 final int callingUid = Binder.getCallingUid(); 920 final int N = instances.size(); 921 for (int i = 0; i < N; i++) { 922 AppWidgetId id = instances.get(i); 923 if (canAccessAppWidgetId(id, callingUid)) { 924 updateAppWidgetInstanceLocked(id, views); 925 } 926 } 927 } 928 } 929 930 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) { 931 updateAppWidgetInstanceLocked(id, views, false); 932 } 933 934 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) { 935 // allow for stale appWidgetIds and other badness 936 // lookup also checks that the calling process can access the appWidgetId 937 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances) 938 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) { 939 940 // We do not want to save this RemoteViews 941 if (!isPartialUpdate) 942 id.views = views; 943 944 // is anyone listening? 945 if (id.host.callbacks != null) { 946 try { 947 // the lock is held, but this is a oneway call 948 id.host.callbacks.updateAppWidget(id.appWidgetId, views); 949 } catch (RemoteException e) { 950 // It failed; remove the callback. No need to prune because 951 // we know that this host is still referenced by this instance. 952 id.host.callbacks = null; 953 } 954 } 955 } 956 } 957 958 void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) { 959 // allow for stale appWidgetIds and other badness 960 // lookup also checks that the calling process can access the appWidgetId 961 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances) 962 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) { 963 // is anyone listening? 964 if (id.host.callbacks != null) { 965 try { 966 // the lock is held, but this is a oneway call 967 id.host.callbacks.viewDataChanged(id.appWidgetId, viewId); 968 } catch (RemoteException e) { 969 // It failed; remove the callback. No need to prune because 970 // we know that this host is still referenced by this instance. 971 id.host.callbacks = null; 972 } 973 } 974 975 // If the host is unavailable, then we call the associated 976 // RemoteViewsFactory.onDataSetChanged() directly 977 if (id.host.callbacks == null) { 978 Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet(); 979 for (FilterComparison key : keys) { 980 if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) { 981 Intent intent = key.getIntent(); 982 983 final ServiceConnection conn = new ServiceConnection() { 984 @Override 985 public void onServiceConnected(ComponentName name, IBinder service) { 986 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub 987 .asInterface(service); 988 try { 989 cb.onDataSetChangedAsync(); 990 } catch (RemoteException e) { 991 e.printStackTrace(); 992 } catch (RuntimeException e) { 993 e.printStackTrace(); 994 } 995 mContext.unbindService(this); 996 } 997 998 @Override 999 public void onServiceDisconnected(android.content.ComponentName name) { 1000 // Do nothing 1001 } 1002 }; 1003 1004 int userId = UserId.getUserId(id.provider.uid); 1005 // Bind to the service and call onDataSetChanged() 1006 final long token = Binder.clearCallingIdentity(); 1007 try { 1008 mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE, userId); 1009 } finally { 1010 Binder.restoreCallingIdentity(token); 1011 } 1012 } 1013 } 1014 } 1015 } 1016 } 1017 1018 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId, 1019 List<RemoteViews> updatedViews) { 1020 int callingUid = enforceCallingUid(packageName); 1021 synchronized (mAppWidgetIds) { 1022 ensureStateLoadedLocked(); 1023 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId); 1024 host.callbacks = callbacks; 1025 1026 updatedViews.clear(); 1027 1028 ArrayList<AppWidgetId> instances = host.instances; 1029 int N = instances.size(); 1030 int[] updatedIds = new int[N]; 1031 for (int i = 0; i < N; i++) { 1032 AppWidgetId id = instances.get(i); 1033 updatedIds[i] = id.appWidgetId; 1034 updatedViews.add(id.views); 1035 } 1036 return updatedIds; 1037 } 1038 } 1039 1040 public void stopListening(int hostId) { 1041 synchronized (mAppWidgetIds) { 1042 ensureStateLoadedLocked(); 1043 Host host = lookupHostLocked(Binder.getCallingUid(), hostId); 1044 if (host != null) { 1045 host.callbacks = null; 1046 pruneHostLocked(host); 1047 } 1048 } 1049 } 1050 1051 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) { 1052 if (id.host.uid == callingUid) { 1053 // Apps hosting the AppWidget have access to it. 1054 return true; 1055 } 1056 if (id.provider != null && id.provider.uid == callingUid) { 1057 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound) 1058 return true; 1059 } 1060 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) { 1061 // Apps that can bind have access to all appWidgetIds. 1062 return true; 1063 } 1064 // Nobody else can access it. 1065 return false; 1066 } 1067 1068 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) { 1069 int callingUid = Binder.getCallingUid(); 1070 final int N = mAppWidgetIds.size(); 1071 for (int i = 0; i < N; i++) { 1072 AppWidgetId id = mAppWidgetIds.get(i); 1073 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) { 1074 return id; 1075 } 1076 } 1077 return null; 1078 } 1079 1080 Provider lookupProviderLocked(ComponentName provider) { 1081 final int N = mInstalledProviders.size(); 1082 for (int i = 0; i < N; i++) { 1083 Provider p = mInstalledProviders.get(i); 1084 if (p.info.provider.equals(provider)) { 1085 return p; 1086 } 1087 } 1088 return null; 1089 } 1090 1091 Host lookupHostLocked(int uid, int hostId) { 1092 final int N = mHosts.size(); 1093 for (int i = 0; i < N; i++) { 1094 Host h = mHosts.get(i); 1095 if (h.uid == uid && h.hostId == hostId) { 1096 return h; 1097 } 1098 } 1099 return null; 1100 } 1101 1102 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) { 1103 final int N = mHosts.size(); 1104 for (int i = 0; i < N; i++) { 1105 Host h = mHosts.get(i); 1106 if (h.hostId == hostId && h.packageName.equals(packageName)) { 1107 return h; 1108 } 1109 } 1110 Host host = new Host(); 1111 host.packageName = packageName; 1112 host.uid = uid; 1113 host.hostId = hostId; 1114 mHosts.add(host); 1115 return host; 1116 } 1117 1118 void pruneHostLocked(Host host) { 1119 if (host.instances.size() == 0 && host.callbacks == null) { 1120 mHosts.remove(host); 1121 } 1122 } 1123 1124 void loadAppWidgetList() { 1125 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1126 try { 1127 List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent, 1128 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 1129 PackageManager.GET_META_DATA, mUserId); 1130 1131 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1132 for (int i = 0; i < N; i++) { 1133 ResolveInfo ri = broadcastReceivers.get(i); 1134 addProviderLocked(ri); 1135 } 1136 } catch (RemoteException re) { 1137 // Shouldn't happen, local call 1138 } 1139 } 1140 1141 boolean addProviderLocked(ResolveInfo ri) { 1142 if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 1143 return false; 1144 } 1145 if (!ri.activityInfo.isEnabled()) { 1146 return false; 1147 } 1148 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName, 1149 ri.activityInfo.name), ri); 1150 if (p != null) { 1151 mInstalledProviders.add(p); 1152 return true; 1153 } else { 1154 return false; 1155 } 1156 } 1157 1158 void removeProviderLocked(int index, Provider p) { 1159 int N = p.instances.size(); 1160 for (int i = 0; i < N; i++) { 1161 AppWidgetId id = p.instances.get(i); 1162 // Call back with empty RemoteViews 1163 updateAppWidgetInstanceLocked(id, null); 1164 // Stop telling the host about updates for this from now on 1165 cancelBroadcasts(p); 1166 // clear out references to this appWidgetId 1167 id.host.instances.remove(id); 1168 mAppWidgetIds.remove(id); 1169 id.provider = null; 1170 pruneHostLocked(id.host); 1171 id.host = null; 1172 } 1173 p.instances.clear(); 1174 mInstalledProviders.remove(index); 1175 mDeletedProviders.add(p); 1176 // no need to send the DISABLE broadcast, since the receiver is gone anyway 1177 cancelBroadcasts(p); 1178 } 1179 1180 void sendEnableIntentLocked(Provider p) { 1181 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED); 1182 intent.setComponent(p.info.provider); 1183 mContext.sendBroadcast(intent, mUserId); 1184 } 1185 1186 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) { 1187 if (appWidgetIds != null && appWidgetIds.length > 0) { 1188 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1189 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 1190 intent.setComponent(p.info.provider); 1191 mContext.sendBroadcast(intent, mUserId); 1192 } 1193 } 1194 1195 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) { 1196 if (p.info.updatePeriodMillis > 0) { 1197 // if this is the first instance, set the alarm. otherwise, 1198 // rely on the fact that we've already set it and that 1199 // PendingIntent.getBroadcast will update the extras. 1200 boolean alreadyRegistered = p.broadcast != null; 1201 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1202 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 1203 intent.setComponent(p.info.provider); 1204 long token = Binder.clearCallingIdentity(); 1205 try { 1206 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent, 1207 PendingIntent.FLAG_UPDATE_CURRENT); 1208 } finally { 1209 Binder.restoreCallingIdentity(token); 1210 } 1211 if (!alreadyRegistered) { 1212 long period = p.info.updatePeriodMillis; 1213 if (period < MIN_UPDATE_PERIOD) { 1214 period = MIN_UPDATE_PERIOD; 1215 } 1216 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock 1217 .elapsedRealtime() 1218 + period, period, p.broadcast); 1219 } 1220 } 1221 } 1222 1223 static int[] getAppWidgetIds(Provider p) { 1224 int instancesSize = p.instances.size(); 1225 int appWidgetIds[] = new int[instancesSize]; 1226 for (int i = 0; i < instancesSize; i++) { 1227 appWidgetIds[i] = p.instances.get(i).appWidgetId; 1228 } 1229 return appWidgetIds; 1230 } 1231 1232 public int[] getAppWidgetIds(ComponentName provider) { 1233 synchronized (mAppWidgetIds) { 1234 ensureStateLoadedLocked(); 1235 Provider p = lookupProviderLocked(provider); 1236 if (p != null && Binder.getCallingUid() == p.uid) { 1237 return getAppWidgetIds(p); 1238 } else { 1239 return new int[0]; 1240 } 1241 } 1242 } 1243 1244 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) { 1245 Provider p = null; 1246 1247 ActivityInfo activityInfo = ri.activityInfo; 1248 XmlResourceParser parser = null; 1249 try { 1250 parser = activityInfo.loadXmlMetaData(mContext.getPackageManager(), 1251 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER); 1252 if (parser == null) { 1253 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER 1254 + " meta-data for " + "AppWidget provider '" + component + '\''); 1255 return null; 1256 } 1257 1258 AttributeSet attrs = Xml.asAttributeSet(parser); 1259 1260 int type; 1261 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1262 && type != XmlPullParser.START_TAG) { 1263 // drain whitespace, comments, etc. 1264 } 1265 1266 String nodeName = parser.getName(); 1267 if (!"appwidget-provider".equals(nodeName)) { 1268 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for" 1269 + " AppWidget provider '" + component + '\''); 1270 return null; 1271 } 1272 1273 p = new Provider(); 1274 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo(); 1275 info.provider = component; 1276 p.uid = activityInfo.applicationInfo.uid; 1277 1278 Resources res = mContext.getPackageManager() 1279 .getResourcesForApplication(activityInfo.applicationInfo); 1280 1281 TypedArray sa = res.obtainAttributes(attrs, 1282 com.android.internal.R.styleable.AppWidgetProviderInfo); 1283 1284 // These dimensions has to be resolved in the application's context. 1285 // We simply send back the raw complex data, which will be 1286 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}. 1287 TypedValue value = sa 1288 .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth); 1289 info.minWidth = value != null ? value.data : 0; 1290 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight); 1291 info.minHeight = value != null ? value.data : 0; 1292 value = sa.peekValue( 1293 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth); 1294 info.minResizeWidth = value != null ? value.data : info.minWidth; 1295 value = sa.peekValue( 1296 com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight); 1297 info.minResizeHeight = value != null ? value.data : info.minHeight; 1298 info.updatePeriodMillis = sa.getInt( 1299 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); 1300 info.initialLayout = sa.getResourceId( 1301 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0); 1302 String className = sa 1303 .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure); 1304 if (className != null) { 1305 info.configure = new ComponentName(component.getPackageName(), className); 1306 } 1307 info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString(); 1308 info.icon = ri.getIconResource(); 1309 info.previewImage = sa.getResourceId( 1310 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0); 1311 info.autoAdvanceViewId = sa.getResourceId( 1312 com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1); 1313 info.resizeMode = sa.getInt( 1314 com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode, 1315 AppWidgetProviderInfo.RESIZE_NONE); 1316 1317 sa.recycle(); 1318 } catch (Exception e) { 1319 // Ok to catch Exception here, because anything going wrong because 1320 // of what a client process passes to us should not be fatal for the 1321 // system process. 1322 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e); 1323 return null; 1324 } finally { 1325 if (parser != null) 1326 parser.close(); 1327 } 1328 return p; 1329 } 1330 1331 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException { 1332 PackageInfo pkgInfo = null; 1333 try { 1334 pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId); 1335 } catch (RemoteException re) { 1336 // Shouldn't happen, local call 1337 } 1338 if (pkgInfo == null || pkgInfo.applicationInfo == null) { 1339 throw new PackageManager.NameNotFoundException(); 1340 } 1341 return pkgInfo.applicationInfo.uid; 1342 } 1343 1344 int enforceCallingUid(String packageName) throws IllegalArgumentException { 1345 int callingUid = Binder.getCallingUid(); 1346 int packageUid; 1347 try { 1348 packageUid = getUidForPackage(packageName); 1349 } catch (PackageManager.NameNotFoundException ex) { 1350 throw new IllegalArgumentException("packageName and uid don't match packageName=" 1351 + packageName); 1352 } 1353 if (!UserId.isSameApp(callingUid, packageUid)) { 1354 throw new IllegalArgumentException("packageName and uid don't match packageName=" 1355 + packageName); 1356 } 1357 return callingUid; 1358 } 1359 1360 void sendInitialBroadcasts() { 1361 synchronized (mAppWidgetIds) { 1362 ensureStateLoadedLocked(); 1363 final int N = mInstalledProviders.size(); 1364 for (int i = 0; i < N; i++) { 1365 Provider p = mInstalledProviders.get(i); 1366 if (p.instances.size() > 0) { 1367 sendEnableIntentLocked(p); 1368 int[] appWidgetIds = getAppWidgetIds(p); 1369 sendUpdateIntentLocked(p, appWidgetIds); 1370 registerForBroadcastsLocked(p, appWidgetIds); 1371 } 1372 } 1373 } 1374 } 1375 1376 // only call from initialization -- it assumes that the data structures are all empty 1377 void loadStateLocked() { 1378 AtomicFile file = savedStateFile(); 1379 try { 1380 FileInputStream stream = file.openRead(); 1381 readStateFromFileLocked(stream); 1382 1383 if (stream != null) { 1384 try { 1385 stream.close(); 1386 } catch (IOException e) { 1387 Slog.w(TAG, "Failed to close state FileInputStream " + e); 1388 } 1389 } 1390 } catch (FileNotFoundException e) { 1391 Slog.w(TAG, "Failed to read state: " + e); 1392 } 1393 } 1394 1395 void saveStateLocked() { 1396 AtomicFile file = savedStateFile(); 1397 FileOutputStream stream; 1398 try { 1399 stream = file.startWrite(); 1400 if (writeStateToFileLocked(stream)) { 1401 file.finishWrite(stream); 1402 } else { 1403 file.failWrite(stream); 1404 Slog.w(TAG, "Failed to save state, restoring backup."); 1405 } 1406 } catch (IOException e) { 1407 Slog.w(TAG, "Failed open state file for write: " + e); 1408 } 1409 } 1410 1411 boolean writeStateToFileLocked(FileOutputStream stream) { 1412 int N; 1413 1414 try { 1415 XmlSerializer out = new FastXmlSerializer(); 1416 out.setOutput(stream, "utf-8"); 1417 out.startDocument(null, true); 1418 out.startTag(null, "gs"); 1419 1420 int providerIndex = 0; 1421 N = mInstalledProviders.size(); 1422 for (int i = 0; i < N; i++) { 1423 Provider p = mInstalledProviders.get(i); 1424 if (p.instances.size() > 0) { 1425 out.startTag(null, "p"); 1426 out.attribute(null, "pkg", p.info.provider.getPackageName()); 1427 out.attribute(null, "cl", p.info.provider.getClassName()); 1428 out.endTag(null, "p"); 1429 p.tag = providerIndex; 1430 providerIndex++; 1431 } 1432 } 1433 1434 N = mHosts.size(); 1435 for (int i = 0; i < N; i++) { 1436 Host host = mHosts.get(i); 1437 out.startTag(null, "h"); 1438 out.attribute(null, "pkg", host.packageName); 1439 out.attribute(null, "id", Integer.toHexString(host.hostId)); 1440 out.endTag(null, "h"); 1441 host.tag = i; 1442 } 1443 1444 N = mAppWidgetIds.size(); 1445 for (int i = 0; i < N; i++) { 1446 AppWidgetId id = mAppWidgetIds.get(i); 1447 out.startTag(null, "g"); 1448 out.attribute(null, "id", Integer.toHexString(id.appWidgetId)); 1449 out.attribute(null, "h", Integer.toHexString(id.host.tag)); 1450 if (id.provider != null) { 1451 out.attribute(null, "p", Integer.toHexString(id.provider.tag)); 1452 } 1453 out.endTag(null, "g"); 1454 } 1455 1456 Iterator<String> it = mPackagesWithBindWidgetPermission.iterator(); 1457 while (it.hasNext()) { 1458 out.startTag(null, "b"); 1459 out.attribute(null, "packageName", it.next()); 1460 out.endTag(null, "b"); 1461 } 1462 1463 out.endTag(null, "gs"); 1464 1465 out.endDocument(); 1466 return true; 1467 } catch (IOException e) { 1468 Slog.w(TAG, "Failed to write state: " + e); 1469 return false; 1470 } 1471 } 1472 1473 void readStateFromFileLocked(FileInputStream stream) { 1474 boolean success = false; 1475 try { 1476 XmlPullParser parser = Xml.newPullParser(); 1477 parser.setInput(stream, null); 1478 1479 int type; 1480 int providerIndex = 0; 1481 HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>(); 1482 do { 1483 type = parser.next(); 1484 if (type == XmlPullParser.START_TAG) { 1485 String tag = parser.getName(); 1486 if ("p".equals(tag)) { 1487 // TODO: do we need to check that this package has the same signature 1488 // as before? 1489 String pkg = parser.getAttributeValue(null, "pkg"); 1490 String cl = parser.getAttributeValue(null, "cl"); 1491 1492 final PackageManager packageManager = mContext.getPackageManager(); 1493 try { 1494 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0); 1495 } catch (PackageManager.NameNotFoundException e) { 1496 String[] pkgs = packageManager 1497 .currentToCanonicalPackageNames(new String[] { pkg }); 1498 pkg = pkgs[0]; 1499 } 1500 1501 Provider p = lookupProviderLocked(new ComponentName(pkg, cl)); 1502 if (p == null && mSafeMode) { 1503 // if we're in safe mode, make a temporary one 1504 p = new Provider(); 1505 p.info = new AppWidgetProviderInfo(); 1506 p.info.provider = new ComponentName(pkg, cl); 1507 p.zombie = true; 1508 mInstalledProviders.add(p); 1509 } 1510 if (p != null) { 1511 // if it wasn't uninstalled or something 1512 loadedProviders.put(providerIndex, p); 1513 } 1514 providerIndex++; 1515 } else if ("h".equals(tag)) { 1516 Host host = new Host(); 1517 1518 // TODO: do we need to check that this package has the same signature 1519 // as before? 1520 host.packageName = parser.getAttributeValue(null, "pkg"); 1521 try { 1522 host.uid = getUidForPackage(host.packageName); 1523 } catch (PackageManager.NameNotFoundException ex) { 1524 host.zombie = true; 1525 } 1526 if (!host.zombie || mSafeMode) { 1527 // In safe mode, we don't discard the hosts we don't recognize 1528 // so that they're not pruned from our list. Otherwise, we do. 1529 host.hostId = Integer 1530 .parseInt(parser.getAttributeValue(null, "id"), 16); 1531 mHosts.add(host); 1532 } 1533 } else if ("b".equals(tag)) { 1534 String packageName = parser.getAttributeValue(null, "packageName"); 1535 if (packageName != null) { 1536 mPackagesWithBindWidgetPermission.add(packageName); 1537 } 1538 } else if ("g".equals(tag)) { 1539 AppWidgetId id = new AppWidgetId(); 1540 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16); 1541 if (id.appWidgetId >= mNextAppWidgetId) { 1542 mNextAppWidgetId = id.appWidgetId + 1; 1543 } 1544 1545 String providerString = parser.getAttributeValue(null, "p"); 1546 if (providerString != null) { 1547 // there's no provider if it hasn't been bound yet. 1548 // maybe we don't have to save this, but it brings the system 1549 // to the state it was in. 1550 int pIndex = Integer.parseInt(providerString, 16); 1551 id.provider = loadedProviders.get(pIndex); 1552 if (false) { 1553 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider " 1554 + pIndex + " which is " + id.provider); 1555 } 1556 if (id.provider == null) { 1557 // This provider is gone. We just let the host figure out 1558 // that this happened when it fails to load it. 1559 continue; 1560 } 1561 } 1562 1563 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16); 1564 id.host = mHosts.get(hIndex); 1565 if (id.host == null) { 1566 // This host is gone. 1567 continue; 1568 } 1569 1570 if (id.provider != null) { 1571 id.provider.instances.add(id); 1572 } 1573 id.host.instances.add(id); 1574 mAppWidgetIds.add(id); 1575 } 1576 } 1577 } while (type != XmlPullParser.END_DOCUMENT); 1578 success = true; 1579 } catch (NullPointerException e) { 1580 Slog.w(TAG, "failed parsing " + e); 1581 } catch (NumberFormatException e) { 1582 Slog.w(TAG, "failed parsing " + e); 1583 } catch (XmlPullParserException e) { 1584 Slog.w(TAG, "failed parsing " + e); 1585 } catch (IOException e) { 1586 Slog.w(TAG, "failed parsing " + e); 1587 } catch (IndexOutOfBoundsException e) { 1588 Slog.w(TAG, "failed parsing " + e); 1589 } 1590 1591 if (success) { 1592 // delete any hosts that didn't manage to get connected (should happen) 1593 // if it matters, they'll be reconnected. 1594 for (int i = mHosts.size() - 1; i >= 0; i--) { 1595 pruneHostLocked(mHosts.get(i)); 1596 } 1597 } else { 1598 // failed reading, clean up 1599 Slog.w(TAG, "Failed to read state, clearing widgets and hosts."); 1600 1601 mAppWidgetIds.clear(); 1602 mHosts.clear(); 1603 final int N = mInstalledProviders.size(); 1604 for (int i = 0; i < N; i++) { 1605 mInstalledProviders.get(i).instances.clear(); 1606 } 1607 } 1608 } 1609 1610 static File getSettingsFile(int userId) { 1611 return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME); 1612 } 1613 1614 AtomicFile savedStateFile() { 1615 File dir = new File("/data/system/users/" + mUserId); 1616 File settingsFile = getSettingsFile(mUserId); 1617 if (!settingsFile.exists() && mUserId == 0) { 1618 if (!dir.exists()) { 1619 dir.mkdirs(); 1620 } 1621 // Migrate old data 1622 File oldFile = new File("/data/system/" + SETTINGS_FILENAME); 1623 // Method doesn't throw an exception on failure. Ignore any errors 1624 // in moving the file (like non-existence) 1625 oldFile.renameTo(settingsFile); 1626 } 1627 return new AtomicFile(settingsFile); 1628 } 1629 1630 void onUserRemoved() { 1631 // prune the ones we don't want to keep 1632 int N = mInstalledProviders.size(); 1633 for (int i = N - 1; i >= 0; i--) { 1634 Provider p = mInstalledProviders.get(i); 1635 cancelBroadcasts(p); 1636 } 1637 getSettingsFile(mUserId).delete(); 1638 } 1639 1640 void addProvidersForPackageLocked(String pkgName) { 1641 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1642 intent.setPackage(pkgName); 1643 List<ResolveInfo> broadcastReceivers; 1644 try { 1645 broadcastReceivers = mPm.queryIntentReceivers(intent, 1646 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 1647 PackageManager.GET_META_DATA, mUserId); 1648 } catch (RemoteException re) { 1649 // Shouldn't happen, local call 1650 return; 1651 } 1652 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1653 for (int i = 0; i < N; i++) { 1654 ResolveInfo ri = broadcastReceivers.get(i); 1655 ActivityInfo ai = ri.activityInfo; 1656 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 1657 continue; 1658 } 1659 if (pkgName.equals(ai.packageName)) { 1660 addProviderLocked(ri); 1661 } 1662 } 1663 } 1664 1665 void updateProvidersForPackageLocked(String pkgName) { 1666 HashSet<String> keep = new HashSet<String>(); 1667 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1668 intent.setPackage(pkgName); 1669 List<ResolveInfo> broadcastReceivers; 1670 try { 1671 broadcastReceivers = mPm.queryIntentReceivers(intent, 1672 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 1673 PackageManager.GET_META_DATA, mUserId); 1674 } catch (RemoteException re) { 1675 // Shouldn't happen, local call 1676 return; 1677 } 1678 1679 // add the missing ones and collect which ones to keep 1680 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1681 for (int i = 0; i < N; i++) { 1682 ResolveInfo ri = broadcastReceivers.get(i); 1683 ActivityInfo ai = ri.activityInfo; 1684 if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 1685 continue; 1686 } 1687 if (pkgName.equals(ai.packageName)) { 1688 ComponentName component = new ComponentName(ai.packageName, ai.name); 1689 Provider p = lookupProviderLocked(component); 1690 if (p == null) { 1691 if (addProviderLocked(ri)) { 1692 keep.add(ai.name); 1693 } 1694 } else { 1695 Provider parsed = parseProviderInfoXml(component, ri); 1696 if (parsed != null) { 1697 keep.add(ai.name); 1698 // Use the new AppWidgetProviderInfo. 1699 p.info = parsed.info; 1700 // If it's enabled 1701 final int M = p.instances.size(); 1702 if (M > 0) { 1703 int[] appWidgetIds = getAppWidgetIds(p); 1704 // Reschedule for the new updatePeriodMillis (don't worry about handling 1705 // it specially if updatePeriodMillis didn't change because we just sent 1706 // an update, and the next one will be updatePeriodMillis from now). 1707 cancelBroadcasts(p); 1708 registerForBroadcastsLocked(p, appWidgetIds); 1709 // If it's currently showing, call back with the new 1710 // AppWidgetProviderInfo. 1711 for (int j = 0; j < M; j++) { 1712 AppWidgetId id = p.instances.get(j); 1713 id.views = null; 1714 if (id.host != null && id.host.callbacks != null) { 1715 try { 1716 id.host.callbacks.providerChanged(id.appWidgetId, p.info); 1717 } catch (RemoteException ex) { 1718 // It failed; remove the callback. No need to prune because 1719 // we know that this host is still referenced by this 1720 // instance. 1721 id.host.callbacks = null; 1722 } 1723 } 1724 } 1725 // Now that we've told the host, push out an update. 1726 sendUpdateIntentLocked(p, appWidgetIds); 1727 } 1728 } 1729 } 1730 } 1731 } 1732 1733 // prune the ones we don't want to keep 1734 N = mInstalledProviders.size(); 1735 for (int i = N - 1; i >= 0; i--) { 1736 Provider p = mInstalledProviders.get(i); 1737 if (pkgName.equals(p.info.provider.getPackageName()) 1738 && !keep.contains(p.info.provider.getClassName())) { 1739 removeProviderLocked(i, p); 1740 } 1741 } 1742 } 1743 1744 void removeProvidersForPackageLocked(String pkgName) { 1745 int N = mInstalledProviders.size(); 1746 for (int i = N - 1; i >= 0; i--) { 1747 Provider p = mInstalledProviders.get(i); 1748 if (pkgName.equals(p.info.provider.getPackageName())) { 1749 removeProviderLocked(i, p); 1750 } 1751 } 1752 1753 // Delete the hosts for this package too 1754 // 1755 // By now, we have removed any AppWidgets that were in any hosts here, 1756 // so we don't need to worry about sending DISABLE broadcasts to them. 1757 N = mHosts.size(); 1758 for (int i = N - 1; i >= 0; i--) { 1759 Host host = mHosts.get(i); 1760 if (pkgName.equals(host.packageName)) { 1761 deleteHostLocked(host); 1762 } 1763 } 1764 } 1765} 1766