blob: 2f79079c9188ad8cc191e1896bb119393a6a0c15 [file] [log] [blame]
Christopher Tated7faf532016-02-25 12:43:38 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.wallpaperbackup;
18
19import android.app.WallpaperManager;
20import android.app.backup.BackupAgent;
21import android.app.backup.BackupDataInput;
22import android.app.backup.BackupDataOutput;
23import android.app.backup.FullBackupDataOutput;
24import android.content.Context;
25import android.os.Environment;
26import android.os.ParcelFileDescriptor;
27import android.os.UserHandle;
28import android.system.Os;
29import android.util.Slog;
30
31import java.io.File;
32import java.io.FileOutputStream;
33import java.io.IOException;
34
35public class WallpaperBackupAgent extends BackupAgent {
36 private static final String TAG = "WallpaperBackup";
37 private static final boolean DEBUG = false;
38
39 // NB: must be kept in sync with WallpaperManagerService but has no
40 // compile-time visiblity.
41
42 // Target filenames within the system's wallpaper directory
43 static final String WALLPAPER = "wallpaper_orig";
44 static final String WALLPAPER_INFO = "wallpaper_info.xml";
45
46 // Names of our local-data stage files/links
47 static final String IMAGE_STAGE = "wallpaper-stage";
48 static final String INFO_STAGE = "wallpaper-info-stage";
49 static final String EMPTY_SENTINEL = "empty";
50
51 private File mWallpaperInfo; // wallpaper metadata file
52 private File mWallpaperFile; // primary wallpaper image file
53
54 private WallpaperManager mWm;
55
56 @Override
57 public void onCreate() {
58 if (DEBUG) {
59 Slog.v(TAG, "onCreate()");
60 }
61
62 File wallpaperDir = Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM);
63 mWallpaperInfo = new File(wallpaperDir, WALLPAPER_INFO);
64 mWallpaperFile = new File(wallpaperDir, WALLPAPER);
65 mWm = (WallpaperManager) getSystemService(Context.WALLPAPER_SERVICE);
66 }
67
68 @Override
69 public void onFullBackup(FullBackupDataOutput data) throws IOException {
70 // To avoid data duplication and disk churn, use links as the stage.
71 final File filesDir = getFilesDir();
72 final File infoStage = new File(filesDir, INFO_STAGE);
73 final File imageStage = new File (filesDir, IMAGE_STAGE);
74 final File empty = new File (filesDir, EMPTY_SENTINEL);
75
76 try {
77 // We always back up this 'empty' file to ensure that the absence of
78 // storable wallpaper imagery still produces a non-empty backup data
79 // stream, otherwise it'd simply be ignored in preflight.
80 FileOutputStream touch = new FileOutputStream(empty);
81 touch.close();
82 fullBackupFile(empty, data);
83
84 // only back up the wallpaper if we've been told it's allowed
85 if (mWm.isWallpaperBackupEligible()) {
86 if (DEBUG) {
87 Slog.v(TAG, "Wallpaper is backup-eligible; linking & writing");
88 }
89 Os.link(mWallpaperInfo.getCanonicalPath(), infoStage.getCanonicalPath());
90 fullBackupFile(infoStage, data);
91 Os.link(mWallpaperFile.getCanonicalPath(), imageStage.getCanonicalPath());
92 fullBackupFile(imageStage, data);
93 } else {
94 if (DEBUG) {
95 Slog.v(TAG, "Wallpaper not backup-eligible; writing no data");
96 }
97 }
98 } catch (Exception e) {
99 Slog.e(TAG, "Unable to back up wallpaper: " + e.getMessage());
100 } finally {
101 if (DEBUG) {
102 Slog.v(TAG, "Removing backup stage links");
103 }
104 infoStage.delete();
105 imageStage.delete();
106 }
107 }
108
109 // We use the default onRestoreFile() implementation that will recreate our stage files,
110 // then postprocess in onRestoreFinished() to move them on top of the live data.
111 //
112 // NOTE: this relies on our local files dir being on the same filesystem as the live
113 // system wallpaper data. If this is not the case then an actual copy operation will
114 // be needed.
115 @Override
116 public void onRestoreFinished() {
117 if (DEBUG) {
118 Slog.v(TAG, "onRestoreFinished()");
119 }
120 final File infoStage = new File(getFilesDir(), INFO_STAGE);
121 final File imageStage = new File (getFilesDir(), IMAGE_STAGE);
122
123 try {
124 // It is valid for the imagery to be absent; it means that we were not permitted
125 // to back up the original image on the source device.
126 if (imageStage.exists()) {
127 if (DEBUG) {
128 Slog.v(TAG, "Got restored wallpaper; renaming into place");
129 }
130 // Rename the image file into place last because that is the trigger for
131 // the wallpaper observer to generate a new crop/scale
132 Os.rename(infoStage.getCanonicalPath(), mWallpaperInfo.getCanonicalPath());
133 Os.rename(imageStage.getCanonicalPath(), mWallpaperFile.getCanonicalPath());
134 }
135 } catch (Exception e) {
136 Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage());
137 mWm.clearWallpaper(WallpaperManager.FLAG_SYSTEM, UserHandle.USER_SYSTEM);
138 } finally {
139 // These "should" not exist because of the renames, but make sure
140 // in case of errors/exceptions/etc.
141 if (DEBUG) {
142 Slog.v(TAG, "Removing restore stage files");
143 }
144 infoStage.delete();
145 imageStage.delete();
146 }
147 }
148
149 //
150 // Key/value API: abstract, so required, but not used
151 //
152
153 @Override
154 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
155 ParcelFileDescriptor newState) throws IOException {
156 // Intentionally blank
157 }
158
159 @Override
160 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
161 throws IOException {
162 // Intentionally blank
163 }
164}