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