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