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