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