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