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