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