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;
18 
19 import android.annotation.Nullable;
20 import android.annotation.UserIdInt;
21 import android.annotation.WorkerThread;
22 import android.app.backup.BackupManager;
23 import android.app.backup.BackupTransport;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ResolveInfo;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.RemoteException;
34 import android.util.ArrayMap;
35 import android.util.ArraySet;
36 import android.util.Slog;
37 
38 import com.android.internal.annotations.GuardedBy;
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.backup.IBackupTransport;
41 import com.android.internal.util.Preconditions;
42 import com.android.server.backup.transport.OnTransportRegisteredListener;
43 import com.android.server.backup.transport.TransportClient;
44 import com.android.server.backup.transport.TransportClientManager;
45 import com.android.server.backup.transport.TransportConnectionListener;
46 import com.android.server.backup.transport.TransportNotAvailableException;
47 import com.android.server.backup.transport.TransportNotRegisteredException;
48 import com.android.server.backup.transport.TransportStats;
49 
50 import java.io.PrintWriter;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.Set;
54 import java.util.function.Consumer;
55 import java.util.function.Predicate;
56 
57 /** Handles in-memory bookkeeping of all BackupTransport objects. */
58 public class TransportManager {
59     private static final String TAG = "BackupTransportManager";
60 
61     @VisibleForTesting
62     public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
63 
64     private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
65     private final @UserIdInt int mUserId;
66     private final PackageManager mPackageManager;
67     private final Set<ComponentName> mTransportWhitelist;
68     private final TransportClientManager mTransportClientManager;
69     private final TransportStats mTransportStats;
70     private OnTransportRegisteredListener mOnTransportRegisteredListener = (c, n) -> {};
71 
72     /**
73      * Lock for registered transports and currently selected transport.
74      *
75      * <p><b>Warning:</b> No calls to {@link IBackupTransport} or calls that result in transport
76      * code being executed such as {@link TransportClient#connect(String)}} and its variants should
77      * be made with this lock held, risk of deadlock.
78      */
79     private final Object mTransportLock = new Object();
80 
81     /** @see #getRegisteredTransportNames() */
82     @GuardedBy("mTransportLock")
83     private final Map<ComponentName, TransportDescription> mRegisteredTransportsDescriptionMap =
84             new ArrayMap<>();
85 
86     @GuardedBy("mTransportLock")
87     @Nullable
88     private volatile String mCurrentTransportName;
89 
TransportManager(@serIdInt int userId, Context context, Set<ComponentName> whitelist, String selectedTransport)90     TransportManager(@UserIdInt int userId, Context context, Set<ComponentName> whitelist,
91             String selectedTransport) {
92         mUserId = userId;
93         mPackageManager = context.getPackageManager();
94         mTransportWhitelist = Preconditions.checkNotNull(whitelist);
95         mCurrentTransportName = selectedTransport;
96         mTransportStats = new TransportStats();
97         mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats);
98     }
99 
100     @VisibleForTesting
TransportManager( @serIdInt int userId, Context context, Set<ComponentName> whitelist, String selectedTransport, TransportClientManager transportClientManager)101     TransportManager(
102             @UserIdInt int userId,
103             Context context,
104             Set<ComponentName> whitelist,
105             String selectedTransport,
106             TransportClientManager transportClientManager) {
107         mUserId = userId;
108         mPackageManager = context.getPackageManager();
109         mTransportWhitelist = Preconditions.checkNotNull(whitelist);
110         mCurrentTransportName = selectedTransport;
111         mTransportStats = new TransportStats();
112         mTransportClientManager = transportClientManager;
113     }
114 
115     /* Sets a listener to be called whenever a transport is registered. */
setOnTransportRegisteredListener(OnTransportRegisteredListener listener)116     public void setOnTransportRegisteredListener(OnTransportRegisteredListener listener) {
117         mOnTransportRegisteredListener = listener;
118     }
119 
120     @WorkerThread
onPackageAdded(String packageName)121     void onPackageAdded(String packageName) {
122         registerTransportsFromPackage(packageName, transportComponent -> true);
123     }
124 
onPackageRemoved(String packageName)125     void onPackageRemoved(String packageName) {
126         synchronized (mTransportLock) {
127             mRegisteredTransportsDescriptionMap.keySet().removeIf(fromPackageFilter(packageName));
128         }
129     }
130 
131     @WorkerThread
onPackageChanged(String packageName, String... components)132     void onPackageChanged(String packageName, String... components) {
133         // Unfortunately this can't be atomic because we risk a deadlock if
134         // registerTransportsFromPackage() is put inside the synchronized block
135         Set<ComponentName> transportComponents = new ArraySet<>(components.length);
136         for (String componentName : components) {
137             transportComponents.add(new ComponentName(packageName, componentName));
138         }
139         synchronized (mTransportLock) {
140             mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains);
141         }
142         registerTransportsFromPackage(packageName, transportComponents::contains);
143     }
144 
145     /**
146      * Returns the {@link ComponentName}s of the registered transports.
147      *
148      * <p>A *registered* transport is a transport that satisfies intent with action
149      * android.backup.TRANSPORT_HOST, returns true for {@link #isTransportTrusted(ComponentName)}
150      * and that we have successfully connected to once.
151      */
getRegisteredTransportComponents()152     ComponentName[] getRegisteredTransportComponents() {
153         synchronized (mTransportLock) {
154             return mRegisteredTransportsDescriptionMap
155                     .keySet()
156                     .toArray(new ComponentName[mRegisteredTransportsDescriptionMap.size()]);
157         }
158     }
159 
160     /**
161      * Returns the names of the registered transports.
162      *
163      * @see #getRegisteredTransportComponents()
164      */
getRegisteredTransportNames()165     String[] getRegisteredTransportNames() {
166         synchronized (mTransportLock) {
167             String[] transportNames = new String[mRegisteredTransportsDescriptionMap.size()];
168             int i = 0;
169             for (TransportDescription description : mRegisteredTransportsDescriptionMap.values()) {
170                 transportNames[i] = description.name;
171                 i++;
172             }
173             return transportNames;
174         }
175     }
176 
177     /** Returns a set with the allowlisted transports. */
getTransportWhitelist()178     Set<ComponentName> getTransportWhitelist() {
179         return mTransportWhitelist;
180     }
181 
182     /** Returns the name of the selected transport or {@code null} if no transport selected. */
183     @Nullable
getCurrentTransportName()184     public String getCurrentTransportName() {
185         return mCurrentTransportName;
186     }
187 
188     /**
189      * Returns the {@link ComponentName} of the host service of the selected transport or
190      * {@code null} if no transport selected.
191      *
192      * @throws TransportNotRegisteredException if the selected transport is not registered.
193      */
194     @Nullable
getCurrentTransportComponent()195     public ComponentName getCurrentTransportComponent()
196             throws TransportNotRegisteredException {
197         synchronized (mTransportLock) {
198             if (mCurrentTransportName == null) {
199                 return null;
200             }
201             return getRegisteredTransportComponentOrThrowLocked(mCurrentTransportName);
202         }
203     }
204 
205     /**
206      * Returns the transport name associated with {@code transportComponent}.
207      *
208      * @throws TransportNotRegisteredException if the transport is not registered.
209      */
getTransportName(ComponentName transportComponent)210     public String getTransportName(ComponentName transportComponent)
211             throws TransportNotRegisteredException {
212         synchronized (mTransportLock) {
213             return getRegisteredTransportDescriptionOrThrowLocked(transportComponent).name;
214         }
215     }
216 
217     /**
218      * Retrieves the transport dir name of {@code transportComponent}.
219      *
220      * @throws TransportNotRegisteredException if the transport is not registered.
221      */
getTransportDirName(ComponentName transportComponent)222     public String getTransportDirName(ComponentName transportComponent)
223             throws TransportNotRegisteredException {
224         synchronized (mTransportLock) {
225             return getRegisteredTransportDescriptionOrThrowLocked(transportComponent)
226                     .transportDirName;
227         }
228     }
229 
230     /**
231      * Retrieves the transport dir name of {@code transportName}.
232      *
233      * @throws TransportNotRegisteredException if the transport is not registered.
234      */
getTransportDirName(String transportName)235     public String getTransportDirName(String transportName) throws TransportNotRegisteredException {
236         synchronized (mTransportLock) {
237             return getRegisteredTransportDescriptionOrThrowLocked(transportName).transportDirName;
238         }
239     }
240 
241     /**
242      * Retrieves the configuration intent of {@code transportName}.
243      *
244      * @throws TransportNotRegisteredException if the transport is not registered.
245      */
246     @Nullable
getTransportConfigurationIntent(String transportName)247     public Intent getTransportConfigurationIntent(String transportName)
248             throws TransportNotRegisteredException {
249         synchronized (mTransportLock) {
250             return getRegisteredTransportDescriptionOrThrowLocked(transportName)
251                     .configurationIntent;
252         }
253     }
254 
255     /**
256      * Retrieves the current destination string of {@code transportName}.
257      *
258      * @throws TransportNotRegisteredException if the transport is not registered.
259      */
getTransportCurrentDestinationString(String transportName)260     public String getTransportCurrentDestinationString(String transportName)
261             throws TransportNotRegisteredException {
262         synchronized (mTransportLock) {
263             return getRegisteredTransportDescriptionOrThrowLocked(transportName)
264                     .currentDestinationString;
265         }
266     }
267 
268     /**
269      * Retrieves the data management intent of {@code transportName}.
270      *
271      * @throws TransportNotRegisteredException if the transport is not registered.
272      */
273     @Nullable
getTransportDataManagementIntent(String transportName)274     public Intent getTransportDataManagementIntent(String transportName)
275             throws TransportNotRegisteredException {
276         synchronized (mTransportLock) {
277             return getRegisteredTransportDescriptionOrThrowLocked(transportName)
278                     .dataManagementIntent;
279         }
280     }
281 
282     /**
283      * Retrieves the data management label of {@code transportName}.
284      *
285      * @throws TransportNotRegisteredException if the transport is not registered.
286      */
287     @Nullable
getTransportDataManagementLabel(String transportName)288     public CharSequence getTransportDataManagementLabel(String transportName)
289             throws TransportNotRegisteredException {
290         synchronized (mTransportLock) {
291             return getRegisteredTransportDescriptionOrThrowLocked(transportName)
292                     .dataManagementLabel;
293         }
294     }
295 
296     /* Returns true if the transport identified by {@code transportName} is registered. */
isTransportRegistered(String transportName)297     public boolean isTransportRegistered(String transportName) {
298         synchronized (mTransportLock) {
299             return getRegisteredTransportEntryLocked(transportName) != null;
300         }
301     }
302 
303     /**
304      * Execute {@code transportConsumer} for each registered transport passing the transport name.
305      * This is called with an internal lock held, ensuring that the transport will remain registered
306      * while {@code transportConsumer} is being executed. Don't do heavy operations in {@code
307      * transportConsumer}.
308      *
309      * <p><b>Warning:</b> Do NOT make any calls to {@link IBackupTransport} or call any variants of
310      * {@link TransportClient#connect(String)} here, otherwise you risk deadlock.
311      */
forEachRegisteredTransport(Consumer<String> transportConsumer)312     public void forEachRegisteredTransport(Consumer<String> transportConsumer) {
313         synchronized (mTransportLock) {
314             for (TransportDescription transportDescription :
315                     mRegisteredTransportsDescriptionMap.values()) {
316                 transportConsumer.accept(transportDescription.name);
317             }
318         }
319     }
320 
321     /**
322      * Updates given values for the transport already registered and identified with {@param
323      * transportComponent}. If the transport is not registered it will log and return.
324      */
updateTransportAttributes( ComponentName transportComponent, String name, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, @Nullable CharSequence dataManagementLabel)325     public void updateTransportAttributes(
326             ComponentName transportComponent,
327             String name,
328             @Nullable Intent configurationIntent,
329             String currentDestinationString,
330             @Nullable Intent dataManagementIntent,
331             @Nullable CharSequence dataManagementLabel) {
332         synchronized (mTransportLock) {
333             TransportDescription description =
334                     mRegisteredTransportsDescriptionMap.get(transportComponent);
335             if (description == null) {
336                 Slog.e(TAG, "Transport " + name + " not registered tried to change description");
337                 return;
338             }
339             description.name = name;
340             description.configurationIntent = configurationIntent;
341             description.currentDestinationString = currentDestinationString;
342             description.dataManagementIntent = dataManagementIntent;
343             description.dataManagementLabel = dataManagementLabel;
344             Slog.d(TAG, "Transport " + name + " updated its attributes");
345         }
346     }
347 
348     @GuardedBy("mTransportLock")
getRegisteredTransportComponentOrThrowLocked(String transportName)349     private ComponentName getRegisteredTransportComponentOrThrowLocked(String transportName)
350             throws TransportNotRegisteredException {
351         ComponentName transportComponent = getRegisteredTransportComponentLocked(transportName);
352         if (transportComponent == null) {
353             throw new TransportNotRegisteredException(transportName);
354         }
355         return transportComponent;
356     }
357 
358     @GuardedBy("mTransportLock")
getRegisteredTransportDescriptionOrThrowLocked( ComponentName transportComponent)359     private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
360             ComponentName transportComponent) throws TransportNotRegisteredException {
361         TransportDescription description =
362                 mRegisteredTransportsDescriptionMap.get(transportComponent);
363         if (description == null) {
364             throw new TransportNotRegisteredException(transportComponent);
365         }
366         return description;
367     }
368 
369     @GuardedBy("mTransportLock")
getRegisteredTransportDescriptionOrThrowLocked( String transportName)370     private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
371             String transportName) throws TransportNotRegisteredException {
372         TransportDescription description = getRegisteredTransportDescriptionLocked(transportName);
373         if (description == null) {
374             throw new TransportNotRegisteredException(transportName);
375         }
376         return description;
377     }
378 
379     @GuardedBy("mTransportLock")
380     @Nullable
getRegisteredTransportComponentLocked(String transportName)381     private ComponentName getRegisteredTransportComponentLocked(String transportName) {
382         Map.Entry<ComponentName, TransportDescription> entry =
383                 getRegisteredTransportEntryLocked(transportName);
384         return (entry == null) ? null : entry.getKey();
385     }
386 
387     @GuardedBy("mTransportLock")
388     @Nullable
getRegisteredTransportDescriptionLocked(String transportName)389     private TransportDescription getRegisteredTransportDescriptionLocked(String transportName) {
390         Map.Entry<ComponentName, TransportDescription> entry =
391                 getRegisteredTransportEntryLocked(transportName);
392         return (entry == null) ? null : entry.getValue();
393     }
394 
395     @GuardedBy("mTransportLock")
396     @Nullable
getRegisteredTransportEntryLocked( String transportName)397     private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked(
398             String transportName) {
399         for (Map.Entry<ComponentName, TransportDescription> entry :
400                 mRegisteredTransportsDescriptionMap.entrySet()) {
401             TransportDescription description = entry.getValue();
402             if (transportName.equals(description.name)) {
403                 return entry;
404             }
405         }
406         return null;
407     }
408 
409     /**
410      * Returns a {@link TransportClient} for {@code transportName} or {@code null} if not
411      * registered.
412      *
413      * @param transportName The name of the transport.
414      * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
415      *     {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
416      *     details.
417      * @return A {@link TransportClient} or null if not registered.
418      */
419     @Nullable
getTransportClient(String transportName, String caller)420     public TransportClient getTransportClient(String transportName, String caller) {
421         try {
422             return getTransportClientOrThrow(transportName, caller);
423         } catch (TransportNotRegisteredException e) {
424             Slog.w(TAG, "Transport " + transportName + " not registered");
425             return null;
426         }
427     }
428 
429     /**
430      * Returns a {@link TransportClient} for {@code transportName} or throws if not registered.
431      *
432      * @param transportName The name of the transport.
433      * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
434      *     {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
435      *     details.
436      * @return A {@link TransportClient}.
437      * @throws TransportNotRegisteredException if the transport is not registered.
438      */
getTransportClientOrThrow(String transportName, String caller)439     public TransportClient getTransportClientOrThrow(String transportName, String caller)
440             throws TransportNotRegisteredException {
441         synchronized (mTransportLock) {
442             ComponentName component = getRegisteredTransportComponentLocked(transportName);
443             if (component == null) {
444                 throw new TransportNotRegisteredException(transportName);
445             }
446             return mTransportClientManager.getTransportClient(component, caller);
447         }
448     }
449 
450     /**
451      * Returns a {@link TransportClient} for the current transport or {@code null} if not
452      * registered.
453      *
454      * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
455      *     {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
456      *     details.
457      * @return A {@link TransportClient} or null if not registered.
458      * @throws IllegalStateException if no transport is selected.
459      */
460     @Nullable
getCurrentTransportClient(String caller)461     public TransportClient getCurrentTransportClient(String caller) {
462         if (mCurrentTransportName == null) {
463             throw new IllegalStateException("No transport selected");
464         }
465         synchronized (mTransportLock) {
466             return getTransportClient(mCurrentTransportName, caller);
467         }
468     }
469 
470     /**
471      * Returns a {@link TransportClient} for the current transport or throws if not registered.
472      *
473      * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
474      *     {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
475      *     details.
476      * @return A {@link TransportClient}.
477      * @throws TransportNotRegisteredException if the transport is not registered.
478      * @throws IllegalStateException if no transport is selected.
479      */
getCurrentTransportClientOrThrow(String caller)480     public TransportClient getCurrentTransportClientOrThrow(String caller)
481             throws TransportNotRegisteredException {
482         if (mCurrentTransportName == null) {
483             throw new IllegalStateException("No transport selected");
484         }
485         synchronized (mTransportLock) {
486             return getTransportClientOrThrow(mCurrentTransportName, caller);
487         }
488     }
489 
490     /**
491      * Disposes of the {@link TransportClient}.
492      *
493      * @param transportClient The {@link TransportClient} to be disposed of.
494      * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
495      *     {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
496      *     details.
497      */
disposeOfTransportClient(TransportClient transportClient, String caller)498     public void disposeOfTransportClient(TransportClient transportClient, String caller) {
499         mTransportClientManager.disposeOfTransportClient(transportClient, caller);
500     }
501 
502     /**
503      * Sets {@code transportName} as selected transport and returns previously selected transport
504      * name. If there was no previous transport it returns null.
505      *
506      * <p>You should NOT call this method in new code. This won't make any checks against {@code
507      * transportName}, putting any operation at risk of a {@link TransportNotRegisteredException} or
508      * another error at the time it's being executed.
509      *
510      * <p>{@link Deprecated} as public, this method can be used as private.
511      */
512     @Deprecated
513     @Nullable
selectTransport(String transportName)514     String selectTransport(String transportName) {
515         synchronized (mTransportLock) {
516             String prevTransport = mCurrentTransportName;
517             mCurrentTransportName = transportName;
518             return prevTransport;
519         }
520     }
521 
522     /**
523      * Tries to register the transport if not registered. If successful also selects the transport.
524      *
525      * @param transportComponent Host of the transport.
526      * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
527      *     or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
528      */
529     @WorkerThread
registerAndSelectTransport(ComponentName transportComponent)530     public int registerAndSelectTransport(ComponentName transportComponent) {
531         // If it's already registered we select and return
532         synchronized (mTransportLock) {
533             try {
534                 selectTransport(getTransportName(transportComponent));
535                 return BackupManager.SUCCESS;
536             } catch (TransportNotRegisteredException e) {
537                 // Fall through and release lock
538             }
539         }
540 
541         // We can't call registerTransport() with the transport lock held
542         int result = registerTransport(transportComponent);
543         if (result != BackupManager.SUCCESS) {
544             return result;
545         }
546         synchronized (mTransportLock) {
547             try {
548                 selectTransport(getTransportName(transportComponent));
549                 return BackupManager.SUCCESS;
550             } catch (TransportNotRegisteredException e) {
551                 Slog.wtf(TAG, "Transport got unregistered");
552                 return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
553             }
554         }
555     }
556 
557     @WorkerThread
registerTransports()558     public void registerTransports() {
559         registerTransportsForIntent(mTransportServiceIntent, transportComponent -> true);
560     }
561 
562     @WorkerThread
registerTransportsFromPackage( String packageName, Predicate<ComponentName> transportComponentFilter)563     private void registerTransportsFromPackage(
564             String packageName, Predicate<ComponentName> transportComponentFilter) {
565         try {
566             mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
567         } catch (PackageManager.NameNotFoundException e) {
568             Slog.e(TAG, "Trying to register transports from package not found " + packageName);
569             return;
570         }
571 
572         registerTransportsForIntent(
573                 new Intent(mTransportServiceIntent).setPackage(packageName),
574                 transportComponentFilter.and(fromPackageFilter(packageName)));
575     }
576 
577     @WorkerThread
registerTransportsForIntent( Intent intent, Predicate<ComponentName> transportComponentFilter)578     private void registerTransportsForIntent(
579             Intent intent, Predicate<ComponentName> transportComponentFilter) {
580         List<ResolveInfo> hosts =
581                 mPackageManager.queryIntentServicesAsUser(intent, 0, mUserId);
582         if (hosts == null) {
583             return;
584         }
585         for (ResolveInfo host : hosts) {
586             ComponentName transportComponent = host.serviceInfo.getComponentName();
587             if (transportComponentFilter.test(transportComponent)
588                     && isTransportTrusted(transportComponent)) {
589                 registerTransport(transportComponent);
590             }
591         }
592     }
593 
594     /** Transport has to be allowlisted and privileged. */
isTransportTrusted(ComponentName transport)595     private boolean isTransportTrusted(ComponentName transport) {
596         if (!mTransportWhitelist.contains(transport)) {
597             Slog.w(
598                     TAG,
599                     "BackupTransport " + transport.flattenToShortString() + " not whitelisted.");
600             return false;
601         }
602         try {
603             PackageInfo packInfo =
604                     mPackageManager.getPackageInfoAsUser(transport.getPackageName(), 0, mUserId);
605             if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
606                     == 0) {
607                 Slog.w(TAG, "Transport package " + transport.getPackageName() + " not privileged");
608                 return false;
609             }
610         } catch (PackageManager.NameNotFoundException e) {
611             Slog.w(TAG, "Package not found.", e);
612             return false;
613         }
614         return true;
615     }
616 
617     /**
618      * Tries to register transport represented by {@code transportComponent}.
619      *
620      * <p><b>Warning:</b> Don't call this with the transport lock held.
621      *
622      * @param transportComponent Host of the transport that we want to register.
623      * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
624      *     or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
625      */
626     @WorkerThread
registerTransport(ComponentName transportComponent)627     private int registerTransport(ComponentName transportComponent) {
628         checkCanUseTransport();
629 
630         if (!isTransportTrusted(transportComponent)) {
631             return BackupManager.ERROR_TRANSPORT_INVALID;
632         }
633 
634         String transportString = transportComponent.flattenToShortString();
635         String callerLogString = "TransportManager.registerTransport()";
636 
637         Bundle extras = new Bundle();
638         extras.putBoolean(BackupTransport.EXTRA_TRANSPORT_REGISTRATION, true);
639 
640         TransportClient transportClient =
641                 mTransportClientManager.getTransportClient(
642                         transportComponent, extras, callerLogString);
643         final IBackupTransport transport;
644         try {
645             transport = transportClient.connectOrThrow(callerLogString);
646         } catch (TransportNotAvailableException e) {
647             Slog.e(TAG, "Couldn't connect to transport " + transportString + " for registration");
648             mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString);
649             return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
650         }
651 
652         int result;
653         try {
654             // This is a temporary fix to allow blocking calls.
655             // TODO: b/147702043. Redesign IBackupTransport so as to make the calls non-blocking.
656             Binder.allowBlocking(transport.asBinder());
657 
658             String transportName = transport.name();
659             String transportDirName = transport.transportDirName();
660             registerTransport(transportComponent, transport);
661             // If registerTransport() hasn't thrown...
662             Slog.d(TAG, "Transport " + transportString + " registered");
663             mOnTransportRegisteredListener.onTransportRegistered(transportName, transportDirName);
664             result = BackupManager.SUCCESS;
665         } catch (RemoteException e) {
666             Slog.e(TAG, "Transport " + transportString + " died while registering");
667             result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
668         }
669 
670         mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString);
671         return result;
672     }
673 
674     /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */
registerTransport(ComponentName transportComponent, IBackupTransport transport)675     private void registerTransport(ComponentName transportComponent, IBackupTransport transport)
676             throws RemoteException {
677         checkCanUseTransport();
678 
679         TransportDescription description =
680                 new TransportDescription(
681                         transport.name(),
682                         transport.transportDirName(),
683                         transport.configurationIntent(),
684                         transport.currentDestinationString(),
685                         transport.dataManagementIntent(),
686                         transport.dataManagementIntentLabel());
687         synchronized (mTransportLock) {
688             mRegisteredTransportsDescriptionMap.put(transportComponent, description);
689         }
690     }
691 
checkCanUseTransport()692     private void checkCanUseTransport() {
693         Preconditions.checkState(
694                 !Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held");
695     }
696 
dumpTransportClients(PrintWriter pw)697     public void dumpTransportClients(PrintWriter pw) {
698         mTransportClientManager.dump(pw);
699     }
700 
dumpTransportStats(PrintWriter pw)701     public void dumpTransportStats(PrintWriter pw) {
702         mTransportStats.dump(pw);
703     }
704 
fromPackageFilter(String packageName)705     private static Predicate<ComponentName> fromPackageFilter(String packageName) {
706         return transportComponent -> packageName.equals(transportComponent.getPackageName());
707     }
708 
709     private static class TransportDescription {
710         private String name;
711         private final String transportDirName;
712         @Nullable private Intent configurationIntent;
713         private String currentDestinationString;
714         @Nullable private Intent dataManagementIntent;
715         @Nullable private CharSequence dataManagementLabel;
716 
TransportDescription( String name, String transportDirName, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, @Nullable CharSequence dataManagementLabel)717         private TransportDescription(
718                 String name,
719                 String transportDirName,
720                 @Nullable Intent configurationIntent,
721                 String currentDestinationString,
722                 @Nullable Intent dataManagementIntent,
723                 @Nullable CharSequence dataManagementLabel) {
724             this.name = name;
725             this.transportDirName = transportDirName;
726             this.configurationIntent = configurationIntent;
727             this.currentDestinationString = currentDestinationString;
728             this.dataManagementIntent = dataManagementIntent;
729             this.dataManagementLabel = dataManagementLabel;
730         }
731     }
732 }
733