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