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