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