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