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