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