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