13f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate/*
23f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * Copyright (C) 2010 The Android Open Source Project
33f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate *
43f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * Licensed under the Apache License, Version 2.0 (the "License");
53f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * you may not use this file except in compliance with the License.
63f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * You may obtain a copy of the License at
73f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate *
83f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate *      http://www.apache.org/licenses/LICENSE-2.0
93f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate *
103f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * Unless required by applicable law or agreed to in writing, software
113f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * distributed under the License is distributed on an "AS IS" BASIS,
123f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * See the License for the specific language governing permissions and
143f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * limitations under the License.
153f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate */
163f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
173f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tatepackage android.app.backup;
183f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
193f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tateimport android.app.WallpaperManager;
203f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tateimport android.content.Context;
213f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tateimport android.graphics.BitmapFactory;
2244bc17c6b517aef35a390c81b5aa79c4f284f744Dianne Hackbornimport android.graphics.Point;
2361f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasaniimport android.os.Environment;
243f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tateimport android.os.ParcelFileDescriptor;
2561f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasaniimport android.os.UserHandle;
263f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tateimport android.util.Slog;
277f765cf588e37a70fa2a1d251aaa8e7b847801b6Christopher Tateimport android.view.Display;
287f765cf588e37a70fa2a1d251aaa8e7b847801b6Christopher Tateimport android.view.WindowManager;
293f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
303f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tateimport java.io.File;
313f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
323f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate/**
333f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * Helper for backing up / restoring wallpapers.  Basically an AbsoluteFileBackupHelper,
343f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * but with logic for deciding what to do with restored wallpaper images.
353f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate *
363f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate * @hide
373f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate */
383f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tatepublic class WallpaperBackupHelper extends FileBackupHelperBase implements BackupHelper {
393f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    private static final String TAG = "WallpaperBackupHelper";
40bf6ee4f509cbe7a44f4cc72f28e6150ca47c066dChristopher Tate    private static final boolean DEBUG = false;
413f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
42406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate    // If 'true', then apply an acceptable-size heuristic at restore time, dropping back
43406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate    // to the factory default wallpaper if the restored one differs "too much" from the
44406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate    // device's preferred wallpaper image dimensions.
45431906b34f33e23b5407fc2f72f8c085ef572816Christopher Tate    private static final boolean REJECT_OUTSIZED_RESTORE = true;
46406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate
471133e29f22252cc36102f41204c3de35779e49d2Christopher Tate    // When outsized restore rejection is enabled, this is the maximum ratio between the
481133e29f22252cc36102f41204c3de35779e49d2Christopher Tate    // source and target image heights that will be permitted.  The ratio is checked both
491133e29f22252cc36102f41204c3de35779e49d2Christopher Tate    // ways (i.e. >= MAX, or <= 1/MAX) to validate restores from both largeer-than-target
501133e29f22252cc36102f41204c3de35779e49d2Christopher Tate    // and smaller-than-target sources.
511133e29f22252cc36102f41204c3de35779e49d2Christopher Tate    private static final double MAX_HEIGHT_RATIO = 1.35;
521133e29f22252cc36102f41204c3de35779e49d2Christopher Tate
531133e29f22252cc36102f41204c3de35779e49d2Christopher Tate    // The height ratio check when applying larger images on smaller screens is separate;
541133e29f22252cc36102f41204c3de35779e49d2Christopher Tate    // in current policy we accept any such restore regardless of the relative dimensions.
551133e29f22252cc36102f41204c3de35779e49d2Christopher Tate    private static final double MIN_HEIGHT_RATIO = 0;
561133e29f22252cc36102f41204c3de35779e49d2Christopher Tate
573f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    // This path must match what the WallpaperManagerService uses
5837ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani    // TODO: Will need to change if backing up non-primary user's wallpaper
5961f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani    public static final String WALLPAPER_IMAGE =
6061f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani            new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
6161f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani                    "wallpaper").getAbsolutePath();
6261f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani    public static final String WALLPAPER_INFO =
6361f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani            new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
6461f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani                    "wallpaper_info.xml").getAbsolutePath();
6537ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani    // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
6637ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani    public static final String WALLPAPER_IMAGE_KEY =
6737ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani            "/data/data/com.android.settings/files/wallpaper";
6837ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani    public static final String WALLPAPER_INFO_KEY = "/data/system/wallpaper_info.xml";
693f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
703f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    // Stage file - should be adjacent to the WALLPAPER_IMAGE location.  The wallpapers
713f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    // will be saved to this file from the restore stream, then renamed to the proper
723f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    // location if it's deemed suitable.
7337ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani    // TODO: Will need to change if backing up non-primary user's wallpaper
7461f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani    private static final String STAGE_FILE =
7561f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani            new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
7661f57379ca2c5b6290c8da7548fa17128f7ab24fAmith Yamasani                    "wallpaper-tmp").getAbsolutePath();
773f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
783f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    Context mContext;
793f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    String[] mFiles;
8037ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani    String[] mKeys;
813f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    double mDesiredMinWidth;
823f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    double mDesiredMinHeight;
833f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
843f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    /**
853f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * Construct a helper for backing up / restoring the files at the given absolute locations
863f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * within the file system.
873f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     *
883f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * @param context
893f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * @param files
903f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     */
9137ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani    public WallpaperBackupHelper(Context context, String[] files, String[] keys) {
923f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate        super(context);
933f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
943f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate        mContext = context;
953f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate        mFiles = files;
9637ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani        mKeys = keys;
973f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
98004c16654f2f86ff3055f3c88b5498eb3254f2e1Christopher Tate        final WindowManager wm =
99004c16654f2f86ff3055f3c88b5498eb3254f2e1Christopher Tate                (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
100004c16654f2f86ff3055f3c88b5498eb3254f2e1Christopher Tate        final WallpaperManager wpm =
101004c16654f2f86ff3055f3c88b5498eb3254f2e1Christopher Tate                (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
102004c16654f2f86ff3055f3c88b5498eb3254f2e1Christopher Tate        final Display d = wm.getDefaultDisplay();
103004c16654f2f86ff3055f3c88b5498eb3254f2e1Christopher Tate        final Point size = new Point();
104004c16654f2f86ff3055f3c88b5498eb3254f2e1Christopher Tate        d.getSize(size);
10531f25696d950dd54e8b339074b98ad6335738f2fChristopher Tate        mDesiredMinWidth = Math.min(size.x, size.y);
1063f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate        mDesiredMinHeight = (double) wpm.getDesiredMinimumHeight();
107004c16654f2f86ff3055f3c88b5498eb3254f2e1Christopher Tate        if (mDesiredMinHeight <= 0) {
10844bc17c6b517aef35a390c81b5aa79c4f284f744Dianne Hackborn            mDesiredMinHeight = size.y;
1097f765cf588e37a70fa2a1d251aaa8e7b847801b6Christopher Tate        }
1107f765cf588e37a70fa2a1d251aaa8e7b847801b6Christopher Tate
11100724cacc8292900c3be8657ea9b3b6284cf4877Christopher Tate        if (DEBUG) {
11200724cacc8292900c3be8657ea9b3b6284cf4877Christopher Tate            Slog.d(TAG, "dmW=" + mDesiredMinWidth + " dmH=" + mDesiredMinHeight);
11300724cacc8292900c3be8657ea9b3b6284cf4877Christopher Tate        }
1143f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    }
1153f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
1163f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    /**
1173f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * Based on oldState, determine which of the files from the application's data directory
1183f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * need to be backed up, write them to the data stream, and fill in newState with the
1193f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * state as it exists now.
1203f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     */
1213f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
1223f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate            ParcelFileDescriptor newState) {
12337ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani        performBackup_checked(oldState, data, newState, mFiles, mKeys);
1243f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    }
1253f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
1263f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    /**
1273f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * Restore one absolute file entity from the restore stream.  If we're restoring the
1283f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * magic wallpaper file, take specific action to determine whether it is suitable for
1293f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     * the current device.
1303f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate     */
1313f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    public void restoreEntity(BackupDataInputStream data) {
1323f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate        final String key = data.getKey();
13337ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani        if (isKeyInList(key, mKeys)) {
13437ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani            if (key.equals(WALLPAPER_IMAGE_KEY)) {
1353f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                // restore the file to the stage for inspection
1363f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                File f = new File(STAGE_FILE);
1373f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                if (writeFile(f, data)) {
1383f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
1393f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                    // Preflight the restored image's dimensions without loading it
1403f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                    BitmapFactory.Options options = new BitmapFactory.Options();
1413f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                    options.inJustDecodeBounds = true;
1423f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                    BitmapFactory.decodeFile(STAGE_FILE, options);
1433f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
14400724cacc8292900c3be8657ea9b3b6284cf4877Christopher Tate                    if (DEBUG) Slog.d(TAG, "Restoring wallpaper image w=" + options.outWidth
1453f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                            + " h=" + options.outHeight);
1463f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate
147406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                    if (REJECT_OUTSIZED_RESTORE) {
148406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                        // We accept any wallpaper that is at least as wide as our preference
149406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                        // (i.e. wide enough to fill the screen), and is within a comfortable
150406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                        // factor of the target height, to avoid significant clipping/scaling/
15131f25696d950dd54e8b339074b98ad6335738f2fChristopher Tate                        // letterboxing.  At this point we know that mDesiredMinWidth is the
15231f25696d950dd54e8b339074b98ad6335738f2fChristopher Tate                        // smallest dimension, regardless of current orientation, so we can
15331f25696d950dd54e8b339074b98ad6335738f2fChristopher Tate                        // safely require that the candidate's width and height both exceed
15431f25696d950dd54e8b339074b98ad6335738f2fChristopher Tate                        // that hard minimum.
155406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                        final double heightRatio = mDesiredMinHeight / options.outHeight;
156406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                        if (options.outWidth < mDesiredMinWidth
15731f25696d950dd54e8b339074b98ad6335738f2fChristopher Tate                                || options.outHeight < mDesiredMinWidth
1581133e29f22252cc36102f41204c3de35779e49d2Christopher Tate                                || heightRatio >= MAX_HEIGHT_RATIO
1591133e29f22252cc36102f41204c3de35779e49d2Christopher Tate                                || heightRatio <= MIN_HEIGHT_RATIO) {
160406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                            // Not wide enough for the screen, or too short/tall to be a good fit
161406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                            // for the height of the screen, broken image file, or the system's
162406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                            // desires for wallpaper size are in a bad state.  Probably one of the
163406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                            // first two.
164406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                            Slog.i(TAG, "Restored image dimensions (w="
165406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                                    + options.outWidth + ", h=" + options.outHeight
166406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                                    + ") too far off target (tw="
167406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                                    + mDesiredMinWidth + ", th=" + mDesiredMinHeight
168406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                                    + "); falling back to default wallpaper.");
169406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                            f.delete();
170406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                            return;
171406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                        }
1723f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                    }
173406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate
174406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                    // We passed the acceptable-dimensions test (if any), so we're going to
175406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                    // use the restored image.
176406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                    // TODO: spin a service to copy the restored image to sd/usb storage,
177406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                    // since it does not exist anywhere other than the private wallpaper
178406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                    // file.
179406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                    Slog.d(TAG, "Applying restored wallpaper image.");
180406abd45471c523aea151f5aca84b008fee02bbeChristopher Tate                    f.renameTo(new File(WALLPAPER_IMAGE));
1813f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                }
18237ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani            } else if (key.equals(WALLPAPER_INFO_KEY)) {
18337ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani                // XML file containing wallpaper info
18437ce3a8af6faab675319d0803b288ab1dddc76beAmith Yamasani                File f = new File(WALLPAPER_INFO);
1853f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate                writeFile(f, data);
1863f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate            }
1873f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate        }
1883f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate    }
1893f64f8d8fc05189777e83b4efd3882cbc661fdebChristopher Tate}
190