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