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