AppWidgetService.java revision d2db2a579440608453994b64eb5b425840f5307a
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 updateAppWidgetProvider(ComponentName provider, RemoteViews views) { 429 synchronized (mAppWidgetIds) { 430 Provider p = lookupProviderLocked(provider); 431 if (p == null) { 432 Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider); 433 return; 434 } 435 ArrayList<AppWidgetId> instances = p.instances; 436 final int N = instances.size(); 437 for (int i=0; i<N; i++) { 438 AppWidgetId id = instances.get(i); 439 updateAppWidgetInstanceLocked(id, views); 440 } 441 } 442 } 443 444 void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) { 445 // allow for stale appWidgetIds and other badness 446 // lookup also checks that the calling process can access the appWidgetId 447 // drop unbound appWidgetIds (shouldn't be possible under normal circumstances) 448 if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) { 449 id.views = views; 450 451 // is anyone listening? 452 if (id.host.callbacks != null) { 453 try { 454 // the lock is held, but this is a oneway call 455 id.host.callbacks.updateAppWidget(id.appWidgetId, views); 456 } catch (RemoteException e) { 457 // It failed; remove the callback. No need to prune because 458 // we know that this host is still referenced by this instance. 459 id.host.callbacks = null; 460 } 461 } 462 } 463 } 464 465 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId, 466 List<RemoteViews> updatedViews) { 467 int callingUid = enforceCallingUid(packageName); 468 synchronized (mAppWidgetIds) { 469 Host host = lookupOrAddHostLocked(callingUid, packageName, hostId); 470 host.callbacks = callbacks; 471 472 updatedViews.clear(); 473 474 ArrayList<AppWidgetId> instances = host.instances; 475 int N = instances.size(); 476 int[] updatedIds = new int[N]; 477 for (int i=0; i<N; i++) { 478 AppWidgetId id = instances.get(i); 479 updatedIds[i] = id.appWidgetId; 480 updatedViews.add(id.views); 481 } 482 return updatedIds; 483 } 484 } 485 486 public void stopListening(int hostId) { 487 synchronized (mAppWidgetIds) { 488 Host host = lookupHostLocked(getCallingUid(), hostId); 489 if (host != null) { 490 host.callbacks = null; 491 pruneHostLocked(host); 492 } 493 } 494 } 495 496 boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) { 497 if (id.host.uid == callingUid) { 498 // Apps hosting the AppWidget have access to it. 499 return true; 500 } 501 if (id.provider != null && id.provider.uid == callingUid) { 502 // Apps providing the AppWidget have access to it (if the appWidgetId has been bound) 503 return true; 504 } 505 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) 506 == PackageManager.PERMISSION_GRANTED) { 507 // Apps that can bind have access to all appWidgetIds. 508 return true; 509 } 510 // Nobody else can access it. 511 return false; 512 } 513 514 AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) { 515 int callingUid = getCallingUid(); 516 final int N = mAppWidgetIds.size(); 517 for (int i=0; i<N; i++) { 518 AppWidgetId id = mAppWidgetIds.get(i); 519 if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) { 520 return id; 521 } 522 } 523 return null; 524 } 525 526 Provider lookupProviderLocked(ComponentName provider) { 527 final String className = provider.getClassName(); 528 final int N = mInstalledProviders.size(); 529 for (int i=0; i<N; i++) { 530 Provider p = mInstalledProviders.get(i); 531 if (p.info.provider.equals(provider) || className.equals(p.info.oldName)) { 532 return p; 533 } 534 } 535 return null; 536 } 537 538 Host lookupHostLocked(int uid, int hostId) { 539 final int N = mHosts.size(); 540 for (int i=0; i<N; i++) { 541 Host h = mHosts.get(i); 542 if (h.uid == uid && h.hostId == hostId) { 543 return h; 544 } 545 } 546 return null; 547 } 548 549 Host lookupOrAddHostLocked(int uid, String packageName, int hostId) { 550 final int N = mHosts.size(); 551 for (int i=0; i<N; i++) { 552 Host h = mHosts.get(i); 553 if (h.hostId == hostId && h.packageName.equals(packageName)) { 554 return h; 555 } 556 } 557 Host host = new Host(); 558 host.packageName = packageName; 559 host.uid = uid; 560 host.hostId = hostId; 561 mHosts.add(host); 562 return host; 563 } 564 565 void pruneHostLocked(Host host) { 566 if (host.instances.size() == 0 && host.callbacks == null) { 567 mHosts.remove(host); 568 } 569 } 570 571 void loadAppWidgetList() { 572 PackageManager pm = mPackageManager; 573 574 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 575 List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent, 576 PackageManager.GET_META_DATA); 577 578 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 579 for (int i=0; i<N; i++) { 580 ResolveInfo ri = broadcastReceivers.get(i); 581 addProviderLocked(ri); 582 } 583 } 584 585 boolean addProviderLocked(ResolveInfo ri) { 586 Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName, 587 ri.activityInfo.name), ri); 588 if (p != null) { 589 mInstalledProviders.add(p); 590 return true; 591 } else { 592 return false; 593 } 594 } 595 596 void removeProviderLocked(int index, Provider p) { 597 int N = p.instances.size(); 598 for (int i=0; i<N; i++) { 599 AppWidgetId id = p.instances.get(i); 600 // Call back with empty RemoteViews 601 updateAppWidgetInstanceLocked(id, null); 602 // Stop telling the host about updates for this from now on 603 cancelBroadcasts(p); 604 // clear out references to this appWidgetId 605 id.host.instances.remove(id); 606 mAppWidgetIds.remove(id); 607 id.provider = null; 608 pruneHostLocked(id.host); 609 id.host = null; 610 } 611 p.instances.clear(); 612 mInstalledProviders.remove(index); 613 // no need to send the DISABLE broadcast, since the receiver is gone anyway 614 cancelBroadcasts(p); 615 } 616 617 void sendEnableIntentLocked(Provider p) { 618 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED); 619 intent.setComponent(p.info.provider); 620 mContext.sendBroadcast(intent); 621 } 622 623 void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) { 624 if (appWidgetIds != null && appWidgetIds.length > 0) { 625 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 626 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 627 intent.setComponent(p.info.provider); 628 mContext.sendBroadcast(intent); 629 } 630 } 631 632 void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) { 633 if (p.info.updatePeriodMillis > 0) { 634 // if this is the first instance, set the alarm. otherwise, 635 // rely on the fact that we've already set it and that 636 // PendingIntent.getBroadcast will update the extras. 637 boolean alreadyRegistered = p.broadcast != null; 638 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 639 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 640 intent.setComponent(p.info.provider); 641 long token = Binder.clearCallingIdentity(); 642 try { 643 p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent, 644 PendingIntent.FLAG_UPDATE_CURRENT); 645 } finally { 646 Binder.restoreCallingIdentity(token); 647 } 648 if (!alreadyRegistered) { 649 long period = p.info.updatePeriodMillis; 650 if (period < MIN_UPDATE_PERIOD) { 651 period = MIN_UPDATE_PERIOD; 652 } 653 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 654 SystemClock.elapsedRealtime() + period, period, p.broadcast); 655 } 656 } 657 } 658 659 static int[] getAppWidgetIds(Provider p) { 660 int instancesSize = p.instances.size(); 661 int appWidgetIds[] = new int[instancesSize]; 662 for (int i=0; i<instancesSize; i++) { 663 appWidgetIds[i] = p.instances.get(i).appWidgetId; 664 } 665 return appWidgetIds; 666 } 667 668 public int[] getAppWidgetIds(ComponentName provider) { 669 synchronized (mAppWidgetIds) { 670 Provider p = lookupProviderLocked(provider); 671 if (p != null && getCallingUid() == p.uid) { 672 return getAppWidgetIds(p); 673 } else { 674 return new int[0]; 675 } 676 } 677 } 678 679 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) { 680 Provider p = null; 681 682 ActivityInfo activityInfo = ri.activityInfo; 683 XmlResourceParser parser = null; 684 try { 685 parser = activityInfo.loadXmlMetaData(mPackageManager, 686 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER); 687 if (parser == null) { 688 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for " 689 + "AppWidget provider '" + component + '\''); 690 return null; 691 } 692 693 AttributeSet attrs = Xml.asAttributeSet(parser); 694 695 int type; 696 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 697 && type != XmlPullParser.START_TAG) { 698 // drain whitespace, comments, etc. 699 } 700 701 String nodeName = parser.getName(); 702 if (!"appwidget-provider".equals(nodeName)) { 703 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for" 704 + " AppWidget provider '" + component + '\''); 705 return null; 706 } 707 708 p = new Provider(); 709 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo(); 710 // If metaData was null, we would have returned earlier when getting 711 // the parser No need to do the check here 712 info.oldName = activityInfo.metaData.getString( 713 AppWidgetManager.META_DATA_APPWIDGET_OLD_NAME); 714 715 info.provider = component; 716 p.uid = activityInfo.applicationInfo.uid; 717 718 Resources res = mPackageManager.getResourcesForApplication( 719 activityInfo.applicationInfo); 720 721 TypedArray sa = res.obtainAttributes(attrs, 722 com.android.internal.R.styleable.AppWidgetProviderInfo); 723 724 // These dimensions has to be resolved in the application's context. 725 // We simply send back the raw complex data, which will be 726 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}. 727 TypedValue value = sa.peekValue( 728 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth); 729 info.minWidth = value != null ? value.data : 0; 730 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight); 731 info.minHeight = value != null ? value.data : 0; 732 733 info.updatePeriodMillis = sa.getInt( 734 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); 735 info.initialLayout = sa.getResourceId( 736 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0); 737 String className = sa.getString( 738 com.android.internal.R.styleable.AppWidgetProviderInfo_configure); 739 if (className != null) { 740 info.configure = new ComponentName(component.getPackageName(), className); 741 } 742 info.label = activityInfo.loadLabel(mPackageManager).toString(); 743 info.icon = ri.getIconResource(); 744 info.previewImage = sa.getResourceId( 745 com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0); 746 747 sa.recycle(); 748 } catch (Exception e) { 749 // Ok to catch Exception here, because anything going wrong because 750 // of what a client process passes to us should not be fatal for the 751 // system process. 752 Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e); 753 return null; 754 } finally { 755 if (parser != null) parser.close(); 756 } 757 return p; 758 } 759 760 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException { 761 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0); 762 if (pkgInfo == null || pkgInfo.applicationInfo == null) { 763 throw new PackageManager.NameNotFoundException(); 764 } 765 return pkgInfo.applicationInfo.uid; 766 } 767 768 int enforceCallingUid(String packageName) throws IllegalArgumentException { 769 int callingUid = getCallingUid(); 770 int packageUid; 771 try { 772 packageUid = getUidForPackage(packageName); 773 } catch (PackageManager.NameNotFoundException ex) { 774 throw new IllegalArgumentException("packageName and uid don't match packageName=" 775 + packageName); 776 } 777 if (callingUid != packageUid && Process.supportsProcesses()) { 778 throw new IllegalArgumentException("packageName and uid don't match packageName=" 779 + packageName); 780 } 781 return callingUid; 782 } 783 784 void sendInitialBroadcasts() { 785 synchronized (mAppWidgetIds) { 786 final int N = mInstalledProviders.size(); 787 for (int i=0; i<N; i++) { 788 Provider p = mInstalledProviders.get(i); 789 if (p.instances.size() > 0) { 790 sendEnableIntentLocked(p); 791 int[] appWidgetIds = getAppWidgetIds(p); 792 sendUpdateIntentLocked(p, appWidgetIds); 793 registerForBroadcastsLocked(p, appWidgetIds); 794 } 795 } 796 } 797 } 798 799 // only call from initialization -- it assumes that the data structures are all empty 800 void loadStateLocked() { 801 File temp = savedStateTempFile(); 802 File real = savedStateRealFile(); 803 804 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the 805 // real one. if there is both a real file and a temp one, assume that the temp one isn't 806 // fully written and delete it. 807 if (real.exists()) { 808 readStateFromFileLocked(real); 809 if (temp.exists()) { 810 //noinspection ResultOfMethodCallIgnored 811 temp.delete(); 812 } 813 } else if (temp.exists()) { 814 readStateFromFileLocked(temp); 815 //noinspection ResultOfMethodCallIgnored 816 temp.renameTo(real); 817 } 818 } 819 820 void saveStateLocked() { 821 File temp = savedStateTempFile(); 822 File real = savedStateRealFile(); 823 824 if (!real.exists()) { 825 // If the real one doesn't exist, it's either because this is the first time 826 // or because something went wrong while copying them. In this case, we can't 827 // trust anything that's in temp. In order to have the loadState code not 828 // use the temporary one until it's fully written, create an empty file 829 // for real, which will we'll shortly delete. 830 try { 831 //noinspection ResultOfMethodCallIgnored 832 real.createNewFile(); 833 } catch (IOException e) { 834 // Ignore 835 } 836 } 837 838 if (temp.exists()) { 839 //noinspection ResultOfMethodCallIgnored 840 temp.delete(); 841 } 842 843 if (!writeStateToFileLocked(temp)) { 844 Slog.w(TAG, "Failed to persist new settings"); 845 return; 846 } 847 848 //noinspection ResultOfMethodCallIgnored 849 real.delete(); 850 //noinspection ResultOfMethodCallIgnored 851 temp.renameTo(real); 852 } 853 854 boolean writeStateToFileLocked(File file) { 855 FileOutputStream stream = null; 856 int N; 857 858 try { 859 stream = new FileOutputStream(file, false); 860 XmlSerializer out = new FastXmlSerializer(); 861 out.setOutput(stream, "utf-8"); 862 out.startDocument(null, true); 863 864 865 out.startTag(null, "gs"); 866 867 int providerIndex = 0; 868 N = mInstalledProviders.size(); 869 for (int i=0; i<N; i++) { 870 Provider p = mInstalledProviders.get(i); 871 if (p.instances.size() > 0) { 872 out.startTag(null, "p"); 873 out.attribute(null, "pkg", p.info.provider.getPackageName()); 874 out.attribute(null, "cl", p.info.provider.getClassName()); 875 out.endTag(null, "p"); 876 p.tag = providerIndex; 877 providerIndex++; 878 } 879 } 880 881 N = mHosts.size(); 882 for (int i=0; i<N; i++) { 883 Host host = mHosts.get(i); 884 out.startTag(null, "h"); 885 out.attribute(null, "pkg", host.packageName); 886 out.attribute(null, "id", Integer.toHexString(host.hostId)); 887 out.endTag(null, "h"); 888 host.tag = i; 889 } 890 891 N = mAppWidgetIds.size(); 892 for (int i=0; i<N; i++) { 893 AppWidgetId id = mAppWidgetIds.get(i); 894 out.startTag(null, "g"); 895 out.attribute(null, "id", Integer.toHexString(id.appWidgetId)); 896 out.attribute(null, "h", Integer.toHexString(id.host.tag)); 897 if (id.provider != null) { 898 out.attribute(null, "p", Integer.toHexString(id.provider.tag)); 899 } 900 out.endTag(null, "g"); 901 } 902 903 out.endTag(null, "gs"); 904 905 out.endDocument(); 906 stream.close(); 907 return true; 908 } catch (IOException e) { 909 try { 910 if (stream != null) { 911 stream.close(); 912 } 913 } catch (IOException ex) { 914 // Ignore 915 } 916 if (file.exists()) { 917 //noinspection ResultOfMethodCallIgnored 918 file.delete(); 919 } 920 return false; 921 } 922 } 923 924 void readStateFromFileLocked(File file) { 925 FileInputStream stream = null; 926 927 boolean success = false; 928 929 try { 930 stream = new FileInputStream(file); 931 XmlPullParser parser = Xml.newPullParser(); 932 parser.setInput(stream, null); 933 934 int type; 935 int providerIndex = 0; 936 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>(); 937 do { 938 type = parser.next(); 939 if (type == XmlPullParser.START_TAG) { 940 String tag = parser.getName(); 941 if ("p".equals(tag)) { 942 // TODO: do we need to check that this package has the same signature 943 // as before? 944 String pkg = parser.getAttributeValue(null, "pkg"); 945 String cl = parser.getAttributeValue(null, "cl"); 946 947 final PackageManager packageManager = mContext.getPackageManager(); 948 try { 949 packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0); 950 } catch (PackageManager.NameNotFoundException e) { 951 String[] pkgs = packageManager.currentToCanonicalPackageNames( 952 new String[] { pkg }); 953 pkg = pkgs[0]; 954 } 955 956 Provider p = lookupProviderLocked(new ComponentName(pkg, cl)); 957 if (p == null && mSafeMode) { 958 // if we're in safe mode, make a temporary one 959 p = new Provider(); 960 p.info = new AppWidgetProviderInfo(); 961 p.info.provider = new ComponentName(pkg, cl); 962 p.zombie = true; 963 mInstalledProviders.add(p); 964 } 965 if (p != null) { 966 // if it wasn't uninstalled or something 967 loadedProviders.put(providerIndex, p); 968 } 969 providerIndex++; 970 } 971 else if ("h".equals(tag)) { 972 Host host = new Host(); 973 974 // TODO: do we need to check that this package has the same signature 975 // as before? 976 host.packageName = parser.getAttributeValue(null, "pkg"); 977 try { 978 host.uid = getUidForPackage(host.packageName); 979 } catch (PackageManager.NameNotFoundException ex) { 980 host.zombie = true; 981 } 982 if (!host.zombie || mSafeMode) { 983 // In safe mode, we don't discard the hosts we don't recognize 984 // so that they're not pruned from our list. Otherwise, we do. 985 host.hostId = Integer.parseInt( 986 parser.getAttributeValue(null, "id"), 16); 987 mHosts.add(host); 988 } 989 } 990 else if ("g".equals(tag)) { 991 AppWidgetId id = new AppWidgetId(); 992 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16); 993 if (id.appWidgetId >= mNextAppWidgetId) { 994 mNextAppWidgetId = id.appWidgetId + 1; 995 } 996 997 String providerString = parser.getAttributeValue(null, "p"); 998 if (providerString != null) { 999 // there's no provider if it hasn't been bound yet. 1000 // maybe we don't have to save this, but it brings the system 1001 // to the state it was in. 1002 int pIndex = Integer.parseInt(providerString, 16); 1003 id.provider = loadedProviders.get(pIndex); 1004 if (false) { 1005 Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider " 1006 + pIndex + " which is " + id.provider); 1007 } 1008 if (id.provider == null) { 1009 // This provider is gone. We just let the host figure out 1010 // that this happened when it fails to load it. 1011 continue; 1012 } 1013 } 1014 1015 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16); 1016 id.host = mHosts.get(hIndex); 1017 if (id.host == null) { 1018 // This host is gone. 1019 continue; 1020 } 1021 1022 if (id.provider != null) { 1023 id.provider.instances.add(id); 1024 } 1025 id.host.instances.add(id); 1026 mAppWidgetIds.add(id); 1027 } 1028 } 1029 } while (type != XmlPullParser.END_DOCUMENT); 1030 success = true; 1031 } catch (NullPointerException e) { 1032 Slog.w(TAG, "failed parsing " + file, e); 1033 } catch (NumberFormatException e) { 1034 Slog.w(TAG, "failed parsing " + file, e); 1035 } catch (XmlPullParserException e) { 1036 Slog.w(TAG, "failed parsing " + file, e); 1037 } catch (IOException e) { 1038 Slog.w(TAG, "failed parsing " + file, e); 1039 } catch (IndexOutOfBoundsException e) { 1040 Slog.w(TAG, "failed parsing " + file, e); 1041 } 1042 try { 1043 if (stream != null) { 1044 stream.close(); 1045 } 1046 } catch (IOException e) { 1047 // Ignore 1048 } 1049 1050 if (success) { 1051 // delete any hosts that didn't manage to get connected (should happen) 1052 // if it matters, they'll be reconnected. 1053 for (int i=mHosts.size()-1; i>=0; i--) { 1054 pruneHostLocked(mHosts.get(i)); 1055 } 1056 } else { 1057 // failed reading, clean up 1058 mAppWidgetIds.clear(); 1059 mHosts.clear(); 1060 final int N = mInstalledProviders.size(); 1061 for (int i=0; i<N; i++) { 1062 mInstalledProviders.get(i).instances.clear(); 1063 } 1064 } 1065 } 1066 1067 File savedStateTempFile() { 1068 return new File("/data/system/" + SETTINGS_TMP_FILENAME); 1069 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME); 1070 } 1071 1072 File savedStateRealFile() { 1073 return new File("/data/system/" + SETTINGS_FILENAME); 1074 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME); 1075 } 1076 1077 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1078 public void onReceive(Context context, Intent intent) { 1079 String action = intent.getAction(); 1080 //Slog.d(TAG, "received " + action); 1081 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { 1082 sendInitialBroadcasts(); 1083 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { 1084 Locale revised = Locale.getDefault(); 1085 if (revised == null || mLocale == null || 1086 !(revised.equals(mLocale))) { 1087 mLocale = revised; 1088 1089 synchronized (mAppWidgetIds) { 1090 int N = mInstalledProviders.size(); 1091 for (int i=N-1; i>=0; i--) { 1092 Provider p = mInstalledProviders.get(i); 1093 String pkgName = p.info.provider.getPackageName(); 1094 updateProvidersForPackageLocked(pkgName); 1095 } 1096 saveStateLocked(); 1097 } 1098 } 1099 } else { 1100 boolean added = false; 1101 String pkgList[] = null; 1102 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { 1103 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1104 added = true; 1105 } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { 1106 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1107 added = false; 1108 } else { 1109 Uri uri = intent.getData(); 1110 if (uri == null) { 1111 return; 1112 } 1113 String pkgName = uri.getSchemeSpecificPart(); 1114 if (pkgName == null) { 1115 return; 1116 } 1117 pkgList = new String[] { pkgName }; 1118 added = Intent.ACTION_PACKAGE_ADDED.equals(action); 1119 } 1120 if (pkgList == null || pkgList.length == 0) { 1121 return; 1122 } 1123 if (added) { 1124 synchronized (mAppWidgetIds) { 1125 Bundle extras = intent.getExtras(); 1126 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { 1127 for (String pkgName : pkgList) { 1128 // The package was just upgraded 1129 updateProvidersForPackageLocked(pkgName); 1130 } 1131 } else { 1132 // The package was just added 1133 for (String pkgName : pkgList) { 1134 addProvidersForPackageLocked(pkgName); 1135 } 1136 } 1137 saveStateLocked(); 1138 } 1139 } else { 1140 Bundle extras = intent.getExtras(); 1141 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { 1142 // The package is being updated. We'll receive a PACKAGE_ADDED shortly. 1143 } else { 1144 synchronized (mAppWidgetIds) { 1145 for (String pkgName : pkgList) { 1146 removeProvidersForPackageLocked(pkgName); 1147 saveStateLocked(); 1148 } 1149 } 1150 } 1151 } 1152 } 1153 } 1154 }; 1155 1156 void addProvidersForPackageLocked(String pkgName) { 1157 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1158 intent.setPackage(pkgName); 1159 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, 1160 PackageManager.GET_META_DATA); 1161 1162 final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1163 for (int i=0; i<N; i++) { 1164 ResolveInfo ri = broadcastReceivers.get(i); 1165 ActivityInfo ai = ri.activityInfo; 1166 1167 if (pkgName.equals(ai.packageName)) { 1168 addProviderLocked(ri); 1169 } 1170 } 1171 } 1172 1173 void updateProvidersForPackageLocked(String pkgName) { 1174 HashSet<String> keep = new HashSet<String>(); 1175 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1176 intent.setPackage(pkgName); 1177 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, 1178 PackageManager.GET_META_DATA); 1179 1180 // add the missing ones and collect which ones to keep 1181 int N = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 1182 for (int i=0; i<N; i++) { 1183 ResolveInfo ri = broadcastReceivers.get(i); 1184 ActivityInfo ai = ri.activityInfo; 1185 if (pkgName.equals(ai.packageName)) { 1186 ComponentName component = new ComponentName(ai.packageName, ai.name); 1187 Provider p = lookupProviderLocked(component); 1188 if (p == null) { 1189 if (addProviderLocked(ri)) { 1190 keep.add(ai.name); 1191 } 1192 } else { 1193 Provider parsed = parseProviderInfoXml(component, ri); 1194 if (parsed != null) { 1195 keep.add(ai.name); 1196 // Use the new AppWidgetProviderInfo. 1197 p.info = parsed.info; 1198 // If it's enabled 1199 final int M = p.instances.size(); 1200 if (M > 0) { 1201 int[] appWidgetIds = getAppWidgetIds(p); 1202 // Reschedule for the new updatePeriodMillis (don't worry about handling 1203 // it specially if updatePeriodMillis didn't change because we just sent 1204 // an update, and the next one will be updatePeriodMillis from now). 1205 cancelBroadcasts(p); 1206 registerForBroadcastsLocked(p, appWidgetIds); 1207 // If it's currently showing, call back with the new AppWidgetProviderInfo. 1208 for (int j=0; j<M; j++) { 1209 AppWidgetId id = p.instances.get(j); 1210 id.views = null; 1211 if (id.host != null && id.host.callbacks != null) { 1212 try { 1213 id.host.callbacks.providerChanged(id.appWidgetId, p.info); 1214 } catch (RemoteException ex) { 1215 // It failed; remove the callback. No need to prune because 1216 // we know that this host is still referenced by this 1217 // instance. 1218 id.host.callbacks = null; 1219 } 1220 } 1221 } 1222 // Now that we've told the host, push out an update. 1223 sendUpdateIntentLocked(p, appWidgetIds); 1224 } 1225 } 1226 } 1227 } 1228 } 1229 1230 // prune the ones we don't want to keep 1231 N = mInstalledProviders.size(); 1232 for (int i=N-1; i>=0; i--) { 1233 Provider p = mInstalledProviders.get(i); 1234 if (pkgName.equals(p.info.provider.getPackageName()) 1235 && !keep.contains(p.info.provider.getClassName())) { 1236 removeProviderLocked(i, p); 1237 } 1238 } 1239 } 1240 1241 void removeProvidersForPackageLocked(String pkgName) { 1242 int N = mInstalledProviders.size(); 1243 for (int i=N-1; i>=0; i--) { 1244 Provider p = mInstalledProviders.get(i); 1245 if (pkgName.equals(p.info.provider.getPackageName())) { 1246 removeProviderLocked(i, p); 1247 } 1248 } 1249 1250 // Delete the hosts for this package too 1251 // 1252 // By now, we have removed any AppWidgets that were in any hosts here, 1253 // so we don't need to worry about sending DISABLE broadcasts to them. 1254 N = mHosts.size(); 1255 for (int i=N-1; i>=0; i--) { 1256 Host host = mHosts.get(i); 1257 if (pkgName.equals(host.packageName)) { 1258 deleteHostLocked(host); 1259 } 1260 } 1261 } 1262} 1263 1264