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