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