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