AppWidgetService.java revision be96b3a2aa7ec6d57ac038d4a5326fc168585ad6
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server; 18 19import android.app.AlarmManager; 20import android.app.PendingIntent; 21import android.appwidget.AppWidgetManager; 22import android.appwidget.AppWidgetProviderInfo; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.pm.ActivityInfo; 29import android.content.pm.PackageManager; 30import android.content.pm.PackageInfo; 31import android.content.pm.ResolveInfo; 32import android.content.res.TypedArray; 33import android.content.res.XmlResourceParser; 34import android.net.Uri; 35import android.os.Binder; 36import android.os.Bundle; 37import android.os.Process; 38import android.os.RemoteException; 39import android.os.SystemClock; 40import android.util.AttributeSet; 41import android.util.Log; 42import android.util.TypedValue; 43import android.util.Xml; 44import android.widget.RemoteViews; 45 46import java.io.IOException; 47import java.io.File; 48import java.io.FileDescriptor; 49import java.io.FileInputStream; 50import java.io.FileOutputStream; 51import java.io.PrintWriter; 52import java.util.ArrayList; 53import java.util.List; 54import java.util.HashMap; 55import java.util.HashSet; 56 57import com.android.internal.appwidget.IAppWidgetService; 58import com.android.internal.appwidget.IAppWidgetHost; 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 private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes 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<AppWidgetId>(); 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<AppWidgetId>(); 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<Provider>(); 111 int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1; 112 final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>(); 113 ArrayList<Host> mHosts = new ArrayList<Host>(); 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<AppWidgetProviderInfo>(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 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 long period = p.info.updatePeriodMillis; 634 if (period < MIN_UPDATE_PERIOD) { 635 period = MIN_UPDATE_PERIOD; 636 } 637 mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 638 SystemClock.elapsedRealtime() + period, period, p.broadcast); 639 } 640 } 641 } 642 643 static int[] getAppWidgetIds(Provider p) { 644 int instancesSize = p.instances.size(); 645 int appWidgetIds[] = new int[instancesSize]; 646 for (int i=0; i<instancesSize; i++) { 647 appWidgetIds[i] = p.instances.get(i).appWidgetId; 648 } 649 return appWidgetIds; 650 } 651 652 public int[] getAppWidgetIds(ComponentName provider) { 653 synchronized (mAppWidgetIds) { 654 Provider p = lookupProviderLocked(provider); 655 if (p != null && getCallingUid() == p.uid) { 656 return getAppWidgetIds(p); 657 } else { 658 return new int[0]; 659 } 660 } 661 } 662 663 private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) { 664 Provider p = null; 665 666 ActivityInfo activityInfo = ri.activityInfo; 667 XmlResourceParser parser = null; 668 try { 669 parser = activityInfo.loadXmlMetaData(mPackageManager, 670 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER); 671 if (parser == null) { 672 Log.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for " 673 + "AppWidget provider '" + component + '\''); 674 return null; 675 } 676 677 AttributeSet attrs = Xml.asAttributeSet(parser); 678 679 int type; 680 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 681 && type != XmlPullParser.START_TAG) { 682 // drain whitespace, comments, etc. 683 } 684 685 String nodeName = parser.getName(); 686 if (!"appwidget-provider".equals(nodeName)) { 687 Log.w(TAG, "Meta-data does not start with appwidget-provider tag for" 688 + " AppWidget provider '" + component + '\''); 689 return null; 690 } 691 692 p = new Provider(); 693 AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo(); 694 695 info.provider = component; 696 p.uid = activityInfo.applicationInfo.uid; 697 698 TypedArray sa = mContext.getResources().obtainAttributes(attrs, 699 com.android.internal.R.styleable.AppWidgetProviderInfo); 700 701 // These dimensions has to be resolved in the application's context. 702 // We simply send back the raw complex data, which will be 703 // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}. 704 TypedValue value = sa.peekValue( 705 com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth); 706 info.minWidth = value != null ? value.data : 0; 707 value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight); 708 info.minHeight = value != null ? value.data : 0; 709 710 info.updatePeriodMillis = sa.getInt( 711 com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0); 712 info.initialLayout = sa.getResourceId( 713 com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0); 714 String className = sa.getString( 715 com.android.internal.R.styleable.AppWidgetProviderInfo_configure); 716 if (className != null) { 717 info.configure = new ComponentName(component.getPackageName(), className); 718 } 719 info.label = activityInfo.loadLabel(mPackageManager).toString(); 720 info.icon = ri.getIconResource(); 721 sa.recycle(); 722 } catch (Exception e) { 723 // Ok to catch Exception here, because anything going wrong because 724 // of what a client process passes to us should not be fatal for the 725 // system process. 726 Log.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e); 727 return null; 728 } finally { 729 if (parser != null) parser.close(); 730 } 731 return p; 732 } 733 734 int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException { 735 PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0); 736 if (pkgInfo == null || pkgInfo.applicationInfo == null) { 737 throw new PackageManager.NameNotFoundException(); 738 } 739 return pkgInfo.applicationInfo.uid; 740 } 741 742 int enforceCallingUid(String packageName) throws IllegalArgumentException { 743 int callingUid = getCallingUid(); 744 int packageUid; 745 try { 746 packageUid = getUidForPackage(packageName); 747 } catch (PackageManager.NameNotFoundException ex) { 748 throw new IllegalArgumentException("packageName and uid don't match packageName=" 749 + packageName); 750 } 751 if (callingUid != packageUid && Process.supportsProcesses()) { 752 throw new IllegalArgumentException("packageName and uid don't match packageName=" 753 + packageName); 754 } 755 return callingUid; 756 } 757 758 void sendInitialBroadcasts() { 759 synchronized (mAppWidgetIds) { 760 final int N = mInstalledProviders.size(); 761 for (int i=0; i<N; i++) { 762 Provider p = mInstalledProviders.get(i); 763 if (p.instances.size() > 0) { 764 sendEnableIntentLocked(p); 765 int[] appWidgetIds = getAppWidgetIds(p); 766 sendUpdateIntentLocked(p, appWidgetIds); 767 registerForBroadcastsLocked(p, appWidgetIds); 768 } 769 } 770 } 771 } 772 773 // only call from initialization -- it assumes that the data structures are all empty 774 void loadStateLocked() { 775 File temp = savedStateTempFile(); 776 File real = savedStateRealFile(); 777 778 // prefer the real file. If it doesn't exist, use the temp one, and then copy it to the 779 // real one. if there is both a real file and a temp one, assume that the temp one isn't 780 // fully written and delete it. 781 if (real.exists()) { 782 readStateFromFileLocked(real); 783 if (temp.exists()) { 784 //noinspection ResultOfMethodCallIgnored 785 temp.delete(); 786 } 787 } else if (temp.exists()) { 788 readStateFromFileLocked(temp); 789 //noinspection ResultOfMethodCallIgnored 790 temp.renameTo(real); 791 } 792 } 793 794 void saveStateLocked() { 795 File temp = savedStateTempFile(); 796 File real = savedStateRealFile(); 797 798 if (!real.exists()) { 799 // If the real one doesn't exist, it's either because this is the first time 800 // or because something went wrong while copying them. In this case, we can't 801 // trust anything that's in temp. In order to have the loadState code not 802 // use the temporary one until it's fully written, create an empty file 803 // for real, which will we'll shortly delete. 804 try { 805 //noinspection ResultOfMethodCallIgnored 806 real.createNewFile(); 807 } catch (IOException e) { 808 // Ignore 809 } 810 } 811 812 if (temp.exists()) { 813 //noinspection ResultOfMethodCallIgnored 814 temp.delete(); 815 } 816 817 writeStateToFileLocked(temp); 818 819 //noinspection ResultOfMethodCallIgnored 820 real.delete(); 821 //noinspection ResultOfMethodCallIgnored 822 temp.renameTo(real); 823 } 824 825 void writeStateToFileLocked(File file) { 826 FileOutputStream stream = null; 827 int N; 828 829 try { 830 stream = new FileOutputStream(file, false); 831 XmlSerializer out = new FastXmlSerializer(); 832 out.setOutput(stream, "utf-8"); 833 out.startDocument(null, true); 834 835 836 out.startTag(null, "gs"); 837 838 int providerIndex = 0; 839 N = mInstalledProviders.size(); 840 for (int i=0; i<N; i++) { 841 Provider p = mInstalledProviders.get(i); 842 if (p.instances.size() > 0) { 843 out.startTag(null, "p"); 844 out.attribute(null, "pkg", p.info.provider.getPackageName()); 845 out.attribute(null, "cl", p.info.provider.getClassName()); 846 out.endTag(null, "h"); 847 p.tag = providerIndex; 848 providerIndex++; 849 } 850 } 851 852 N = mHosts.size(); 853 for (int i=0; i<N; i++) { 854 Host host = mHosts.get(i); 855 out.startTag(null, "h"); 856 out.attribute(null, "pkg", host.packageName); 857 out.attribute(null, "id", Integer.toHexString(host.hostId)); 858 out.endTag(null, "h"); 859 host.tag = i; 860 } 861 862 N = mAppWidgetIds.size(); 863 for (int i=0; i<N; i++) { 864 AppWidgetId id = mAppWidgetIds.get(i); 865 out.startTag(null, "g"); 866 out.attribute(null, "id", Integer.toHexString(id.appWidgetId)); 867 out.attribute(null, "h", Integer.toHexString(id.host.tag)); 868 if (id.provider != null) { 869 out.attribute(null, "p", Integer.toHexString(id.provider.tag)); 870 } 871 out.endTag(null, "g"); 872 } 873 874 out.endTag(null, "gs"); 875 876 out.endDocument(); 877 stream.close(); 878 } catch (IOException e) { 879 try { 880 if (stream != null) { 881 stream.close(); 882 } 883 } catch (IOException ex) { 884 // Ignore 885 } 886 if (file.exists()) { 887 //noinspection ResultOfMethodCallIgnored 888 file.delete(); 889 } 890 } 891 } 892 893 void readStateFromFileLocked(File file) { 894 FileInputStream stream = null; 895 896 boolean success = false; 897 898 try { 899 stream = new FileInputStream(file); 900 XmlPullParser parser = Xml.newPullParser(); 901 parser.setInput(stream, null); 902 903 int type; 904 int providerIndex = 0; 905 HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>(); 906 do { 907 type = parser.next(); 908 if (type == XmlPullParser.START_TAG) { 909 String tag = parser.getName(); 910 if ("p".equals(tag)) { 911 // TODO: do we need to check that this package has the same signature 912 // as before? 913 String pkg = parser.getAttributeValue(null, "pkg"); 914 String cl = parser.getAttributeValue(null, "cl"); 915 Provider p = lookupProviderLocked(new ComponentName(pkg, cl)); 916 if (p == null && mSafeMode) { 917 // if we're in safe mode, make a temporary one 918 p = new Provider(); 919 p.info = new AppWidgetProviderInfo(); 920 p.info.provider = new ComponentName(pkg, cl); 921 p.zombie = true; 922 mInstalledProviders.add(p); 923 } 924 if (p != null) { 925 // if it wasn't uninstalled or something 926 loadedProviders.put(providerIndex, p); 927 } 928 providerIndex++; 929 } 930 else if ("h".equals(tag)) { 931 Host host = new Host(); 932 933 // TODO: do we need to check that this package has the same signature 934 // as before? 935 host.packageName = parser.getAttributeValue(null, "pkg"); 936 try { 937 host.uid = getUidForPackage(host.packageName); 938 } catch (PackageManager.NameNotFoundException ex) { 939 host.zombie = true; 940 } 941 if (!host.zombie || mSafeMode) { 942 // In safe mode, we don't discard the hosts we don't recognize 943 // so that they're not pruned from our list. Otherwise, we do. 944 host.hostId = Integer.parseInt( 945 parser.getAttributeValue(null, "id"), 16); 946 mHosts.add(host); 947 } 948 } 949 else if ("g".equals(tag)) { 950 AppWidgetId id = new AppWidgetId(); 951 id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16); 952 if (id.appWidgetId >= mNextAppWidgetId) { 953 mNextAppWidgetId = id.appWidgetId + 1; 954 } 955 956 String providerString = parser.getAttributeValue(null, "p"); 957 if (providerString != null) { 958 // there's no provider if it hasn't been bound yet. 959 // maybe we don't have to save this, but it brings the system 960 // to the state it was in. 961 int pIndex = Integer.parseInt(providerString, 16); 962 id.provider = loadedProviders.get(pIndex); 963 if (false) { 964 Log.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider " 965 + pIndex + " which is " + id.provider); 966 } 967 if (id.provider == null) { 968 // This provider is gone. We just let the host figure out 969 // that this happened when it fails to load it. 970 continue; 971 } 972 } 973 974 int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16); 975 id.host = mHosts.get(hIndex); 976 if (id.host == null) { 977 // This host is gone. 978 continue; 979 } 980 981 if (id.provider != null) { 982 id.provider.instances.add(id); 983 } 984 id.host.instances.add(id); 985 mAppWidgetIds.add(id); 986 } 987 } 988 } while (type != XmlPullParser.END_DOCUMENT); 989 success = true; 990 } catch (NullPointerException e) { 991 Log.w(TAG, "failed parsing " + file, e); 992 } catch (NumberFormatException e) { 993 Log.w(TAG, "failed parsing " + file, e); 994 } catch (XmlPullParserException e) { 995 Log.w(TAG, "failed parsing " + file, e); 996 } catch (IOException e) { 997 Log.w(TAG, "failed parsing " + file, e); 998 } catch (IndexOutOfBoundsException e) { 999 Log.w(TAG, "failed parsing " + file, e); 1000 } 1001 try { 1002 if (stream != null) { 1003 stream.close(); 1004 } 1005 } catch (IOException e) { 1006 // Ignore 1007 } 1008 1009 if (success) { 1010 // delete any hosts that didn't manage to get connected (should happen) 1011 // if it matters, they'll be reconnected. 1012 final int N = mHosts.size(); 1013 for (int i=0; i<N; i++) { 1014 pruneHostLocked(mHosts.get(i)); 1015 } 1016 } else { 1017 // failed reading, clean up 1018 mAppWidgetIds.clear(); 1019 mHosts.clear(); 1020 final int N = mInstalledProviders.size(); 1021 for (int i=0; i<N; i++) { 1022 mInstalledProviders.get(i).instances.clear(); 1023 } 1024 } 1025 } 1026 1027 File savedStateTempFile() { 1028 return new File("/data/system/" + SETTINGS_TMP_FILENAME); 1029 //return new File(mContext.getFilesDir(), SETTINGS_FILENAME); 1030 } 1031 1032 File savedStateRealFile() { 1033 return new File("/data/system/" + SETTINGS_FILENAME); 1034 //return new File(mContext.getFilesDir(), SETTINGS_TMP_FILENAME); 1035 } 1036 1037 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1038 public void onReceive(Context context, Intent intent) { 1039 String action = intent.getAction(); 1040 //Log.d(TAG, "received " + action); 1041 if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { 1042 sendInitialBroadcasts(); 1043 } else { 1044 Uri uri = intent.getData(); 1045 if (uri == null) { 1046 return; 1047 } 1048 String pkgName = uri.getSchemeSpecificPart(); 1049 if (pkgName == null) { 1050 return; 1051 } 1052 1053 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { 1054 synchronized (mAppWidgetIds) { 1055 Bundle extras = intent.getExtras(); 1056 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { 1057 // The package was just upgraded 1058 updateProvidersForPackageLocked(pkgName); 1059 } else { 1060 // The package was just added 1061 addProvidersForPackageLocked(pkgName); 1062 } 1063 saveStateLocked(); 1064 } 1065 } 1066 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 1067 Bundle extras = intent.getExtras(); 1068 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { 1069 // The package is being updated. We'll receive a PACKAGE_ADDED shortly. 1070 } else { 1071 synchronized (mAppWidgetIds) { 1072 removeProvidersForPackageLocked(pkgName); 1073 saveStateLocked(); 1074 } 1075 } 1076 } 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 addProvidersForPackageLocked(String pkgName) { 1084 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1085 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, 1086 PackageManager.GET_META_DATA); 1087 1088 final int N = broadcastReceivers.size(); 1089 for (int i=0; i<N; i++) { 1090 ResolveInfo ri = broadcastReceivers.get(i); 1091 ActivityInfo ai = ri.activityInfo; 1092 1093 if (pkgName.equals(ai.packageName)) { 1094 addProviderLocked(ri); 1095 } 1096 } 1097 } 1098 1099 // TODO: If there's a better way of matching an intent filter against the 1100 // packages for a given package, use that. 1101 void updateProvidersForPackageLocked(String pkgName) { 1102 HashSet<String> keep = new HashSet<String>(); 1103 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 1104 List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, 1105 PackageManager.GET_META_DATA); 1106 1107 // add the missing ones and collect which ones to keep 1108 int N = broadcastReceivers.size(); 1109 for (int i=0; i<N; i++) { 1110 ResolveInfo ri = broadcastReceivers.get(i); 1111 ActivityInfo ai = ri.activityInfo; 1112 if (pkgName.equals(ai.packageName)) { 1113 ComponentName component = new ComponentName(ai.packageName, ai.name); 1114 Provider p = lookupProviderLocked(component); 1115 if (p == null) { 1116 if (addProviderLocked(ri)) { 1117 keep.add(ai.name); 1118 } 1119 } else { 1120 Provider parsed = parseProviderInfoXml(component, ri); 1121 if (parsed != null) { 1122 keep.add(ai.name); 1123 // Use the new AppWidgetProviderInfo. 1124 p.info = parsed.info; 1125 // If it's enabled 1126 final int M = p.instances.size(); 1127 if (M > 0) { 1128 int[] appWidgetIds = getAppWidgetIds(p); 1129 // Reschedule for the new updatePeriodMillis (don't worry about handling 1130 // it specially if updatePeriodMillis didn't change because we just sent 1131 // an update, and the next one will be updatePeriodMillis from now). 1132 cancelBroadcasts(p); 1133 registerForBroadcastsLocked(p, appWidgetIds); 1134 // If it's currently showing, call back with the new AppWidgetProviderInfo. 1135 for (int j=0; j<M; j++) { 1136 AppWidgetId id = p.instances.get(j); 1137 if (id.host != null && id.host.callbacks != null) { 1138 try { 1139 id.host.callbacks.providerChanged(id.appWidgetId, p.info); 1140 } catch (RemoteException ex) { 1141 // It failed; remove the callback. No need to prune because 1142 // we know that this host is still referenced by this 1143 // instance. 1144 id.host.callbacks = null; 1145 } 1146 } 1147 } 1148 // Now that we've told the host, push out an update. 1149 sendUpdateIntentLocked(p, appWidgetIds); 1150 } 1151 } 1152 } 1153 } 1154 } 1155 1156 // prune the ones we don't want to keep 1157 N = mInstalledProviders.size(); 1158 for (int i=N-1; i>=0; i--) { 1159 Provider p = mInstalledProviders.get(i); 1160 if (pkgName.equals(p.info.provider.getPackageName()) 1161 && !keep.contains(p.info.provider.getClassName())) { 1162 removeProviderLocked(i, p); 1163 } 1164 } 1165 } 1166 1167 void removeProvidersForPackageLocked(String pkgName) { 1168 int N = mInstalledProviders.size(); 1169 for (int i=N-1; i>=0; i--) { 1170 Provider p = mInstalledProviders.get(i); 1171 if (pkgName.equals(p.info.provider.getPackageName())) { 1172 removeProviderLocked(i, p); 1173 } 1174 } 1175 1176 // Delete the hosts for this package too 1177 // 1178 // By now, we have removed any AppWidgets that were in any hosts here, 1179 // so we don't need to worry about sending DISABLE broadcasts to them. 1180 N = mHosts.size(); 1181 for (int i=N-1; i>=0; i--) { 1182 Host host = mHosts.get(i); 1183 if (pkgName.equals(host.packageName)) { 1184 deleteHostLocked(host); 1185 } 1186 } 1187 } 1188} 1189 1190