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