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

Commit 4204f165 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use normal API for legacy wallpaper restore" into oc-mr1-dev

parents 57d0423e 9f22443b
Loading
Loading
Loading
Loading
+27 −126
Original line number Diff line number Diff line
@@ -18,20 +18,19 @@ package android.app.backup;

import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.util.Slog;
import android.view.Display;
import android.view.WindowManager;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * Helper for backing up / restoring wallpapers.  Basically an AbsoluteFileBackupHelper,
 * but with logic for deciding what to do with restored wallpaper images.
 * We no longer back up wallpapers with this helper, but we do need to process restores
 * of legacy backup payloads.  We just take the restored image as-is and apply it as the
 * system wallpaper using the public "set the wallpaper" API.
 *
 * @hide
 */
@@ -39,83 +38,34 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu
    private static final String TAG = "WallpaperBackupHelper";
    private static final boolean DEBUG = false;

    // If 'true', then apply an acceptable-size heuristic at restore time, dropping back
    // to the factory default wallpaper if the restored one differs "too much" from the
    // device's preferred wallpaper image dimensions.
    private static final boolean REJECT_OUTSIZED_RESTORE = false;

    // When outsized restore rejection is enabled, this is the maximum ratio between the
    // source and target image heights that will be permitted.  The ratio is checked both
    // ways (i.e. >= MAX, or <= 1/MAX) to validate restores from both largeer-than-target
    // and smaller-than-target sources.
    private static final double MAX_HEIGHT_RATIO = 1.35;

    // The height ratio check when applying larger images on smaller screens is separate;
    // in current policy we accept any such restore regardless of the relative dimensions.
    private static final double MIN_HEIGHT_RATIO = 0;

    // This path must match what the WallpaperManagerService uses
    // TODO: Will need to change if backing up non-primary user's wallpaper
    // http://b/22388012
    public static final String WALLPAPER_IMAGE =
            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
                    "wallpaper").getAbsolutePath();
    public static final String WALLPAPER_ORIG_IMAGE =
            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
                    "wallpaper_orig").getAbsolutePath();
    public static final String WALLPAPER_INFO =
            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
                    "wallpaper_info.xml").getAbsolutePath();
    // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
    // Key that legacy wallpaper imagery was stored under
    public static final String WALLPAPER_IMAGE_KEY =
            "/data/data/com.android.settings/files/wallpaper";
    public static final String WALLPAPER_INFO_KEY = "/data/system/wallpaper_info.xml";

    // Stage file - should be adjacent to the WALLPAPER_IMAGE location.  The wallpapers
    // will be saved to this file from the restore stream, then renamed to the proper
    // location if it's deemed suitable.
    // TODO: Will need to change if backing up non-primary user's wallpaper
    // http://b/22388012
    // Stage file that the restored imagery is stored to prior to being applied
    // as the system wallpaper.
    private static final String STAGE_FILE =
            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
                    "wallpaper-tmp").getAbsolutePath();

    Context mContext;
    String[] mFiles;
    String[] mKeys;
    double mDesiredMinWidth;
    double mDesiredMinHeight;
    private final String[] mKeys;
    private final WallpaperManager mWpm;

    /**
     * Construct a helper for backing up / restoring the files at the given absolute locations
     * within the file system.
     * Legacy wallpaper restores, from back when the imagery was stored under the
     * "android" system package as file key/value entities.
     *
     * @param context
     * @param files
     */
    public WallpaperBackupHelper(Context context, String[] files, String[] keys) {
    public WallpaperBackupHelper(Context context, String[] keys) {
        super(context);

        mContext = context;
        mFiles = files;
        mKeys = keys;

        final WindowManager wm =
                (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        final WallpaperManager wpm =
                (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
        final Display d = wm.getDefaultDisplay();
        final Point size = new Point();
        d.getSize(size);
        mDesiredMinWidth = Math.min(size.x, size.y);
        mDesiredMinHeight = (double) wpm.getDesiredMinimumHeight();
        if (mDesiredMinHeight <= 0) {
            mDesiredMinHeight = size.y;
        }

        if (DEBUG) {
            Slog.d(TAG, "dmW=" + mDesiredMinWidth + " dmH=" + mDesiredMinHeight);
        }
        mWpm = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
    }

    /**
@@ -126,13 +76,12 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu
    @Override
    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
            ParcelFileDescriptor newState) {
        performBackup_checked(oldState, data, newState, mFiles, mKeys);
        // Intentionally no-op; we don't back up the wallpaper this way any more.
    }

    /**
     * Restore one absolute file entity from the restore stream.  If we're restoring the
     * magic wallpaper file, take specific action to determine whether it is suitable for
     * the current device.
     * magic wallpaper file, apply it as the system wallpaper.
     */
    @Override
    public void restoreEntity(BackupDataInputStream data) {
@@ -140,69 +89,21 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu
        if (isKeyInList(key, mKeys)) {
            if (key.equals(WALLPAPER_IMAGE_KEY)) {
                // restore the file to the stage for inspection
                File f = new File(STAGE_FILE);
                if (writeFile(f, data)) {

                    // Preflight the restored image's dimensions without loading it
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inJustDecodeBounds = true;
                    BitmapFactory.decodeFile(STAGE_FILE, options);

                    if (DEBUG) Slog.d(TAG, "Restoring wallpaper image w=" + options.outWidth
                            + " h=" + options.outHeight);

                    if (REJECT_OUTSIZED_RESTORE) {
                        // We accept any wallpaper that is at least as wide as our preference
                        // (i.e. wide enough to fill the screen), and is within a comfortable
                        // factor of the target height, to avoid significant clipping/scaling/
                        // letterboxing.  At this point we know that mDesiredMinWidth is the
                        // smallest dimension, regardless of current orientation, so we can
                        // safely require that the candidate's width and height both exceed
                        // that hard minimum.
                        final double heightRatio = mDesiredMinHeight / options.outHeight;
                        if (options.outWidth < mDesiredMinWidth
                                || options.outHeight < mDesiredMinWidth
                                || heightRatio >= MAX_HEIGHT_RATIO
                                || heightRatio <= MIN_HEIGHT_RATIO) {
                            // Not wide enough for the screen, or too short/tall to be a good fit
                            // for the height of the screen, broken image file, or the system's
                            // desires for wallpaper size are in a bad state.  Probably one of the
                            // first two.
                            Slog.i(TAG, "Restored image dimensions (w="
                                    + options.outWidth + ", h=" + options.outHeight
                                    + ") too far off target (tw="
                                    + mDesiredMinWidth + ", th=" + mDesiredMinHeight
                                    + "); falling back to default wallpaper.");
                            f.delete();
                            return;
                File stage = new File(STAGE_FILE);
                try {
                    if (writeFile(stage, data)) {
                        try (FileInputStream in = new FileInputStream(stage)) {
                            mWpm.setStream(in);
                        } catch (IOException e) {
                            Slog.e(TAG, "Unable to set restored wallpaper: " + e.getMessage());
                        }
                    } else {
                        Slog.e(TAG, "Unable to save restored wallpaper");
                    }

                    // We passed the acceptable-dimensions test (if any), so we're going to
                    // use the restored image.  That comes last, when we are done restoring
                    // both the pixels and the metadata.
                }
            } else if (key.equals(WALLPAPER_INFO_KEY)) {
                // XML file containing wallpaper info
                File f = new File(WALLPAPER_INFO);
                writeFile(f, data);
                } finally {
                    stage.delete();
                }
            }
        }

    /**
     * Hook for the agent to call this helper upon completion of the restore.  We do this
     * upon completion so that we know both the imagery and the wallpaper info have
     * been emplaced without requiring either or relying on ordering.
     */
    public void onRestoreFinished() {
        final File f = new File(STAGE_FILE);
        if (f.exists()) {
            // TODO: spin a service to copy the restored image to sd/usb storage,
            // since it does not exist anywhere other than the private wallpaper
            // file.
            Slog.d(TAG, "Applying restored wallpaper image.");
            f.renameTo(new File(WALLPAPER_ORIG_IMAGE));
        }
    }
}
+13 −34
Original line number Diff line number Diff line
@@ -35,7 +35,8 @@ import java.io.File;
import java.io.IOException;

/**
 * Backup agent for various system-managed data, currently just the system wallpaper
 * Backup agent for various system-managed data.  Wallpapers are now handled by a
 * separate package, but we still process restores from legacy datasets here.
 */
public class SystemBackupAgent extends BackupAgentHelper {
    private static final String TAG = "SystemBackupAgent";
@@ -61,16 +62,19 @@ public class SystemBackupAgent extends BackupAgentHelper {
    // TODO: http://b/22388012
    private static final String WALLPAPER_IMAGE_DIR =
            Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM).getAbsolutePath();
    private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE;
    public static final String WALLPAPER_IMAGE =
            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
                    "wallpaper").getAbsolutePath();

    // TODO: Will need to change if backing up non-primary user's wallpaper
    // TODO: http://b/22388012
    private static final String WALLPAPER_INFO_DIR =
            Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM).getAbsolutePath();
    private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO;
    public static final String WALLPAPER_INFO =
            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
                    "wallpaper_info.xml").getAbsolutePath();
    // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
    private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
    private static final String WALLPAPER_INFO_KEY = WallpaperBackupHelper.WALLPAPER_INFO_KEY;

    private WallpaperBackupHelper mWallpaperHelper = null;

@@ -98,13 +102,11 @@ public class SystemBackupAgent extends BackupAgentHelper {
        // Slot in a restore helper for the older wallpaper backup schema to support restore
        // from devices still generating data in that format.
        mWallpaperHelper = new WallpaperBackupHelper(this,
                new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO },
                new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} );
                new String[] { WALLPAPER_IMAGE_KEY} );
        addHelper(WALLPAPER_HELPER, mWallpaperHelper);

        // On restore, we also support a long-ago wallpaper data schema "system_files"
        addHelper("system_files", new WallpaperBackupHelper(this,
                new String[] { WALLPAPER_IMAGE },
                new String[] { WALLPAPER_IMAGE_KEY} ));

        addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
@@ -115,27 +117,12 @@ public class SystemBackupAgent extends BackupAgentHelper {
        addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
        addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());

        try {
        super.onRestore(data, appVersionCode, newState);

            IWallpaperManager wallpaper = (IWallpaperManager) ServiceManager.getService(
                    Context.WALLPAPER_SERVICE);
            if (wallpaper != null) {
                try {
                    wallpaper.settingsRestored();
                } catch (RemoteException re) {
                    Slog.e(TAG, "Couldn't restore settings\n" + re);
                }
            }
        } catch (IOException ex) {
            // If there was a failure, delete everything for the wallpaper, this is too aggressive,
            // but this is hopefully a rare failure.
            Slog.d(TAG, "restore failed", ex);
            (new File(WALLPAPER_IMAGE)).delete();
            (new File(WALLPAPER_INFO)).delete();
        }
    }

    /**
     * Support for 'adb restore' of legacy archives
     */
    @Override
    public void onRestoreFile(ParcelFileDescriptor data, long size,
            int type, String domain, String path, long mode, long mtime)
@@ -183,12 +170,4 @@ public class SystemBackupAgent extends BackupAgentHelper {
            }
        }
    }

    @Override
    public void onRestoreFinished() {
        // helper will be null following 'adb restore' or other full-data operation
        if (mWallpaperHelper != null) {
            mWallpaperHelper.onRestoreFinished();
        }
    }
}
+33 −13
Original line number Diff line number Diff line
@@ -2307,16 +2307,36 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
    }

    private void migrateFromOld() {
        File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
        File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
        if (oldWallpaper.exists()) {
        // 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);
            oldWallpaper.renameTo(newWallpaper);

        // 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(XmlPullParser parser, String name, int defValue) {
@@ -2376,12 +2396,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
        JournaledFile journal = makeJournaledFile(userId);
        FileInputStream stream = null;
        File file = journal.chooseForRead();
        if (!file.exists()) {
            // This should only happen one time, when upgrading from a legacy system
            migrateFromOld();
        }

        WallpaperData wallpaper = mWallpaperMap.get(userId);
        if (wallpaper == null) {
            // Do this once per boot
            migrateFromOld();

            wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
            wallpaper.allowBackup = true;
            mWallpaperMap.put(userId, wallpaper);