1 /* 2 * Copyright (C) 2023 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.app.WallpaperManager.ORIENTATION_UNKNOWN; 22 import static android.view.Display.DEFAULT_DISPLAY; 23 24 import static com.android.server.wallpaper.WallpaperDisplayHelper.DisplayData; 25 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER; 26 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_CROP; 27 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_INFO; 28 import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir; 29 import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked; 30 import static com.android.window.flags.Flags.multiCrop; 31 32 import android.annotation.Nullable; 33 import android.app.WallpaperColors; 34 import android.app.WallpaperManager; 35 import android.app.WallpaperManager.SetWallpaperFlags; 36 import android.app.backup.WallpaperBackupHelper; 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.content.pm.PackageManager; 40 import android.content.res.Resources; 41 import android.graphics.Color; 42 import android.graphics.Rect; 43 import android.os.FileUtils; 44 import android.util.Pair; 45 import android.util.Slog; 46 import android.util.SparseArray; 47 import android.util.Xml; 48 49 import com.android.internal.R; 50 import com.android.internal.annotations.VisibleForTesting; 51 import com.android.internal.util.JournaledFile; 52 import com.android.modules.utils.TypedXmlPullParser; 53 import com.android.modules.utils.TypedXmlSerializer; 54 import com.android.server.wallpaper.WallpaperData.BindSource; 55 56 import libcore.io.IoUtils; 57 58 import org.xmlpull.v1.XmlPullParser; 59 import org.xmlpull.v1.XmlPullParserException; 60 61 import java.io.File; 62 import java.io.FileInputStream; 63 import java.io.FileNotFoundException; 64 import java.io.FileOutputStream; 65 import java.io.IOException; 66 import java.io.InputStream; 67 import java.util.HashMap; 68 import java.util.List; 69 import java.util.Map; 70 71 /** 72 * Helper for the wallpaper loading / saving / xml parsing 73 * Only meant to be used lock held by WallpaperManagerService 74 * Only meant to be instantiated once by WallpaperManagerService 75 * @hide 76 */ 77 public class WallpaperDataParser { 78 79 private static final String TAG = WallpaperDataParser.class.getSimpleName(); 80 private static final boolean DEBUG = false; 81 private final ComponentName mImageWallpaper; 82 private final WallpaperDisplayHelper mWallpaperDisplayHelper; 83 private final WallpaperCropper mWallpaperCropper; 84 private final Context mContext; 85 WallpaperDataParser(Context context, WallpaperDisplayHelper wallpaperDisplayHelper, WallpaperCropper wallpaperCropper)86 WallpaperDataParser(Context context, WallpaperDisplayHelper wallpaperDisplayHelper, 87 WallpaperCropper wallpaperCropper) { 88 mContext = context; 89 mWallpaperDisplayHelper = wallpaperDisplayHelper; 90 mWallpaperCropper = wallpaperCropper; 91 mImageWallpaper = ComponentName.unflattenFromString( 92 context.getResources().getString(R.string.image_wallpaper_component)); 93 } 94 makeJournaledFile(int userId)95 private JournaledFile makeJournaledFile(int userId) { 96 final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath(); 97 return new JournaledFile(new File(base), new File(base + ".tmp")); 98 } 99 100 static class WallpaperLoadingResult { 101 102 private final WallpaperData mSystemWallpaperData; 103 104 @Nullable 105 private final WallpaperData mLockWallpaperData; 106 107 private final boolean mSuccess; 108 WallpaperLoadingResult( WallpaperData systemWallpaperData, WallpaperData lockWallpaperData, boolean success)109 private WallpaperLoadingResult( 110 WallpaperData systemWallpaperData, 111 WallpaperData lockWallpaperData, 112 boolean success) { 113 mSystemWallpaperData = systemWallpaperData; 114 mLockWallpaperData = lockWallpaperData; 115 mSuccess = success; 116 } 117 getSystemWallpaperData()118 public WallpaperData getSystemWallpaperData() { 119 return mSystemWallpaperData; 120 } 121 getLockWallpaperData()122 public WallpaperData getLockWallpaperData() { 123 return mLockWallpaperData; 124 } 125 success()126 public boolean success() { 127 return mSuccess; 128 } 129 } 130 131 /** 132 * Load the system wallpaper (and the lock wallpaper, if it exists) from disk 133 * @param userId the id of the user for which the wallpaper should be loaded 134 * @param keepDimensionHints if false, parse and set the 135 * {@link DisplayData} width and height for the specified userId 136 * @param migrateFromOld whether the current wallpaper is pre-N and needs migration 137 * @param which The wallpaper(s) to load. 138 * @return a {@link WallpaperLoadingResult} object containing the wallpaper data. 139 */ loadSettingsLocked(int userId, boolean keepDimensionHints, boolean migrateFromOld, @SetWallpaperFlags int which)140 public WallpaperLoadingResult loadSettingsLocked(int userId, boolean keepDimensionHints, 141 boolean migrateFromOld, @SetWallpaperFlags int which) { 142 // TODO(b/270726737) remove the "keepDimensionHints" arg when removing the multi crop flag 143 JournaledFile journal = makeJournaledFile(userId); 144 FileInputStream stream = null; 145 File file = journal.chooseForRead(); 146 147 boolean loadSystem = (which & FLAG_SYSTEM) != 0; 148 boolean loadLock = (which & FLAG_LOCK) != 0; 149 WallpaperData wallpaper = null; 150 WallpaperData lockWallpaper = null; 151 152 if (loadSystem) { 153 // Do this once per boot 154 if (migrateFromOld) migrateFromOld(); 155 wallpaper = new WallpaperData(userId, FLAG_SYSTEM); 156 wallpaper.allowBackup = true; 157 if (!wallpaper.cropExists()) { 158 if (wallpaper.sourceExists()) { 159 mWallpaperCropper.generateCrop(wallpaper); 160 } else { 161 Slog.i(TAG, "No static wallpaper imagery; defaults will be shown"); 162 } 163 } 164 } 165 166 final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY); 167 boolean success = false; 168 169 try { 170 stream = new FileInputStream(file); 171 TypedXmlPullParser parser = Xml.resolvePullParser(stream); 172 173 int type; 174 do { 175 type = parser.next(); 176 if (type == XmlPullParser.START_TAG) { 177 String tag = parser.getName(); 178 if (("wp".equals(tag) && loadSystem) || ("kwp".equals(tag) && loadLock)) { 179 if ("kwp".equals(tag)) { 180 lockWallpaper = new WallpaperData(userId, FLAG_LOCK); 181 } 182 WallpaperData wallpaperToParse = 183 "wp".equals(tag) ? wallpaper : lockWallpaper; 184 185 if (!multiCrop()) { 186 parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints); 187 } 188 189 String comp = parser.getAttributeValue(null, "component"); 190 wallpaperToParse.nextWallpaperComponent = comp != null 191 ? ComponentName.unflattenFromString(comp) 192 : null; 193 if (wallpaperToParse.nextWallpaperComponent == null 194 || "android".equals(wallpaperToParse.nextWallpaperComponent 195 .getPackageName())) { 196 wallpaperToParse.nextWallpaperComponent = mImageWallpaper; 197 } 198 199 if (multiCrop()) { 200 parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints); 201 } 202 203 if (DEBUG) { 204 Slog.v(TAG, "mWidth:" + wpdData.mWidth); 205 Slog.v(TAG, "mHeight:" + wpdData.mHeight); 206 Slog.v(TAG, "cropRect:" + wallpaper.cropHint); 207 Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors); 208 Slog.v(TAG, "mName:" + wallpaper.name); 209 Slog.v(TAG, "mNextWallpaperComponent:" 210 + wallpaper.nextWallpaperComponent); 211 } 212 } 213 } 214 } while (type != XmlPullParser.END_DOCUMENT); 215 success = true; 216 } catch (FileNotFoundException e) { 217 Slog.w(TAG, "no current wallpaper -- first boot?"); 218 } catch (NullPointerException e) { 219 Slog.w(TAG, "failed parsing " + file + " " + e); 220 } catch (NumberFormatException e) { 221 Slog.w(TAG, "failed parsing " + file + " " + e); 222 } catch (XmlPullParserException e) { 223 Slog.w(TAG, "failed parsing " + file + " " + e); 224 } catch (IOException e) { 225 Slog.w(TAG, "failed parsing " + file + " " + e); 226 } catch (IndexOutOfBoundsException e) { 227 Slog.w(TAG, "failed parsing " + file + " " + e); 228 } 229 IoUtils.closeQuietly(stream); 230 231 mWallpaperDisplayHelper.ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY); 232 233 if (loadSystem) { 234 if (!success) { 235 wallpaper.cropHint.set(0, 0, 0, 0); 236 wpdData.mPadding.set(0, 0, 0, 0); 237 wallpaper.name = ""; 238 } else { 239 if (wallpaper.wallpaperId <= 0) { 240 wallpaper.wallpaperId = makeWallpaperIdLocked(); 241 if (DEBUG) { 242 Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId 243 + "); now " + wallpaper.wallpaperId); 244 } 245 } 246 } 247 ensureSaneWallpaperData(wallpaper); 248 wallpaper.mWhich = lockWallpaper != null ? FLAG_SYSTEM : FLAG_SYSTEM | FLAG_LOCK; 249 } 250 251 if (loadLock) { 252 if (!success) lockWallpaper = null; 253 if (lockWallpaper != null) { 254 ensureSaneWallpaperData(lockWallpaper); 255 lockWallpaper.mWhich = FLAG_LOCK; 256 } 257 } 258 259 return new WallpaperLoadingResult(wallpaper, lockWallpaper, success); 260 } 261 ensureSaneWallpaperData(WallpaperData wallpaper)262 private void ensureSaneWallpaperData(WallpaperData wallpaper) { 263 // Only overwrite cropHint if the rectangle is invalid. 264 if (wallpaper.cropHint.width() < 0 265 || wallpaper.cropHint.height() < 0) { 266 wallpaper.cropHint.set(0, 0, 0, 0); 267 } 268 } 269 270 migrateFromOld()271 private void migrateFromOld() { 272 // Pre-N, what existed is the one we're now using as the display crop 273 File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP); 274 // In the very-long-ago, imagery lived with the settings app 275 File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY); 276 File newWallpaper = new File(getWallpaperDir(0), WALLPAPER); 277 278 // Migrations from earlier wallpaper image storage schemas 279 if (preNWallpaper.exists()) { 280 if (!newWallpaper.exists()) { 281 // we've got the 'wallpaper' crop file but not the nominal source image, 282 // so do the simple "just take everything" straight copy of legacy data 283 if (DEBUG) { 284 Slog.i(TAG, "Migrating wallpaper schema"); 285 } 286 FileUtils.copyFile(preNWallpaper, newWallpaper); 287 } // else we're in the usual modern case: both source & crop exist 288 } else if (originalWallpaper.exists()) { 289 // VERY old schema; make sure things exist and are in the right place 290 if (DEBUG) { 291 Slog.i(TAG, "Migrating antique wallpaper schema"); 292 } 293 File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY); 294 if (oldInfo.exists()) { 295 File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO); 296 oldInfo.renameTo(newInfo); 297 } 298 299 FileUtils.copyFile(originalWallpaper, preNWallpaper); 300 originalWallpaper.renameTo(newWallpaper); 301 } 302 } 303 304 @VisibleForTesting parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper, boolean keepDimensionHints)305 void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper, 306 boolean keepDimensionHints) throws XmlPullParserException { 307 final int id = parser.getAttributeInt(null, "id", -1); 308 if (id != -1) { 309 wallpaper.wallpaperId = id; 310 if (id > WallpaperUtils.getCurrentWallpaperId()) { 311 WallpaperUtils.setCurrentWallpaperId(id); 312 } 313 } else { 314 wallpaper.wallpaperId = makeWallpaperIdLocked(); 315 } 316 317 Rect legacyCropHint = new Rect( 318 getAttributeInt(parser, "cropLeft", 0), 319 getAttributeInt(parser, "cropTop", 0), 320 getAttributeInt(parser, "cropRight", 0), 321 getAttributeInt(parser, "cropBottom", 0)); 322 Rect totalCropHint = new Rect( 323 getAttributeInt(parser, "totalCropLeft", 0), 324 getAttributeInt(parser, "totalCropTop", 0), 325 getAttributeInt(parser, "totalCropRight", 0), 326 getAttributeInt(parser, "totalCropBottom", 0)); 327 if (multiCrop() && mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) { 328 wallpaper.mCropHints = new SparseArray<>(); 329 for (Pair<Integer, String> pair: screenDimensionPairs()) { 330 Rect cropHint = new Rect( 331 parser.getAttributeInt(null, "cropLeft" + pair.second, 0), 332 parser.getAttributeInt(null, "cropTop" + pair.second, 0), 333 parser.getAttributeInt(null, "cropRight" + pair.second, 0), 334 parser.getAttributeInt(null, "cropBottom" + pair.second, 0)); 335 if (!cropHint.isEmpty()) wallpaper.mCropHints.put(pair.first, cropHint); 336 if (!cropHint.isEmpty() && cropHint.equals(legacyCropHint)) { 337 wallpaper.mOrientationWhenSet = pair.first; 338 } 339 } 340 if (wallpaper.mCropHints.size() == 0 && totalCropHint.isEmpty()) { 341 // migration case: the crops per screen orientation are not specified. 342 if (!legacyCropHint.isEmpty()) { 343 wallpaper.cropHint.set(legacyCropHint); 344 } 345 } else { 346 wallpaper.cropHint.set(totalCropHint); 347 } 348 wallpaper.mSampleSize = parser.getAttributeFloat(null, "sampleSize", 1f); 349 } else if (!multiCrop()) { 350 wallpaper.cropHint.set(legacyCropHint); 351 } 352 final DisplayData wpData = mWallpaperDisplayHelper 353 .getDisplayDataOrCreate(DEFAULT_DISPLAY); 354 if (!keepDimensionHints && !multiCrop()) { 355 wpData.mWidth = parser.getAttributeInt(null, "width", 0); 356 wpData.mHeight = parser.getAttributeInt(null, "height", 0); 357 } 358 if (!multiCrop()) { 359 wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0); 360 wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0); 361 wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0); 362 wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0); 363 } 364 wallpaper.mWallpaperDimAmount = getAttributeFloat(parser, "dimAmount", 0f); 365 BindSource bindSource; 366 try { 367 bindSource = Enum.valueOf(BindSource.class, 368 getAttributeString(parser, "bindSource", BindSource.UNKNOWN.name())); 369 } catch (IllegalArgumentException | NullPointerException e) { 370 bindSource = BindSource.UNKNOWN; 371 } 372 wallpaper.mBindSource = bindSource; 373 int dimAmountsCount = getAttributeInt(parser, "dimAmountsCount", 0); 374 if (dimAmountsCount > 0) { 375 SparseArray<Float> allDimAmounts = new SparseArray<>(dimAmountsCount); 376 for (int i = 0; i < dimAmountsCount; i++) { 377 int uid = getAttributeInt(parser, "dimUID" + i, 0); 378 float dimValue = getAttributeFloat(parser, "dimValue" + i, 0f); 379 allDimAmounts.put(uid, dimValue); 380 } 381 wallpaper.mUidToDimAmount = allDimAmounts; 382 } 383 int colorsCount = getAttributeInt(parser, "colorsCount", 0); 384 int allColorsCount = getAttributeInt(parser, "allColorsCount", 0); 385 if (allColorsCount > 0) { 386 Map<Integer, Integer> allColors = new HashMap<>(allColorsCount); 387 for (int i = 0; i < allColorsCount; i++) { 388 int colorInt = getAttributeInt(parser, "allColorsValue" + i, 0); 389 int population = getAttributeInt(parser, "allColorsPopulation" + i, 0); 390 allColors.put(colorInt, population); 391 } 392 int colorHints = getAttributeInt(parser, "colorHints", 0); 393 wallpaper.primaryColors = new WallpaperColors(allColors, colorHints); 394 } else if (colorsCount > 0) { 395 Color primary = null, secondary = null, tertiary = null; 396 for (int i = 0; i < colorsCount; i++) { 397 Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0)); 398 if (i == 0) { 399 primary = color; 400 } else if (i == 1) { 401 secondary = color; 402 } else if (i == 2) { 403 tertiary = color; 404 } else { 405 break; 406 } 407 } 408 int colorHints = getAttributeInt(parser, "colorHints", 0); 409 wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints); 410 } 411 wallpaper.name = parser.getAttributeValue(null, "name"); 412 wallpaper.allowBackup = parser.getAttributeBoolean(null, "backup", false); 413 } 414 getAttributeInt(TypedXmlPullParser parser, String name, int defValue)415 private static int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) { 416 return parser.getAttributeInt(null, name, defValue); 417 } 418 getAttributeFloat(TypedXmlPullParser parser, String name, float defValue)419 private static float getAttributeFloat(TypedXmlPullParser parser, String name, float defValue) { 420 return parser.getAttributeFloat(null, name, defValue); 421 } 422 getAttributeString(XmlPullParser parser, String name, String defValue)423 private String getAttributeString(XmlPullParser parser, String name, String defValue) { 424 String s = parser.getAttributeValue(null, name); 425 return (s != null) ? s : defValue; 426 } 427 saveSettingsLocked(int userId, WallpaperData wallpaper, WallpaperData lockWallpaper)428 void saveSettingsLocked(int userId, WallpaperData wallpaper, WallpaperData lockWallpaper) { 429 JournaledFile journal = makeJournaledFile(userId); 430 FileOutputStream fstream = null; 431 try { 432 fstream = new FileOutputStream(journal.chooseForWrite(), false); 433 TypedXmlSerializer out = Xml.resolveSerializer(fstream); 434 out.startDocument(null, true); 435 436 if (wallpaper != null) { 437 writeWallpaperAttributes(out, "wp", wallpaper); 438 } 439 440 if (lockWallpaper != null) { 441 writeWallpaperAttributes(out, "kwp", lockWallpaper); 442 } 443 444 out.endDocument(); 445 446 fstream.flush(); 447 FileUtils.sync(fstream); 448 fstream.close(); 449 journal.commit(); 450 } catch (IOException e) { 451 IoUtils.closeQuietly(fstream); 452 journal.rollback(); 453 } 454 } 455 456 @VisibleForTesting writeWallpaperAttributes(TypedXmlSerializer out, String tag, WallpaperData wallpaper)457 void writeWallpaperAttributes(TypedXmlSerializer out, String tag, WallpaperData wallpaper) 458 throws IllegalArgumentException, IllegalStateException, IOException { 459 if (DEBUG) { 460 Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId); 461 } 462 out.startTag(null, tag); 463 out.attributeInt(null, "id", wallpaper.wallpaperId); 464 465 if (multiCrop() && mImageWallpaper.equals(wallpaper.wallpaperComponent)) { 466 if (wallpaper.mCropHints == null) { 467 Slog.e(TAG, "cropHints should not be null when saved"); 468 wallpaper.mCropHints = new SparseArray<>(); 469 } 470 Rect rectToPutInLegacyCrop = new Rect(wallpaper.cropHint); 471 for (Pair<Integer, String> pair : screenDimensionPairs()) { 472 Rect cropHint = wallpaper.mCropHints.get(pair.first); 473 if (cropHint == null) continue; 474 out.attributeInt(null, "cropLeft" + pair.second, cropHint.left); 475 out.attributeInt(null, "cropTop" + pair.second, cropHint.top); 476 out.attributeInt(null, "cropRight" + pair.second, cropHint.right); 477 out.attributeInt(null, "cropBottom" + pair.second, cropHint.bottom); 478 479 // to support back compatibility in B&R, save the crops for one orientation in the 480 // legacy "cropLeft", "cropTop", "cropRight", "cropBottom" entries 481 int orientationToPutInLegacyCrop = wallpaper.mOrientationWhenSet; 482 if (mWallpaperDisplayHelper.isFoldable()) { 483 int unfoldedOrientation = mWallpaperDisplayHelper 484 .getUnfoldedOrientation(orientationToPutInLegacyCrop); 485 if (unfoldedOrientation != ORIENTATION_UNKNOWN) { 486 orientationToPutInLegacyCrop = unfoldedOrientation; 487 } 488 } 489 if (pair.first == orientationToPutInLegacyCrop) { 490 rectToPutInLegacyCrop.set(cropHint); 491 } 492 } 493 out.attributeInt(null, "cropLeft", rectToPutInLegacyCrop.left); 494 out.attributeInt(null, "cropTop", rectToPutInLegacyCrop.top); 495 out.attributeInt(null, "cropRight", rectToPutInLegacyCrop.right); 496 out.attributeInt(null, "cropBottom", rectToPutInLegacyCrop.bottom); 497 498 out.attributeInt(null, "totalCropLeft", wallpaper.cropHint.left); 499 out.attributeInt(null, "totalCropTop", wallpaper.cropHint.top); 500 out.attributeInt(null, "totalCropRight", wallpaper.cropHint.right); 501 out.attributeInt(null, "totalCropBottom", wallpaper.cropHint.bottom); 502 out.attributeFloat(null, "sampleSize", wallpaper.mSampleSize); 503 } else if (!multiCrop()) { 504 final DisplayData wpdData = 505 mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY); 506 out.attributeInt(null, "width", wpdData.mWidth); 507 out.attributeInt(null, "height", wpdData.mHeight); 508 out.attributeInt(null, "cropLeft", wallpaper.cropHint.left); 509 out.attributeInt(null, "cropTop", wallpaper.cropHint.top); 510 out.attributeInt(null, "cropRight", wallpaper.cropHint.right); 511 out.attributeInt(null, "cropBottom", wallpaper.cropHint.bottom); 512 if (wpdData.mPadding.left != 0) { 513 out.attributeInt(null, "paddingLeft", wpdData.mPadding.left); 514 } 515 if (wpdData.mPadding.top != 0) { 516 out.attributeInt(null, "paddingTop", wpdData.mPadding.top); 517 } 518 if (wpdData.mPadding.right != 0) { 519 out.attributeInt(null, "paddingRight", wpdData.mPadding.right); 520 } 521 if (wpdData.mPadding.bottom != 0) { 522 out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom); 523 } 524 } 525 526 out.attributeFloat(null, "dimAmount", wallpaper.mWallpaperDimAmount); 527 out.attribute(null, "bindSource", wallpaper.mBindSource.name()); 528 int dimAmountsCount = wallpaper.mUidToDimAmount.size(); 529 out.attributeInt(null, "dimAmountsCount", dimAmountsCount); 530 if (dimAmountsCount > 0) { 531 int index = 0; 532 for (int i = 0; i < wallpaper.mUidToDimAmount.size(); i++) { 533 out.attributeInt(null, "dimUID" + index, wallpaper.mUidToDimAmount.keyAt(i)); 534 out.attributeFloat(null, "dimValue" + index, wallpaper.mUidToDimAmount.valueAt(i)); 535 index++; 536 } 537 } 538 539 if (wallpaper.primaryColors != null) { 540 int colorsCount = wallpaper.primaryColors.getMainColors().size(); 541 out.attributeInt(null, "colorsCount", colorsCount); 542 if (colorsCount > 0) { 543 for (int i = 0; i < colorsCount; i++) { 544 final Color wc = wallpaper.primaryColors.getMainColors().get(i); 545 out.attributeInt(null, "colorValue" + i, wc.toArgb()); 546 } 547 } 548 549 int allColorsCount = wallpaper.primaryColors.getAllColors().size(); 550 out.attributeInt(null, "allColorsCount", allColorsCount); 551 if (allColorsCount > 0) { 552 int index = 0; 553 for (Map.Entry<Integer, Integer> entry : wallpaper.primaryColors.getAllColors() 554 .entrySet()) { 555 out.attributeInt(null, "allColorsValue" + index, entry.getKey()); 556 out.attributeInt(null, "allColorsPopulation" + index, entry.getValue()); 557 index++; 558 } 559 } 560 561 out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints()); 562 } 563 564 out.attribute(null, "name", wallpaper.name); 565 if (wallpaper.wallpaperComponent != null 566 && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) { 567 out.attribute(null, "component", 568 wallpaper.wallpaperComponent.flattenToShortString()); 569 } 570 571 if (wallpaper.allowBackup) { 572 out.attributeBoolean(null, "backup", true); 573 } 574 575 out.endTag(null, tag); 576 } 577 578 // Restore the named resource bitmap to both source + crop files restoreNamedResourceLocked(WallpaperData wallpaper)579 boolean restoreNamedResourceLocked(WallpaperData wallpaper) { 580 if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) { 581 String resName = wallpaper.name.substring(4); 582 583 String pkg = null; 584 int colon = resName.indexOf(':'); 585 if (colon > 0) { 586 pkg = resName.substring(0, colon); 587 } 588 589 String ident = null; 590 int slash = resName.lastIndexOf('/'); 591 if (slash > 0) { 592 ident = resName.substring(slash + 1); 593 } 594 595 String type = null; 596 if (colon > 0 && slash > 0 && (slash - colon) > 1) { 597 type = resName.substring(colon + 1, slash); 598 } 599 600 if (pkg != null && ident != null && type != null) { 601 int resId = -1; 602 InputStream res = null; 603 FileOutputStream fos = null; 604 FileOutputStream cos = null; 605 try { 606 Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED); 607 Resources r = c.getResources(); 608 resId = r.getIdentifier(resName, null, null); 609 if (resId == 0) { 610 Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type 611 + " ident=" + ident); 612 return false; 613 } 614 615 res = r.openRawResource(resId); 616 if (wallpaper.getWallpaperFile().exists()) { 617 wallpaper.getWallpaperFile().delete(); 618 wallpaper.getCropFile().delete(); 619 } 620 fos = new FileOutputStream(wallpaper.getWallpaperFile()); 621 cos = new FileOutputStream(wallpaper.getCropFile()); 622 623 byte[] buffer = new byte[32768]; 624 int amt; 625 while ((amt = res.read(buffer)) > 0) { 626 fos.write(buffer, 0, amt); 627 cos.write(buffer, 0, amt); 628 } 629 // mWallpaperObserver will notice the close and send the change broadcast 630 631 Slog.v(TAG, "Restored wallpaper: " + resName); 632 return true; 633 } catch (PackageManager.NameNotFoundException e) { 634 Slog.e(TAG, "Package name " + pkg + " not found"); 635 } catch (Resources.NotFoundException e) { 636 Slog.e(TAG, "Resource not found: " + resId); 637 } catch (IOException e) { 638 Slog.e(TAG, "IOException while restoring wallpaper ", e); 639 } finally { 640 IoUtils.closeQuietly(res); 641 if (fos != null) { 642 FileUtils.sync(fos); 643 } 644 if (cos != null) { 645 FileUtils.sync(cos); 646 } 647 IoUtils.closeQuietly(fos); 648 IoUtils.closeQuietly(cos); 649 } 650 } 651 } 652 return false; 653 } 654 screenDimensionPairs()655 private static List<Pair<Integer, String>> screenDimensionPairs() { 656 return List.of( 657 new Pair<>(WallpaperManager.PORTRAIT, "Portrait"), 658 new Pair<>(WallpaperManager.LANDSCAPE, "Landscape"), 659 new Pair<>(WallpaperManager.SQUARE_PORTRAIT, "SquarePortrait"), 660 new Pair<>(WallpaperManager.SQUARE_LANDSCAPE, "SquareLandscape")); 661 } 662 } 663