Home | History | Annotate | Download | only in backup
      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 
     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
     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. */
    113     public void setOnTransportRegisteredListener(OnTransportRegisteredListener listener) {
    114         mOnTransportRegisteredListener = listener;
    115     }
    116 
    117     @WorkerThread
    118     void onPackageAdded(String packageName) {
    119         registerTransportsFromPackage(packageName, transportComponent -> true);
    120     }
    121 
    122     void onPackageRemoved(String packageName) {
    123         synchronized (mTransportLock) {
    124             mRegisteredTransportsDescriptionMap.keySet().removeIf(fromPackageFilter(packageName));
    125         }
    126     }
    127 
    128     @WorkerThread
    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      */
    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      */
    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. */
    175     Set<ComponentName> getTransportWhitelist() {
    176         return mTransportWhitelist;
    177     }
    178 
    179     @Nullable
    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      */
    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      */
    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      */
    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
    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      */
    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
    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
    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. */
    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      */
    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      */
    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")
    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")
    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
    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
    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
    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
    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      */
    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
    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      */
    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      */
    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
    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
    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
    519     public void registerTransports() {
    520         registerTransportsForIntent(mTransportServiceIntent, transportComponent -> true);
    521     }
    522 
    523     @WorkerThread
    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
    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. */
    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
    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. */
    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 
    646     private void checkCanUseTransport() {
    647         Preconditions.checkState(
    648                 !Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held");
    649     }
    650 
    651     public void dumpTransportClients(PrintWriter pw) {
    652         mTransportClientManager.dump(pw);
    653     }
    654 
    655     public void dumpTransportStats(PrintWriter pw) {
    656         mTransportStats.dump(pw);
    657     }
    658 
    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 
    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