• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.TAG;
21 import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
22 import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
23 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
24 
25 import android.annotation.UserIdInt;
26 import android.app.ApplicationThreadConstants;
27 import android.app.IBackupAgent;
28 import android.app.backup.BackupManagerMonitor;
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 (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                 Slog.d(TAG, "Calling doFullBackup() on " + packageName);
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                         OpType.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, BackupManagerMonitorEventSender backupManagerMonitorEventSender)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             BackupManagerMonitorEventSender backupManagerMonitorEventSender) {
198         this.backupManagerService = backupManagerService;
199         mOutput = output;
200         mPreflightHook = preflightHook;
201         mPkg = pkg;
202         mIncludeApks = alsoApks;
203         mTimeoutMonitor = timeoutMonitor;
204         mQuota = quota;
205         mOpToken = opToken;
206         mTransportFlags = transportFlags;
207         mAgentTimeoutParameters =
208                 Objects.requireNonNull(
209                         backupManagerService.getAgentTimeoutParameters(),
210                         "Timeout parameters cannot be null");
211         mBackupEligibilityRules = backupEligibilityRules;
212         mBackupManagerMonitorEventSender = backupManagerMonitorEventSender;
213     }
214 
preflightCheck()215     public int preflightCheck() throws RemoteException {
216         if (mPreflightHook == null) {
217             if (DEBUG) {
218                 Slog.v(TAG, "No preflight check");
219             }
220             return BackupTransport.TRANSPORT_OK;
221         }
222         if (initializeAgent()) {
223             int result = mPreflightHook.preflightFullBackup(mPkg, mAgent);
224             if (DEBUG) {
225                 Slog.v(TAG, "preflight returned " + result);
226             }
227 
228             // Clear any logs generated during preflight
229             mAgent.clearBackupRestoreEventLogger();
230             return result;
231         } else {
232             Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
233             return BackupTransport.AGENT_ERROR;
234         }
235     }
236 
backupOnePackage()237     public int backupOnePackage() throws RemoteException {
238         int result = BackupTransport.AGENT_ERROR;
239 
240         if (initializeAgent()) {
241             ParcelFileDescriptor[] pipes = null;
242             try {
243                 pipes = ParcelFileDescriptor.createPipe();
244 
245                 FullBackupRunner runner =
246                         new FullBackupRunner(
247                                 backupManagerService,
248                                 mPkg,
249                                 mAgent,
250                                 pipes[1],
251                                 mOpToken,
252                                 mIncludeApks);
253                 pipes[1].close(); // the runner has dup'd it
254                 pipes[1] = null;
255                 Thread t = new Thread(runner, "app-data-runner");
256                 t.start();
257 
258                 FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput);
259 
260                 if (!backupManagerService.waitUntilOperationComplete(mOpToken)) {
261                     Slog.e(TAG, "Full backup failed on package " + mPkg.packageName);
262                 } else {
263                     if (DEBUG) {
264                         Slog.d(TAG, "Full package backup success: " + mPkg.packageName);
265                     }
266                     result = BackupTransport.TRANSPORT_OK;
267                 }
268 
269                 mBackupManagerMonitorEventSender.monitorAgentLoggingResults(mPkg, mAgent);
270             } catch (IOException e) {
271                 Slog.e(TAG, "Error backing up " + mPkg.packageName + ": " + e.getMessage());
272                 // This is likely due to the app process dying.
273                 mBackupManagerMonitorEventSender.monitorEvent(
274                         BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_AGENT_PIPE_BROKEN,
275                         mPkg,
276                         BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
277                         /* extras= */ null);
278                 result = BackupTransport.AGENT_ERROR;
279             } finally {
280                 try {
281                     // flush after every package
282                     mOutput.flush();
283                     if (pipes != null) {
284                         if (pipes[0] != null) {
285                             pipes[0].close();
286                         }
287                         if (pipes[1] != null) {
288                             pipes[1].close();
289                         }
290                     }
291                 } catch (IOException e) {
292                     Slog.w(TAG, "Error bringing down backup stack");
293                     result = BackupTransport.TRANSPORT_ERROR;
294                 }
295             }
296         } else {
297             Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
298         }
299         tearDown();
300         return result;
301     }
302 
sendQuotaExceeded(long backupDataBytes, long quotaBytes)303     public void sendQuotaExceeded(long backupDataBytes, long quotaBytes) {
304         if (initializeAgent()) {
305             try {
306                 RemoteCall.execute(
307                         callback -> mAgent.doQuotaExceeded(backupDataBytes, quotaBytes, callback),
308                         mAgentTimeoutParameters.getQuotaExceededTimeoutMillis());
309             } catch (RemoteException e) {
310                 Slog.e(TAG, "Remote exception while telling agent about quota exceeded");
311             }
312         }
313     }
314 
initializeAgent()315     private boolean initializeAgent() {
316         if (mAgent == null) {
317             if (DEBUG) {
318                 Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
319             }
320             mAgent =
321                     backupManagerService.getBackupAgentConnectionManager().bindToAgentSynchronous(
322                             mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL,
323                             mBackupEligibilityRules.getBackupDestination());
324         }
325         return mAgent != null;
326     }
327 
tearDown()328     private void tearDown() {
329         if (mPkg != null) {
330             backupManagerService.getBackupAgentConnectionManager().unbindAgent(
331                     mPkg.applicationInfo, /* allowKill= */ true);
332         }
333     }
334 }
335