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.restore; 18 19 import static com.android.server.backup.BackupManagerService.DEBUG; 20 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT; 21 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_GET_RESTORE_SETS; 22 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.app.backup.BackupAgent; 27 import android.app.backup.BackupAnnotations.BackupDestination; 28 import android.app.backup.IBackupManagerMonitor; 29 import android.app.backup.IRestoreObserver; 30 import android.app.backup.IRestoreSession; 31 import android.app.backup.RestoreSet; 32 import android.content.pm.PackageInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PackageManager.NameNotFoundException; 35 import android.content.pm.PackageManagerInternal; 36 import android.os.Binder; 37 import android.os.Handler; 38 import android.os.Message; 39 import android.util.Slog; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.server.LocalServices; 43 import com.android.server.backup.BackupWakeLock; 44 import com.android.server.backup.Flags; 45 import com.android.server.backup.TransportManager; 46 import com.android.server.backup.UserBackupManagerService; 47 import com.android.server.backup.internal.OnTaskFinishedListener; 48 import com.android.server.backup.params.RestoreGetSetsParams; 49 import com.android.server.backup.params.RestoreParams; 50 import com.android.server.backup.transport.TransportConnection; 51 import com.android.server.backup.utils.BackupEligibilityRules; 52 53 import java.util.List; 54 import java.util.function.BiFunction; 55 56 /** 57 * Restore session. 58 */ 59 public class ActiveRestoreSession extends IRestoreSession.Stub { 60 private static final String TAG = "RestoreSession"; 61 private static final String DEVICE_NAME_FOR_D2D_SET = "D2D"; 62 63 private final TransportManager mTransportManager; 64 private final String mTransportName; 65 private final UserBackupManagerService mBackupManagerService; 66 private final int mUserId; 67 private final BackupEligibilityRules mBackupEligibilityRules; 68 @Nullable private final String mPackageName; 69 public List<RestoreSet> mRestoreSets = null; 70 boolean mEnded = false; 71 boolean mTimedOut = false; 72 ActiveRestoreSession( UserBackupManagerService backupManagerService, @Nullable String packageName, String transportName, BackupEligibilityRules backupEligibilityRules)73 public ActiveRestoreSession( 74 UserBackupManagerService backupManagerService, 75 @Nullable String packageName, 76 String transportName, 77 BackupEligibilityRules backupEligibilityRules) { 78 mBackupManagerService = backupManagerService; 79 mPackageName = packageName; 80 mTransportManager = backupManagerService.getTransportManager(); 81 mTransportName = transportName; 82 mUserId = backupManagerService.getUserId(); 83 mBackupEligibilityRules = backupEligibilityRules; 84 } 85 markTimedOut()86 public void markTimedOut() { 87 mTimedOut = true; 88 } 89 90 // --- Binder interface --- getAvailableRestoreSets(IRestoreObserver observer, IBackupManagerMonitor monitor)91 public synchronized int getAvailableRestoreSets(IRestoreObserver observer, 92 IBackupManagerMonitor monitor) { 93 mBackupManagerService.getContext().enforceCallingOrSelfPermission( 94 android.Manifest.permission.BACKUP, 95 "getAvailableRestoreSets"); 96 if (observer == null) { 97 throw new IllegalArgumentException("Observer must not be null"); 98 } 99 100 if (mEnded) { 101 throw new IllegalStateException("Restore session already ended"); 102 } 103 104 if (mTimedOut) { 105 Slog.i(TAG, "Session already timed out"); 106 return -1; 107 } 108 109 final long oldId = Binder.clearCallingIdentity(); 110 try { 111 TransportConnection transportConnection = 112 mTransportManager.getTransportClient( 113 mTransportName, "RestoreSession.getAvailableRestoreSets()"); 114 if (transportConnection == null) { 115 Slog.w(TAG, "Null transport client getting restore sets"); 116 return -1; 117 } 118 119 // We know we're doing legit work now, so halt the timeout 120 // until we're done. It gets started again when the result 121 // comes in. 122 mBackupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT); 123 124 BackupWakeLock wakelock = mBackupManagerService.getWakeLock(); 125 wakelock.acquire(); 126 127 // Prevent lambda from leaking 'this' 128 TransportManager transportManager = mTransportManager; 129 OnTaskFinishedListener listener = caller -> { 130 transportManager.disposeOfTransportClient(transportConnection, caller); 131 wakelock.release(); 132 }; 133 Message msg = mBackupManagerService.getBackupHandler().obtainMessage( 134 MSG_RUN_GET_RESTORE_SETS, 135 new RestoreGetSetsParams(transportConnection, this, observer, monitor, 136 listener)); 137 mBackupManagerService.getBackupHandler().sendMessage(msg); 138 return 0; 139 } catch (Exception e) { 140 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 141 return -1; 142 } finally { 143 Binder.restoreCallingIdentity(oldId); 144 } 145 } 146 restoreAll(long token, IRestoreObserver observer, IBackupManagerMonitor monitor)147 public synchronized int restoreAll(long token, IRestoreObserver observer, 148 IBackupManagerMonitor monitor) { 149 mBackupManagerService.getContext().enforceCallingOrSelfPermission( 150 android.Manifest.permission.BACKUP, 151 "performRestore"); 152 153 Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 154 + " observer=" + observer); 155 156 if (mEnded) { 157 throw new IllegalStateException("Restore session already ended"); 158 } 159 160 if (mTimedOut) { 161 Slog.i(TAG, "Session already timed out"); 162 return -1; 163 } 164 165 if (mRestoreSets == null) { 166 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 167 return -1; 168 } 169 170 if (mPackageName != null) { 171 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 172 return -1; 173 } 174 175 if (!mTransportManager.isTransportRegistered(mTransportName)) { 176 Slog.e(TAG, "Transport " + mTransportName + " not registered"); 177 return -1; 178 } 179 180 synchronized (mBackupManagerService.getQueueLock()) { 181 for (int i = 0; i < mRestoreSets.size(); i++) { 182 if (token == mRestoreSets.get(i).token) { 183 final long oldId = Binder.clearCallingIdentity(); 184 RestoreSet restoreSet = mRestoreSets.get(i); 185 try { 186 return sendRestoreToHandlerLocked( 187 (transportClient, listener) -> 188 RestoreParams.createForRestoreAll( 189 transportClient, 190 observer, 191 monitor, 192 token, 193 listener, 194 getBackupEligibilityRules(restoreSet)), 195 "RestoreSession.restoreAll()"); 196 } finally { 197 Binder.restoreCallingIdentity(oldId); 198 } 199 } 200 } 201 } 202 203 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 204 return -1; 205 } 206 207 // Restores of more than a single package are treated as 'system' restores restorePackages(long token, @Nullable IRestoreObserver observer, @NonNull String[] packages, @Nullable IBackupManagerMonitor monitor)208 public synchronized int restorePackages(long token, @Nullable IRestoreObserver observer, 209 @NonNull String[] packages, @Nullable IBackupManagerMonitor monitor) { 210 mBackupManagerService.getContext().enforceCallingOrSelfPermission( 211 android.Manifest.permission.BACKUP, 212 "performRestore"); 213 214 StringBuilder b = new StringBuilder(128); 215 b.append("restorePackages token="); 216 b.append(Long.toHexString(token)); 217 b.append(" observer="); 218 if (observer == null) { 219 b.append("null"); 220 } else { 221 b.append(observer.toString()); 222 } 223 b.append(" monitor="); 224 if (monitor == null) { 225 b.append("null"); 226 } else { 227 b.append(monitor.toString()); 228 } 229 b.append(" packages="); 230 if (packages == null) { 231 b.append("null"); 232 } else { 233 b.append('{'); 234 boolean first = true; 235 for (String s : packages) { 236 if (!first) { 237 b.append(", "); 238 } else { 239 first = false; 240 } 241 b.append(s); 242 } 243 b.append('}'); 244 } 245 Slog.d(TAG, b.toString()); 246 247 if (mEnded) { 248 throw new IllegalStateException("Restore session already ended"); 249 } 250 251 if (mTimedOut) { 252 Slog.i(TAG, "Session already timed out"); 253 return -1; 254 } 255 256 if (mRestoreSets == null) { 257 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 258 return -1; 259 } 260 261 if (mPackageName != null) { 262 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 263 return -1; 264 } 265 266 if (!mTransportManager.isTransportRegistered(mTransportName)) { 267 Slog.e(TAG, "Transport " + mTransportName + " not registered"); 268 return -1; 269 } 270 271 synchronized (mBackupManagerService.getQueueLock()) { 272 for (int i = 0; i < mRestoreSets.size(); i++) { 273 if (token == mRestoreSets.get(i).token) { 274 final long oldId = Binder.clearCallingIdentity(); 275 RestoreSet restoreSet = mRestoreSets.get(i); 276 try { 277 return sendRestoreToHandlerLocked( 278 (transportClient, listener) -> 279 RestoreParams.createForRestorePackages( 280 transportClient, 281 observer, 282 monitor, 283 token, 284 packages, 285 /* isSystemRestore */ packages.length > 1, 286 listener, 287 getBackupEligibilityRules(restoreSet)), 288 "RestoreSession.restorePackages(" + packages.length + " packages)"); 289 } finally { 290 Binder.restoreCallingIdentity(oldId); 291 } 292 } 293 } 294 } 295 296 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 297 return -1; 298 } 299 300 @VisibleForTesting getBackupEligibilityRules(RestoreSet restoreSet)301 BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) { 302 // TODO(b/182986784): Remove device name comparison once a designated field for operation 303 // type is added to RestoreSet object. 304 int backupDestination = DEVICE_NAME_FOR_D2D_SET.equals(restoreSet.device) 305 ? BackupDestination.DEVICE_TRANSFER : BackupDestination.CLOUD; 306 307 if (!Flags.enableSkippingRestoreLaunchedApps()) { 308 return mBackupManagerService.getEligibilityRulesForOperation(backupDestination); 309 } 310 311 boolean skipRestoreForLaunchedApps = (restoreSet.backupTransportFlags 312 & BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS) != 0; 313 314 return new BackupEligibilityRules(mBackupManagerService.getPackageManager(), 315 LocalServices.getService(PackageManagerInternal.class), 316 mUserId, 317 mBackupManagerService.getContext(), 318 backupDestination, 319 skipRestoreForLaunchedApps); 320 } 321 restorePackage(String packageName, IRestoreObserver observer, IBackupManagerMonitor monitor)322 public synchronized int restorePackage(String packageName, IRestoreObserver observer, 323 IBackupManagerMonitor monitor) { 324 Slog.d(TAG, "restorePackage pkg=" + packageName + " obs=" + observer 325 + "monitor=" + monitor); 326 327 if (mEnded) { 328 throw new IllegalStateException("Restore session already ended"); 329 } 330 331 if (mTimedOut) { 332 Slog.i(TAG, "Session already timed out"); 333 return -1; 334 } 335 336 if (mPackageName != null) { 337 if (!mPackageName.equals(packageName)) { 338 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 339 + " on session for package " + mPackageName); 340 return -1; 341 } 342 } 343 344 final PackageInfo app; 345 try { 346 app = mBackupManagerService.getPackageManager().getPackageInfoAsUser( 347 packageName, 0, mUserId); 348 } catch (NameNotFoundException nnf) { 349 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 350 return -1; 351 } 352 353 // If the caller is not privileged and is not coming from the target 354 // app's uid, throw a permission exception back to the caller. 355 int perm = mBackupManagerService.getContext().checkPermission( 356 android.Manifest.permission.BACKUP, 357 Binder.getCallingPid(), Binder.getCallingUid()); 358 if ((perm == PackageManager.PERMISSION_DENIED) && 359 (app.applicationInfo.uid != Binder.getCallingUid())) { 360 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 361 + " or calling uid=" + Binder.getCallingUid()); 362 throw new SecurityException("No permission to restore other packages"); 363 } 364 365 if (!mTransportManager.isTransportRegistered(mTransportName)) { 366 Slog.e(TAG, "Transport " + mTransportName + " not registered"); 367 return -1; 368 } 369 370 // So far so good; we're allowed to try to restore this package. 371 final long oldId = Binder.clearCallingIdentity(); 372 try { 373 // Check whether there is data for it in the current dataset, falling back 374 // to the ancestral dataset if not. 375 long token = mBackupManagerService.getAvailableRestoreToken(packageName); 376 Slog.d(TAG, "restorePackage pkg=" + packageName 377 + " token=" + Long.toHexString(token)); 378 379 // If we didn't come up with a place to look -- no ancestral dataset and 380 // the app has never been backed up from this device -- there's nothing 381 // to do but return failure. 382 if (token == 0) { 383 Slog.w(TAG, "No data available for this package; not restoring"); 384 return -1; 385 } 386 387 return sendRestoreToHandlerLocked( 388 (transportClient, listener) -> 389 RestoreParams.createForSinglePackage( 390 transportClient, 391 observer, 392 monitor, 393 token, 394 app, 395 listener, 396 mBackupEligibilityRules), 397 "RestoreSession.restorePackage(" + packageName + ")"); 398 } finally { 399 Binder.restoreCallingIdentity(oldId); 400 } 401 } 402 setRestoreSets(List<RestoreSet> restoreSets)403 public void setRestoreSets(List<RestoreSet> restoreSets) { 404 mRestoreSets = restoreSets; 405 } 406 407 /** 408 * Returns 0 if operation sent or -1 otherwise. 409 */ sendRestoreToHandlerLocked( BiFunction<TransportConnection, OnTaskFinishedListener, RestoreParams> restoreParamsBuilder, String callerLogString)410 private int sendRestoreToHandlerLocked( 411 BiFunction<TransportConnection, OnTaskFinishedListener, 412 RestoreParams> restoreParamsBuilder, String callerLogString) { 413 TransportConnection transportConnection = 414 mTransportManager.getTransportClient(mTransportName, callerLogString); 415 if (transportConnection == null) { 416 Slog.e(TAG, "Transport " + mTransportName + " got unregistered"); 417 return -1; 418 } 419 420 // Stop the session timeout until we finalize the restore 421 Handler backupHandler = mBackupManagerService.getBackupHandler(); 422 backupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT); 423 424 BackupWakeLock wakelock = mBackupManagerService.getWakeLock(); 425 wakelock.acquire(); 426 if (DEBUG) { 427 Slog.d(TAG, callerLogString); 428 } 429 430 // Prevent lambda from leaking 'this' 431 TransportManager transportManager = mTransportManager; 432 OnTaskFinishedListener listener = caller -> { 433 transportManager.disposeOfTransportClient(transportConnection, caller); 434 wakelock.release(); 435 }; 436 Message msg = backupHandler.obtainMessage(MSG_RUN_RESTORE); 437 msg.obj = restoreParamsBuilder.apply(transportConnection, listener); 438 backupHandler.sendMessage(msg); 439 return 0; 440 } 441 442 // Posted to the handler to tear down a restore session in a cleanly synchronized way 443 public class EndRestoreRunnable implements Runnable { 444 445 UserBackupManagerService mBackupManager; 446 ActiveRestoreSession mSession; 447 EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session)448 public EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session) { 449 mBackupManager = manager; 450 mSession = session; 451 } 452 run()453 public void run() { 454 // clean up the session's bookkeeping 455 synchronized (mSession) { 456 mSession.mEnded = true; 457 } 458 459 // clean up the BackupManagerImpl side of the bookkeeping 460 // and cancel any pending timeout message 461 mBackupManager.clearRestoreSession(mSession); 462 } 463 } 464 endRestoreSession()465 public synchronized void endRestoreSession() { 466 Slog.d(TAG, "endRestoreSession"); 467 468 if (mTimedOut) { 469 Slog.i(TAG, "Session already timed out"); 470 return; 471 } 472 473 if (mEnded) { 474 throw new IllegalStateException("Restore session already ended"); 475 } 476 477 mBackupManagerService.getBackupHandler().post( 478 new EndRestoreRunnable(mBackupManagerService, this)); 479 } 480 } 481