1 /* 2 * Copyright (C) 2022 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.transport; 18 19 import android.annotation.Nullable; 20 import android.app.backup.BackupAnnotations; 21 import android.app.backup.BackupTransport; 22 import android.app.backup.IBackupManagerMonitor; 23 import android.app.backup.RestoreDescription; 24 import android.app.backup.RestoreSet; 25 import android.content.Intent; 26 import android.content.pm.PackageInfo; 27 import android.os.IBinder; 28 import android.os.ParcelFileDescriptor; 29 import android.os.RemoteException; 30 import android.util.ArraySet; 31 import android.util.Slog; 32 33 import com.android.internal.backup.IBackupTransport; 34 import com.android.internal.infra.AndroidFuture; 35 import com.android.server.backup.BackupAndRestoreFeatureFlags; 36 37 import java.util.ArrayDeque; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Queue; 41 import java.util.Set; 42 import java.util.concurrent.CancellationException; 43 import java.util.concurrent.ExecutionException; 44 import java.util.concurrent.TimeUnit; 45 import java.util.concurrent.TimeoutException; 46 47 /** 48 * Client to {@link com.android.internal.backup.IBackupTransport}. Manages the call to the remote 49 * transport service and delivers the results. 50 */ 51 public class BackupTransportClient { 52 private static final String TAG = "BackupTransportClient"; 53 54 private final IBackupTransport mTransportBinder; 55 private final TransportStatusCallbackPool mCallbackPool; 56 private final TransportFutures mTransportFutures; 57 BackupTransportClient(IBackupTransport transportBinder)58 BackupTransportClient(IBackupTransport transportBinder) { 59 mTransportBinder = transportBinder; 60 mCallbackPool = new TransportStatusCallbackPool(); 61 mTransportFutures = new TransportFutures(); 62 } 63 64 /** 65 * See {@link IBackupTransport#name()}. 66 */ name()67 public String name() throws RemoteException { 68 AndroidFuture<String> resultFuture = mTransportFutures.newFuture(); 69 mTransportBinder.name(resultFuture); 70 return getFutureResult(resultFuture); 71 } 72 73 /** 74 * See {@link IBackupTransport#configurationIntent()} 75 */ configurationIntent()76 public Intent configurationIntent() throws RemoteException { 77 AndroidFuture<Intent> resultFuture = mTransportFutures.newFuture(); 78 mTransportBinder.configurationIntent(resultFuture); 79 return getFutureResult(resultFuture); 80 } 81 82 /** 83 * See {@link IBackupTransport#currentDestinationString()} 84 */ currentDestinationString()85 public String currentDestinationString() throws RemoteException { 86 AndroidFuture<String> resultFuture = mTransportFutures.newFuture(); 87 mTransportBinder.currentDestinationString(resultFuture); 88 return getFutureResult(resultFuture); 89 } 90 91 /** 92 * See {@link IBackupTransport#dataManagementIntent()} 93 */ dataManagementIntent()94 public Intent dataManagementIntent() throws RemoteException { 95 AndroidFuture<Intent> resultFuture = mTransportFutures.newFuture(); 96 mTransportBinder.dataManagementIntent(resultFuture); 97 return getFutureResult(resultFuture); 98 } 99 100 /** 101 * See {@link IBackupTransport#dataManagementIntentLabel()} 102 */ 103 @Nullable dataManagementIntentLabel()104 public CharSequence dataManagementIntentLabel() throws RemoteException { 105 AndroidFuture<CharSequence> resultFuture = mTransportFutures.newFuture(); 106 mTransportBinder.dataManagementIntentLabel(resultFuture); 107 return getFutureResult(resultFuture); 108 } 109 110 /** 111 * See {@link IBackupTransport#transportDirName()} 112 */ transportDirName()113 public String transportDirName() throws RemoteException { 114 AndroidFuture<String> resultFuture = mTransportFutures.newFuture(); 115 mTransportBinder.transportDirName(resultFuture); 116 return getFutureResult(resultFuture); 117 } 118 119 /** 120 * See {@link IBackupTransport#initializeDevice()} 121 */ initializeDevice()122 public int initializeDevice() throws RemoteException { 123 TransportStatusCallback callback = mCallbackPool.acquire(); 124 try { 125 mTransportBinder.initializeDevice(callback); 126 return callback.getOperationStatus(); 127 } finally { 128 mCallbackPool.recycle(callback); 129 } 130 } 131 132 /** 133 * See {@link IBackupTransport#clearBackupData(PackageInfo)} 134 */ clearBackupData(PackageInfo packageInfo)135 public int clearBackupData(PackageInfo packageInfo) throws RemoteException { 136 TransportStatusCallback callback = mCallbackPool.acquire(); 137 try { 138 mTransportBinder.clearBackupData(packageInfo, callback); 139 return callback.getOperationStatus(); 140 } finally { 141 mCallbackPool.recycle(callback); 142 } 143 } 144 145 /** 146 * See {@link IBackupTransport#finishBackup()} 147 */ finishBackup()148 public int finishBackup() throws RemoteException { 149 TransportStatusCallback callback = mCallbackPool.acquire(); 150 try { 151 mTransportBinder.finishBackup(callback); 152 return callback.getOperationStatus(); 153 } finally { 154 mCallbackPool.recycle(callback); 155 } 156 } 157 158 /** 159 * See {@link IBackupTransport#requestBackupTime()} 160 */ requestBackupTime()161 public long requestBackupTime() throws RemoteException { 162 AndroidFuture<Long> resultFuture = mTransportFutures.newFuture(); 163 mTransportBinder.requestBackupTime(resultFuture); 164 Long result = getFutureResult(resultFuture); 165 return result == null ? BackupTransport.TRANSPORT_ERROR : result; 166 } 167 168 /** 169 * See {@link IBackupTransport#performBackup(PackageInfo, ParcelFileDescriptor, int)} 170 */ performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)171 public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags) 172 throws RemoteException { 173 TransportStatusCallback callback = mCallbackPool.acquire(); 174 try { 175 mTransportBinder.performBackup(packageInfo, inFd, flags, callback); 176 return callback.getOperationStatus(); 177 } finally { 178 mCallbackPool.recycle(callback); 179 } 180 } 181 182 /** 183 * See {@link IBackupTransport#getAvailableRestoreSets()} 184 */ getAvailableRestoreSets()185 public List<RestoreSet> getAvailableRestoreSets() throws RemoteException { 186 AndroidFuture<List<RestoreSet>> resultFuture = mTransportFutures.newFuture(); 187 mTransportBinder.getAvailableRestoreSets(resultFuture); 188 List<RestoreSet> result = getFutureResult(resultFuture); 189 return result; 190 } 191 192 /** 193 * See {@link IBackupTransport#getCurrentRestoreSet()} 194 */ getCurrentRestoreSet()195 public long getCurrentRestoreSet() throws RemoteException { 196 AndroidFuture<Long> resultFuture = mTransportFutures.newFuture(); 197 mTransportBinder.getCurrentRestoreSet(resultFuture); 198 Long result = getFutureResult(resultFuture); 199 return result == null ? BackupTransport.TRANSPORT_ERROR : result; 200 } 201 202 /** 203 * See {@link IBackupTransport#startRestore(long, PackageInfo[])} 204 */ startRestore(long token, PackageInfo[] packages)205 public int startRestore(long token, PackageInfo[] packages) throws RemoteException { 206 TransportStatusCallback callback = mCallbackPool.acquire(); 207 try { 208 mTransportBinder.startRestore(token, packages, callback); 209 return callback.getOperationStatus(); 210 } finally { 211 mCallbackPool.recycle(callback); 212 } 213 } 214 215 /** 216 * See {@link IBackupTransport#nextRestorePackage()} 217 */ nextRestorePackage()218 public RestoreDescription nextRestorePackage() throws RemoteException { 219 AndroidFuture<RestoreDescription> resultFuture = mTransportFutures.newFuture(); 220 mTransportBinder.nextRestorePackage(resultFuture); 221 return getFutureResult(resultFuture); 222 } 223 224 /** 225 * See {@link IBackupTransport#getRestoreData(ParcelFileDescriptor)} 226 */ getRestoreData(ParcelFileDescriptor outFd)227 public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException { 228 TransportStatusCallback callback = mCallbackPool.acquire(); 229 try { 230 mTransportBinder.getRestoreData(outFd, callback); 231 return callback.getOperationStatus(); 232 } finally { 233 mCallbackPool.recycle(callback); 234 } 235 } 236 237 /** 238 * See {@link IBackupTransport#finishRestore()} 239 */ finishRestore()240 public void finishRestore() throws RemoteException { 241 TransportStatusCallback callback = mCallbackPool.acquire(); 242 try { 243 mTransportBinder.finishRestore(callback); 244 callback.getOperationStatus(); 245 } finally { 246 mCallbackPool.recycle(callback); 247 } 248 } 249 250 /** 251 * See {@link IBackupTransport#requestFullBackupTime()} 252 */ requestFullBackupTime()253 public long requestFullBackupTime() throws RemoteException { 254 AndroidFuture<Long> resultFuture = mTransportFutures.newFuture(); 255 mTransportBinder.requestFullBackupTime(resultFuture); 256 Long result = getFutureResult(resultFuture); 257 return result == null ? BackupTransport.TRANSPORT_ERROR : result; 258 } 259 260 /** 261 * See {@link IBackupTransport#performFullBackup(PackageInfo, ParcelFileDescriptor, int)} 262 */ performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket, int flags)263 public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket, 264 int flags) throws RemoteException { 265 TransportStatusCallback callback = mCallbackPool.acquire(); 266 try { 267 mTransportBinder.performFullBackup(targetPackage, socket, flags, callback); 268 return callback.getOperationStatus(); 269 } finally { 270 mCallbackPool.recycle(callback); 271 } 272 } 273 274 /** 275 * See {@link IBackupTransport#checkFullBackupSize(long)} 276 */ checkFullBackupSize(long size)277 public int checkFullBackupSize(long size) throws RemoteException { 278 TransportStatusCallback callback = mCallbackPool.acquire(); 279 try { 280 mTransportBinder.checkFullBackupSize(size, callback); 281 return callback.getOperationStatus(); 282 } finally { 283 mCallbackPool.recycle(callback); 284 } 285 } 286 287 /** 288 * See {@link IBackupTransport#sendBackupData(int)} 289 */ sendBackupData(int numBytes)290 public int sendBackupData(int numBytes) throws RemoteException { 291 TransportStatusCallback callback = mCallbackPool.acquire(); 292 mTransportBinder.sendBackupData(numBytes, callback); 293 try { 294 return callback.getOperationStatus(); 295 } finally { 296 mCallbackPool.recycle(callback); 297 } 298 } 299 300 /** 301 * See {@link IBackupTransport#cancelFullBackup()} 302 */ cancelFullBackup()303 public void cancelFullBackup() throws RemoteException { 304 TransportStatusCallback callback = mCallbackPool.acquire(); 305 try { 306 mTransportBinder.cancelFullBackup(callback); 307 callback.getOperationStatus(); 308 } finally { 309 mCallbackPool.recycle(callback); 310 } 311 } 312 313 /** 314 * See {@link IBackupTransport#isAppEligibleForBackup(PackageInfo, boolean)} 315 */ isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)316 public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup) 317 throws RemoteException { 318 AndroidFuture<Boolean> resultFuture = mTransportFutures.newFuture(); 319 mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup, resultFuture); 320 Boolean result = getFutureResult(resultFuture); 321 return result != null && result; 322 } 323 324 /** 325 * See {@link IBackupTransport#getBackupQuota(String, boolean)} 326 */ getBackupQuota(String packageName, boolean isFullBackup)327 public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException { 328 AndroidFuture<Long> resultFuture = mTransportFutures.newFuture(); 329 mTransportBinder.getBackupQuota(packageName, isFullBackup, resultFuture); 330 Long result = getFutureResult(resultFuture); 331 return result == null ? BackupTransport.TRANSPORT_ERROR : result; 332 } 333 334 /** 335 * See {@link IBackupTransport#getNextFullRestoreDataChunk(ParcelFileDescriptor)} 336 */ getNextFullRestoreDataChunk(ParcelFileDescriptor socket)337 public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException { 338 TransportStatusCallback callback = mCallbackPool.acquire(); 339 try { 340 mTransportBinder.getNextFullRestoreDataChunk(socket, callback); 341 return callback.getOperationStatus(); 342 } finally { 343 mCallbackPool.recycle(callback); 344 } 345 } 346 347 /** 348 * See {@link IBackupTransport#abortFullRestore()} 349 */ abortFullRestore()350 public int abortFullRestore() throws RemoteException { 351 TransportStatusCallback callback = mCallbackPool.acquire(); 352 try { 353 mTransportBinder.abortFullRestore(callback); 354 return callback.getOperationStatus(); 355 } finally { 356 mCallbackPool.recycle(callback); 357 } 358 } 359 360 /** 361 * See {@link IBackupTransport#getTransportFlags()} 362 */ getTransportFlags()363 public int getTransportFlags() throws RemoteException { 364 AndroidFuture<Integer> resultFuture = mTransportFutures.newFuture(); 365 mTransportBinder.getTransportFlags(resultFuture); 366 Integer result = getFutureResult(resultFuture); 367 return result == null ? BackupTransport.TRANSPORT_ERROR : result; 368 } 369 370 /** 371 * See {@link IBackupTransport#getBackupManagerMonitor()} 372 */ getBackupManagerMonitor()373 public IBackupManagerMonitor getBackupManagerMonitor() throws RemoteException { 374 AndroidFuture<IBackupManagerMonitor> resultFuture = mTransportFutures.newFuture(); 375 mTransportBinder.getBackupManagerMonitor(resultFuture); 376 return IBackupManagerMonitor.Stub.asInterface((IBinder) getFutureResult(resultFuture)); 377 } 378 379 /** 380 * See 381 * {@link IBackupTransport#getPackagesThatShouldNotUseRestrictedMode(List, int, AndroidFuture)}. 382 */ getPackagesThatShouldNotUseRestrictedMode(Set<String> packageNames, @BackupAnnotations.OperationType int operationType)383 public Set<String> getPackagesThatShouldNotUseRestrictedMode(Set<String> packageNames, 384 @BackupAnnotations.OperationType 385 int operationType) throws RemoteException { 386 AndroidFuture<List<String>> resultFuture = mTransportFutures.newFuture(); 387 mTransportBinder.getPackagesThatShouldNotUseRestrictedMode(List.copyOf(packageNames), 388 operationType, 389 resultFuture); 390 List<String> resultList = getFutureResult(resultFuture); 391 Set<String> set = new ArraySet<>(); 392 if (resultList == null) { 393 return set; 394 } 395 set.addAll(resultList); 396 return set; 397 } 398 399 /** 400 * Allows the {@link TransportConnection} to notify this client 401 * if the underlying transport has become unusable. If that happens 402 * we want to cancel all active futures or callbacks. 403 */ onBecomingUnusable()404 void onBecomingUnusable() { 405 mCallbackPool.cancelActiveCallbacks(); 406 mTransportFutures.cancelActiveFutures(); 407 } 408 getFutureResult(AndroidFuture<T> future)409 private <T> T getFutureResult(AndroidFuture<T> future) { 410 try { 411 return future.get(BackupAndRestoreFeatureFlags.getBackupTransportFutureTimeoutMillis(), 412 TimeUnit.MILLISECONDS); 413 } catch (InterruptedException | ExecutionException | TimeoutException 414 | CancellationException e) { 415 Slog.w(TAG, "Failed to get result from transport:", e); 416 return null; 417 } finally { 418 mTransportFutures.remove(future); 419 } 420 } 421 422 private static class TransportFutures { 423 private final Object mActiveFuturesLock = new Object(); 424 private final Set<AndroidFuture<?>> mActiveFutures = new HashSet<>(); 425 newFuture()426 <T> AndroidFuture<T> newFuture() { 427 AndroidFuture<T> future = new AndroidFuture<>(); 428 synchronized (mActiveFuturesLock) { 429 mActiveFutures.add(future); 430 } 431 return future; 432 } 433 remove(AndroidFuture<T> future)434 <T> void remove(AndroidFuture<T> future) { 435 synchronized (mActiveFuturesLock) { 436 mActiveFutures.remove(future); 437 } 438 } 439 cancelActiveFutures()440 void cancelActiveFutures() { 441 synchronized (mActiveFuturesLock) { 442 for (AndroidFuture<?> future : mActiveFutures) { 443 try { 444 future.cancel(true); 445 } catch (CancellationException ignored) { 446 // This is expected, so ignore the exception. 447 } 448 } 449 mActiveFutures.clear(); 450 } 451 } 452 } 453 454 private static class TransportStatusCallbackPool { 455 private static final int MAX_POOL_SIZE = 100; 456 457 private final Object mPoolLock = new Object(); 458 private final Queue<TransportStatusCallback> mCallbackPool = new ArrayDeque<>(); 459 private final Set<TransportStatusCallback> mActiveCallbacks = new HashSet<>(); 460 acquire()461 TransportStatusCallback acquire() { 462 synchronized (mPoolLock) { 463 TransportStatusCallback callback = mCallbackPool.poll(); 464 if (callback == null) { 465 callback = new TransportStatusCallback(); 466 } 467 callback.reset(); 468 mActiveCallbacks.add(callback); 469 return callback; 470 } 471 } 472 recycle(TransportStatusCallback callback)473 void recycle(TransportStatusCallback callback) { 474 synchronized (mPoolLock) { 475 mActiveCallbacks.remove(callback); 476 if (mCallbackPool.size() > MAX_POOL_SIZE) { 477 Slog.d(TAG, "TransportStatusCallback pool size exceeded"); 478 return; 479 } 480 mCallbackPool.add(callback); 481 } 482 } 483 cancelActiveCallbacks()484 void cancelActiveCallbacks() { 485 synchronized (mPoolLock) { 486 for (TransportStatusCallback callback : mActiveCallbacks) { 487 try { 488 callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR); 489 // This waits for status to propagate before the callback is reset. 490 callback.getOperationStatus(); 491 } catch (RemoteException ex) { 492 // Nothing we can do. 493 } 494 if (mCallbackPool.size() < MAX_POOL_SIZE) { 495 mCallbackPool.add(callback); 496 } 497 } 498 mActiveCallbacks.clear(); 499 } 500 } 501 } 502 } 503