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