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