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