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