Merge "Move xml & loading logic to a new class"

This commit is contained in:
Aurélien Pomini
2023-02-03 16:39:52 +00:00
committed by Android (Google) Code Review
5 changed files with 579 additions and 451 deletions

View File

@@ -30,7 +30,7 @@ import android.app.WallpaperManager.SetWallpaperFlags;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.RemoteCallbackList;
import android.util.ArrayMap;
import android.util.SparseArray;
import java.io.File;
@@ -115,7 +115,7 @@ class WallpaperData {
* A map to keep track of the dimming set by different applications. The key is the calling
* UID and the value is the dim amount.
*/
ArrayMap<Integer, Float> mUidToDimAmount = new ArrayMap<>();
SparseArray<Float> mUidToDimAmount = new SparseArray<>();
/**
* Whether we need to extract the wallpaper colors again to calculate the dark hints

View File

@@ -0,0 +1,550 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wallpaper;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.wallpaper.WallpaperDisplayHelper.DisplayData;
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_CROP;
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_INFO;
import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked;
import android.annotation.Nullable;
import android.app.WallpaperColors;
import android.app.backup.WallpaperBackupHelper;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.FileUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.JournaledFile;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* Helper for the wallpaper loading / saving / xml parsing
* Only meant to be used lock held by WallpaperManagerService
* Only meant to be instantiated once by WallpaperManagerService
*/
class WallpaperDataParser {
private static final String TAG = WallpaperDataParser.class.getSimpleName();
private static final boolean DEBUG = false;
private final ComponentName mImageWallpaper;
private final WallpaperDisplayHelper mWallpaperDisplayHelper;
private final WallpaperCropper mWallpaperCropper;
private final Context mContext;
WallpaperDataParser(Context context, WallpaperDisplayHelper wallpaperDisplayHelper,
WallpaperCropper wallpaperCropper) {
mContext = context;
mWallpaperDisplayHelper = wallpaperDisplayHelper;
mWallpaperCropper = wallpaperCropper;
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
}
private JournaledFile makeJournaledFile(int userId) {
final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
static class WallpaperLoadingResult {
private final WallpaperData mSystemWallpaperData;
@Nullable
private final WallpaperData mLockWallpaperData;
private final boolean mSuccess;
private WallpaperLoadingResult(
WallpaperData systemWallpaperData,
WallpaperData lockWallpaperData,
boolean success) {
mSystemWallpaperData = systemWallpaperData;
mLockWallpaperData = lockWallpaperData;
mSuccess = success;
}
public WallpaperData getSystemWallpaperData() {
return mSystemWallpaperData;
}
public WallpaperData getLockWallpaperData() {
return mLockWallpaperData;
}
public boolean success() {
return mSuccess;
}
}
/**
* Load the system wallpaper (and the lock wallpaper, if it exists) from disk
* @param userId the id of the user for which the wallpaper should be loaded
* @param keepDimensionHints if false, parse and set the
* {@link DisplayData} width and height for the specified userId
* @param wallpaper the wallpaper object to reuse to do the modifications.
* If null, a new object will be created.
* @param lockWallpaper the lock wallpaper object to reuse to do the modifications.
* If null, a new object will be created.
* @return a {@link WallpaperLoadingResult} object containing the wallpaper data.
* This object will contain the {@code wallpaper} and
* {@code lockWallpaper} provided as parameters, if they are not null.
*/
public WallpaperLoadingResult loadSettingsLocked(int userId, boolean keepDimensionHints,
WallpaperData wallpaper, WallpaperData lockWallpaper) {
JournaledFile journal = makeJournaledFile(userId);
FileInputStream stream = null;
File file = journal.chooseForRead();
if (wallpaper == null) {
// Do this once per boot
migrateFromOld();
wallpaper = new WallpaperData(userId, FLAG_SYSTEM);
wallpaper.allowBackup = true;
if (!wallpaper.cropExists()) {
if (wallpaper.sourceExists()) {
mWallpaperCropper.generateCrop(wallpaper);
} else {
Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
}
}
}
final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
boolean success = false;
try {
stream = new FileInputStream(file);
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
do {
type = parser.next();
if (type == XmlPullParser.START_TAG) {
String tag = parser.getName();
if ("wp".equals(tag)) {
// Common to system + lock wallpapers
parseWallpaperAttributes(parser, wallpaper, keepDimensionHints);
// A system wallpaper might also be a live wallpaper
String comp = parser.getAttributeValue(null, "component");
wallpaper.nextWallpaperComponent = comp != null
? ComponentName.unflattenFromString(comp)
: null;
if (wallpaper.nextWallpaperComponent == null
|| "android".equals(wallpaper.nextWallpaperComponent
.getPackageName())) {
wallpaper.nextWallpaperComponent = mImageWallpaper;
}
if (DEBUG) {
Slog.v(TAG, "mWidth:" + wpdData.mWidth);
Slog.v(TAG, "mHeight:" + wpdData.mHeight);
Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
Slog.v(TAG, "mName:" + wallpaper.name);
Slog.v(TAG, "mNextWallpaperComponent:"
+ wallpaper.nextWallpaperComponent);
}
} else if ("kwp".equals(tag)) {
// keyguard-specific wallpaper for this user
if (lockWallpaper == null) {
lockWallpaper = new WallpaperData(userId, FLAG_LOCK);
}
parseWallpaperAttributes(parser, lockWallpaper, false);
}
}
} while (type != XmlPullParser.END_DOCUMENT);
success = true;
} catch (FileNotFoundException e) {
Slog.w(TAG, "no current wallpaper -- first boot?");
} catch (NullPointerException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (NumberFormatException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (XmlPullParserException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (IOException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (IndexOutOfBoundsException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
}
IoUtils.closeQuietly(stream);
if (!success) {
wallpaper.cropHint.set(0, 0, 0, 0);
wpdData.mPadding.set(0, 0, 0, 0);
wallpaper.name = "";
lockWallpaper = null;
} else {
if (wallpaper.wallpaperId <= 0) {
wallpaper.wallpaperId = makeWallpaperIdLocked();
if (DEBUG) {
Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
+ "); now " + wallpaper.wallpaperId);
}
}
}
mWallpaperDisplayHelper.ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
ensureSaneWallpaperData(wallpaper);
if (lockWallpaper != null) {
ensureSaneWallpaperData(lockWallpaper);
lockWallpaper.mWhich = FLAG_LOCK;
wallpaper.mWhich = FLAG_SYSTEM;
} else {
wallpaper.mWhich = FLAG_SYSTEM | FLAG_LOCK;
}
return new WallpaperLoadingResult(wallpaper, lockWallpaper, success);
}
private void ensureSaneWallpaperData(WallpaperData wallpaper) {
// Only overwrite cropHint if the rectangle is invalid.
if (wallpaper.cropHint.width() < 0
|| wallpaper.cropHint.height() < 0) {
wallpaper.cropHint.set(0, 0, 0, 0);
}
}
private void migrateFromOld() {
// Pre-N, what existed is the one we're now using as the display crop
File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP);
// In the very-long-ago, imagery lived with the settings app
File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
// Migrations from earlier wallpaper image storage schemas
if (preNWallpaper.exists()) {
if (!newWallpaper.exists()) {
// we've got the 'wallpaper' crop file but not the nominal source image,
// so do the simple "just take everything" straight copy of legacy data
if (DEBUG) {
Slog.i(TAG, "Migrating wallpaper schema");
}
FileUtils.copyFile(preNWallpaper, newWallpaper);
} // else we're in the usual modern case: both source & crop exist
} else if (originalWallpaper.exists()) {
// VERY old schema; make sure things exist and are in the right place
if (DEBUG) {
Slog.i(TAG, "Migrating antique wallpaper schema");
}
File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
if (oldInfo.exists()) {
File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
oldInfo.renameTo(newInfo);
}
FileUtils.copyFile(originalWallpaper, preNWallpaper);
originalWallpaper.renameTo(newWallpaper);
}
}
@VisibleForTesting
void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
boolean keepDimensionHints) throws XmlPullParserException {
final int id = parser.getAttributeInt(null, "id", -1);
if (id != -1) {
wallpaper.wallpaperId = id;
if (id > WallpaperUtils.getCurrentWallpaperId()) {
WallpaperUtils.setCurrentWallpaperId(id);
}
} else {
wallpaper.wallpaperId = makeWallpaperIdLocked();
}
final DisplayData wpData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
if (!keepDimensionHints) {
wpData.mWidth = parser.getAttributeInt(null, "width");
wpData.mHeight = parser.getAttributeInt(null, "height");
}
wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
wallpaper.mWallpaperDimAmount = getAttributeFloat(parser, "dimAmount", 0f);
int dimAmountsCount = getAttributeInt(parser, "dimAmountsCount", 0);
if (dimAmountsCount > 0) {
SparseArray<Float> allDimAmounts = new SparseArray<>(dimAmountsCount);
for (int i = 0; i < dimAmountsCount; i++) {
int uid = getAttributeInt(parser, "dimUID" + i, 0);
float dimValue = getAttributeFloat(parser, "dimValue" + i, 0f);
allDimAmounts.put(uid, dimValue);
}
wallpaper.mUidToDimAmount = allDimAmounts;
}
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
int allColorsCount = getAttributeInt(parser, "allColorsCount", 0);
if (allColorsCount > 0) {
Map<Integer, Integer> allColors = new HashMap<>(allColorsCount);
for (int i = 0; i < allColorsCount; i++) {
int colorInt = getAttributeInt(parser, "allColorsValue" + i, 0);
int population = getAttributeInt(parser, "allColorsPopulation" + i, 0);
allColors.put(colorInt, population);
}
int colorHints = getAttributeInt(parser, "colorHints", 0);
wallpaper.primaryColors = new WallpaperColors(allColors, colorHints);
} else if (colorsCount > 0) {
Color primary = null, secondary = null, tertiary = null;
for (int i = 0; i < colorsCount; i++) {
Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
if (i == 0) {
primary = color;
} else if (i == 1) {
secondary = color;
} else if (i == 2) {
tertiary = color;
} else {
break;
}
}
int colorHints = getAttributeInt(parser, "colorHints", 0);
wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
}
wallpaper.name = parser.getAttributeValue(null, "name");
wallpaper.allowBackup = parser.getAttributeBoolean(null, "backup", false);
}
private int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) {
return parser.getAttributeInt(null, name, defValue);
}
private float getAttributeFloat(TypedXmlPullParser parser, String name, float defValue) {
return parser.getAttributeFloat(null, name, defValue);
}
void saveSettingsLocked(int userId, WallpaperData wallpaper, WallpaperData lockWallpaper) {
JournaledFile journal = makeJournaledFile(userId);
FileOutputStream fstream = null;
try {
fstream = new FileOutputStream(journal.chooseForWrite(), false);
TypedXmlSerializer out = Xml.resolveSerializer(fstream);
out.startDocument(null, true);
if (wallpaper != null) {
writeWallpaperAttributes(out, "wp", wallpaper);
}
if (lockWallpaper != null) {
writeWallpaperAttributes(out, "kwp", lockWallpaper);
}
out.endDocument();
fstream.flush();
FileUtils.sync(fstream);
fstream.close();
journal.commit();
} catch (IOException e) {
IoUtils.closeQuietly(fstream);
journal.rollback();
}
}
@VisibleForTesting
void writeWallpaperAttributes(TypedXmlSerializer out, String tag, WallpaperData wallpaper)
throws IllegalArgumentException, IllegalStateException, IOException {
if (DEBUG) {
Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
}
final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
out.startTag(null, tag);
out.attributeInt(null, "id", wallpaper.wallpaperId);
out.attributeInt(null, "width", wpdData.mWidth);
out.attributeInt(null, "height", wpdData.mHeight);
out.attributeInt(null, "cropLeft", wallpaper.cropHint.left);
out.attributeInt(null, "cropTop", wallpaper.cropHint.top);
out.attributeInt(null, "cropRight", wallpaper.cropHint.right);
out.attributeInt(null, "cropBottom", wallpaper.cropHint.bottom);
if (wpdData.mPadding.left != 0) {
out.attributeInt(null, "paddingLeft", wpdData.mPadding.left);
}
if (wpdData.mPadding.top != 0) {
out.attributeInt(null, "paddingTop", wpdData.mPadding.top);
}
if (wpdData.mPadding.right != 0) {
out.attributeInt(null, "paddingRight", wpdData.mPadding.right);
}
if (wpdData.mPadding.bottom != 0) {
out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom);
}
out.attributeFloat(null, "dimAmount", wallpaper.mWallpaperDimAmount);
int dimAmountsCount = wallpaper.mUidToDimAmount.size();
out.attributeInt(null, "dimAmountsCount", dimAmountsCount);
if (dimAmountsCount > 0) {
int index = 0;
for (int i = 0; i < wallpaper.mUidToDimAmount.size(); i++) {
out.attributeInt(null, "dimUID" + index, wallpaper.mUidToDimAmount.keyAt(i));
out.attributeFloat(null, "dimValue" + index, wallpaper.mUidToDimAmount.valueAt(i));
index++;
}
}
if (wallpaper.primaryColors != null) {
int colorsCount = wallpaper.primaryColors.getMainColors().size();
out.attributeInt(null, "colorsCount", colorsCount);
if (colorsCount > 0) {
for (int i = 0; i < colorsCount; i++) {
final Color wc = wallpaper.primaryColors.getMainColors().get(i);
out.attributeInt(null, "colorValue" + i, wc.toArgb());
}
}
int allColorsCount = wallpaper.primaryColors.getAllColors().size();
out.attributeInt(null, "allColorsCount", allColorsCount);
if (allColorsCount > 0) {
int index = 0;
for (Map.Entry<Integer, Integer> entry : wallpaper.primaryColors.getAllColors()
.entrySet()) {
out.attributeInt(null, "allColorsValue" + index, entry.getKey());
out.attributeInt(null, "allColorsPopulation" + index, entry.getValue());
index++;
}
}
out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints());
}
out.attribute(null, "name", wallpaper.name);
if (wallpaper.wallpaperComponent != null
&& !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
out.attribute(null, "component",
wallpaper.wallpaperComponent.flattenToShortString());
}
if (wallpaper.allowBackup) {
out.attributeBoolean(null, "backup", true);
}
out.endTag(null, tag);
}
// Restore the named resource bitmap to both source + crop files
boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
String resName = wallpaper.name.substring(4);
String pkg = null;
int colon = resName.indexOf(':');
if (colon > 0) {
pkg = resName.substring(0, colon);
}
String ident = null;
int slash = resName.lastIndexOf('/');
if (slash > 0) {
ident = resName.substring(slash + 1);
}
String type = null;
if (colon > 0 && slash > 0 && (slash - colon) > 1) {
type = resName.substring(colon + 1, slash);
}
if (pkg != null && ident != null && type != null) {
int resId = -1;
InputStream res = null;
FileOutputStream fos = null;
FileOutputStream cos = null;
try {
Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
Resources r = c.getResources();
resId = r.getIdentifier(resName, null, null);
if (resId == 0) {
Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
+ " ident=" + ident);
return false;
}
res = r.openRawResource(resId);
if (wallpaper.wallpaperFile.exists()) {
wallpaper.wallpaperFile.delete();
wallpaper.cropFile.delete();
}
fos = new FileOutputStream(wallpaper.wallpaperFile);
cos = new FileOutputStream(wallpaper.cropFile);
byte[] buffer = new byte[32768];
int amt;
while ((amt = res.read(buffer)) > 0) {
fos.write(buffer, 0, amt);
cos.write(buffer, 0, amt);
}
// mWallpaperObserver will notice the close and send the change broadcast
Slog.v(TAG, "Restored wallpaper: " + resName);
return true;
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Package name " + pkg + " not found");
} catch (Resources.NotFoundException e) {
Slog.e(TAG, "Resource not found: " + resId);
} catch (IOException e) {
Slog.e(TAG, "IOException while restoring wallpaper ", e);
} finally {
IoUtils.closeQuietly(res);
if (fos != null) {
FileUtils.sync(fos);
}
if (cos != null) {
FileUtils.sync(cos);
}
IoUtils.closeQuietly(fos);
IoUtils.closeQuietly(cos);
}
}
}
return false;
}
}

View File

@@ -30,6 +30,7 @@ import android.view.DisplayInfo;
import com.android.server.wm.WindowManagerInternal;
import java.util.function.Consumer;
/**
* Internal class used to store all the display data relevant to the wallpapers
*/

View File

@@ -35,7 +35,6 @@ import static com.android.server.wallpaper.WallpaperDisplayHelper.DisplayData;
import static com.android.server.wallpaper.WallpaperUtils.RECORD_FILE;
import static com.android.server.wallpaper.WallpaperUtils.RECORD_LOCK_FILE;
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_CROP;
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_INFO;
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_LOCK_ORIG;
import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
@@ -56,7 +55,6 @@ import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.app.WallpaperManager.SetWallpaperFlags;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.WallpaperBackupHelper;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -70,10 +68,8 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.display.DisplayManager;
@@ -101,12 +97,10 @@ import android.service.wallpaper.IWallpaperService;
import android.service.wallpaper.WallpaperService;
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.Display;
import com.android.internal.R;
@@ -114,9 +108,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.JournaledFile;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -125,22 +116,16 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.WindowManagerInternal;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -152,7 +137,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private static final String TAG = "WallpaperManagerService";
private static final boolean DEBUG = false;
private static final boolean DEBUG_LIVE = true;
private static final boolean DEBUG_CROP = true;
private static final @NonNull RectF LOCAL_COLOR_BOUNDS =
new RectF(0, 0, 1, 1);
@@ -848,6 +832,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private boolean mInAmbientMode;
private LocalColorRepository mLocalColorRepo = new LocalColorRepository();
@VisibleForTesting
final WallpaperDataParser mWallpaperDataParser;
@VisibleForTesting
final WallpaperDisplayHelper mWallpaperDisplayHelper;
final WallpaperCropper mWallpaperCropper;
@@ -1612,6 +1599,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
dm.registerDisplayListener(mDisplayListener, null /* handler */);
mWallpaperDisplayHelper = new WallpaperDisplayHelper(dm, mWindowManagerInternal);
mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
mWallpaperDataParser = new WallpaperDataParser(
mContext, mWallpaperDisplayHelper, mWallpaperCropper);
mActivityManager = mContext.getSystemService(ActivityManager.class);
mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>();
@@ -1899,7 +1888,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// bound into place
wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
final WallpaperData fallback = new WallpaperData(wallpaper.userId, FLAG_LOCK);
ensureSaneWallpaperData(fallback);
bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
mWaitingForUnlock = true;
}
@@ -2630,12 +2618,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
*
* @param uidToDimAmountMap Map of UIDs to dim amounts
*/
private float getHighestDimAmountFromMap(ArrayMap<Integer, Float> uidToDimAmountMap) {
private float getHighestDimAmountFromMap(SparseArray<Float> uidToDimAmountMap) {
float maxDimAmount = 0.0f;
for (Map.Entry<Integer, Float> entry : uidToDimAmountMap.entrySet()) {
if (entry.getValue() > maxDimAmount) {
maxDimAmount = entry.getValue();
}
for (int i = 0; i < uidToDimAmountMap.size(); i++) {
maxDimAmount = Math.max(maxDimAmount, uidToDimAmountMap.valueAt(i));
}
return maxDimAmount;
}
@@ -3432,169 +3418,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
private JournaledFile makeJournaledFile(int userId) {
final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
void saveSettingsLocked(int userId) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
t.traceBegin("WPMS.saveSettingsLocked-" + userId);
JournaledFile journal = makeJournaledFile(userId);
FileOutputStream fstream = null;
try {
fstream = new FileOutputStream(journal.chooseForWrite(), false);
TypedXmlSerializer out = Xml.resolveSerializer(fstream);
out.startDocument(null, true);
WallpaperData wallpaper;
wallpaper = mWallpaperMap.get(userId);
if (wallpaper != null) {
writeWallpaperAttributes(out, "wp", wallpaper);
}
wallpaper = mLockWallpaperMap.get(userId);
if (wallpaper != null) {
writeWallpaperAttributes(out, "kwp", wallpaper);
}
out.endDocument();
fstream.flush();
FileUtils.sync(fstream);
fstream.close();
journal.commit();
} catch (IOException e) {
IoUtils.closeQuietly(fstream);
journal.rollback();
}
mWallpaperDataParser.saveSettingsLocked(
userId, mWallpaperMap.get(userId), mLockWallpaperMap.get(userId));
t.traceEnd();
}
@VisibleForTesting
void writeWallpaperAttributes(TypedXmlSerializer out, String tag,
WallpaperData wallpaper)
throws IllegalArgumentException, IllegalStateException, IOException {
if (DEBUG) {
Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
}
final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
out.startTag(null, tag);
out.attributeInt(null, "id", wallpaper.wallpaperId);
out.attributeInt(null, "width", wpdData.mWidth);
out.attributeInt(null, "height", wpdData.mHeight);
out.attributeInt(null, "cropLeft", wallpaper.cropHint.left);
out.attributeInt(null, "cropTop", wallpaper.cropHint.top);
out.attributeInt(null, "cropRight", wallpaper.cropHint.right);
out.attributeInt(null, "cropBottom", wallpaper.cropHint.bottom);
if (wpdData.mPadding.left != 0) {
out.attributeInt(null, "paddingLeft", wpdData.mPadding.left);
}
if (wpdData.mPadding.top != 0) {
out.attributeInt(null, "paddingTop", wpdData.mPadding.top);
}
if (wpdData.mPadding.right != 0) {
out.attributeInt(null, "paddingRight", wpdData.mPadding.right);
}
if (wpdData.mPadding.bottom != 0) {
out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom);
}
out.attributeFloat(null, "dimAmount", wallpaper.mWallpaperDimAmount);
int dimAmountsCount = wallpaper.mUidToDimAmount.size();
out.attributeInt(null, "dimAmountsCount", dimAmountsCount);
if (dimAmountsCount > 0) {
int index = 0;
for (Map.Entry<Integer, Float> entry : wallpaper.mUidToDimAmount.entrySet()) {
out.attributeInt(null, "dimUID" + index, entry.getKey());
out.attributeFloat(null, "dimValue" + index, entry.getValue());
index++;
}
}
if (wallpaper.primaryColors != null) {
int colorsCount = wallpaper.primaryColors.getMainColors().size();
out.attributeInt(null, "colorsCount", colorsCount);
if (colorsCount > 0) {
for (int i = 0; i < colorsCount; i++) {
final Color wc = wallpaper.primaryColors.getMainColors().get(i);
out.attributeInt(null, "colorValue" + i, wc.toArgb());
}
}
int allColorsCount = wallpaper.primaryColors.getAllColors().size();
out.attributeInt(null, "allColorsCount", allColorsCount);
if (allColorsCount > 0) {
int index = 0;
for (Map.Entry<Integer, Integer> entry : wallpaper.primaryColors.getAllColors()
.entrySet()) {
out.attributeInt(null, "allColorsValue" + index, entry.getKey());
out.attributeInt(null, "allColorsPopulation" + index, entry.getValue());
index++;
}
}
out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints());
}
out.attribute(null, "name", wallpaper.name);
if (wallpaper.wallpaperComponent != null
&& !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
out.attribute(null, "component",
wallpaper.wallpaperComponent.flattenToShortString());
}
if (wallpaper.allowBackup) {
out.attributeBoolean(null, "backup", true);
}
out.endTag(null, tag);
}
private void migrateFromOld() {
// Pre-N, what existed is the one we're now using as the display crop
File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP);
// In the very-long-ago, imagery lived with the settings app
File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
// Migrations from earlier wallpaper image storage schemas
if (preNWallpaper.exists()) {
if (!newWallpaper.exists()) {
// we've got the 'wallpaper' crop file but not the nominal source image,
// so do the simple "just take everything" straight copy of legacy data
if (DEBUG) {
Slog.i(TAG, "Migrating wallpaper schema");
}
FileUtils.copyFile(preNWallpaper, newWallpaper);
} // else we're in the usual modern case: both source & crop exist
} else if (originalWallpaper.exists()) {
// VERY old schema; make sure things exist and are in the right place
if (DEBUG) {
Slog.i(TAG, "Migrating antique wallpaper schema");
}
File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
if (oldInfo.exists()) {
File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
oldInfo.renameTo(newInfo);
}
FileUtils.copyFile(originalWallpaper, preNWallpaper);
originalWallpaper.renameTo(newWallpaper);
}
}
private int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) {
return parser.getAttributeInt(null, name, defValue);
}
private float getAttributeFloat(TypedXmlPullParser parser, String name, float defValue) {
return parser.getAttributeFloat(null, name, defValue);
}
/**
* Determines and returns the current wallpaper for the given user and destination, creating
* a valid entry if it does not already exist and adding it to the appropriate wallpaper map.
@@ -3629,14 +3460,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (which == FLAG_LOCK) {
wallpaper = new WallpaperData(userId, FLAG_LOCK);
mLockWallpaperMap.put(userId, wallpaper);
ensureSaneWallpaperData(wallpaper);
} else {
// rationality fallback: we're in bad shape, but establishing a known
// valid system+lock WallpaperData will keep us from dying.
Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
wallpaper = new WallpaperData(userId, FLAG_SYSTEM);
mWallpaperMap.put(userId, wallpaper);
ensureSaneWallpaperData(wallpaper);
}
}
}
@@ -3644,114 +3473,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
JournaledFile journal = makeJournaledFile(userId);
FileInputStream stream = null;
File file = journal.chooseForRead();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
// Do this once per boot
migrateFromOld();
wallpaper = new WallpaperData(userId, FLAG_SYSTEM);
wallpaper.allowBackup = true;
mWallpaperMap.put(userId, wallpaper);
if (!wallpaper.cropExists()) {
if (wallpaper.sourceExists()) {
mWallpaperCropper.generateCrop(wallpaper);
} else {
Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
}
}
initializeFallbackWallpaper();
}
boolean success = false;
final DisplayData wpdData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
try {
stream = new FileInputStream(file);
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
do {
type = parser.next();
if (type == XmlPullParser.START_TAG) {
String tag = parser.getName();
if ("wp".equals(tag)) {
// Common to system + lock wallpapers
parseWallpaperAttributes(parser, wallpaper, keepDimensionHints);
// A system wallpaper might also be a live wallpaper
String comp = parser.getAttributeValue(null, "component");
wallpaper.nextWallpaperComponent = comp != null
? ComponentName.unflattenFromString(comp)
: null;
if (wallpaper.nextWallpaperComponent == null
|| "android".equals(wallpaper.nextWallpaperComponent
.getPackageName())) {
wallpaper.nextWallpaperComponent = mImageWallpaper;
}
if (DEBUG) {
Slog.v(TAG, "mWidth:" + wpdData.mWidth);
Slog.v(TAG, "mHeight:" + wpdData.mHeight);
Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
Slog.v(TAG, "mName:" + wallpaper.name);
Slog.v(TAG, "mNextWallpaperComponent:"
+ wallpaper.nextWallpaperComponent);
}
} else if ("kwp".equals(tag)) {
// keyguard-specific wallpaper for this user
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper == null) {
lockWallpaper = new WallpaperData(userId, FLAG_LOCK);
mLockWallpaperMap.put(userId, lockWallpaper);
}
parseWallpaperAttributes(parser, lockWallpaper, false);
}
}
} while (type != XmlPullParser.END_DOCUMENT);
success = true;
} catch (FileNotFoundException e) {
Slog.w(TAG, "no current wallpaper -- first boot?");
} catch (NullPointerException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (NumberFormatException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (XmlPullParserException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (IOException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
} catch (IndexOutOfBoundsException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
}
IoUtils.closeQuietly(stream);
if (!success) {
wallpaper.cropHint.set(0, 0, 0, 0);
wpdData.mPadding.set(0, 0, 0, 0);
wallpaper.name = "";
initializeFallbackWallpaper();
WallpaperData wallpaperData = mWallpaperMap.get(userId);
WallpaperData lockWallpaperData = mLockWallpaperMap.get(userId);
WallpaperDataParser.WallpaperLoadingResult result = mWallpaperDataParser.loadSettingsLocked(
userId, keepDimensionHints, wallpaperData, lockWallpaperData);
mWallpaperMap.put(userId, result.getSystemWallpaperData());
if (result.success()) {
mLockWallpaperMap.put(userId, result.getLockWallpaperData());
} else {
mLockWallpaperMap.remove(userId);
} else {
if (wallpaper.wallpaperId <= 0) {
wallpaper.wallpaperId = makeWallpaperIdLocked();
if (DEBUG) {
Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
+ "); now " + wallpaper.wallpaperId);
}
}
}
mWallpaperDisplayHelper.ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
ensureSaneWallpaperData(wallpaper);
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper != null) {
ensureSaneWallpaperData(lockWallpaper);
lockWallpaper.mWhich = FLAG_LOCK;
wallpaper.mWhich = FLAG_SYSTEM;
} else {
wallpaper.mWhich = FLAG_SYSTEM | FLAG_LOCK;
}
}
@@ -3766,84 +3498,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
private void ensureSaneWallpaperData(WallpaperData wallpaper) {
// Only overwrite cropHint if the rectangle is invalid.
if (wallpaper.cropHint.width() < 0
|| wallpaper.cropHint.height() < 0) {
wallpaper.cropHint.set(0, 0, 0, 0);
}
}
@VisibleForTesting
void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
boolean keepDimensionHints) throws XmlPullParserException {
final int id = parser.getAttributeInt(null, "id", -1);
if (id != -1) {
wallpaper.wallpaperId = id;
if (id > WallpaperUtils.getCurrentWallpaperId()) {
WallpaperUtils.setCurrentWallpaperId(id);
}
} else {
wallpaper.wallpaperId = makeWallpaperIdLocked();
}
final DisplayData wpData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
if (!keepDimensionHints) {
wpData.mWidth = parser.getAttributeInt(null, "width");
wpData.mHeight = parser.getAttributeInt(null, "height");
}
wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
wallpaper.mWallpaperDimAmount = getAttributeFloat(parser, "dimAmount", 0f);
int dimAmountsCount = getAttributeInt(parser, "dimAmountsCount", 0);
if (dimAmountsCount > 0) {
ArrayMap<Integer, Float> allDimAmounts = new ArrayMap<>(dimAmountsCount);
for (int i = 0; i < dimAmountsCount; i++) {
int uid = getAttributeInt(parser, "dimUID" + i, 0);
float dimValue = getAttributeFloat(parser, "dimValue" + i, 0f);
allDimAmounts.put(uid, dimValue);
}
wallpaper.mUidToDimAmount = allDimAmounts;
}
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
int allColorsCount = getAttributeInt(parser, "allColorsCount", 0);
if (allColorsCount > 0) {
Map<Integer, Integer> allColors = new HashMap<>(allColorsCount);
for (int i = 0; i < allColorsCount; i++) {
int colorInt = getAttributeInt(parser, "allColorsValue" + i, 0);
int population = getAttributeInt(parser, "allColorsPopulation" + i, 0);
allColors.put(colorInt, population);
}
int colorHints = getAttributeInt(parser, "colorHints", 0);
wallpaper.primaryColors = new WallpaperColors(allColors, colorHints);
} else if (colorsCount > 0) {
Color primary = null, secondary = null, tertiary = null;
for (int i = 0; i < colorsCount; i++) {
Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
if (i == 0) {
primary = color;
} else if (i == 1) {
secondary = color;
} else if (i == 2) {
tertiary = color;
} else {
break;
}
}
int colorHints = getAttributeInt(parser, "colorHints", 0);
wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
}
wallpaper.name = parser.getAttributeValue(null, "name");
wallpaper.allowBackup = parser.getAttributeBoolean(null, "backup", false);
}
// Called by SystemBackupAgent after files are restored to disk.
public void settingsRestored() {
// Verify caller is the system
@@ -3878,7 +3532,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
success = true;
} else {
if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
success = restoreNamedResourceLocked(wallpaper);
success = mWallpaperDataParser.restoreNamedResourceLocked(wallpaper);
}
if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
+ " id=" + wallpaper.wallpaperId);
@@ -3901,83 +3555,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
// Restore the named resource bitmap to both source + crop files
private boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
String resName = wallpaper.name.substring(4);
String pkg = null;
int colon = resName.indexOf(':');
if (colon > 0) {
pkg = resName.substring(0, colon);
}
String ident = null;
int slash = resName.lastIndexOf('/');
if (slash > 0) {
ident = resName.substring(slash+1);
}
String type = null;
if (colon > 0 && slash > 0 && (slash-colon) > 1) {
type = resName.substring(colon+1, slash);
}
if (pkg != null && ident != null && type != null) {
int resId = -1;
InputStream res = null;
FileOutputStream fos = null;
FileOutputStream cos = null;
try {
Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
Resources r = c.getResources();
resId = r.getIdentifier(resName, null, null);
if (resId == 0) {
Slog.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
+ " ident=" + ident);
return false;
}
res = r.openRawResource(resId);
if (wallpaper.wallpaperFile.exists()) {
wallpaper.wallpaperFile.delete();
wallpaper.cropFile.delete();
}
fos = new FileOutputStream(wallpaper.wallpaperFile);
cos = new FileOutputStream(wallpaper.cropFile);
byte[] buffer = new byte[32768];
int amt;
while ((amt=res.read(buffer)) > 0) {
fos.write(buffer, 0, amt);
cos.write(buffer, 0, amt);
}
// mWallpaperObserver will notice the close and send the change broadcast
Slog.v(TAG, "Restored wallpaper: " + resName);
return true;
} catch (NameNotFoundException e) {
Slog.e(TAG, "Package name " + pkg + " not found");
} catch (Resources.NotFoundException e) {
Slog.e(TAG, "Resource not found: " + resId);
} catch (IOException e) {
Slog.e(TAG, "IOException while restoring wallpaper ", e);
} finally {
IoUtils.closeQuietly(res);
if (fos != null) {
FileUtils.sync(fos);
}
if (cos != null) {
FileUtils.sync(cos);
}
IoUtils.closeQuietly(fos);
IoUtils.closeQuietly(cos);
}
}
}
return false;
}
@Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -4008,9 +3585,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
pw.print(" mWallpaperDimAmount="); pw.println(wallpaper.mWallpaperDimAmount);
pw.print(" isColorExtracted="); pw.println(wallpaper.mIsColorExtractedFromDim);
pw.println(" mUidToDimAmount:");
for (Map.Entry<Integer, Float> entry : wallpaper.mUidToDimAmount.entrySet()) {
pw.print(" UID="); pw.print(entry.getKey());
pw.print(" dimAmount="); pw.println(entry.getValue());
for (int j = 0; j < wallpaper.mUidToDimAmount.size(); j++) {
pw.print(" UID="); pw.print(wallpaper.mUidToDimAmount.keyAt(j));
pw.print(" dimAmount="); pw.println(wallpaper.mUidToDimAmount.valueAt(j));
}
if (wallpaper.connection != null) {
WallpaperConnection conn = wallpaper.connection;

View File

@@ -200,7 +200,6 @@ public class WallpaperManagerServiceTests {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ExtendedMockito.doAnswer(invocation -> {
int userId = (invocation.getArgument(0));
return getWallpaperTestDir(userId);
@@ -398,7 +397,8 @@ public class WallpaperManagerServiceTests {
TypedXmlSerializer serializer = Xml.newBinarySerializer();
serializer.setOutput(new ByteArrayOutputStream(), StandardCharsets.UTF_8.name());
serializer.startDocument(StandardCharsets.UTF_8.name(), true);
mService.writeWallpaperAttributes(serializer, "wp", systemWallpaperData);
mService.mWallpaperDataParser.writeWallpaperAttributes(
serializer, "wp", systemWallpaperData);
} catch (IOException e) {
fail("exception occurred while writing system wallpaper attributes");
}
@@ -409,7 +409,7 @@ public class WallpaperManagerServiceTests {
systemWallpaperData.cropFile.getAbsolutePath());
try {
TypedXmlPullParser parser = Xml.newBinaryPullParser();
mService.parseWallpaperAttributes(parser, shouldMatchSystem, true);
mService.mWallpaperDataParser.parseWallpaperAttributes(parser, shouldMatchSystem, true);
} catch (XmlPullParserException e) {
fail("exception occurred while parsing wallpaper");
}