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