1 /* 2 * Copyright (C) 2008 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 static android.os.FileObserver.*; 20 import static android.os.ParcelFileDescriptor.*; 21 22 import android.app.IWallpaperManager; 23 import android.app.IWallpaperManagerCallback; 24 import android.app.PendingIntent; 25 import android.app.WallpaperInfo; 26 import android.app.backup.BackupManager; 27 import android.app.backup.WallpaperBackupHelper; 28 import android.content.BroadcastReceiver; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.ServiceConnection; 34 import android.content.pm.PackageManager; 35 import android.content.pm.ResolveInfo; 36 import android.content.pm.ServiceInfo; 37 import android.content.pm.PackageManager.NameNotFoundException; 38 import android.content.res.Resources; 39 import android.os.Binder; 40 import android.os.Bundle; 41 import android.os.Environment; 42 import android.os.FileUtils; 43 import android.os.IBinder; 44 import android.os.RemoteException; 45 import android.os.FileObserver; 46 import android.os.ParcelFileDescriptor; 47 import android.os.RemoteCallbackList; 48 import android.os.ServiceManager; 49 import android.os.SystemClock; 50 import android.os.UserId; 51 import android.service.wallpaper.IWallpaperConnection; 52 import android.service.wallpaper.IWallpaperEngine; 53 import android.service.wallpaper.IWallpaperService; 54 import android.service.wallpaper.WallpaperService; 55 import android.util.Slog; 56 import android.util.SparseArray; 57 import android.util.Xml; 58 import android.view.Display; 59 import android.view.IWindowManager; 60 import android.view.WindowManager; 61 62 import java.io.FileDescriptor; 63 import java.io.IOException; 64 import java.io.InputStream; 65 import java.io.File; 66 import java.io.FileNotFoundException; 67 import java.io.FileInputStream; 68 import java.io.FileOutputStream; 69 import java.io.PrintWriter; 70 import java.util.List; 71 72 import org.xmlpull.v1.XmlPullParser; 73 import org.xmlpull.v1.XmlPullParserException; 74 import org.xmlpull.v1.XmlSerializer; 75 76 import com.android.internal.content.PackageMonitor; 77 import com.android.internal.util.FastXmlSerializer; 78 import com.android.internal.util.JournaledFile; 79 import com.android.server.am.ActivityManagerService; 80 81 class WallpaperManagerService extends IWallpaperManager.Stub { 82 static final String TAG = "WallpaperService"; 83 static final boolean DEBUG = false; 84 85 final Object mLock = new Object[0]; 86 87 /** 88 * Minimum time between crashes of a wallpaper service for us to consider 89 * restarting it vs. just reverting to the static wallpaper. 90 */ 91 static final long MIN_WALLPAPER_CRASH_TIME = 10000; 92 93 static final File WALLPAPER_BASE_DIR = new File("/data/system/users"); 94 static final String WALLPAPER = "wallpaper"; 95 static final String WALLPAPER_INFO = "wallpaper_info.xml"; 96 97 /** 98 * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks 99 * that the wallpaper has changed. The CREATE is triggered when there is no 100 * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered 101 * everytime the wallpaper is changed. 102 */ 103 private class WallpaperObserver extends FileObserver { 104 105 final WallpaperData mWallpaper; 106 final File mWallpaperDir; 107 final File mWallpaperFile; 108 109 public WallpaperObserver(WallpaperData wallpaper) { 110 super(getWallpaperDir(wallpaper.userId).getAbsolutePath(), 111 CLOSE_WRITE | DELETE | DELETE_SELF); 112 mWallpaperDir = getWallpaperDir(wallpaper.userId); 113 mWallpaper = wallpaper; 114 mWallpaperFile = new File(mWallpaperDir, WALLPAPER); 115 } 116 117 @Override 118 public void onEvent(int event, String path) { 119 if (path == null) { 120 return; 121 } 122 synchronized (mLock) { 123 // changing the wallpaper means we'll need to back up the new one 124 long origId = Binder.clearCallingIdentity(); 125 BackupManager bm = new BackupManager(mContext); 126 bm.dataChanged(); 127 Binder.restoreCallingIdentity(origId); 128 129 File changedFile = new File(mWallpaperDir, path); 130 if (mWallpaperFile.equals(changedFile)) { 131 notifyCallbacksLocked(mWallpaper); 132 if (mWallpaper.wallpaperComponent == null || event != CLOSE_WRITE 133 || mWallpaper.imageWallpaperPending) { 134 if (event == CLOSE_WRITE) { 135 mWallpaper.imageWallpaperPending = false; 136 } 137 bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true, 138 false, mWallpaper); 139 saveSettingsLocked(mWallpaper); 140 } 141 } 142 } 143 } 144 } 145 146 final Context mContext; 147 final IWindowManager mIWindowManager; 148 final MyPackageMonitor mMonitor; 149 WallpaperData mLastWallpaper; 150 151 SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>(); 152 153 int mCurrentUserId; 154 155 static class WallpaperData { 156 157 int userId; 158 159 File wallpaperFile; 160 161 /** 162 * Client is currently writing a new image wallpaper. 163 */ 164 boolean imageWallpaperPending; 165 166 /** 167 * Resource name if using a picture from the wallpaper gallery 168 */ 169 String name = ""; 170 171 /** 172 * The component name of the currently set live wallpaper. 173 */ 174 ComponentName wallpaperComponent; 175 176 /** 177 * The component name of the wallpaper that should be set next. 178 */ 179 ComponentName nextWallpaperComponent; 180 181 /** 182 * Name of the component used to display bitmap wallpapers from either the gallery or 183 * built-in wallpapers. 184 */ 185 ComponentName imageWallpaperComponent = new ComponentName("com.android.systemui", 186 "com.android.systemui.ImageWallpaper"); 187 188 WallpaperConnection connection; 189 long lastDiedTime; 190 boolean wallpaperUpdating; 191 WallpaperObserver wallpaperObserver; 192 193 /** 194 * List of callbacks registered they should each be notified when the wallpaper is changed. 195 */ 196 private RemoteCallbackList<IWallpaperManagerCallback> callbacks 197 = new RemoteCallbackList<IWallpaperManagerCallback>(); 198 199 int width = -1; 200 int height = -1; 201 202 WallpaperData(int userId) { 203 this.userId = userId; 204 wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER); 205 } 206 } 207 208 class WallpaperConnection extends IWallpaperConnection.Stub 209 implements ServiceConnection { 210 final WallpaperInfo mInfo; 211 final Binder mToken = new Binder(); 212 IWallpaperService mService; 213 IWallpaperEngine mEngine; 214 WallpaperData mWallpaper; 215 216 public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) { 217 mInfo = info; 218 mWallpaper = wallpaper; 219 } 220 221 public void onServiceConnected(ComponentName name, IBinder service) { 222 synchronized (mLock) { 223 if (mWallpaper.connection == this) { 224 mWallpaper.lastDiedTime = SystemClock.uptimeMillis(); 225 mService = IWallpaperService.Stub.asInterface(service); 226 attachServiceLocked(this, mWallpaper); 227 // XXX should probably do saveSettingsLocked() later 228 // when we have an engine, but I'm not sure about 229 // locking there and anyway we always need to be able to 230 // recover if there is something wrong. 231 saveSettingsLocked(mWallpaper); 232 } 233 } 234 } 235 236 public void onServiceDisconnected(ComponentName name) { 237 synchronized (mLock) { 238 mService = null; 239 mEngine = null; 240 if (mWallpaper.connection == this) { 241 Slog.w(TAG, "Wallpaper service gone: " + mWallpaper.wallpaperComponent); 242 if (!mWallpaper.wallpaperUpdating 243 && (mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME) 244 > SystemClock.uptimeMillis() 245 && mWallpaper.userId == mCurrentUserId) { 246 Slog.w(TAG, "Reverting to built-in wallpaper!"); 247 clearWallpaperLocked(true, mWallpaper.userId); 248 } 249 } 250 } 251 } 252 253 public void attachEngine(IWallpaperEngine engine) { 254 mEngine = engine; 255 } 256 257 public ParcelFileDescriptor setWallpaper(String name) { 258 synchronized (mLock) { 259 if (mWallpaper.connection == this) { 260 return updateWallpaperBitmapLocked(name, mWallpaper); 261 } 262 return null; 263 } 264 } 265 } 266 267 class MyPackageMonitor extends PackageMonitor { 268 @Override 269 public void onPackageUpdateFinished(String packageName, int uid) { 270 synchronized (mLock) { 271 for (int i = 0; i < mWallpaperMap.size(); i++) { 272 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 273 if (wallpaper.wallpaperComponent != null 274 && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { 275 wallpaper.wallpaperUpdating = false; 276 ComponentName comp = wallpaper.wallpaperComponent; 277 clearWallpaperComponentLocked(wallpaper); 278 // Do this only for the current user's wallpaper 279 if (wallpaper.userId == mCurrentUserId 280 && !bindWallpaperComponentLocked(comp, false, false, wallpaper)) { 281 Slog.w(TAG, "Wallpaper no longer available; reverting to default"); 282 clearWallpaperLocked(false, wallpaper.userId); 283 } 284 } 285 } 286 } 287 } 288 289 @Override 290 public void onPackageModified(String packageName) { 291 synchronized (mLock) { 292 for (int i = 0; i < mWallpaperMap.size(); i++) { 293 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 294 if (wallpaper.wallpaperComponent == null 295 || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { 296 continue; 297 } 298 doPackagesChangedLocked(true, wallpaper); 299 } 300 } 301 } 302 303 @Override 304 public void onPackageUpdateStarted(String packageName, int uid) { 305 synchronized (mLock) { 306 for (int i = 0; i < mWallpaperMap.size(); i++) { 307 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 308 if (wallpaper.wallpaperComponent != null 309 && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { 310 wallpaper.wallpaperUpdating = true; 311 } 312 } 313 } 314 } 315 316 @Override 317 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { 318 synchronized (mLock) { 319 boolean changed = false; 320 for (int i = 0; i < mWallpaperMap.size(); i++) { 321 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 322 boolean res = doPackagesChangedLocked(doit, wallpaper); 323 changed |= res; 324 } 325 return changed; 326 } 327 } 328 329 @Override 330 public void onSomePackagesChanged() { 331 synchronized (mLock) { 332 for (int i = 0; i < mWallpaperMap.size(); i++) { 333 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 334 doPackagesChangedLocked(true, wallpaper); 335 } 336 } 337 } 338 339 boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) { 340 boolean changed = false; 341 if (wallpaper.wallpaperComponent != null) { 342 int change = isPackageDisappearing(wallpaper.wallpaperComponent 343 .getPackageName()); 344 if (change == PACKAGE_PERMANENT_CHANGE 345 || change == PACKAGE_TEMPORARY_CHANGE) { 346 changed = true; 347 if (doit) { 348 Slog.w(TAG, "Wallpaper uninstalled, removing: " 349 + wallpaper.wallpaperComponent); 350 clearWallpaperLocked(false, wallpaper.userId); 351 } 352 } 353 } 354 if (wallpaper.nextWallpaperComponent != null) { 355 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent 356 .getPackageName()); 357 if (change == PACKAGE_PERMANENT_CHANGE 358 || change == PACKAGE_TEMPORARY_CHANGE) { 359 wallpaper.nextWallpaperComponent = null; 360 } 361 } 362 if (wallpaper.wallpaperComponent != null 363 && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) { 364 try { 365 mContext.getPackageManager().getServiceInfo( 366 wallpaper.wallpaperComponent, 0); 367 } catch (NameNotFoundException e) { 368 Slog.w(TAG, "Wallpaper component gone, removing: " 369 + wallpaper.wallpaperComponent); 370 clearWallpaperLocked(false, wallpaper.userId); 371 } 372 } 373 if (wallpaper.nextWallpaperComponent != null 374 && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) { 375 try { 376 mContext.getPackageManager().getServiceInfo( 377 wallpaper.nextWallpaperComponent, 0); 378 } catch (NameNotFoundException e) { 379 wallpaper.nextWallpaperComponent = null; 380 } 381 } 382 return changed; 383 } 384 } 385 386 public WallpaperManagerService(Context context) { 387 if (DEBUG) Slog.v(TAG, "WallpaperService startup"); 388 mContext = context; 389 mIWindowManager = IWindowManager.Stub.asInterface( 390 ServiceManager.getService(Context.WINDOW_SERVICE)); 391 mMonitor = new MyPackageMonitor(); 392 mMonitor.register(context, null, true); 393 WALLPAPER_BASE_DIR.mkdirs(); 394 loadSettingsLocked(0); 395 } 396 397 private static File getWallpaperDir(int userId) { 398 return new File(WALLPAPER_BASE_DIR + "/" + userId); 399 } 400 401 @Override 402 protected void finalize() throws Throwable { 403 super.finalize(); 404 for (int i = 0; i < mWallpaperMap.size(); i++) { 405 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 406 wallpaper.wallpaperObserver.stopWatching(); 407 } 408 } 409 410 public void systemReady() { 411 if (DEBUG) Slog.v(TAG, "systemReady"); 412 WallpaperData wallpaper = mWallpaperMap.get(0); 413 switchWallpaper(wallpaper); 414 wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper); 415 wallpaper.wallpaperObserver.startWatching(); 416 417 IntentFilter userFilter = new IntentFilter(); 418 userFilter.addAction(Intent.ACTION_USER_SWITCHED); 419 userFilter.addAction(Intent.ACTION_USER_REMOVED); 420 mContext.registerReceiver(new BroadcastReceiver() { 421 @Override 422 public void onReceive(Context context, Intent intent) { 423 String action = intent.getAction(); 424 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 425 switchUser(intent.getIntExtra(Intent.EXTRA_USERID, 0)); 426 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 427 removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0)); 428 } 429 } 430 }, userFilter); 431 } 432 433 String getName() { 434 return mWallpaperMap.get(0).name; 435 } 436 437 void removeUser(int userId) { 438 synchronized (mLock) { 439 WallpaperData wallpaper = mWallpaperMap.get(userId); 440 if (wallpaper != null) { 441 wallpaper.wallpaperObserver.stopWatching(); 442 mWallpaperMap.remove(userId); 443 } 444 File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER); 445 wallpaperFile.delete(); 446 File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO); 447 wallpaperInfoFile.delete(); 448 } 449 } 450 451 void switchUser(int userId) { 452 synchronized (mLock) { 453 mCurrentUserId = userId; 454 WallpaperData wallpaper = mWallpaperMap.get(userId); 455 if (wallpaper == null) { 456 wallpaper = new WallpaperData(userId); 457 mWallpaperMap.put(userId, wallpaper); 458 loadSettingsLocked(userId); 459 wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper); 460 wallpaper.wallpaperObserver.startWatching(); 461 } 462 switchWallpaper(wallpaper); 463 } 464 } 465 466 void switchWallpaper(WallpaperData wallpaper) { 467 synchronized (mLock) { 468 RuntimeException e = null; 469 try { 470 ComponentName cname = wallpaper.wallpaperComponent != null ? 471 wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent; 472 if (bindWallpaperComponentLocked(cname, true, false, wallpaper)) { 473 return; 474 } 475 } catch (RuntimeException e1) { 476 e = e1; 477 } 478 Slog.w(TAG, "Failure starting previous wallpaper", e); 479 clearWallpaperLocked(false, wallpaper.userId); 480 } 481 } 482 483 public void clearWallpaper() { 484 if (DEBUG) Slog.v(TAG, "clearWallpaper"); 485 synchronized (mLock) { 486 clearWallpaperLocked(false, UserId.getCallingUserId()); 487 } 488 } 489 490 void clearWallpaperLocked(boolean defaultFailed, int userId) { 491 WallpaperData wallpaper = mWallpaperMap.get(userId); 492 File f = new File(getWallpaperDir(userId), WALLPAPER); 493 if (f.exists()) { 494 f.delete(); 495 } 496 final long ident = Binder.clearCallingIdentity(); 497 RuntimeException e = null; 498 try { 499 wallpaper.imageWallpaperPending = false; 500 if (userId != mCurrentUserId) return; 501 if (bindWallpaperComponentLocked(defaultFailed 502 ? wallpaper.imageWallpaperComponent 503 : null, true, false, wallpaper)) { 504 return; 505 } 506 } catch (IllegalArgumentException e1) { 507 e = e1; 508 } finally { 509 Binder.restoreCallingIdentity(ident); 510 } 511 512 // This can happen if the default wallpaper component doesn't 513 // exist. This should be a system configuration problem, but 514 // let's not let it crash the system and just live with no 515 // wallpaper. 516 Slog.e(TAG, "Default wallpaper component not found!", e); 517 clearWallpaperComponentLocked(wallpaper); 518 } 519 520 public void setDimensionHints(int width, int height) throws RemoteException { 521 checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); 522 523 int userId = UserId.getCallingUserId(); 524 WallpaperData wallpaper = mWallpaperMap.get(userId); 525 if (wallpaper == null) { 526 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); 527 } 528 if (width <= 0 || height <= 0) { 529 throw new IllegalArgumentException("width and height must be > 0"); 530 } 531 532 synchronized (mLock) { 533 if (width != wallpaper.width || height != wallpaper.height) { 534 wallpaper.width = width; 535 wallpaper.height = height; 536 saveSettingsLocked(wallpaper); 537 if (mCurrentUserId != userId) return; // Don't change the properties now 538 if (wallpaper.connection != null) { 539 if (wallpaper.connection.mEngine != null) { 540 try { 541 wallpaper.connection.mEngine.setDesiredSize( 542 width, height); 543 } catch (RemoteException e) { 544 } 545 notifyCallbacksLocked(wallpaper); 546 } 547 } 548 } 549 } 550 } 551 552 public int getWidthHint() throws RemoteException { 553 synchronized (mLock) { 554 WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId()); 555 return wallpaper.width; 556 } 557 } 558 559 public int getHeightHint() throws RemoteException { 560 synchronized (mLock) { 561 WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId()); 562 return wallpaper.height; 563 } 564 } 565 566 public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, 567 Bundle outParams) { 568 synchronized (mLock) { 569 // This returns the current user's wallpaper, if called by a system service. Else it 570 // returns the wallpaper for the calling user. 571 int callingUid = Binder.getCallingUid(); 572 int wallpaperUserId = 0; 573 if (callingUid == android.os.Process.SYSTEM_UID) { 574 wallpaperUserId = mCurrentUserId; 575 } else { 576 wallpaperUserId = UserId.getUserId(callingUid); 577 } 578 WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId); 579 try { 580 if (outParams != null) { 581 outParams.putInt("width", wallpaper.width); 582 outParams.putInt("height", wallpaper.height); 583 } 584 wallpaper.callbacks.register(cb); 585 File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER); 586 if (!f.exists()) { 587 return null; 588 } 589 return ParcelFileDescriptor.open(f, MODE_READ_ONLY); 590 } catch (FileNotFoundException e) { 591 /* Shouldn't happen as we check to see if the file exists */ 592 Slog.w(TAG, "Error getting wallpaper", e); 593 } 594 return null; 595 } 596 } 597 598 public WallpaperInfo getWallpaperInfo() { 599 int userId = UserId.getCallingUserId(); 600 synchronized (mLock) { 601 WallpaperData wallpaper = mWallpaperMap.get(userId); 602 if (wallpaper.connection != null) { 603 return wallpaper.connection.mInfo; 604 } 605 return null; 606 } 607 } 608 609 public ParcelFileDescriptor setWallpaper(String name) { 610 if (DEBUG) Slog.v(TAG, "setWallpaper"); 611 int userId = UserId.getCallingUserId(); 612 WallpaperData wallpaper = mWallpaperMap.get(userId); 613 if (wallpaper == null) { 614 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); 615 } 616 checkPermission(android.Manifest.permission.SET_WALLPAPER); 617 synchronized (mLock) { 618 final long ident = Binder.clearCallingIdentity(); 619 try { 620 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper); 621 if (pfd != null) { 622 wallpaper.imageWallpaperPending = true; 623 } 624 return pfd; 625 } finally { 626 Binder.restoreCallingIdentity(ident); 627 } 628 } 629 } 630 631 ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper) { 632 if (name == null) name = ""; 633 try { 634 File dir = getWallpaperDir(wallpaper.userId); 635 if (!dir.exists()) { 636 dir.mkdir(); 637 FileUtils.setPermissions( 638 dir.getPath(), 639 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 640 -1, -1); 641 } 642 ParcelFileDescriptor fd = ParcelFileDescriptor.open(new File(dir, WALLPAPER), 643 MODE_CREATE|MODE_READ_WRITE); 644 wallpaper.name = name; 645 return fd; 646 } catch (FileNotFoundException e) { 647 Slog.w(TAG, "Error setting wallpaper", e); 648 } 649 return null; 650 } 651 652 public void setWallpaperComponent(ComponentName name) { 653 if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); 654 int userId = UserId.getCallingUserId(); 655 WallpaperData wallpaper = mWallpaperMap.get(userId); 656 if (wallpaper == null) { 657 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); 658 } 659 checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); 660 synchronized (mLock) { 661 final long ident = Binder.clearCallingIdentity(); 662 try { 663 wallpaper.imageWallpaperPending = false; 664 bindWallpaperComponentLocked(name, false, true, wallpaper); 665 } finally { 666 Binder.restoreCallingIdentity(ident); 667 } 668 } 669 } 670 671 boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, 672 boolean fromUser, WallpaperData wallpaper) { 673 if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName); 674 // Has the component changed? 675 if (!force) { 676 if (wallpaper.connection != null) { 677 if (wallpaper.wallpaperComponent == null) { 678 if (componentName == null) { 679 if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default"); 680 // Still using default wallpaper. 681 return true; 682 } 683 } else if (wallpaper.wallpaperComponent.equals(componentName)) { 684 // Changing to same wallpaper. 685 if (DEBUG) Slog.v(TAG, "same wallpaper"); 686 return true; 687 } 688 } 689 } 690 691 try { 692 if (componentName == null) { 693 String defaultComponent = 694 mContext.getString(com.android.internal.R.string.default_wallpaper_component); 695 if (defaultComponent != null) { 696 // See if there is a default wallpaper component specified 697 componentName = ComponentName.unflattenFromString(defaultComponent); 698 if (DEBUG) Slog.v(TAG, "Use default component wallpaper:" + componentName); 699 } 700 if (componentName == null) { 701 // Fall back to static image wallpaper 702 componentName = wallpaper.imageWallpaperComponent; 703 //clearWallpaperComponentLocked(); 704 //return; 705 if (DEBUG) Slog.v(TAG, "Using image wallpaper"); 706 } 707 } 708 ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName, 709 PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS); 710 if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) { 711 String msg = "Selected service does not require " 712 + android.Manifest.permission.BIND_WALLPAPER 713 + ": " + componentName; 714 if (fromUser) { 715 throw new SecurityException(msg); 716 } 717 Slog.w(TAG, msg); 718 return false; 719 } 720 721 WallpaperInfo wi = null; 722 723 Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); 724 if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) { 725 // Make sure the selected service is actually a wallpaper service. 726 List<ResolveInfo> ris = mContext.getPackageManager() 727 .queryIntentServices(intent, PackageManager.GET_META_DATA); 728 for (int i=0; i<ris.size(); i++) { 729 ServiceInfo rsi = ris.get(i).serviceInfo; 730 if (rsi.name.equals(si.name) && 731 rsi.packageName.equals(si.packageName)) { 732 try { 733 wi = new WallpaperInfo(mContext, ris.get(i)); 734 } catch (XmlPullParserException e) { 735 if (fromUser) { 736 throw new IllegalArgumentException(e); 737 } 738 Slog.w(TAG, e); 739 return false; 740 } catch (IOException e) { 741 if (fromUser) { 742 throw new IllegalArgumentException(e); 743 } 744 Slog.w(TAG, e); 745 return false; 746 } 747 break; 748 } 749 } 750 if (wi == null) { 751 String msg = "Selected service is not a wallpaper: " 752 + componentName; 753 if (fromUser) { 754 throw new SecurityException(msg); 755 } 756 Slog.w(TAG, msg); 757 return false; 758 } 759 } 760 761 // Bind the service! 762 if (DEBUG) Slog.v(TAG, "Binding to:" + componentName); 763 WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper); 764 intent.setComponent(componentName); 765 int serviceUserId = wallpaper.userId; 766 // Because the image wallpaper is running in the system ui 767 if (componentName.equals(wallpaper.imageWallpaperComponent)) { 768 serviceUserId = 0; 769 } 770 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, 771 com.android.internal.R.string.wallpaper_binding_label); 772 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( 773 mContext, 0, 774 Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER), 775 mContext.getText(com.android.internal.R.string.chooser_wallpaper)), 776 0)); 777 if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) { 778 String msg = "Unable to bind service: " 779 + componentName; 780 if (fromUser) { 781 throw new IllegalArgumentException(msg); 782 } 783 Slog.w(TAG, msg); 784 return false; 785 } 786 if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) { 787 detachWallpaperLocked(mLastWallpaper); 788 } 789 wallpaper.wallpaperComponent = componentName; 790 wallpaper.connection = newConn; 791 wallpaper.lastDiedTime = SystemClock.uptimeMillis(); 792 try { 793 if (wallpaper.userId == mCurrentUserId) { 794 if (DEBUG) 795 Slog.v(TAG, "Adding window token: " + newConn.mToken); 796 mIWindowManager.addWindowToken(newConn.mToken, 797 WindowManager.LayoutParams.TYPE_WALLPAPER); 798 mLastWallpaper = wallpaper; 799 } 800 } catch (RemoteException e) { 801 } 802 } catch (PackageManager.NameNotFoundException e) { 803 String msg = "Unknown component " + componentName; 804 if (fromUser) { 805 throw new IllegalArgumentException(msg); 806 } 807 Slog.w(TAG, msg); 808 return false; 809 } 810 return true; 811 } 812 813 void detachWallpaperLocked(WallpaperData wallpaper) { 814 if (wallpaper.connection != null) { 815 if (wallpaper.connection.mEngine != null) { 816 try { 817 wallpaper.connection.mEngine.destroy(); 818 } catch (RemoteException e) { 819 } 820 } 821 mContext.unbindService(wallpaper.connection); 822 try { 823 if (DEBUG) 824 Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken); 825 mIWindowManager.removeWindowToken(wallpaper.connection.mToken); 826 } catch (RemoteException e) { 827 } 828 wallpaper.connection.mService = null; 829 wallpaper.connection.mEngine = null; 830 wallpaper.connection = null; 831 } 832 } 833 834 void clearWallpaperComponentLocked(WallpaperData wallpaper) { 835 wallpaper.wallpaperComponent = null; 836 detachWallpaperLocked(wallpaper); 837 } 838 839 void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) { 840 try { 841 conn.mService.attach(conn, conn.mToken, 842 WindowManager.LayoutParams.TYPE_WALLPAPER, false, 843 wallpaper.width, wallpaper.height); 844 } catch (RemoteException e) { 845 Slog.w(TAG, "Failed attaching wallpaper; clearing", e); 846 if (!wallpaper.wallpaperUpdating) { 847 bindWallpaperComponentLocked(null, false, false, wallpaper); 848 } 849 } 850 } 851 852 private void notifyCallbacksLocked(WallpaperData wallpaper) { 853 final int n = wallpaper.callbacks.beginBroadcast(); 854 for (int i = 0; i < n; i++) { 855 try { 856 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged(); 857 } catch (RemoteException e) { 858 859 // The RemoteCallbackList will take care of removing 860 // the dead object for us. 861 } 862 } 863 wallpaper.callbacks.finishBroadcast(); 864 final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED); 865 mContext.sendBroadcast(intent); 866 } 867 868 private void checkPermission(String permission) { 869 if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) { 870 throw new SecurityException("Access denied to process: " + Binder.getCallingPid() 871 + ", must have permission " + permission); 872 } 873 } 874 875 private static JournaledFile makeJournaledFile(int userId) { 876 final String base = getWallpaperDir(userId) + "/" + WALLPAPER_INFO; 877 return new JournaledFile(new File(base), new File(base + ".tmp")); 878 } 879 880 private void saveSettingsLocked(WallpaperData wallpaper) { 881 JournaledFile journal = makeJournaledFile(wallpaper.userId); 882 FileOutputStream stream = null; 883 try { 884 stream = new FileOutputStream(journal.chooseForWrite(), false); 885 XmlSerializer out = new FastXmlSerializer(); 886 out.setOutput(stream, "utf-8"); 887 out.startDocument(null, true); 888 889 out.startTag(null, "wp"); 890 out.attribute(null, "width", Integer.toString(wallpaper.width)); 891 out.attribute(null, "height", Integer.toString(wallpaper.height)); 892 out.attribute(null, "name", wallpaper.name); 893 if (wallpaper.wallpaperComponent != null 894 && !wallpaper.wallpaperComponent.equals(wallpaper.imageWallpaperComponent)) { 895 out.attribute(null, "component", 896 wallpaper.wallpaperComponent.flattenToShortString()); 897 } 898 out.endTag(null, "wp"); 899 900 out.endDocument(); 901 stream.close(); 902 journal.commit(); 903 } catch (IOException e) { 904 try { 905 if (stream != null) { 906 stream.close(); 907 } 908 } catch (IOException ex) { 909 // Ignore 910 } 911 journal.rollback(); 912 } 913 } 914 915 private void migrateFromOld() { 916 File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY); 917 File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY); 918 if (oldWallpaper.exists()) { 919 File newWallpaper = new File(getWallpaperDir(0), WALLPAPER); 920 oldWallpaper.renameTo(newWallpaper); 921 } 922 if (oldInfo.exists()) { 923 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO); 924 oldInfo.renameTo(newInfo); 925 } 926 } 927 928 private void loadSettingsLocked(int userId) { 929 if (DEBUG) Slog.v(TAG, "loadSettingsLocked"); 930 931 JournaledFile journal = makeJournaledFile(userId); 932 FileInputStream stream = null; 933 File file = journal.chooseForRead(); 934 if (!file.exists()) { 935 // This should only happen one time, when upgrading from a legacy system 936 migrateFromOld(); 937 } 938 WallpaperData wallpaper = mWallpaperMap.get(userId); 939 if (wallpaper == null) { 940 wallpaper = new WallpaperData(userId); 941 mWallpaperMap.put(userId, wallpaper); 942 } 943 boolean success = false; 944 try { 945 stream = new FileInputStream(file); 946 XmlPullParser parser = Xml.newPullParser(); 947 parser.setInput(stream, null); 948 949 int type; 950 do { 951 type = parser.next(); 952 if (type == XmlPullParser.START_TAG) { 953 String tag = parser.getName(); 954 if ("wp".equals(tag)) { 955 wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width")); 956 wallpaper.height = Integer.parseInt(parser 957 .getAttributeValue(null, "height")); 958 wallpaper.name = parser.getAttributeValue(null, "name"); 959 String comp = parser.getAttributeValue(null, "component"); 960 wallpaper.nextWallpaperComponent = comp != null 961 ? ComponentName.unflattenFromString(comp) 962 : null; 963 if (wallpaper.nextWallpaperComponent == null 964 || "android".equals(wallpaper.nextWallpaperComponent 965 .getPackageName())) { 966 wallpaper.nextWallpaperComponent = wallpaper.imageWallpaperComponent; 967 } 968 969 if (DEBUG) { 970 Slog.v(TAG, "mWidth:" + wallpaper.width); 971 Slog.v(TAG, "mHeight:" + wallpaper.height); 972 Slog.v(TAG, "mName:" + wallpaper.name); 973 Slog.v(TAG, "mNextWallpaperComponent:" 974 + wallpaper.nextWallpaperComponent); 975 } 976 } 977 } 978 } while (type != XmlPullParser.END_DOCUMENT); 979 success = true; 980 } catch (NullPointerException e) { 981 Slog.w(TAG, "failed parsing " + file + " " + e); 982 } catch (NumberFormatException e) { 983 Slog.w(TAG, "failed parsing " + file + " " + e); 984 } catch (XmlPullParserException e) { 985 Slog.w(TAG, "failed parsing " + file + " " + e); 986 } catch (IOException e) { 987 Slog.w(TAG, "failed parsing " + file + " " + e); 988 } catch (IndexOutOfBoundsException e) { 989 Slog.w(TAG, "failed parsing " + file + " " + e); 990 } 991 try { 992 if (stream != null) { 993 stream.close(); 994 } 995 } catch (IOException e) { 996 // Ignore 997 } 998 999 if (!success) { 1000 wallpaper.width = -1; 1001 wallpaper.height = -1; 1002 wallpaper.name = ""; 1003 } 1004 1005 // We always want to have some reasonable width hint. 1006 WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); 1007 Display d = wm.getDefaultDisplay(); 1008 int baseSize = d.getMaximumSizeDimension(); 1009 if (wallpaper.width < baseSize) { 1010 wallpaper.width = baseSize; 1011 } 1012 if (wallpaper.height < baseSize) { 1013 wallpaper.height = baseSize; 1014 } 1015 } 1016 1017 // Called by SystemBackupAgent after files are restored to disk. 1018 void settingsRestored() { 1019 // TODO: If necessary, make it work for secondary users as well. This currently assumes 1020 // restores only to the primary user 1021 if (DEBUG) Slog.v(TAG, "settingsRestored"); 1022 WallpaperData wallpaper = null; 1023 boolean success = false; 1024 synchronized (mLock) { 1025 loadSettingsLocked(0); 1026 wallpaper = mWallpaperMap.get(0); 1027 if (wallpaper.nextWallpaperComponent != null 1028 && !wallpaper.nextWallpaperComponent.equals(wallpaper.imageWallpaperComponent)) { 1029 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false, 1030 wallpaper)) { 1031 // No such live wallpaper or other failure; fall back to the default 1032 // live wallpaper (since the profile being restored indicated that the 1033 // user had selected a live rather than static one). 1034 bindWallpaperComponentLocked(null, false, false, wallpaper); 1035 } 1036 success = true; 1037 } else { 1038 // If there's a wallpaper name, we use that. If that can't be loaded, then we 1039 // use the default. 1040 if ("".equals(wallpaper.name)) { 1041 if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty"); 1042 success = true; 1043 } else { 1044 if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource"); 1045 success = restoreNamedResourceLocked(wallpaper); 1046 } 1047 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success); 1048 if (success) { 1049 bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false, 1050 wallpaper); 1051 } 1052 } 1053 } 1054 1055 if (!success) { 1056 Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'"); 1057 wallpaper.name = ""; 1058 getWallpaperDir(0).delete(); 1059 } 1060 1061 synchronized (mLock) { 1062 saveSettingsLocked(wallpaper); 1063 } 1064 } 1065 1066 boolean restoreNamedResourceLocked(WallpaperData wallpaper) { 1067 if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) { 1068 String resName = wallpaper.name.substring(4); 1069 1070 String pkg = null; 1071 int colon = resName.indexOf(':'); 1072 if (colon > 0) { 1073 pkg = resName.substring(0, colon); 1074 } 1075 1076 String ident = null; 1077 int slash = resName.lastIndexOf('/'); 1078 if (slash > 0) { 1079 ident = resName.substring(slash+1); 1080 } 1081 1082 String type = null; 1083 if (colon > 0 && slash > 0 && (slash-colon) > 1) { 1084 type = resName.substring(colon+1, slash); 1085 } 1086 1087 if (pkg != null && ident != null && type != null) { 1088 int resId = -1; 1089 InputStream res = null; 1090 FileOutputStream fos = null; 1091 try { 1092 Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED); 1093 Resources r = c.getResources(); 1094 resId = r.getIdentifier(resName, null, null); 1095 if (resId == 0) { 1096 Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type 1097 + " ident=" + ident); 1098 return false; 1099 } 1100 1101 res = r.openRawResource(resId); 1102 if (wallpaper.wallpaperFile.exists()) { 1103 wallpaper.wallpaperFile.delete(); 1104 } 1105 fos = new FileOutputStream(wallpaper.wallpaperFile); 1106 1107 byte[] buffer = new byte[32768]; 1108 int amt; 1109 while ((amt=res.read(buffer)) > 0) { 1110 fos.write(buffer, 0, amt); 1111 } 1112 // mWallpaperObserver will notice the close and send the change broadcast 1113 1114 Slog.v(TAG, "Restored wallpaper: " + resName); 1115 return true; 1116 } catch (NameNotFoundException e) { 1117 Slog.e(TAG, "Package name " + pkg + " not found"); 1118 } catch (Resources.NotFoundException e) { 1119 Slog.e(TAG, "Resource not found: " + resId); 1120 } catch (IOException e) { 1121 Slog.e(TAG, "IOException while restoring wallpaper ", e); 1122 } finally { 1123 if (res != null) { 1124 try { 1125 res.close(); 1126 } catch (IOException ex) {} 1127 } 1128 if (fos != null) { 1129 FileUtils.sync(fos); 1130 try { 1131 fos.close(); 1132 } catch (IOException ex) {} 1133 } 1134 } 1135 } 1136 } 1137 return false; 1138 } 1139 1140 @Override 1141 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1142 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 1143 != PackageManager.PERMISSION_GRANTED) { 1144 1145 pw.println("Permission Denial: can't dump wallpaper service from from pid=" 1146 + Binder.getCallingPid() 1147 + ", uid=" + Binder.getCallingUid()); 1148 return; 1149 } 1150 1151 synchronized (mLock) { 1152 pw.println("Current Wallpaper Service state:"); 1153 for (int i = 0; i < mWallpaperMap.size(); i++) { 1154 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 1155 pw.println(" User " + wallpaper.userId + ":"); 1156 pw.print(" mWidth="); 1157 pw.print(wallpaper.width); 1158 pw.print(" mHeight="); 1159 pw.println(wallpaper.height); 1160 pw.print(" mName="); 1161 pw.println(wallpaper.name); 1162 pw.print(" mWallpaperComponent="); 1163 pw.println(wallpaper.wallpaperComponent); 1164 if (wallpaper.connection != null) { 1165 WallpaperConnection conn = wallpaper.connection; 1166 pw.print(" Wallpaper connection "); 1167 pw.print(conn); 1168 pw.println(":"); 1169 if (conn.mInfo != null) { 1170 pw.print(" mInfo.component="); 1171 pw.println(conn.mInfo.getComponent()); 1172 } 1173 pw.print(" mToken="); 1174 pw.println(conn.mToken); 1175 pw.print(" mService="); 1176 pw.println(conn.mService); 1177 pw.print(" mEngine="); 1178 pw.println(conn.mEngine); 1179 pw.print(" mLastDiedTime="); 1180 pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis()); 1181 } 1182 } 1183 } 1184 } 1185 } 1186