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