• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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