1 /*
2  * Copyright (C) 2017 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 
17 package com.android.server.backup.fullbackup;
18 
19 import static com.android.server.backup.BackupManagerService.DEBUG;
20 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
21 import static com.android.server.backup.BackupManagerService.TAG;
22 import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
23 import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
24 import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
25 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
26 
27 import android.annotation.UserIdInt;
28 import android.app.ApplicationThreadConstants;
29 import android.app.IBackupAgent;
30 import android.app.backup.BackupTransport;
31 import android.app.backup.FullBackupDataOutput;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.PackageInfo;
34 import android.content.pm.PackageManager;
35 import android.os.ParcelFileDescriptor;
36 import android.os.RemoteException;
37 import android.util.Slog;
38 
39 import com.android.server.AppWidgetBackupBridge;
40 import com.android.server.backup.BackupAgentTimeoutParameters;
41 import com.android.server.backup.BackupRestoreTask;
42 import com.android.server.backup.UserBackupManagerService;
43 import com.android.server.backup.remote.RemoteCall;
44 import com.android.server.backup.utils.BackupEligibilityRules;
45 import com.android.server.backup.utils.FullBackupUtils;
46 
47 import java.io.File;
48 import java.io.IOException;
49 import java.io.OutputStream;
50 import java.util.Objects;
51 
52 /**
53  * Core logic for performing one package's full backup, gathering the tarball from the application
54  * and emitting it to the designated OutputStream.
55  */
56 public class FullBackupEngine {
57     private UserBackupManagerService backupManagerService;
58     private OutputStream mOutput;
59     private FullBackupPreflight mPreflightHook;
60     private BackupRestoreTask mTimeoutMonitor;
61     private IBackupAgent mAgent;
62     private boolean mIncludeApks;
63     private PackageInfo mPkg;
64     private final long mQuota;
65     private final int mOpToken;
66     private final int mTransportFlags;
67     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
68     private final BackupEligibilityRules mBackupEligibilityRules;
69 
70     class FullBackupRunner implements Runnable {
71         private final @UserIdInt int mUserId;
72         private final PackageManager mPackageManager;
73         private final PackageInfo mPackage;
74         private final IBackupAgent mAgent;
75         private final ParcelFileDescriptor mPipe;
76         private final int mToken;
77         private final boolean mIncludeApks;
78         private final File mFilesDir;
79 
FullBackupRunner( UserBackupManagerService userBackupManagerService, PackageInfo packageInfo, IBackupAgent agent, ParcelFileDescriptor pipe, int token, boolean includeApks)80         FullBackupRunner(
81                 UserBackupManagerService userBackupManagerService,
82                 PackageInfo packageInfo,
83                 IBackupAgent agent,
84                 ParcelFileDescriptor pipe,
85                 int token,
86                 boolean includeApks)
87                 throws IOException {
88             mUserId = userBackupManagerService.getUserId();
89             mPackageManager = backupManagerService.getPackageManager();
90             mPackage = packageInfo;
91             mAgent = agent;
92             mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
93             mToken = token;
94             mIncludeApks = includeApks;
95             mFilesDir = userBackupManagerService.getDataDir();
96         }
97 
98         @Override
run()99         public void run() {
100             try {
101                 FullBackupDataOutput output =
102                         new FullBackupDataOutput(mPipe, /* quota */ -1, mTransportFlags);
103                 AppMetadataBackupWriter appMetadataBackupWriter =
104                         new AppMetadataBackupWriter(output, mPackageManager);
105 
106                 String packageName = mPackage.packageName;
107                 boolean isSharedStorage = SHARED_BACKUP_AGENT_PACKAGE.equals(packageName);
108                 boolean writeApk =
109                         shouldWriteApk(mPackage.applicationInfo, mIncludeApks, isSharedStorage);
110 
111                 if (!isSharedStorage) {
112                     if (MORE_DEBUG) {
113                         Slog.d(TAG, "Writing manifest for " + packageName);
114                     }
115 
116                     File manifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
117                     appMetadataBackupWriter.backupManifest(
118                             mPackage, manifestFile, mFilesDir, writeApk);
119                     manifestFile.delete();
120 
121                     // Write widget data.
122                     byte[] widgetData =
123                             AppWidgetBackupBridge.getWidgetState(packageName, mUserId);
124                     if (widgetData != null && widgetData.length > 0) {
125                         File metadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
126                         appMetadataBackupWriter.backupWidget(
127                                 mPackage, metadataFile, mFilesDir, widgetData);
128                         metadataFile.delete();
129                     }
130                 }
131 
132                 // TODO(b/113807190): Look into removing, only used for 'adb backup'.
133                 if (writeApk) {
134                     appMetadataBackupWriter.backupApk(mPackage);
135                     appMetadataBackupWriter.backupObb(mUserId, mPackage);
136                 }
137 
138                 if (DEBUG) {
139                     Slog.d(TAG, "Calling doFullBackup() on " + packageName);
140                 }
141 
142                 long timeout =
143                         isSharedStorage
144                                 ? mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis()
145                                 : mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
146                 backupManagerService.prepareOperationTimeout(
147                         mToken,
148                         timeout,
149                         mTimeoutMonitor /* in parent class */,
150                         OP_TYPE_BACKUP_WAIT);
151                 mAgent.doFullBackup(
152                         mPipe,
153                         mQuota,
154                         mToken,
155                         backupManagerService.getBackupManagerBinder(),
156                         mTransportFlags);
157             } catch (IOException e) {
158                 Slog.e(TAG, "Error running full backup for " + mPackage.packageName, e);
159             } catch (RemoteException e) {
160                 Slog.e(
161                         TAG,
162                         "Remote agent vanished during full backup of " + mPackage.packageName,
163                         e);
164             } finally {
165                 try {
166                     mPipe.close();
167                 } catch (IOException e) {
168                 }
169             }
170         }
171 
172         /**
173          * Don't write apks for system-bundled apps that are not upgraded.
174          */
shouldWriteApk( ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage)175         private boolean shouldWriteApk(
176                 ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) {
177             boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
178             boolean isUpdatedSystemApp =
179                     (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
180             return includeApks
181                     && !isSharedStorage
182                     && (!isSystemApp || isUpdatedSystemApp);
183         }
184     }
185 
FullBackupEngine( UserBackupManagerService backupManagerService, OutputStream output, FullBackupPreflight preflightHook, PackageInfo pkg, boolean alsoApks, BackupRestoreTask timeoutMonitor, long quota, int opToken, int transportFlags, BackupEligibilityRules backupEligibilityRules)186     public FullBackupEngine(
187             UserBackupManagerService backupManagerService,
188             OutputStream output,
189             FullBackupPreflight preflightHook,
190             PackageInfo pkg,
191             boolean alsoApks,
192             BackupRestoreTask timeoutMonitor,
193             long quota,
194             int opToken,
195             int transportFlags,
196             BackupEligibilityRules backupEligibilityRules) {
197         this.backupManagerService = backupManagerService;
198         mOutput = output;
199         mPreflightHook = preflightHook;
200         mPkg = pkg;
201         mIncludeApks = alsoApks;
202         mTimeoutMonitor = timeoutMonitor;
203         mQuota = quota;
204         mOpToken = opToken;
205         mTransportFlags = transportFlags;
206         mAgentTimeoutParameters =
207                 Objects.requireNonNull(
208                         backupManagerService.getAgentTimeoutParameters(),
209                         "Timeout parameters cannot be null");
210         mBackupEligibilityRules = backupEligibilityRules;
211     }
212 
preflightCheck()213     public int preflightCheck() throws RemoteException {
214         if (mPreflightHook == null) {
215             if (MORE_DEBUG) {
216                 Slog.v(TAG, "No preflight check");
217             }
218             return BackupTransport.TRANSPORT_OK;
219         }
220         if (initializeAgent()) {
221             int result = mPreflightHook.preflightFullBackup(mPkg, mAgent);
222             if (MORE_DEBUG) {
223                 Slog.v(TAG, "preflight returned " + result);
224             }
225             return result;
226         } else {
227             Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
228             return BackupTransport.AGENT_ERROR;
229         }
230     }
231 
backupOnePackage()232     public int backupOnePackage() throws RemoteException {
233         int result = BackupTransport.AGENT_ERROR;
234 
235         if (initializeAgent()) {
236             ParcelFileDescriptor[] pipes = null;
237             try {
238                 pipes = ParcelFileDescriptor.createPipe();
239 
240                 FullBackupRunner runner =
241                         new FullBackupRunner(
242                                 backupManagerService,
243                                 mPkg,
244                                 mAgent,
245                                 pipes[1],
246                                 mOpToken,
247                                 mIncludeApks);
248                 pipes[1].close(); // the runner has dup'd it
249                 pipes[1] = null;
250                 Thread t = new Thread(runner, "app-data-runner");
251                 t.start();
252 
253                 FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput);
254 
255                 if (!backupManagerService.waitUntilOperationComplete(mOpToken)) {
256                     Slog.e(TAG, "Full backup failed on package " + mPkg.packageName);
257                 } else {
258                     if (MORE_DEBUG) {
259                         Slog.d(TAG, "Full package backup success: " + mPkg.packageName);
260                     }
261                     result = BackupTransport.TRANSPORT_OK;
262                 }
263             } catch (IOException e) {
264                 Slog.e(TAG, "Error backing up " + mPkg.packageName + ": " + e.getMessage());
265                 result = BackupTransport.AGENT_ERROR;
266             } finally {
267                 try {
268                     // flush after every package
269                     mOutput.flush();
270                     if (pipes != null) {
271                         if (pipes[0] != null) {
272                             pipes[0].close();
273                         }
274                         if (pipes[1] != null) {
275                             pipes[1].close();
276                         }
277                     }
278                 } catch (IOException e) {
279                     Slog.w(TAG, "Error bringing down backup stack");
280                     result = BackupTransport.TRANSPORT_ERROR;
281                 }
282             }
283         } else {
284             Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
285         }
286         tearDown();
287         return result;
288     }
289 
sendQuotaExceeded(long backupDataBytes, long quotaBytes)290     public void sendQuotaExceeded(long backupDataBytes, long quotaBytes) {
291         if (initializeAgent()) {
292             try {
293                 RemoteCall.execute(
294                         callback -> mAgent.doQuotaExceeded(backupDataBytes, quotaBytes, callback),
295                         mAgentTimeoutParameters.getQuotaExceededTimeoutMillis());
296             } catch (RemoteException e) {
297                 Slog.e(TAG, "Remote exception while telling agent about quota exceeded");
298             }
299         }
300     }
301 
initializeAgent()302     private boolean initializeAgent() {
303         if (mAgent == null) {
304             if (MORE_DEBUG) {
305                 Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
306             }
307             mAgent =
308                     backupManagerService.bindToAgentSynchronous(
309                             mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL,
310                             mBackupEligibilityRules.getOperationType());
311         }
312         return mAgent != null;
313     }
314 
tearDown()315     private void tearDown() {
316         if (mPkg != null) {
317             backupManagerService.tearDownAgentAndKill(mPkg.applicationInfo);
318         }
319     }
320 }
321