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