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