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