Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c19033da authored by Aurélien Pomini's avatar Aurélien Pomini Committed by Android (Google) Code Review
Browse files

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

parents 9efbe6c0 fe2f1f78
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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
+550 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -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
 */