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