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