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.wallpaper; 18 19 import static android.app.WallpaperManager.FLAG_LOCK; 20 import static android.app.WallpaperManager.FLAG_SYSTEM; 21 import static android.os.ParcelFileDescriptor.MODE_CREATE; 22 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; 23 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; 24 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; 25 import static android.view.Display.DEFAULT_DISPLAY; 26 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 27 28 import android.app.ActivityManager; 29 import android.app.AppGlobals; 30 import android.app.AppOpsManager; 31 import android.app.IWallpaperManager; 32 import android.app.IWallpaperManagerCallback; 33 import android.app.PendingIntent; 34 import android.app.UserSwitchObserver; 35 import android.app.WallpaperInfo; 36 import android.app.WallpaperManager; 37 import android.app.admin.DevicePolicyManager; 38 import android.app.backup.WallpaperBackupHelper; 39 import android.content.BroadcastReceiver; 40 import android.content.ComponentName; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.content.ServiceConnection; 45 import android.content.pm.IPackageManager; 46 import android.content.pm.PackageManager; 47 import android.content.pm.PackageManager.NameNotFoundException; 48 import android.content.pm.ResolveInfo; 49 import android.content.pm.ServiceInfo; 50 import android.content.pm.UserInfo; 51 import android.content.res.Resources; 52 import android.graphics.Bitmap; 53 import android.graphics.BitmapFactory; 54 import android.graphics.BitmapRegionDecoder; 55 import android.graphics.Point; 56 import android.graphics.Rect; 57 import android.os.Binder; 58 import android.os.Bundle; 59 import android.os.Environment; 60 import android.os.FileObserver; 61 import android.os.FileUtils; 62 import android.os.Handler; 63 import android.os.IBinder; 64 import android.os.IRemoteCallback; 65 import android.os.Process; 66 import android.os.ParcelFileDescriptor; 67 import android.os.RemoteCallbackList; 68 import android.os.RemoteException; 69 import android.os.SELinux; 70 import android.os.ServiceManager; 71 import android.os.SystemClock; 72 import android.os.UserHandle; 73 import android.os.UserManager; 74 import android.service.wallpaper.IWallpaperConnection; 75 import android.service.wallpaper.IWallpaperEngine; 76 import android.service.wallpaper.IWallpaperService; 77 import android.service.wallpaper.WallpaperService; 78 import android.system.ErrnoException; 79 import android.system.Os; 80 import android.util.EventLog; 81 import android.util.Slog; 82 import android.util.SparseArray; 83 import android.util.Xml; 84 import android.view.Display; 85 import android.view.IWindowManager; 86 import android.view.WindowManager; 87 88 import com.android.internal.R; 89 import com.android.internal.content.PackageMonitor; 90 import com.android.internal.os.BackgroundThread; 91 import com.android.internal.util.DumpUtils; 92 import com.android.internal.util.FastXmlSerializer; 93 import com.android.internal.util.JournaledFile; 94 import com.android.server.EventLogTags; 95 import com.android.server.FgThread; 96 import com.android.server.SystemService; 97 98 import libcore.io.IoUtils; 99 100 import org.xmlpull.v1.XmlPullParser; 101 import org.xmlpull.v1.XmlPullParserException; 102 import org.xmlpull.v1.XmlSerializer; 103 104 import java.io.BufferedOutputStream; 105 import java.io.File; 106 import java.io.FileDescriptor; 107 import java.io.FileInputStream; 108 import java.io.FileNotFoundException; 109 import java.io.FileOutputStream; 110 import java.io.IOException; 111 import java.io.InputStream; 112 import java.io.PrintWriter; 113 import java.nio.charset.StandardCharsets; 114 import java.util.Arrays; 115 import java.util.List; 116 import java.util.Objects; 117 118 public class WallpaperManagerService extends IWallpaperManager.Stub { 119 static final String TAG = "WallpaperManagerService"; 120 static final boolean DEBUG = false; 121 static final boolean DEBUG_LIVE = DEBUG || true; 122 123 public static class Lifecycle extends SystemService { 124 private WallpaperManagerService mService; 125 Lifecycle(Context context)126 public Lifecycle(Context context) { 127 super(context); 128 } 129 130 @Override onStart()131 public void onStart() { 132 mService = new WallpaperManagerService(getContext()); 133 publishBinderService(Context.WALLPAPER_SERVICE, mService); 134 } 135 136 @Override onBootPhase(int phase)137 public void onBootPhase(int phase) { 138 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { 139 mService.systemReady(); 140 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 141 mService.switchUser(UserHandle.USER_SYSTEM, null); 142 } 143 } 144 145 @Override onUnlockUser(int userHandle)146 public void onUnlockUser(int userHandle) { 147 mService.onUnlockUser(userHandle); 148 } 149 } 150 151 final Object mLock = new Object(); 152 153 /** 154 * Minimum time between crashes of a wallpaper service for us to consider 155 * restarting it vs. just reverting to the static wallpaper. 156 */ 157 static final long MIN_WALLPAPER_CRASH_TIME = 10000; 158 static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128; 159 static final String WALLPAPER = "wallpaper_orig"; 160 static final String WALLPAPER_CROP = "wallpaper"; 161 static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig"; 162 static final String WALLPAPER_LOCK_CROP = "wallpaper_lock"; 163 static final String WALLPAPER_INFO = "wallpaper_info.xml"; 164 165 // All the various per-user state files we need to be aware of 166 static final String[] sPerUserFiles = new String[] { 167 WALLPAPER, WALLPAPER_CROP, 168 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP, 169 WALLPAPER_INFO 170 }; 171 172 /** 173 * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks 174 * that the wallpaper has changed. The CREATE is triggered when there is no 175 * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered 176 * every time the wallpaper is changed. 177 */ 178 private class WallpaperObserver extends FileObserver { 179 180 final int mUserId; 181 final WallpaperData mWallpaper; 182 final File mWallpaperDir; 183 final File mWallpaperFile; 184 final File mWallpaperLockFile; 185 WallpaperObserver(WallpaperData wallpaper)186 public WallpaperObserver(WallpaperData wallpaper) { 187 super(getWallpaperDir(wallpaper.userId).getAbsolutePath(), 188 CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF); 189 mUserId = wallpaper.userId; 190 mWallpaperDir = getWallpaperDir(wallpaper.userId); 191 mWallpaper = wallpaper; 192 mWallpaperFile = new File(mWallpaperDir, WALLPAPER); 193 mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG); 194 } 195 dataForEvent(boolean sysChanged, boolean lockChanged)196 private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) { 197 WallpaperData wallpaper = null; 198 synchronized (mLock) { 199 if (lockChanged) { 200 wallpaper = mLockWallpaperMap.get(mUserId); 201 } 202 if (wallpaper == null) { 203 // no lock-specific wallpaper exists, or sys case, handled together 204 wallpaper = mWallpaperMap.get(mUserId); 205 } 206 } 207 return (wallpaper != null) ? wallpaper : mWallpaper; 208 } 209 210 @Override onEvent(int event, String path)211 public void onEvent(int event, String path) { 212 if (path == null) { 213 return; 214 } 215 final boolean moved = (event == MOVED_TO); 216 final boolean written = (event == CLOSE_WRITE || moved); 217 final File changedFile = new File(mWallpaperDir, path); 218 219 // System and system+lock changes happen on the system wallpaper input file; 220 // lock-only changes happen on the dedicated lock wallpaper input file 221 final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile)); 222 final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile)); 223 WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged); 224 225 if (DEBUG) { 226 Slog.v(TAG, "Wallpaper file change: evt=" + event 227 + " path=" + path 228 + " sys=" + sysWallpaperChanged 229 + " lock=" + lockWallpaperChanged 230 + " imagePending=" + wallpaper.imageWallpaperPending 231 + " whichPending=0x" + Integer.toHexString(wallpaper.whichPending) 232 + " written=" + written); 233 } 234 235 if (moved && lockWallpaperChanged) { 236 // We just migrated sys -> lock to preserve imagery for an impending 237 // new system-only wallpaper. Tell keyguard about it and make sure it 238 // has the right SELinux label. 239 if (DEBUG) { 240 Slog.i(TAG, "Sys -> lock MOVED_TO"); 241 } 242 SELinux.restorecon(changedFile); 243 notifyLockWallpaperChanged(); 244 return; 245 } 246 247 synchronized (mLock) { 248 if (sysWallpaperChanged || lockWallpaperChanged) { 249 notifyCallbacksLocked(wallpaper); 250 if (wallpaper.wallpaperComponent == null 251 || event != CLOSE_WRITE // includes the MOVED_TO case 252 || wallpaper.imageWallpaperPending) { 253 if (written) { 254 // The image source has finished writing the source image, 255 // so we now produce the crop rect (in the background), and 256 // only publish the new displayable (sub)image as a result 257 // of that work. 258 if (DEBUG) { 259 Slog.v(TAG, "Wallpaper written; generating crop"); 260 } 261 SELinux.restorecon(changedFile); 262 if (moved) { 263 // This is a restore, so generate the crop using any just-restored new 264 // crop guidelines, making sure to preserve our local dimension hints. 265 // We also make sure to reapply the correct SELinux label. 266 if (DEBUG) { 267 Slog.v(TAG, "moved-to, therefore restore; reloading metadata"); 268 } 269 loadSettingsLocked(wallpaper.userId, true); 270 } 271 generateCrop(wallpaper); 272 if (DEBUG) { 273 Slog.v(TAG, "Crop done; invoking completion callback"); 274 } 275 wallpaper.imageWallpaperPending = false; 276 if (wallpaper.setComplete != null) { 277 try { 278 wallpaper.setComplete.onWallpaperChanged(); 279 } catch (RemoteException e) { 280 // if this fails we don't really care; the setting app may just 281 // have crashed and that sort of thing is a fact of life. 282 } 283 } 284 if (sysWallpaperChanged) { 285 // If this was the system wallpaper, rebind... 286 bindWallpaperComponentLocked(mImageWallpaper, true, 287 false, wallpaper, null); 288 } 289 if (lockWallpaperChanged 290 || (wallpaper.whichPending & FLAG_LOCK) != 0) { 291 if (DEBUG) { 292 Slog.i(TAG, "Lock-relevant wallpaper changed"); 293 } 294 // either a lock-only wallpaper commit or a system+lock event. 295 // if it's system-plus-lock we need to wipe the lock bookkeeping; 296 // we're falling back to displaying the system wallpaper there. 297 if (!lockWallpaperChanged) { 298 mLockWallpaperMap.remove(wallpaper.userId); 299 } 300 // and in any case, tell keyguard about it 301 notifyLockWallpaperChanged(); 302 } 303 saveSettingsLocked(wallpaper.userId); 304 } 305 } 306 } 307 } 308 } 309 } 310 notifyLockWallpaperChanged()311 void notifyLockWallpaperChanged() { 312 final IWallpaperManagerCallback cb = mKeyguardListener; 313 if (cb != null) { 314 try { 315 cb.onWallpaperChanged(); 316 } catch (RemoteException e) { 317 // Oh well it went away; no big deal 318 } 319 } 320 } 321 322 /** 323 * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped 324 * for display. 325 */ generateCrop(WallpaperData wallpaper)326 private void generateCrop(WallpaperData wallpaper) { 327 boolean success = false; 328 329 Rect cropHint = new Rect(wallpaper.cropHint); 330 331 if (DEBUG) { 332 Slog.v(TAG, "Generating crop for new wallpaper(s): 0x" 333 + Integer.toHexString(wallpaper.whichPending) 334 + " to " + wallpaper.cropFile.getName() 335 + " crop=(" + cropHint.width() + 'x' + cropHint.height() 336 + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')'); 337 } 338 339 // Analyse the source; needed in multiple cases 340 BitmapFactory.Options options = new BitmapFactory.Options(); 341 options.inJustDecodeBounds = true; 342 BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options); 343 if (options.outWidth <= 0 || options.outHeight <= 0) { 344 Slog.w(TAG, "Invalid wallpaper data"); 345 success = false; 346 } else { 347 boolean needCrop = false; 348 boolean needScale = false; 349 350 // Empty crop means use the full image 351 if (cropHint.isEmpty()) { 352 cropHint.left = cropHint.top = 0; 353 cropHint.right = options.outWidth; 354 cropHint.bottom = options.outHeight; 355 } else { 356 // force the crop rect to lie within the measured bounds 357 cropHint.offset( 358 (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0), 359 (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0)); 360 361 // If the crop hint was larger than the image we just overshot. Patch things up. 362 if (cropHint.left < 0) { 363 cropHint.left = 0; 364 } 365 if (cropHint.top < 0) { 366 cropHint.top = 0; 367 } 368 369 // Don't bother cropping if what we're left with is identity 370 needCrop = (options.outHeight > cropHint.height() 371 || options.outWidth > cropHint.width()); 372 } 373 374 // scale if the crop height winds up not matching the recommended metrics 375 needScale = (wallpaper.height != cropHint.height()); 376 377 if (DEBUG) { 378 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height()); 379 Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height); 380 Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight); 381 Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale); 382 } 383 384 if (!needCrop && !needScale) { 385 // Simple case: the nominal crop fits what we want, so we take 386 // the whole thing and just copy the image file directly. 387 if (DEBUG) { 388 Slog.v(TAG, "Null crop of new wallpaper; copying"); 389 } 390 success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile); 391 if (!success) { 392 wallpaper.cropFile.delete(); 393 // TODO: fall back to default wallpaper in this case 394 } 395 } else { 396 // Fancy case: crop and scale. First, we decode and scale down if appropriate. 397 FileOutputStream f = null; 398 BufferedOutputStream bos = null; 399 try { 400 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance( 401 wallpaper.wallpaperFile.getAbsolutePath(), false); 402 403 // This actually downsamples only by powers of two, but that's okay; we do 404 // a proper scaling blit later. This is to minimize transient RAM use. 405 // We calculate the largest power-of-two under the actual ratio rather than 406 // just let the decode take care of it because we also want to remap where the 407 // cropHint rectangle lies in the decoded [super]rect. 408 final BitmapFactory.Options scaler; 409 final int actualScale = cropHint.height() / wallpaper.height; 410 int scale = 1; 411 while (2*scale < actualScale) { 412 scale *= 2; 413 } 414 if (scale > 1) { 415 scaler = new BitmapFactory.Options(); 416 scaler.inSampleSize = scale; 417 if (DEBUG) { 418 Slog.v(TAG, "Downsampling cropped rect with scale " + scale); 419 } 420 } else { 421 scaler = null; 422 } 423 Bitmap cropped = decoder.decodeRegion(cropHint, scaler); 424 decoder.recycle(); 425 426 if (cropped == null) { 427 Slog.e(TAG, "Could not decode new wallpaper"); 428 } else { 429 // We've got the extracted crop; now we want to scale it properly to 430 // the desired rectangle. That's a height-biased operation: make it 431 // fit the hinted height, and accept whatever width we end up with. 432 cropHint.offsetTo(0, 0); 433 cropHint.right /= scale; // adjust by downsampling factor 434 cropHint.bottom /= scale; 435 final float heightR = ((float)wallpaper.height) / ((float)cropHint.height()); 436 if (DEBUG) { 437 Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint); 438 } 439 final int destWidth = (int)(cropHint.width() * heightR); 440 final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped, 441 destWidth, wallpaper.height, true); 442 if (DEBUG) { 443 Slog.v(TAG, "Final extract:"); 444 Slog.v(TAG, " dims: w=" + wallpaper.width 445 + " h=" + wallpaper.height); 446 Slog.v(TAG, " out: w=" + finalCrop.getWidth() 447 + " h=" + finalCrop.getHeight()); 448 } 449 450 f = new FileOutputStream(wallpaper.cropFile); 451 bos = new BufferedOutputStream(f, 32*1024); 452 finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos); 453 bos.flush(); // don't rely on the implicit flush-at-close when noting success 454 success = true; 455 } 456 } catch (Exception e) { 457 if (DEBUG) { 458 Slog.e(TAG, "Error decoding crop", e); 459 } 460 } finally { 461 IoUtils.closeQuietly(bos); 462 IoUtils.closeQuietly(f); 463 } 464 } 465 } 466 467 if (!success) { 468 Slog.e(TAG, "Unable to apply new wallpaper"); 469 wallpaper.cropFile.delete(); 470 } 471 472 if (wallpaper.cropFile.exists()) { 473 boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile()); 474 if (DEBUG) { 475 Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon); 476 } 477 } 478 } 479 480 final Context mContext; 481 final IWindowManager mIWindowManager; 482 final IPackageManager mIPackageManager; 483 final MyPackageMonitor mMonitor; 484 final AppOpsManager mAppOpsManager; 485 WallpaperData mLastWallpaper; 486 IWallpaperManagerCallback mKeyguardListener; 487 boolean mWaitingForUnlock; 488 boolean mShuttingDown; 489 490 /** 491 * ID of the current wallpaper, changed every time anything sets a wallpaper. 492 * This is used for external detection of wallpaper update activity. 493 */ 494 int mWallpaperId; 495 496 /** 497 * Name of the component used to display bitmap wallpapers from either the gallery or 498 * built-in wallpapers. 499 */ 500 final ComponentName mImageWallpaper; 501 502 /** 503 * Name of the default wallpaper component; might be different from mImageWallpaper 504 */ 505 final ComponentName mDefaultWallpaperComponent; 506 507 final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>(); 508 final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>(); 509 510 final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>(); 511 int mCurrentUserId; 512 513 static class WallpaperData { 514 515 int userId; 516 517 final File wallpaperFile; // source image 518 final File cropFile; // eventual destination 519 520 /** 521 * True while the client is writing a new wallpaper 522 */ 523 boolean imageWallpaperPending; 524 525 /** 526 * Which new wallpapers are being written; mirrors the 'which' 527 * selector bit field to setWallpaper(). 528 */ 529 int whichPending; 530 531 /** 532 * Callback once the set + crop is finished 533 */ 534 IWallpaperManagerCallback setComplete; 535 536 /** 537 * Is the OS allowed to back up this wallpaper imagery? 538 */ 539 boolean allowBackup; 540 541 /** 542 * Resource name if using a picture from the wallpaper gallery 543 */ 544 String name = ""; 545 546 /** 547 * The component name of the currently set live wallpaper. 548 */ 549 ComponentName wallpaperComponent; 550 551 /** 552 * The component name of the wallpaper that should be set next. 553 */ 554 ComponentName nextWallpaperComponent; 555 556 /** 557 * The ID of this wallpaper 558 */ 559 int wallpaperId; 560 561 WallpaperConnection connection; 562 long lastDiedTime; 563 boolean wallpaperUpdating; 564 WallpaperObserver wallpaperObserver; 565 566 /** 567 * List of callbacks registered they should each be notified when the wallpaper is changed. 568 */ 569 private RemoteCallbackList<IWallpaperManagerCallback> callbacks 570 = new RemoteCallbackList<IWallpaperManagerCallback>(); 571 572 int width = -1; 573 int height = -1; 574 575 /** 576 * The crop hint supplied for displaying a subset of the source image 577 */ 578 final Rect cropHint = new Rect(0, 0, 0, 0); 579 580 final Rect padding = new Rect(0, 0, 0, 0); 581 WallpaperData(int userId, String inputFileName, String cropFileName)582 WallpaperData(int userId, String inputFileName, String cropFileName) { 583 this.userId = userId; 584 final File wallpaperDir = getWallpaperDir(userId); 585 wallpaperFile = new File(wallpaperDir, inputFileName); 586 cropFile = new File(wallpaperDir, cropFileName); 587 } 588 589 // Called during initialization of a given user's wallpaper bookkeeping cropExists()590 boolean cropExists() { 591 return cropFile.exists(); 592 } 593 sourceExists()594 boolean sourceExists() { 595 return wallpaperFile.exists(); 596 } 597 } 598 makeWallpaperIdLocked()599 int makeWallpaperIdLocked() { 600 do { 601 ++mWallpaperId; 602 } while (mWallpaperId == 0); 603 return mWallpaperId; 604 } 605 606 class WallpaperConnection extends IWallpaperConnection.Stub 607 implements ServiceConnection { 608 609 /** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the 610 * middle of an update). If exceeded, the wallpaper gets reset to the system default. */ 611 private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 10000; 612 613 final WallpaperInfo mInfo; 614 final Binder mToken = new Binder(); 615 IWallpaperService mService; 616 IWallpaperEngine mEngine; 617 WallpaperData mWallpaper; 618 IRemoteCallback mReply; 619 620 boolean mDimensionsChanged = false; 621 boolean mPaddingChanged = false; 622 623 private Runnable mResetRunnable = () -> { 624 synchronized (mLock) { 625 if (mShuttingDown) { 626 // Don't expect wallpaper services to relaunch during shutdown 627 if (DEBUG_LIVE) { 628 Slog.i(TAG, "Ignoring relaunch timeout during shutdown"); 629 } 630 return; 631 } 632 633 if (!mWallpaper.wallpaperUpdating 634 && mWallpaper.userId == mCurrentUserId) { 635 Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent 636 + ", reverting to built-in wallpaper!"); 637 clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, 638 null); 639 } 640 } 641 }; 642 WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper)643 public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) { 644 mInfo = info; 645 mWallpaper = wallpaper; 646 } 647 648 @Override onServiceConnected(ComponentName name, IBinder service)649 public void onServiceConnected(ComponentName name, IBinder service) { 650 synchronized (mLock) { 651 if (mWallpaper.connection == this) { 652 mService = IWallpaperService.Stub.asInterface(service); 653 attachServiceLocked(this, mWallpaper); 654 // XXX should probably do saveSettingsLocked() later 655 // when we have an engine, but I'm not sure about 656 // locking there and anyway we always need to be able to 657 // recover if there is something wrong. 658 saveSettingsLocked(mWallpaper.userId); 659 FgThread.getHandler().removeCallbacks(mResetRunnable); 660 } 661 } 662 } 663 664 @Override onServiceDisconnected(ComponentName name)665 public void onServiceDisconnected(ComponentName name) { 666 synchronized (mLock) { 667 Slog.w(TAG, "Wallpaper service gone: " + name); 668 if (!Objects.equals(name, mWallpaper.wallpaperComponent)) { 669 Slog.e(TAG, "Does not match expected wallpaper component " 670 + mWallpaper.wallpaperComponent); 671 } 672 mService = null; 673 mEngine = null; 674 if (mWallpaper.connection == this) { 675 // There is an inherent ordering race between this callback and the 676 // package monitor that receives notice that a package is being updated, 677 // so we cannot quite trust at this moment that we know for sure that 678 // this is not an update. If we think this is a genuine non-update 679 // wallpaper outage, we do our "wait for reset" work as a continuation, 680 // a short time in the future, specifically to allow any pending package 681 // update message on this same looper thread to be processed. 682 if (!mWallpaper.wallpaperUpdating) { 683 mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this), 684 1000); 685 } 686 } 687 } 688 } 689 processDisconnect(final ServiceConnection connection)690 private void processDisconnect(final ServiceConnection connection) { 691 synchronized (mLock) { 692 // The wallpaper disappeared. If this isn't a system-default one, track 693 // crashes and fall back to default if it continues to misbehave. 694 if (connection == mWallpaper.connection) { 695 final ComponentName wpService = mWallpaper.wallpaperComponent; 696 if (!mWallpaper.wallpaperUpdating 697 && mWallpaper.userId == mCurrentUserId 698 && !Objects.equals(mDefaultWallpaperComponent, wpService) 699 && !Objects.equals(mImageWallpaper, wpService)) { 700 // There is a race condition which causes 701 // {@link #mWallpaper.wallpaperUpdating} to be false even if it is 702 // currently updating since the broadcast notifying us is async. 703 // This race is overcome by the general rule that we only reset the 704 // wallpaper if its service was shut down twice 705 // during {@link #MIN_WALLPAPER_CRASH_TIME} millis. 706 if (mWallpaper.lastDiedTime != 0 707 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME 708 > SystemClock.uptimeMillis()) { 709 Slog.w(TAG, "Reverting to built-in wallpaper!"); 710 clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); 711 } else { 712 mWallpaper.lastDiedTime = SystemClock.uptimeMillis(); 713 714 // If we didn't reset it right away, do so after we couldn't connect to 715 // it for an extended amount of time to avoid having a black wallpaper. 716 final Handler fgHandler = FgThread.getHandler(); 717 fgHandler.removeCallbacks(mResetRunnable); 718 fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS); 719 if (DEBUG_LIVE) { 720 Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService); 721 } 722 } 723 final String flattened = wpService.flattenToString(); 724 EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED, 725 flattened.substring(0, Math.min(flattened.length(), 726 MAX_WALLPAPER_COMPONENT_LOG_LENGTH))); 727 } 728 } else { 729 if (DEBUG_LIVE) { 730 Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring"); 731 } 732 } 733 } 734 } 735 736 @Override attachEngine(IWallpaperEngine engine)737 public void attachEngine(IWallpaperEngine engine) { 738 synchronized (mLock) { 739 mEngine = engine; 740 if (mDimensionsChanged) { 741 try { 742 mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height); 743 } catch (RemoteException e) { 744 Slog.w(TAG, "Failed to set wallpaper dimensions", e); 745 } 746 mDimensionsChanged = false; 747 } 748 if (mPaddingChanged) { 749 try { 750 mEngine.setDisplayPadding(mWallpaper.padding); 751 } catch (RemoteException e) { 752 Slog.w(TAG, "Failed to set wallpaper padding", e); 753 } 754 mPaddingChanged = false; 755 } 756 } 757 } 758 759 @Override engineShown(IWallpaperEngine engine)760 public void engineShown(IWallpaperEngine engine) { 761 synchronized (mLock) { 762 if (mReply != null) { 763 long ident = Binder.clearCallingIdentity(); 764 try { 765 mReply.sendResult(null); 766 } catch (RemoteException e) { 767 Binder.restoreCallingIdentity(ident); 768 } 769 mReply = null; 770 } 771 } 772 } 773 774 @Override setWallpaper(String name)775 public ParcelFileDescriptor setWallpaper(String name) { 776 synchronized (mLock) { 777 if (mWallpaper.connection == this) { 778 return updateWallpaperBitmapLocked(name, mWallpaper, null); 779 } 780 return null; 781 } 782 } 783 } 784 785 class MyPackageMonitor extends PackageMonitor { 786 @Override onPackageUpdateFinished(String packageName, int uid)787 public void onPackageUpdateFinished(String packageName, int uid) { 788 synchronized (mLock) { 789 if (mCurrentUserId != getChangingUserId()) { 790 return; 791 } 792 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 793 if (wallpaper != null) { 794 final ComponentName wpService = wallpaper.wallpaperComponent; 795 if (wpService != null && wpService.getPackageName().equals(packageName)) { 796 if (DEBUG_LIVE) { 797 Slog.i(TAG, "Wallpaper " + wpService + " update has finished"); 798 } 799 wallpaper.wallpaperUpdating = false; 800 clearWallpaperComponentLocked(wallpaper); 801 if (!bindWallpaperComponentLocked(wpService, false, false, 802 wallpaper, null)) { 803 Slog.w(TAG, "Wallpaper " + wpService 804 + " no longer available; reverting to default"); 805 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); 806 } 807 } 808 } 809 } 810 } 811 812 @Override onPackageModified(String packageName)813 public void onPackageModified(String packageName) { 814 synchronized (mLock) { 815 if (mCurrentUserId != getChangingUserId()) { 816 return; 817 } 818 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 819 if (wallpaper != null) { 820 if (wallpaper.wallpaperComponent == null 821 || !wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { 822 return; 823 } 824 doPackagesChangedLocked(true, wallpaper); 825 } 826 } 827 } 828 829 @Override onPackageUpdateStarted(String packageName, int uid)830 public void onPackageUpdateStarted(String packageName, int uid) { 831 synchronized (mLock) { 832 if (mCurrentUserId != getChangingUserId()) { 833 return; 834 } 835 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 836 if (wallpaper != null) { 837 if (wallpaper.wallpaperComponent != null 838 && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { 839 if (DEBUG_LIVE) { 840 Slog.i(TAG, "Wallpaper service " + wallpaper.wallpaperComponent 841 + " is updating"); 842 } 843 wallpaper.wallpaperUpdating = true; 844 if (wallpaper.connection != null) { 845 FgThread.getHandler().removeCallbacks( 846 wallpaper.connection.mResetRunnable); 847 } 848 } 849 } 850 } 851 } 852 853 @Override onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit)854 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { 855 synchronized (mLock) { 856 boolean changed = false; 857 if (mCurrentUserId != getChangingUserId()) { 858 return false; 859 } 860 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 861 if (wallpaper != null) { 862 boolean res = doPackagesChangedLocked(doit, wallpaper); 863 changed |= res; 864 } 865 return changed; 866 } 867 } 868 869 @Override onSomePackagesChanged()870 public void onSomePackagesChanged() { 871 synchronized (mLock) { 872 if (mCurrentUserId != getChangingUserId()) { 873 return; 874 } 875 WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId); 876 if (wallpaper != null) { 877 doPackagesChangedLocked(true, wallpaper); 878 } 879 } 880 } 881 doPackagesChangedLocked(boolean doit, WallpaperData wallpaper)882 boolean doPackagesChangedLocked(boolean doit, WallpaperData wallpaper) { 883 boolean changed = false; 884 if (wallpaper.wallpaperComponent != null) { 885 int change = isPackageDisappearing(wallpaper.wallpaperComponent 886 .getPackageName()); 887 if (change == PACKAGE_PERMANENT_CHANGE 888 || change == PACKAGE_TEMPORARY_CHANGE) { 889 changed = true; 890 if (doit) { 891 Slog.w(TAG, "Wallpaper uninstalled, removing: " 892 + wallpaper.wallpaperComponent); 893 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); 894 } 895 } 896 } 897 if (wallpaper.nextWallpaperComponent != null) { 898 int change = isPackageDisappearing(wallpaper.nextWallpaperComponent 899 .getPackageName()); 900 if (change == PACKAGE_PERMANENT_CHANGE 901 || change == PACKAGE_TEMPORARY_CHANGE) { 902 wallpaper.nextWallpaperComponent = null; 903 } 904 } 905 if (wallpaper.wallpaperComponent != null 906 && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) { 907 try { 908 mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent, 909 PackageManager.MATCH_DIRECT_BOOT_AWARE 910 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 911 } catch (NameNotFoundException e) { 912 Slog.w(TAG, "Wallpaper component gone, removing: " 913 + wallpaper.wallpaperComponent); 914 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); 915 } 916 } 917 if (wallpaper.nextWallpaperComponent != null 918 && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) { 919 try { 920 mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent, 921 PackageManager.MATCH_DIRECT_BOOT_AWARE 922 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 923 } catch (NameNotFoundException e) { 924 wallpaper.nextWallpaperComponent = null; 925 } 926 } 927 return changed; 928 } 929 } 930 WallpaperManagerService(Context context)931 public WallpaperManagerService(Context context) { 932 if (DEBUG) Slog.v(TAG, "WallpaperService startup"); 933 mContext = context; 934 mShuttingDown = false; 935 mImageWallpaper = ComponentName.unflattenFromString( 936 context.getResources().getString(R.string.image_wallpaper_component)); 937 mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context); 938 mIWindowManager = IWindowManager.Stub.asInterface( 939 ServiceManager.getService(Context.WINDOW_SERVICE)); 940 mIPackageManager = AppGlobals.getPackageManager(); 941 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 942 mMonitor = new MyPackageMonitor(); 943 mMonitor.register(context, null, UserHandle.ALL, true); 944 getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs(); 945 loadSettingsLocked(UserHandle.USER_SYSTEM, false); 946 } 947 getWallpaperDir(int userId)948 private static File getWallpaperDir(int userId) { 949 return Environment.getUserSystemDirectory(userId); 950 } 951 952 @Override finalize()953 protected void finalize() throws Throwable { 954 super.finalize(); 955 for (int i = 0; i < mWallpaperMap.size(); i++) { 956 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 957 wallpaper.wallpaperObserver.stopWatching(); 958 } 959 } 960 systemReady()961 void systemReady() { 962 if (DEBUG) Slog.v(TAG, "systemReady"); 963 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM); 964 // If we think we're going to be using the system image wallpaper imagery, make 965 // sure we have something to render 966 if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) { 967 // No crop file? Make sure we've finished the processing sequence if necessary 968 if (!wallpaper.cropExists()) { 969 if (DEBUG) { 970 Slog.i(TAG, "No crop; regenerating from source"); 971 } 972 generateCrop(wallpaper); 973 } 974 // Still nothing? Fall back to default. 975 if (!wallpaper.cropExists()) { 976 if (DEBUG) { 977 Slog.i(TAG, "Unable to regenerate crop; resetting"); 978 } 979 clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null); 980 } 981 } else { 982 if (DEBUG) { 983 Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring"); 984 } 985 } 986 987 IntentFilter userFilter = new IntentFilter(); 988 userFilter.addAction(Intent.ACTION_USER_REMOVED); 989 mContext.registerReceiver(new BroadcastReceiver() { 990 @Override 991 public void onReceive(Context context, Intent intent) { 992 final String action = intent.getAction(); 993 if (Intent.ACTION_USER_REMOVED.equals(action)) { 994 onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 995 UserHandle.USER_NULL)); 996 } 997 } 998 }, userFilter); 999 1000 final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); 1001 mContext.registerReceiver(new BroadcastReceiver() { 1002 @Override 1003 public void onReceive(Context context, Intent intent) { 1004 if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { 1005 if (DEBUG) { 1006 Slog.i(TAG, "Shutting down"); 1007 } 1008 synchronized (mLock) { 1009 mShuttingDown = true; 1010 } 1011 } 1012 } 1013 }, shutdownFilter); 1014 1015 try { 1016 ActivityManager.getService().registerUserSwitchObserver( 1017 new UserSwitchObserver() { 1018 @Override 1019 public void onUserSwitching(int newUserId, IRemoteCallback reply) { 1020 switchUser(newUserId, reply); 1021 } 1022 }, TAG); 1023 } catch (RemoteException e) { 1024 e.rethrowAsRuntimeException(); 1025 } 1026 } 1027 1028 /** Called by SystemBackupAgent */ getName()1029 public String getName() { 1030 // Verify caller is the system 1031 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { 1032 throw new RuntimeException("getName() can only be called from the system process"); 1033 } 1034 synchronized (mLock) { 1035 return mWallpaperMap.get(0).name; 1036 } 1037 } 1038 stopObserver(WallpaperData wallpaper)1039 void stopObserver(WallpaperData wallpaper) { 1040 if (wallpaper != null) { 1041 if (wallpaper.wallpaperObserver != null) { 1042 wallpaper.wallpaperObserver.stopWatching(); 1043 wallpaper.wallpaperObserver = null; 1044 } 1045 } 1046 } 1047 stopObserversLocked(int userId)1048 void stopObserversLocked(int userId) { 1049 stopObserver(mWallpaperMap.get(userId)); 1050 stopObserver(mLockWallpaperMap.get(userId)); 1051 mWallpaperMap.remove(userId); 1052 mLockWallpaperMap.remove(userId); 1053 } 1054 onUnlockUser(final int userId)1055 void onUnlockUser(final int userId) { 1056 synchronized (mLock) { 1057 if (mCurrentUserId == userId) { 1058 if (mWaitingForUnlock) { 1059 // If we're switching users, now is when we transition the wallpaper 1060 switchUser(userId, null); 1061 } 1062 1063 // Make sure that the SELinux labeling of all the relevant files is correct. 1064 // This corrects for mislabeling bugs that might have arisen from move-to 1065 // operations involving the wallpaper files. This isn't timing-critical, 1066 // so we do it in the background to avoid holding up the user unlock operation. 1067 if (mUserRestorecon.get(userId) != Boolean.TRUE) { 1068 mUserRestorecon.put(userId, Boolean.TRUE); 1069 Runnable relabeler = new Runnable() { 1070 @Override 1071 public void run() { 1072 final File wallpaperDir = getWallpaperDir(userId); 1073 for (String filename : sPerUserFiles) { 1074 File f = new File(wallpaperDir, filename); 1075 if (f.exists()) { 1076 SELinux.restorecon(f); 1077 } 1078 } 1079 } 1080 }; 1081 BackgroundThread.getHandler().post(relabeler); 1082 } 1083 } 1084 } 1085 } 1086 onRemoveUser(int userId)1087 void onRemoveUser(int userId) { 1088 if (userId < 1) return; 1089 1090 final File wallpaperDir = getWallpaperDir(userId); 1091 synchronized (mLock) { 1092 stopObserversLocked(userId); 1093 for (String filename : sPerUserFiles) { 1094 new File(wallpaperDir, filename).delete(); 1095 } 1096 mUserRestorecon.remove(userId); 1097 } 1098 } 1099 switchUser(int userId, IRemoteCallback reply)1100 void switchUser(int userId, IRemoteCallback reply) { 1101 synchronized (mLock) { 1102 mCurrentUserId = userId; 1103 WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); 1104 // Not started watching yet, in case wallpaper data was loaded for other reasons. 1105 if (wallpaper.wallpaperObserver == null) { 1106 wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper); 1107 wallpaper.wallpaperObserver.startWatching(); 1108 } 1109 switchWallpaper(wallpaper, reply); 1110 } 1111 } 1112 switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply)1113 void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) { 1114 synchronized (mLock) { 1115 mWaitingForUnlock = false; 1116 final ComponentName cname = wallpaper.wallpaperComponent != null ? 1117 wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent; 1118 if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) { 1119 // We failed to bind the desired wallpaper, but that might 1120 // happen if the wallpaper isn't direct-boot aware 1121 ServiceInfo si = null; 1122 try { 1123 si = mIPackageManager.getServiceInfo(cname, 1124 PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId); 1125 } catch (RemoteException ignored) { 1126 } 1127 1128 if (si == null) { 1129 Slog.w(TAG, "Failure starting previous wallpaper; clearing"); 1130 clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply); 1131 } else { 1132 Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked"); 1133 // We might end up persisting the current wallpaper data 1134 // while locked, so pretend like the component was actually 1135 // bound into place 1136 wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent; 1137 final WallpaperData fallback = new WallpaperData(wallpaper.userId, 1138 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); 1139 ensureSaneWallpaperData(fallback); 1140 bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply); 1141 mWaitingForUnlock = true; 1142 } 1143 } 1144 } 1145 } 1146 1147 @Override clearWallpaper(String callingPackage, int which, int userId)1148 public void clearWallpaper(String callingPackage, int which, int userId) { 1149 if (DEBUG) Slog.v(TAG, "clearWallpaper"); 1150 checkPermission(android.Manifest.permission.SET_WALLPAPER); 1151 if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) { 1152 return; 1153 } 1154 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1155 Binder.getCallingUid(), userId, false, true, "clearWallpaper", null); 1156 1157 synchronized (mLock) { 1158 clearWallpaperLocked(false, which, userId, null); 1159 } 1160 } 1161 clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply)1162 void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) { 1163 if (which != FLAG_SYSTEM && which != FLAG_LOCK) { 1164 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read"); 1165 } 1166 1167 WallpaperData wallpaper = null; 1168 if (which == FLAG_LOCK) { 1169 wallpaper = mLockWallpaperMap.get(userId); 1170 if (wallpaper == null) { 1171 // It's already gone; we're done. 1172 if (DEBUG) { 1173 Slog.i(TAG, "Lock wallpaper already cleared"); 1174 } 1175 return; 1176 } 1177 } else { 1178 wallpaper = mWallpaperMap.get(userId); 1179 if (wallpaper == null) { 1180 // Might need to bring it in the first time to establish our rewrite 1181 loadSettingsLocked(userId, false); 1182 wallpaper = mWallpaperMap.get(userId); 1183 } 1184 } 1185 if (wallpaper == null) { 1186 return; 1187 } 1188 1189 final long ident = Binder.clearCallingIdentity(); 1190 try { 1191 if (wallpaper.wallpaperFile.exists()) { 1192 wallpaper.wallpaperFile.delete(); 1193 wallpaper.cropFile.delete(); 1194 if (which == FLAG_LOCK) { 1195 mLockWallpaperMap.remove(userId); 1196 final IWallpaperManagerCallback cb = mKeyguardListener; 1197 if (cb != null) { 1198 if (DEBUG) { 1199 Slog.i(TAG, "Notifying keyguard of lock wallpaper clear"); 1200 } 1201 try { 1202 cb.onWallpaperChanged(); 1203 } catch (RemoteException e) { 1204 // Oh well it went away; no big deal 1205 } 1206 } 1207 saveSettingsLocked(userId); 1208 return; 1209 } 1210 } 1211 1212 RuntimeException e = null; 1213 try { 1214 wallpaper.imageWallpaperPending = false; 1215 if (userId != mCurrentUserId) return; 1216 if (bindWallpaperComponentLocked(defaultFailed 1217 ? mImageWallpaper 1218 : null, true, false, wallpaper, reply)) { 1219 return; 1220 } 1221 } catch (IllegalArgumentException e1) { 1222 e = e1; 1223 } 1224 1225 // This can happen if the default wallpaper component doesn't 1226 // exist. This should be a system configuration problem, but 1227 // let's not let it crash the system and just live with no 1228 // wallpaper. 1229 Slog.e(TAG, "Default wallpaper component not found!", e); 1230 clearWallpaperComponentLocked(wallpaper); 1231 if (reply != null) { 1232 try { 1233 reply.sendResult(null); 1234 } catch (RemoteException e1) { 1235 } 1236 } 1237 } finally { 1238 Binder.restoreCallingIdentity(ident); 1239 } 1240 } 1241 hasNamedWallpaper(String name)1242 public boolean hasNamedWallpaper(String name) { 1243 synchronized (mLock) { 1244 List<UserInfo> users; 1245 long ident = Binder.clearCallingIdentity(); 1246 try { 1247 users = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)).getUsers(); 1248 } finally { 1249 Binder.restoreCallingIdentity(ident); 1250 } 1251 for (UserInfo user: users) { 1252 // ignore managed profiles 1253 if (user.isManagedProfile()) { 1254 continue; 1255 } 1256 WallpaperData wd = mWallpaperMap.get(user.id); 1257 if (wd == null) { 1258 // User hasn't started yet, so load her settings to peek at the wallpaper 1259 loadSettingsLocked(user.id, false); 1260 wd = mWallpaperMap.get(user.id); 1261 } 1262 if (wd != null && name.equals(wd.name)) { 1263 return true; 1264 } 1265 } 1266 } 1267 return false; 1268 } 1269 getDefaultDisplaySize()1270 private Point getDefaultDisplaySize() { 1271 Point p = new Point(); 1272 WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); 1273 Display d = wm.getDefaultDisplay(); 1274 d.getRealSize(p); 1275 return p; 1276 } 1277 setDimensionHints(int width, int height, String callingPackage)1278 public void setDimensionHints(int width, int height, String callingPackage) 1279 throws RemoteException { 1280 checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); 1281 if (!isWallpaperSupported(callingPackage)) { 1282 return; 1283 } 1284 synchronized (mLock) { 1285 int userId = UserHandle.getCallingUserId(); 1286 WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); 1287 if (width <= 0 || height <= 0) { 1288 throw new IllegalArgumentException("width and height must be > 0"); 1289 } 1290 // Make sure it is at least as large as the display. 1291 Point displaySize = getDefaultDisplaySize(); 1292 width = Math.max(width, displaySize.x); 1293 height = Math.max(height, displaySize.y); 1294 1295 if (width != wallpaper.width || height != wallpaper.height) { 1296 wallpaper.width = width; 1297 wallpaper.height = height; 1298 saveSettingsLocked(userId); 1299 if (mCurrentUserId != userId) return; // Don't change the properties now 1300 if (wallpaper.connection != null) { 1301 if (wallpaper.connection.mEngine != null) { 1302 try { 1303 wallpaper.connection.mEngine.setDesiredSize( 1304 width, height); 1305 } catch (RemoteException e) { 1306 } 1307 notifyCallbacksLocked(wallpaper); 1308 } else if (wallpaper.connection.mService != null) { 1309 // We've attached to the service but the engine hasn't attached back to us 1310 // yet. This means it will be created with the previous dimensions, so we 1311 // need to update it to the new dimensions once it attaches. 1312 wallpaper.connection.mDimensionsChanged = true; 1313 } 1314 } 1315 } 1316 } 1317 } 1318 getWidthHint()1319 public int getWidthHint() throws RemoteException { 1320 synchronized (mLock) { 1321 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); 1322 if (wallpaper != null) { 1323 return wallpaper.width; 1324 } else { 1325 return 0; 1326 } 1327 } 1328 } 1329 getHeightHint()1330 public int getHeightHint() throws RemoteException { 1331 synchronized (mLock) { 1332 WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId()); 1333 if (wallpaper != null) { 1334 return wallpaper.height; 1335 } else { 1336 return 0; 1337 } 1338 } 1339 } 1340 setDisplayPadding(Rect padding, String callingPackage)1341 public void setDisplayPadding(Rect padding, String callingPackage) { 1342 checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); 1343 if (!isWallpaperSupported(callingPackage)) { 1344 return; 1345 } 1346 synchronized (mLock) { 1347 int userId = UserHandle.getCallingUserId(); 1348 WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); 1349 if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) { 1350 throw new IllegalArgumentException("padding must be positive: " + padding); 1351 } 1352 1353 if (!padding.equals(wallpaper.padding)) { 1354 wallpaper.padding.set(padding); 1355 saveSettingsLocked(userId); 1356 if (mCurrentUserId != userId) return; // Don't change the properties now 1357 if (wallpaper.connection != null) { 1358 if (wallpaper.connection.mEngine != null) { 1359 try { 1360 wallpaper.connection.mEngine.setDisplayPadding(padding); 1361 } catch (RemoteException e) { 1362 } 1363 notifyCallbacksLocked(wallpaper); 1364 } else if (wallpaper.connection.mService != null) { 1365 // We've attached to the service but the engine hasn't attached back to us 1366 // yet. This means it will be created with the previous dimensions, so we 1367 // need to update it to the new dimensions once it attaches. 1368 wallpaper.connection.mPaddingChanged = true; 1369 } 1370 } 1371 } 1372 } 1373 } 1374 1375 @Override getWallpaper(IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId)1376 public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, final int which, 1377 Bundle outParams, int wallpaperUserId) { 1378 wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1379 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null); 1380 1381 if (which != FLAG_SYSTEM && which != FLAG_LOCK) { 1382 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read"); 1383 } 1384 1385 synchronized (mLock) { 1386 final SparseArray<WallpaperData> whichSet = 1387 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap; 1388 WallpaperData wallpaper = whichSet.get(wallpaperUserId); 1389 if (wallpaper == null) { 1390 // common case, this is the first lookup post-boot of the system or 1391 // unified lock, so we bring up the saved state lazily now and recheck. 1392 loadSettingsLocked(wallpaperUserId, false); 1393 wallpaper = whichSet.get(wallpaperUserId); 1394 if (wallpaper == null) { 1395 return null; 1396 } 1397 } 1398 try { 1399 if (outParams != null) { 1400 outParams.putInt("width", wallpaper.width); 1401 outParams.putInt("height", wallpaper.height); 1402 } 1403 if (cb != null) { 1404 wallpaper.callbacks.register(cb); 1405 } 1406 if (!wallpaper.cropFile.exists()) { 1407 return null; 1408 } 1409 return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY); 1410 } catch (FileNotFoundException e) { 1411 /* Shouldn't happen as we check to see if the file exists */ 1412 Slog.w(TAG, "Error getting wallpaper", e); 1413 } 1414 return null; 1415 } 1416 } 1417 1418 @Override getWallpaperInfo(int userId)1419 public WallpaperInfo getWallpaperInfo(int userId) { 1420 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1421 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null); 1422 synchronized (mLock) { 1423 WallpaperData wallpaper = mWallpaperMap.get(userId); 1424 if (wallpaper != null && wallpaper.connection != null) { 1425 return wallpaper.connection.mInfo; 1426 } 1427 return null; 1428 } 1429 } 1430 1431 @Override getWallpaperIdForUser(int which, int userId)1432 public int getWallpaperIdForUser(int which, int userId) { 1433 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1434 Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null); 1435 1436 if (which != FLAG_SYSTEM && which != FLAG_LOCK) { 1437 throw new IllegalArgumentException("Must specify exactly one kind of wallpaper"); 1438 } 1439 1440 final SparseArray<WallpaperData> map = 1441 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap; 1442 synchronized (mLock) { 1443 WallpaperData wallpaper = map.get(userId); 1444 if (wallpaper != null) { 1445 return wallpaper.wallpaperId; 1446 } 1447 } 1448 return -1; 1449 } 1450 1451 @Override setLockWallpaperCallback(IWallpaperManagerCallback cb)1452 public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) { 1453 checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW); 1454 synchronized (mLock) { 1455 mKeyguardListener = cb; 1456 } 1457 return true; 1458 } 1459 1460 @Override setWallpaper(String name, String callingPackage, Rect cropHint, boolean allowBackup, Bundle extras, int which, IWallpaperManagerCallback completion, int userId)1461 public ParcelFileDescriptor setWallpaper(String name, String callingPackage, 1462 Rect cropHint, boolean allowBackup, Bundle extras, int which, 1463 IWallpaperManagerCallback completion, int userId) { 1464 userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, 1465 false /* all */, true /* full */, "changing wallpaper", null /* pkg */); 1466 checkPermission(android.Manifest.permission.SET_WALLPAPER); 1467 1468 if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) { 1469 final String msg = "Must specify a valid wallpaper category to set"; 1470 Slog.e(TAG, msg); 1471 throw new IllegalArgumentException(msg); 1472 } 1473 1474 if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) { 1475 return null; 1476 } 1477 1478 // "null" means the no-op crop, preserving the full input image 1479 if (cropHint == null) { 1480 cropHint = new Rect(0, 0, 0, 0); 1481 } else { 1482 if (cropHint.isEmpty() 1483 || cropHint.left < 0 1484 || cropHint.top < 0) { 1485 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint); 1486 } 1487 } 1488 1489 synchronized (mLock) { 1490 if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which)); 1491 WallpaperData wallpaper; 1492 1493 /* If we're setting system but not lock, and lock is currently sharing the system 1494 * wallpaper, we need to migrate that image over to being lock-only before 1495 * the caller here writes new bitmap data. 1496 */ 1497 if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) { 1498 if (DEBUG) { 1499 Slog.i(TAG, "Migrating system->lock to preserve"); 1500 } 1501 migrateSystemToLockWallpaperLocked(userId); 1502 } 1503 1504 wallpaper = getWallpaperSafeLocked(userId, which); 1505 final long ident = Binder.clearCallingIdentity(); 1506 try { 1507 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras); 1508 if (pfd != null) { 1509 wallpaper.imageWallpaperPending = true; 1510 wallpaper.whichPending = which; 1511 wallpaper.setComplete = completion; 1512 wallpaper.cropHint.set(cropHint); 1513 wallpaper.allowBackup = allowBackup; 1514 } 1515 return pfd; 1516 } finally { 1517 Binder.restoreCallingIdentity(ident); 1518 } 1519 } 1520 } 1521 migrateSystemToLockWallpaperLocked(int userId)1522 private void migrateSystemToLockWallpaperLocked(int userId) { 1523 WallpaperData sysWP = mWallpaperMap.get(userId); 1524 if (sysWP == null) { 1525 if (DEBUG) { 1526 Slog.i(TAG, "No system wallpaper? Not tracking for lock-only"); 1527 } 1528 return; 1529 } 1530 1531 // We know a-priori that there is no lock-only wallpaper currently 1532 WallpaperData lockWP = new WallpaperData(userId, 1533 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); 1534 lockWP.wallpaperId = sysWP.wallpaperId; 1535 lockWP.cropHint.set(sysWP.cropHint); 1536 lockWP.width = sysWP.width; 1537 lockWP.height = sysWP.height; 1538 lockWP.allowBackup = sysWP.allowBackup; 1539 1540 // Migrate the bitmap files outright; no need to copy 1541 try { 1542 Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath()); 1543 Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath()); 1544 } catch (ErrnoException e) { 1545 Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage()); 1546 lockWP.wallpaperFile.delete(); 1547 lockWP.cropFile.delete(); 1548 return; 1549 } 1550 1551 mLockWallpaperMap.put(userId, lockWP); 1552 } 1553 updateWallpaperBitmapLocked(String name, WallpaperData wallpaper, Bundle extras)1554 ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper, 1555 Bundle extras) { 1556 if (name == null) name = ""; 1557 try { 1558 File dir = getWallpaperDir(wallpaper.userId); 1559 if (!dir.exists()) { 1560 dir.mkdir(); 1561 FileUtils.setPermissions( 1562 dir.getPath(), 1563 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 1564 -1, -1); 1565 } 1566 ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile, 1567 MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE); 1568 if (!SELinux.restorecon(wallpaper.wallpaperFile)) { 1569 return null; 1570 } 1571 wallpaper.name = name; 1572 wallpaper.wallpaperId = makeWallpaperIdLocked(); 1573 if (extras != null) { 1574 extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId); 1575 } 1576 if (DEBUG) { 1577 Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId 1578 + " name=" + name + " file=" + wallpaper.wallpaperFile.getName()); 1579 } 1580 return fd; 1581 } catch (FileNotFoundException e) { 1582 Slog.w(TAG, "Error setting wallpaper", e); 1583 } 1584 return null; 1585 } 1586 1587 @Override setWallpaperComponentChecked(ComponentName name, String callingPackage, int userId)1588 public void setWallpaperComponentChecked(ComponentName name, String callingPackage, 1589 int userId) { 1590 1591 if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) { 1592 setWallpaperComponent(name, userId); 1593 } 1594 } 1595 1596 // ToDo: Remove this version of the function 1597 @Override setWallpaperComponent(ComponentName name)1598 public void setWallpaperComponent(ComponentName name) { 1599 setWallpaperComponent(name, UserHandle.getCallingUserId()); 1600 } 1601 setWallpaperComponent(ComponentName name, int userId)1602 private void setWallpaperComponent(ComponentName name, int userId) { 1603 userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, 1604 false /* all */, true /* full */, "changing live wallpaper", null /* pkg */); 1605 checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); 1606 1607 synchronized (mLock) { 1608 if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); 1609 WallpaperData wallpaper = mWallpaperMap.get(userId); 1610 if (wallpaper == null) { 1611 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); 1612 } 1613 final long ident = Binder.clearCallingIdentity(); 1614 1615 // Live wallpapers can't be specified for keyguard. If we're using a static 1616 // system+lock image currently, migrate the system wallpaper to be a lock-only 1617 // image as part of making a different live component active as the system 1618 // wallpaper. 1619 if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) { 1620 if (mLockWallpaperMap.get(userId) == null) { 1621 // We're using the static imagery and there is no lock-specific image in place, 1622 // therefore it's a shared system+lock image that we need to migrate. 1623 migrateSystemToLockWallpaperLocked(userId); 1624 } 1625 } 1626 1627 try { 1628 wallpaper.imageWallpaperPending = false; 1629 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { 1630 wallpaper.wallpaperId = makeWallpaperIdLocked(); 1631 notifyCallbacksLocked(wallpaper); 1632 } 1633 } finally { 1634 Binder.restoreCallingIdentity(ident); 1635 } 1636 } 1637 } 1638 bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply)1639 boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, 1640 boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) { 1641 if (DEBUG_LIVE) { 1642 Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName); 1643 } 1644 // Has the component changed? 1645 if (!force) { 1646 if (wallpaper.connection != null) { 1647 if (wallpaper.wallpaperComponent == null) { 1648 if (componentName == null) { 1649 if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default"); 1650 // Still using default wallpaper. 1651 return true; 1652 } 1653 } else if (wallpaper.wallpaperComponent.equals(componentName)) { 1654 // Changing to same wallpaper. 1655 if (DEBUG) Slog.v(TAG, "same wallpaper"); 1656 return true; 1657 } 1658 } 1659 } 1660 1661 try { 1662 if (componentName == null) { 1663 componentName = mDefaultWallpaperComponent; 1664 if (componentName == null) { 1665 // Fall back to static image wallpaper 1666 componentName = mImageWallpaper; 1667 //clearWallpaperComponentLocked(); 1668 //return; 1669 if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper"); 1670 } 1671 } 1672 int serviceUserId = wallpaper.userId; 1673 ServiceInfo si = mIPackageManager.getServiceInfo(componentName, 1674 PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId); 1675 if (si == null) { 1676 // The wallpaper component we're trying to use doesn't exist 1677 Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable"); 1678 return false; 1679 } 1680 if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) { 1681 String msg = "Selected service does not require " 1682 + android.Manifest.permission.BIND_WALLPAPER 1683 + ": " + componentName; 1684 if (fromUser) { 1685 throw new SecurityException(msg); 1686 } 1687 Slog.w(TAG, msg); 1688 return false; 1689 } 1690 1691 WallpaperInfo wi = null; 1692 1693 Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); 1694 if (componentName != null && !componentName.equals(mImageWallpaper)) { 1695 // Make sure the selected service is actually a wallpaper service. 1696 List<ResolveInfo> ris = 1697 mIPackageManager.queryIntentServices(intent, 1698 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 1699 PackageManager.GET_META_DATA, serviceUserId).getList(); 1700 for (int i=0; i<ris.size(); i++) { 1701 ServiceInfo rsi = ris.get(i).serviceInfo; 1702 if (rsi.name.equals(si.name) && 1703 rsi.packageName.equals(si.packageName)) { 1704 try { 1705 wi = new WallpaperInfo(mContext, ris.get(i)); 1706 } catch (XmlPullParserException e) { 1707 if (fromUser) { 1708 throw new IllegalArgumentException(e); 1709 } 1710 Slog.w(TAG, e); 1711 return false; 1712 } catch (IOException e) { 1713 if (fromUser) { 1714 throw new IllegalArgumentException(e); 1715 } 1716 Slog.w(TAG, e); 1717 return false; 1718 } 1719 break; 1720 } 1721 } 1722 if (wi == null) { 1723 String msg = "Selected service is not a wallpaper: " 1724 + componentName; 1725 if (fromUser) { 1726 throw new SecurityException(msg); 1727 } 1728 Slog.w(TAG, msg); 1729 return false; 1730 } 1731 } 1732 1733 // Bind the service! 1734 if (DEBUG) Slog.v(TAG, "Binding to:" + componentName); 1735 WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper); 1736 intent.setComponent(componentName); 1737 intent.putExtra(Intent.EXTRA_CLIENT_LABEL, 1738 com.android.internal.R.string.wallpaper_binding_label); 1739 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( 1740 mContext, 0, 1741 Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER), 1742 mContext.getText(com.android.internal.R.string.chooser_wallpaper)), 1743 0, null, new UserHandle(serviceUserId))); 1744 if (!mContext.bindServiceAsUser(intent, newConn, 1745 Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI 1746 | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, 1747 new UserHandle(serviceUserId))) { 1748 String msg = "Unable to bind service: " 1749 + componentName; 1750 if (fromUser) { 1751 throw new IllegalArgumentException(msg); 1752 } 1753 Slog.w(TAG, msg); 1754 return false; 1755 } 1756 if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) { 1757 detachWallpaperLocked(mLastWallpaper); 1758 } 1759 wallpaper.wallpaperComponent = componentName; 1760 wallpaper.connection = newConn; 1761 newConn.mReply = reply; 1762 try { 1763 if (wallpaper.userId == mCurrentUserId) { 1764 if (DEBUG) 1765 Slog.v(TAG, "Adding window token: " + newConn.mToken); 1766 mIWindowManager.addWindowToken(newConn.mToken, TYPE_WALLPAPER, DEFAULT_DISPLAY); 1767 mLastWallpaper = wallpaper; 1768 } 1769 } catch (RemoteException e) { 1770 } 1771 } catch (RemoteException e) { 1772 String msg = "Remote exception for " + componentName + "\n" + e; 1773 if (fromUser) { 1774 throw new IllegalArgumentException(msg); 1775 } 1776 Slog.w(TAG, msg); 1777 return false; 1778 } 1779 return true; 1780 } 1781 detachWallpaperLocked(WallpaperData wallpaper)1782 void detachWallpaperLocked(WallpaperData wallpaper) { 1783 if (wallpaper.connection != null) { 1784 if (wallpaper.connection.mReply != null) { 1785 try { 1786 wallpaper.connection.mReply.sendResult(null); 1787 } catch (RemoteException e) { 1788 } 1789 wallpaper.connection.mReply = null; 1790 } 1791 if (wallpaper.connection.mEngine != null) { 1792 try { 1793 wallpaper.connection.mEngine.destroy(); 1794 } catch (RemoteException e) { 1795 } 1796 } 1797 mContext.unbindService(wallpaper.connection); 1798 try { 1799 if (DEBUG) 1800 Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken); 1801 mIWindowManager.removeWindowToken(wallpaper.connection.mToken, DEFAULT_DISPLAY); 1802 } catch (RemoteException e) { 1803 } 1804 wallpaper.connection.mService = null; 1805 wallpaper.connection.mEngine = null; 1806 wallpaper.connection = null; 1807 } 1808 } 1809 clearWallpaperComponentLocked(WallpaperData wallpaper)1810 void clearWallpaperComponentLocked(WallpaperData wallpaper) { 1811 wallpaper.wallpaperComponent = null; 1812 detachWallpaperLocked(wallpaper); 1813 } 1814 attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper)1815 void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) { 1816 try { 1817 conn.mService.attach(conn, conn.mToken, 1818 TYPE_WALLPAPER, false, 1819 wallpaper.width, wallpaper.height, wallpaper.padding); 1820 } catch (RemoteException e) { 1821 Slog.w(TAG, "Failed attaching wallpaper; clearing", e); 1822 if (!wallpaper.wallpaperUpdating) { 1823 bindWallpaperComponentLocked(null, false, false, wallpaper, null); 1824 } 1825 } 1826 } 1827 notifyCallbacksLocked(WallpaperData wallpaper)1828 private void notifyCallbacksLocked(WallpaperData wallpaper) { 1829 final int n = wallpaper.callbacks.beginBroadcast(); 1830 for (int i = 0; i < n; i++) { 1831 try { 1832 wallpaper.callbacks.getBroadcastItem(i).onWallpaperChanged(); 1833 } catch (RemoteException e) { 1834 1835 // The RemoteCallbackList will take care of removing 1836 // the dead object for us. 1837 } 1838 } 1839 wallpaper.callbacks.finishBroadcast(); 1840 final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED); 1841 mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); 1842 } 1843 checkPermission(String permission)1844 private void checkPermission(String permission) { 1845 if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) { 1846 throw new SecurityException("Access denied to process: " + Binder.getCallingPid() 1847 + ", must have permission " + permission); 1848 } 1849 } 1850 1851 /** 1852 * Certain user types do not support wallpapers (e.g. managed profiles). The check is 1853 * implemented through through the OP_WRITE_WALLPAPER AppOp. 1854 */ isWallpaperSupported(String callingPackage)1855 public boolean isWallpaperSupported(String callingPackage) { 1856 return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(), 1857 callingPackage) == AppOpsManager.MODE_ALLOWED; 1858 } 1859 1860 @Override isSetWallpaperAllowed(String callingPackage)1861 public boolean isSetWallpaperAllowed(String callingPackage) { 1862 final PackageManager pm = mContext.getPackageManager(); 1863 String[] uidPackages = pm.getPackagesForUid(Binder.getCallingUid()); 1864 boolean uidMatchPackage = Arrays.asList(uidPackages).contains(callingPackage); 1865 if (!uidMatchPackage) { 1866 return false; // callingPackage was faked. 1867 } 1868 1869 final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); 1870 if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) { 1871 return true; 1872 } 1873 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 1874 return !um.hasUserRestriction(UserManager.DISALLOW_SET_WALLPAPER); 1875 } 1876 1877 @Override isWallpaperBackupEligible(int which, int userId)1878 public boolean isWallpaperBackupEligible(int which, int userId) { 1879 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 1880 throw new SecurityException("Only the system may call isWallpaperBackupEligible"); 1881 } 1882 1883 WallpaperData wallpaper = (which == FLAG_LOCK) 1884 ? mLockWallpaperMap.get(userId) 1885 : mWallpaperMap.get(userId); 1886 return (wallpaper != null) ? wallpaper.allowBackup : false; 1887 } 1888 makeJournaledFile(int userId)1889 private static JournaledFile makeJournaledFile(int userId) { 1890 final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath(); 1891 return new JournaledFile(new File(base), new File(base + ".tmp")); 1892 } 1893 saveSettingsLocked(int userId)1894 private void saveSettingsLocked(int userId) { 1895 JournaledFile journal = makeJournaledFile(userId); 1896 FileOutputStream fstream = null; 1897 BufferedOutputStream stream = null; 1898 try { 1899 XmlSerializer out = new FastXmlSerializer(); 1900 fstream = new FileOutputStream(journal.chooseForWrite(), false); 1901 stream = new BufferedOutputStream(fstream); 1902 out.setOutput(stream, StandardCharsets.UTF_8.name()); 1903 out.startDocument(null, true); 1904 1905 WallpaperData wallpaper; 1906 1907 wallpaper = mWallpaperMap.get(userId); 1908 if (wallpaper != null) { 1909 writeWallpaperAttributes(out, "wp", wallpaper); 1910 } 1911 wallpaper = mLockWallpaperMap.get(userId); 1912 if (wallpaper != null) { 1913 writeWallpaperAttributes(out, "kwp", wallpaper); 1914 } 1915 1916 out.endDocument(); 1917 1918 stream.flush(); // also flushes fstream 1919 FileUtils.sync(fstream); 1920 stream.close(); // also closes fstream 1921 journal.commit(); 1922 } catch (IOException e) { 1923 IoUtils.closeQuietly(stream); 1924 journal.rollback(); 1925 } 1926 } 1927 writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)1928 private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper) 1929 throws IllegalArgumentException, IllegalStateException, IOException { 1930 out.startTag(null, tag); 1931 out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); 1932 out.attribute(null, "width", Integer.toString(wallpaper.width)); 1933 out.attribute(null, "height", Integer.toString(wallpaper.height)); 1934 1935 out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left)); 1936 out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top)); 1937 out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right)); 1938 out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom)); 1939 1940 if (wallpaper.padding.left != 0) { 1941 out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left)); 1942 } 1943 if (wallpaper.padding.top != 0) { 1944 out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top)); 1945 } 1946 if (wallpaper.padding.right != 0) { 1947 out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right)); 1948 } 1949 if (wallpaper.padding.bottom != 0) { 1950 out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom)); 1951 } 1952 1953 out.attribute(null, "name", wallpaper.name); 1954 if (wallpaper.wallpaperComponent != null 1955 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) { 1956 out.attribute(null, "component", 1957 wallpaper.wallpaperComponent.flattenToShortString()); 1958 } 1959 1960 if (wallpaper.allowBackup) { 1961 out.attribute(null, "backup", "true"); 1962 } 1963 1964 out.endTag(null, tag); 1965 } 1966 migrateFromOld()1967 private void migrateFromOld() { 1968 File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY); 1969 File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY); 1970 if (oldWallpaper.exists()) { 1971 File newWallpaper = new File(getWallpaperDir(0), WALLPAPER); 1972 oldWallpaper.renameTo(newWallpaper); 1973 } 1974 if (oldInfo.exists()) { 1975 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO); 1976 oldInfo.renameTo(newInfo); 1977 } 1978 } 1979 getAttributeInt(XmlPullParser parser, String name, int defValue)1980 private int getAttributeInt(XmlPullParser parser, String name, int defValue) { 1981 String value = parser.getAttributeValue(null, name); 1982 if (value == null) { 1983 return defValue; 1984 } 1985 return Integer.parseInt(value); 1986 } 1987 1988 /** 1989 * Sometimes it is expected the wallpaper map may not have a user's data. E.g. This could 1990 * happen during user switch. The async user switch observer may not have received 1991 * the event yet. We use this safe method when we don't care about this ordering and just 1992 * want to update the data. The data is going to be applied when the user switch observer 1993 * is eventually executed. 1994 */ getWallpaperSafeLocked(int userId, int which)1995 private WallpaperData getWallpaperSafeLocked(int userId, int which) { 1996 // We're setting either just system (work with the system wallpaper), 1997 // both (also work with the system wallpaper), or just the lock 1998 // wallpaper (update against the existing lock wallpaper if any). 1999 // Combined or just-system operations use the 'system' WallpaperData 2000 // for this use; lock-only operations use the dedicated one. 2001 final SparseArray<WallpaperData> whichSet = 2002 (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap; 2003 WallpaperData wallpaper = whichSet.get(userId); 2004 if (wallpaper == null) { 2005 // common case, this is the first lookup post-boot of the system or 2006 // unified lock, so we bring up the saved state lazily now and recheck. 2007 loadSettingsLocked(userId, false); 2008 wallpaper = whichSet.get(userId); 2009 // if it's still null here, this is a lock-only operation and there is not 2010 // yet a lock-only wallpaper set for this user, so we need to establish 2011 // it now. 2012 if (wallpaper == null) { 2013 if (which == FLAG_LOCK) { 2014 wallpaper = new WallpaperData(userId, 2015 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); 2016 mLockWallpaperMap.put(userId, wallpaper); 2017 ensureSaneWallpaperData(wallpaper); 2018 } else { 2019 // sanity fallback: we're in bad shape, but establishing a known 2020 // valid system+lock WallpaperData will keep us from dying. 2021 Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!"); 2022 wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP); 2023 mWallpaperMap.put(userId, wallpaper); 2024 ensureSaneWallpaperData(wallpaper); 2025 } 2026 } 2027 } 2028 return wallpaper; 2029 } 2030 loadSettingsLocked(int userId, boolean keepDimensionHints)2031 private void loadSettingsLocked(int userId, boolean keepDimensionHints) { 2032 if (DEBUG) Slog.v(TAG, "loadSettingsLocked"); 2033 2034 JournaledFile journal = makeJournaledFile(userId); 2035 FileInputStream stream = null; 2036 File file = journal.chooseForRead(); 2037 if (!file.exists()) { 2038 // This should only happen one time, when upgrading from a legacy system 2039 migrateFromOld(); 2040 } 2041 WallpaperData wallpaper = mWallpaperMap.get(userId); 2042 if (wallpaper == null) { 2043 wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP); 2044 wallpaper.allowBackup = true; 2045 mWallpaperMap.put(userId, wallpaper); 2046 if (!wallpaper.cropExists()) { 2047 if (wallpaper.sourceExists()) { 2048 generateCrop(wallpaper); 2049 } else { 2050 Slog.i(TAG, "No static wallpaper imagery; defaults will be shown"); 2051 } 2052 } 2053 } 2054 boolean success = false; 2055 try { 2056 stream = new FileInputStream(file); 2057 XmlPullParser parser = Xml.newPullParser(); 2058 parser.setInput(stream, StandardCharsets.UTF_8.name()); 2059 2060 int type; 2061 do { 2062 type = parser.next(); 2063 if (type == XmlPullParser.START_TAG) { 2064 String tag = parser.getName(); 2065 if ("wp".equals(tag)) { 2066 // Common to system + lock wallpapers 2067 parseWallpaperAttributes(parser, wallpaper, keepDimensionHints); 2068 2069 // A system wallpaper might also be a live wallpaper 2070 String comp = parser.getAttributeValue(null, "component"); 2071 wallpaper.nextWallpaperComponent = comp != null 2072 ? ComponentName.unflattenFromString(comp) 2073 : null; 2074 if (wallpaper.nextWallpaperComponent == null 2075 || "android".equals(wallpaper.nextWallpaperComponent 2076 .getPackageName())) { 2077 wallpaper.nextWallpaperComponent = mImageWallpaper; 2078 } 2079 2080 if (DEBUG) { 2081 Slog.v(TAG, "mWidth:" + wallpaper.width); 2082 Slog.v(TAG, "mHeight:" + wallpaper.height); 2083 Slog.v(TAG, "cropRect:" + wallpaper.cropHint); 2084 Slog.v(TAG, "mName:" + wallpaper.name); 2085 Slog.v(TAG, "mNextWallpaperComponent:" 2086 + wallpaper.nextWallpaperComponent); 2087 } 2088 } else if ("kwp".equals(tag)) { 2089 // keyguard-specific wallpaper for this user 2090 WallpaperData lockWallpaper = mLockWallpaperMap.get(userId); 2091 if (lockWallpaper == null) { 2092 lockWallpaper = new WallpaperData(userId, 2093 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP); 2094 mLockWallpaperMap.put(userId, lockWallpaper); 2095 } 2096 parseWallpaperAttributes(parser, lockWallpaper, false); 2097 } 2098 } 2099 } while (type != XmlPullParser.END_DOCUMENT); 2100 success = true; 2101 } catch (FileNotFoundException e) { 2102 Slog.w(TAG, "no current wallpaper -- first boot?"); 2103 } catch (NullPointerException e) { 2104 Slog.w(TAG, "failed parsing " + file + " " + e); 2105 } catch (NumberFormatException e) { 2106 Slog.w(TAG, "failed parsing " + file + " " + e); 2107 } catch (XmlPullParserException e) { 2108 Slog.w(TAG, "failed parsing " + file + " " + e); 2109 } catch (IOException e) { 2110 Slog.w(TAG, "failed parsing " + file + " " + e); 2111 } catch (IndexOutOfBoundsException e) { 2112 Slog.w(TAG, "failed parsing " + file + " " + e); 2113 } 2114 IoUtils.closeQuietly(stream); 2115 2116 if (!success) { 2117 wallpaper.width = -1; 2118 wallpaper.height = -1; 2119 wallpaper.cropHint.set(0, 0, 0, 0); 2120 wallpaper.padding.set(0, 0, 0, 0); 2121 wallpaper.name = ""; 2122 2123 mLockWallpaperMap.remove(userId); 2124 } else { 2125 if (wallpaper.wallpaperId <= 0) { 2126 wallpaper.wallpaperId = makeWallpaperIdLocked(); 2127 if (DEBUG) { 2128 Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId 2129 + "); now " + wallpaper.wallpaperId); 2130 } 2131 } 2132 } 2133 2134 ensureSaneWallpaperData(wallpaper); 2135 WallpaperData lockWallpaper = mLockWallpaperMap.get(userId); 2136 if (lockWallpaper != null) { 2137 ensureSaneWallpaperData(lockWallpaper); 2138 } 2139 } 2140 ensureSaneWallpaperData(WallpaperData wallpaper)2141 private void ensureSaneWallpaperData(WallpaperData wallpaper) { 2142 // We always want to have some reasonable width hint. 2143 int baseSize = getMaximumSizeDimension(); 2144 if (wallpaper.width < baseSize) { 2145 wallpaper.width = baseSize; 2146 } 2147 if (wallpaper.height < baseSize) { 2148 wallpaper.height = baseSize; 2149 } 2150 // and crop, if not previously specified 2151 if (wallpaper.cropHint.width() <= 0 2152 || wallpaper.cropHint.height() <= 0) { 2153 wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height); 2154 } 2155 } 2156 parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper, boolean keepDimensionHints)2157 private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper, 2158 boolean keepDimensionHints) { 2159 final String idString = parser.getAttributeValue(null, "id"); 2160 if (idString != null) { 2161 final int id = wallpaper.wallpaperId = Integer.parseInt(idString); 2162 if (id > mWallpaperId) { 2163 mWallpaperId = id; 2164 } 2165 } else { 2166 wallpaper.wallpaperId = makeWallpaperIdLocked(); 2167 } 2168 2169 if (!keepDimensionHints) { 2170 wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width")); 2171 wallpaper.height = Integer.parseInt(parser 2172 .getAttributeValue(null, "height")); 2173 } 2174 wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0); 2175 wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0); 2176 wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0); 2177 wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0); 2178 wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0); 2179 wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0); 2180 wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0); 2181 wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0); 2182 wallpaper.name = parser.getAttributeValue(null, "name"); 2183 wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup")); 2184 } 2185 getMaximumSizeDimension()2186 private int getMaximumSizeDimension() { 2187 WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); 2188 Display d = wm.getDefaultDisplay(); 2189 return d.getMaximumSizeDimension(); 2190 } 2191 2192 // Called by SystemBackupAgent after files are restored to disk. settingsRestored()2193 public void settingsRestored() { 2194 // Verify caller is the system 2195 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { 2196 throw new RuntimeException("settingsRestored() can only be called from the system process"); 2197 } 2198 // TODO: If necessary, make it work for secondary users as well. This currently assumes 2199 // restores only to the primary user 2200 if (DEBUG) Slog.v(TAG, "settingsRestored"); 2201 WallpaperData wallpaper = null; 2202 boolean success = false; 2203 synchronized (mLock) { 2204 loadSettingsLocked(UserHandle.USER_SYSTEM, false); 2205 wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM); 2206 wallpaper.wallpaperId = makeWallpaperIdLocked(); // always bump id at restore 2207 wallpaper.allowBackup = true; // by definition if it was restored 2208 if (wallpaper.nextWallpaperComponent != null 2209 && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) { 2210 if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false, 2211 wallpaper, null)) { 2212 // No such live wallpaper or other failure; fall back to the default 2213 // live wallpaper (since the profile being restored indicated that the 2214 // user had selected a live rather than static one). 2215 bindWallpaperComponentLocked(null, false, false, wallpaper, null); 2216 } 2217 success = true; 2218 } else { 2219 // If there's a wallpaper name, we use that. If that can't be loaded, then we 2220 // use the default. 2221 if ("".equals(wallpaper.name)) { 2222 if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty"); 2223 success = true; 2224 } else { 2225 if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource"); 2226 success = restoreNamedResourceLocked(wallpaper); 2227 } 2228 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success 2229 + " id=" + wallpaper.wallpaperId); 2230 if (success) { 2231 generateCrop(wallpaper); // based on the new image + metadata 2232 bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false, 2233 wallpaper, null); 2234 } 2235 } 2236 } 2237 2238 if (!success) { 2239 Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'"); 2240 wallpaper.name = ""; 2241 getWallpaperDir(UserHandle.USER_SYSTEM).delete(); 2242 } 2243 2244 synchronized (mLock) { 2245 saveSettingsLocked(UserHandle.USER_SYSTEM); 2246 } 2247 } 2248 2249 // Restore the named resource bitmap to both source + crop files restoreNamedResourceLocked(WallpaperData wallpaper)2250 boolean restoreNamedResourceLocked(WallpaperData wallpaper) { 2251 if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) { 2252 String resName = wallpaper.name.substring(4); 2253 2254 String pkg = null; 2255 int colon = resName.indexOf(':'); 2256 if (colon > 0) { 2257 pkg = resName.substring(0, colon); 2258 } 2259 2260 String ident = null; 2261 int slash = resName.lastIndexOf('/'); 2262 if (slash > 0) { 2263 ident = resName.substring(slash+1); 2264 } 2265 2266 String type = null; 2267 if (colon > 0 && slash > 0 && (slash-colon) > 1) { 2268 type = resName.substring(colon+1, slash); 2269 } 2270 2271 if (pkg != null && ident != null && type != null) { 2272 int resId = -1; 2273 InputStream res = null; 2274 FileOutputStream fos = null; 2275 FileOutputStream cos = null; 2276 try { 2277 Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED); 2278 Resources r = c.getResources(); 2279 resId = r.getIdentifier(resName, null, null); 2280 if (resId == 0) { 2281 Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type 2282 + " ident=" + ident); 2283 return false; 2284 } 2285 2286 res = r.openRawResource(resId); 2287 if (wallpaper.wallpaperFile.exists()) { 2288 wallpaper.wallpaperFile.delete(); 2289 wallpaper.cropFile.delete(); 2290 } 2291 fos = new FileOutputStream(wallpaper.wallpaperFile); 2292 cos = new FileOutputStream(wallpaper.cropFile); 2293 2294 byte[] buffer = new byte[32768]; 2295 int amt; 2296 while ((amt=res.read(buffer)) > 0) { 2297 fos.write(buffer, 0, amt); 2298 cos.write(buffer, 0, amt); 2299 } 2300 // mWallpaperObserver will notice the close and send the change broadcast 2301 2302 Slog.v(TAG, "Restored wallpaper: " + resName); 2303 return true; 2304 } catch (NameNotFoundException e) { 2305 Slog.e(TAG, "Package name " + pkg + " not found"); 2306 } catch (Resources.NotFoundException e) { 2307 Slog.e(TAG, "Resource not found: " + resId); 2308 } catch (IOException e) { 2309 Slog.e(TAG, "IOException while restoring wallpaper ", e); 2310 } finally { 2311 IoUtils.closeQuietly(res); 2312 if (fos != null) { 2313 FileUtils.sync(fos); 2314 } 2315 if (cos != null) { 2316 FileUtils.sync(cos); 2317 } 2318 IoUtils.closeQuietly(fos); 2319 IoUtils.closeQuietly(cos); 2320 } 2321 } 2322 } 2323 return false; 2324 } 2325 2326 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)2327 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2328 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 2329 2330 synchronized (mLock) { 2331 pw.println("System wallpaper state:"); 2332 for (int i = 0; i < mWallpaperMap.size(); i++) { 2333 WallpaperData wallpaper = mWallpaperMap.valueAt(i); 2334 pw.print(" User "); pw.print(wallpaper.userId); 2335 pw.print(": id="); pw.println(wallpaper.wallpaperId); 2336 pw.print(" mWidth="); 2337 pw.print(wallpaper.width); 2338 pw.print(" mHeight="); 2339 pw.println(wallpaper.height); 2340 pw.print(" mCropHint="); pw.println(wallpaper.cropHint); 2341 pw.print(" mPadding="); pw.println(wallpaper.padding); 2342 pw.print(" mName="); pw.println(wallpaper.name); 2343 pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent); 2344 if (wallpaper.connection != null) { 2345 WallpaperConnection conn = wallpaper.connection; 2346 pw.print(" Wallpaper connection "); 2347 pw.print(conn); 2348 pw.println(":"); 2349 if (conn.mInfo != null) { 2350 pw.print(" mInfo.component="); 2351 pw.println(conn.mInfo.getComponent()); 2352 } 2353 pw.print(" mToken="); 2354 pw.println(conn.mToken); 2355 pw.print(" mService="); 2356 pw.println(conn.mService); 2357 pw.print(" mEngine="); 2358 pw.println(conn.mEngine); 2359 pw.print(" mLastDiedTime="); 2360 pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis()); 2361 } 2362 } 2363 pw.println("Lock wallpaper state:"); 2364 for (int i = 0; i < mLockWallpaperMap.size(); i++) { 2365 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i); 2366 pw.print(" User "); pw.print(wallpaper.userId); 2367 pw.print(": id="); pw.println(wallpaper.wallpaperId); 2368 pw.print(" mWidth="); pw.print(wallpaper.width); 2369 pw.print(" mHeight="); pw.println(wallpaper.height); 2370 pw.print(" mCropHint="); pw.println(wallpaper.cropHint); 2371 pw.print(" mPadding="); pw.println(wallpaper.padding); 2372 pw.print(" mName="); pw.println(wallpaper.name); 2373 } 2374 2375 } 2376 } 2377 } 2378