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