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