diff options
author | nathch <nathch@google.com> | 2019-09-21 22:06:40 +0100 |
---|---|---|
committer | nathch <nathch@google.com> | 2019-09-22 17:23:17 +0100 |
commit | b85b36ac09813172e73a8bdf594bd077139a3a6f (patch) | |
tree | 8bfe17f1929f1858aaa80eafc9cf682cfd4a2f3c /services/backup/java | |
parent | e187e20a8a537efc3b18e99174abf6540659e19b (diff) |
Move transport client code into its own lib
We will route backup through the BackupEncryption APK which will
implement an intermediate transport which will encrypt (or decrypt) the data when
sending it (receiving it) from the real transport.
Since both backup services and IntermediateTransport need to bind to
IBackupTransport, they both need the transport client code so we move that
into its own lib.
Bug: 139269919
Test: atest -v RunBackupFrameworksServicesRoboTests
Test: atest -v $(find frameworks/base/services/tests/servicestests/src/com/android/server/backup -name '\''*Test.java'\'')'
Test: atest -v CtsBackupTestCases CtsBackupHostTestCases
Change-Id: Id986162ac71adf8638e5797169ef70e0d3d58b0c
Diffstat (limited to 'services/backup/java')
9 files changed, 0 insertions, 1920 deletions
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java deleted file mode 100644 index 30ce4cf2fd3f..000000000000 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ /dev/null @@ -1,727 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup; - -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.annotation.WorkerThread; -import android.app.backup.BackupManager; -import android.app.backup.BackupTransport; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Slog; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.backup.IBackupTransport; -import com.android.internal.util.Preconditions; -import com.android.server.backup.transport.OnTransportRegisteredListener; -import com.android.server.backup.transport.TransportClient; -import com.android.server.backup.transport.TransportClientManager; -import com.android.server.backup.transport.TransportConnectionListener; -import com.android.server.backup.transport.TransportNotAvailableException; -import com.android.server.backup.transport.TransportNotRegisteredException; -import com.android.server.backup.transport.TransportStats; - -import java.io.PrintWriter; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Predicate; - -/** Handles in-memory bookkeeping of all BackupTransport objects. */ -public class TransportManager { - private static final String TAG = "BackupTransportManager"; - - @VisibleForTesting - public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST"; - - private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST); - private final @UserIdInt int mUserId; - private final PackageManager mPackageManager; - private final Set<ComponentName> mTransportWhitelist; - private final TransportClientManager mTransportClientManager; - private final TransportStats mTransportStats; - private OnTransportRegisteredListener mOnTransportRegisteredListener = (c, n) -> {}; - - /** - * Lock for registered transports and currently selected transport. - * - * <p><b>Warning:</b> No calls to {@link IBackupTransport} or calls that result in transport - * code being executed such as {@link TransportClient#connect(String)}} and its variants should - * be made with this lock held, risk of deadlock. - */ - private final Object mTransportLock = new Object(); - - /** @see #getRegisteredTransportNames() */ - @GuardedBy("mTransportLock") - private final Map<ComponentName, TransportDescription> mRegisteredTransportsDescriptionMap = - new ArrayMap<>(); - - @GuardedBy("mTransportLock") - @Nullable - private volatile String mCurrentTransportName; - - TransportManager(@UserIdInt int userId, Context context, Set<ComponentName> whitelist, - String selectedTransport) { - mUserId = userId; - mPackageManager = context.getPackageManager(); - mTransportWhitelist = Preconditions.checkNotNull(whitelist); - mCurrentTransportName = selectedTransport; - mTransportStats = new TransportStats(); - mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats); - } - - @VisibleForTesting - TransportManager( - @UserIdInt int userId, - Context context, - Set<ComponentName> whitelist, - String selectedTransport, - TransportClientManager transportClientManager) { - mUserId = userId; - mPackageManager = context.getPackageManager(); - mTransportWhitelist = Preconditions.checkNotNull(whitelist); - mCurrentTransportName = selectedTransport; - mTransportStats = new TransportStats(); - mTransportClientManager = transportClientManager; - } - - /* Sets a listener to be called whenever a transport is registered. */ - public void setOnTransportRegisteredListener(OnTransportRegisteredListener listener) { - mOnTransportRegisteredListener = listener; - } - - @WorkerThread - void onPackageAdded(String packageName) { - registerTransportsFromPackage(packageName, transportComponent -> true); - } - - void onPackageRemoved(String packageName) { - synchronized (mTransportLock) { - mRegisteredTransportsDescriptionMap.keySet().removeIf(fromPackageFilter(packageName)); - } - } - - @WorkerThread - void onPackageChanged(String packageName, String... components) { - // Unfortunately this can't be atomic because we risk a deadlock if - // registerTransportsFromPackage() is put inside the synchronized block - Set<ComponentName> transportComponents = new ArraySet<>(components.length); - for (String componentName : components) { - transportComponents.add(new ComponentName(packageName, componentName)); - } - synchronized (mTransportLock) { - mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains); - } - registerTransportsFromPackage(packageName, transportComponents::contains); - } - - /** - * Returns the {@link ComponentName}s of the registered transports. - * - * <p>A *registered* transport is a transport that satisfies intent with action - * android.backup.TRANSPORT_HOST, returns true for {@link #isTransportTrusted(ComponentName)} - * and that we have successfully connected to once. - */ - ComponentName[] getRegisteredTransportComponents() { - synchronized (mTransportLock) { - return mRegisteredTransportsDescriptionMap - .keySet() - .toArray(new ComponentName[mRegisteredTransportsDescriptionMap.size()]); - } - } - - /** - * Returns the names of the registered transports. - * - * @see #getRegisteredTransportComponents() - */ - String[] getRegisteredTransportNames() { - synchronized (mTransportLock) { - String[] transportNames = new String[mRegisteredTransportsDescriptionMap.size()]; - int i = 0; - for (TransportDescription description : mRegisteredTransportsDescriptionMap.values()) { - transportNames[i] = description.name; - i++; - } - return transportNames; - } - } - - /** Returns a set with the whitelisted transports. */ - Set<ComponentName> getTransportWhitelist() { - return mTransportWhitelist; - } - - /** Returns the name of the selected transport or {@code null} if no transport selected. */ - @Nullable - public String getCurrentTransportName() { - return mCurrentTransportName; - } - - /** - * Returns the {@link ComponentName} of the host service of the selected transport or - * {@code null} if no transport selected. - * - * @throws TransportNotRegisteredException if the selected transport is not registered. - */ - @Nullable - public ComponentName getCurrentTransportComponent() - throws TransportNotRegisteredException { - synchronized (mTransportLock) { - if (mCurrentTransportName == null) { - return null; - } - return getRegisteredTransportComponentOrThrowLocked(mCurrentTransportName); - } - } - - /** - * Returns the transport name associated with {@code transportComponent}. - * - * @throws TransportNotRegisteredException if the transport is not registered. - */ - public String getTransportName(ComponentName transportComponent) - throws TransportNotRegisteredException { - synchronized (mTransportLock) { - return getRegisteredTransportDescriptionOrThrowLocked(transportComponent).name; - } - } - - /** - * Retrieves the transport dir name of {@code transportComponent}. - * - * @throws TransportNotRegisteredException if the transport is not registered. - */ - public String getTransportDirName(ComponentName transportComponent) - throws TransportNotRegisteredException { - synchronized (mTransportLock) { - return getRegisteredTransportDescriptionOrThrowLocked(transportComponent) - .transportDirName; - } - } - - /** - * Retrieves the transport dir name of {@code transportName}. - * - * @throws TransportNotRegisteredException if the transport is not registered. - */ - public String getTransportDirName(String transportName) throws TransportNotRegisteredException { - synchronized (mTransportLock) { - return getRegisteredTransportDescriptionOrThrowLocked(transportName).transportDirName; - } - } - - /** - * Retrieves the configuration intent of {@code transportName}. - * - * @throws TransportNotRegisteredException if the transport is not registered. - */ - @Nullable - public Intent getTransportConfigurationIntent(String transportName) - throws TransportNotRegisteredException { - synchronized (mTransportLock) { - return getRegisteredTransportDescriptionOrThrowLocked(transportName) - .configurationIntent; - } - } - - /** - * Retrieves the current destination string of {@code transportName}. - * - * @throws TransportNotRegisteredException if the transport is not registered. - */ - public String getTransportCurrentDestinationString(String transportName) - throws TransportNotRegisteredException { - synchronized (mTransportLock) { - return getRegisteredTransportDescriptionOrThrowLocked(transportName) - .currentDestinationString; - } - } - - /** - * Retrieves the data management intent of {@code transportName}. - * - * @throws TransportNotRegisteredException if the transport is not registered. - */ - @Nullable - public Intent getTransportDataManagementIntent(String transportName) - throws TransportNotRegisteredException { - synchronized (mTransportLock) { - return getRegisteredTransportDescriptionOrThrowLocked(transportName) - .dataManagementIntent; - } - } - - /** - * Retrieves the data management label of {@code transportName}. - * - * @throws TransportNotRegisteredException if the transport is not registered. - */ - @Nullable - public CharSequence getTransportDataManagementLabel(String transportName) - throws TransportNotRegisteredException { - synchronized (mTransportLock) { - return getRegisteredTransportDescriptionOrThrowLocked(transportName) - .dataManagementLabel; - } - } - - /* Returns true if the transport identified by {@code transportName} is registered. */ - public boolean isTransportRegistered(String transportName) { - synchronized (mTransportLock) { - return getRegisteredTransportEntryLocked(transportName) != null; - } - } - - /** - * Execute {@code transportConsumer} for each registered transport passing the transport name. - * This is called with an internal lock held, ensuring that the transport will remain registered - * while {@code transportConsumer} is being executed. Don't do heavy operations in {@code - * transportConsumer}. - * - * <p><b>Warning:</b> Do NOT make any calls to {@link IBackupTransport} or call any variants of - * {@link TransportClient#connect(String)} here, otherwise you risk deadlock. - */ - public void forEachRegisteredTransport(Consumer<String> transportConsumer) { - synchronized (mTransportLock) { - for (TransportDescription transportDescription : - mRegisteredTransportsDescriptionMap.values()) { - transportConsumer.accept(transportDescription.name); - } - } - } - - /** - * Updates given values for the transport already registered and identified with {@param - * transportComponent}. If the transport is not registered it will log and return. - */ - public void updateTransportAttributes( - ComponentName transportComponent, - String name, - @Nullable Intent configurationIntent, - String currentDestinationString, - @Nullable Intent dataManagementIntent, - @Nullable CharSequence dataManagementLabel) { - synchronized (mTransportLock) { - TransportDescription description = - mRegisteredTransportsDescriptionMap.get(transportComponent); - if (description == null) { - Slog.e(TAG, "Transport " + name + " not registered tried to change description"); - return; - } - description.name = name; - description.configurationIntent = configurationIntent; - description.currentDestinationString = currentDestinationString; - description.dataManagementIntent = dataManagementIntent; - description.dataManagementLabel = dataManagementLabel; - Slog.d(TAG, "Transport " + name + " updated its attributes"); - } - } - - @GuardedBy("mTransportLock") - private ComponentName getRegisteredTransportComponentOrThrowLocked(String transportName) - throws TransportNotRegisteredException { - ComponentName transportComponent = getRegisteredTransportComponentLocked(transportName); - if (transportComponent == null) { - throw new TransportNotRegisteredException(transportName); - } - return transportComponent; - } - - @GuardedBy("mTransportLock") - private TransportDescription getRegisteredTransportDescriptionOrThrowLocked( - ComponentName transportComponent) throws TransportNotRegisteredException { - TransportDescription description = - mRegisteredTransportsDescriptionMap.get(transportComponent); - if (description == null) { - throw new TransportNotRegisteredException(transportComponent); - } - return description; - } - - @GuardedBy("mTransportLock") - private TransportDescription getRegisteredTransportDescriptionOrThrowLocked( - String transportName) throws TransportNotRegisteredException { - TransportDescription description = getRegisteredTransportDescriptionLocked(transportName); - if (description == null) { - throw new TransportNotRegisteredException(transportName); - } - return description; - } - - @GuardedBy("mTransportLock") - @Nullable - private ComponentName getRegisteredTransportComponentLocked(String transportName) { - Map.Entry<ComponentName, TransportDescription> entry = - getRegisteredTransportEntryLocked(transportName); - return (entry == null) ? null : entry.getKey(); - } - - @GuardedBy("mTransportLock") - @Nullable - private TransportDescription getRegisteredTransportDescriptionLocked(String transportName) { - Map.Entry<ComponentName, TransportDescription> entry = - getRegisteredTransportEntryLocked(transportName); - return (entry == null) ? null : entry.getValue(); - } - - @GuardedBy("mTransportLock") - @Nullable - private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked( - String transportName) { - for (Map.Entry<ComponentName, TransportDescription> entry : - mRegisteredTransportsDescriptionMap.entrySet()) { - TransportDescription description = entry.getValue(); - if (transportName.equals(description.name)) { - return entry; - } - } - return null; - } - - /** - * Returns a {@link TransportClient} for {@code transportName} or {@code null} if not - * registered. - * - * @param transportName The name of the transport. - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more - * details. - * @return A {@link TransportClient} or null if not registered. - */ - @Nullable - public TransportClient getTransportClient(String transportName, String caller) { - try { - return getTransportClientOrThrow(transportName, caller); - } catch (TransportNotRegisteredException e) { - Slog.w(TAG, "Transport " + transportName + " not registered"); - return null; - } - } - - /** - * Returns a {@link TransportClient} for {@code transportName} or throws if not registered. - * - * @param transportName The name of the transport. - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more - * details. - * @return A {@link TransportClient}. - * @throws TransportNotRegisteredException if the transport is not registered. - */ - public TransportClient getTransportClientOrThrow(String transportName, String caller) - throws TransportNotRegisteredException { - synchronized (mTransportLock) { - ComponentName component = getRegisteredTransportComponentLocked(transportName); - if (component == null) { - throw new TransportNotRegisteredException(transportName); - } - return mTransportClientManager.getTransportClient(component, caller); - } - } - - /** - * Returns a {@link TransportClient} for the current transport or {@code null} if not - * registered. - * - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more - * details. - * @return A {@link TransportClient} or null if not registered. - * @throws IllegalStateException if no transport is selected. - */ - @Nullable - public TransportClient getCurrentTransportClient(String caller) { - if (mCurrentTransportName == null) { - throw new IllegalStateException("No transport selected"); - } - synchronized (mTransportLock) { - return getTransportClient(mCurrentTransportName, caller); - } - } - - /** - * Returns a {@link TransportClient} for the current transport or throws if not registered. - * - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more - * details. - * @return A {@link TransportClient}. - * @throws TransportNotRegisteredException if the transport is not registered. - * @throws IllegalStateException if no transport is selected. - */ - public TransportClient getCurrentTransportClientOrThrow(String caller) - throws TransportNotRegisteredException { - if (mCurrentTransportName == null) { - throw new IllegalStateException("No transport selected"); - } - synchronized (mTransportLock) { - return getTransportClientOrThrow(mCurrentTransportName, caller); - } - } - - /** - * Disposes of the {@link TransportClient}. - * - * @param transportClient The {@link TransportClient} to be disposed of. - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more - * details. - */ - public void disposeOfTransportClient(TransportClient transportClient, String caller) { - mTransportClientManager.disposeOfTransportClient(transportClient, caller); - } - - /** - * Sets {@code transportName} as selected transport and returns previously selected transport - * name. If there was no previous transport it returns null. - * - * <p>You should NOT call this method in new code. This won't make any checks against {@code - * transportName}, putting any operation at risk of a {@link TransportNotRegisteredException} or - * another error at the time it's being executed. - * - * <p>{@link Deprecated} as public, this method can be used as private. - */ - @Deprecated - @Nullable - String selectTransport(String transportName) { - synchronized (mTransportLock) { - String prevTransport = mCurrentTransportName; - mCurrentTransportName = transportName; - return prevTransport; - } - } - - /** - * Tries to register the transport if not registered. If successful also selects the transport. - * - * @param transportComponent Host of the transport. - * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} - * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. - */ - @WorkerThread - public int registerAndSelectTransport(ComponentName transportComponent) { - // If it's already registered we select and return - synchronized (mTransportLock) { - try { - selectTransport(getTransportName(transportComponent)); - return BackupManager.SUCCESS; - } catch (TransportNotRegisteredException e) { - // Fall through and release lock - } - } - - // We can't call registerTransport() with the transport lock held - int result = registerTransport(transportComponent); - if (result != BackupManager.SUCCESS) { - return result; - } - synchronized (mTransportLock) { - try { - selectTransport(getTransportName(transportComponent)); - return BackupManager.SUCCESS; - } catch (TransportNotRegisteredException e) { - Slog.wtf(TAG, "Transport got unregistered"); - return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; - } - } - } - - @WorkerThread - public void registerTransports() { - registerTransportsForIntent(mTransportServiceIntent, transportComponent -> true); - } - - @WorkerThread - private void registerTransportsFromPackage( - String packageName, Predicate<ComponentName> transportComponentFilter) { - try { - mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(TAG, "Trying to register transports from package not found " + packageName); - return; - } - - registerTransportsForIntent( - new Intent(mTransportServiceIntent).setPackage(packageName), - transportComponentFilter.and(fromPackageFilter(packageName))); - } - - @WorkerThread - private void registerTransportsForIntent( - Intent intent, Predicate<ComponentName> transportComponentFilter) { - List<ResolveInfo> hosts = - mPackageManager.queryIntentServicesAsUser(intent, 0, mUserId); - if (hosts == null) { - return; - } - for (ResolveInfo host : hosts) { - ComponentName transportComponent = host.serviceInfo.getComponentName(); - if (transportComponentFilter.test(transportComponent) - && isTransportTrusted(transportComponent)) { - registerTransport(transportComponent); - } - } - } - - /** Transport has to be whitelisted and privileged. */ - private boolean isTransportTrusted(ComponentName transport) { - if (!mTransportWhitelist.contains(transport)) { - Slog.w( - TAG, - "BackupTransport " + transport.flattenToShortString() + " not whitelisted."); - return false; - } - try { - PackageInfo packInfo = - mPackageManager.getPackageInfoAsUser(transport.getPackageName(), 0, mUserId); - if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) - == 0) { - Slog.w(TAG, "Transport package " + transport.getPackageName() + " not privileged"); - return false; - } - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Package not found.", e); - return false; - } - return true; - } - - /** - * Tries to register transport represented by {@code transportComponent}. - * - * <p><b>Warning:</b> Don't call this with the transport lock held. - * - * @param transportComponent Host of the transport that we want to register. - * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} - * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. - */ - @WorkerThread - private int registerTransport(ComponentName transportComponent) { - checkCanUseTransport(); - - if (!isTransportTrusted(transportComponent)) { - return BackupManager.ERROR_TRANSPORT_INVALID; - } - - String transportString = transportComponent.flattenToShortString(); - String callerLogString = "TransportManager.registerTransport()"; - - Bundle extras = new Bundle(); - extras.putBoolean(BackupTransport.EXTRA_TRANSPORT_REGISTRATION, true); - - TransportClient transportClient = - mTransportClientManager.getTransportClient( - transportComponent, extras, callerLogString); - final IBackupTransport transport; - try { - transport = transportClient.connectOrThrow(callerLogString); - } catch (TransportNotAvailableException e) { - Slog.e(TAG, "Couldn't connect to transport " + transportString + " for registration"); - mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); - return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; - } - - int result; - try { - String transportName = transport.name(); - String transportDirName = transport.transportDirName(); - registerTransport(transportComponent, transport); - // If registerTransport() hasn't thrown... - Slog.d(TAG, "Transport " + transportString + " registered"); - mOnTransportRegisteredListener.onTransportRegistered(transportName, transportDirName); - result = BackupManager.SUCCESS; - } catch (RemoteException e) { - Slog.e(TAG, "Transport " + transportString + " died while registering"); - result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE; - } - - mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); - return result; - } - - /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */ - private void registerTransport(ComponentName transportComponent, IBackupTransport transport) - throws RemoteException { - checkCanUseTransport(); - - TransportDescription description = - new TransportDescription( - transport.name(), - transport.transportDirName(), - transport.configurationIntent(), - transport.currentDestinationString(), - transport.dataManagementIntent(), - transport.dataManagementIntentLabel()); - synchronized (mTransportLock) { - mRegisteredTransportsDescriptionMap.put(transportComponent, description); - } - } - - private void checkCanUseTransport() { - Preconditions.checkState( - !Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held"); - } - - public void dumpTransportClients(PrintWriter pw) { - mTransportClientManager.dump(pw); - } - - public void dumpTransportStats(PrintWriter pw) { - mTransportStats.dump(pw); - } - - private static Predicate<ComponentName> fromPackageFilter(String packageName) { - return transportComponent -> packageName.equals(transportComponent.getPackageName()); - } - - private static class TransportDescription { - private String name; - private final String transportDirName; - @Nullable private Intent configurationIntent; - private String currentDestinationString; - @Nullable private Intent dataManagementIntent; - @Nullable private CharSequence dataManagementLabel; - - private TransportDescription( - String name, - String transportDirName, - @Nullable Intent configurationIntent, - String currentDestinationString, - @Nullable Intent dataManagementIntent, - @Nullable CharSequence dataManagementLabel) { - this.name = name; - this.transportDirName = transportDirName; - this.configurationIntent = configurationIntent; - this.currentDestinationString = currentDestinationString; - this.dataManagementIntent = dataManagementIntent; - this.dataManagementLabel = dataManagementLabel; - } - } -} diff --git a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java b/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java deleted file mode 100644 index 391ec2d7f294..000000000000 --- a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.transport; - -import com.android.server.backup.TransportManager; - -/** - * Listener called when a transport is registered with the {@link TransportManager}. Can be set - * using {@link TransportManager#setOnTransportRegisteredListener(OnTransportRegisteredListener)}. - */ -@FunctionalInterface -public interface OnTransportRegisteredListener { - /** - * Called when a transport is successfully registered. - * @param transportName The name of the transport. - * @param transportDirName The dir name of the transport. - */ - public void onTransportRegistered(String transportName, String transportDirName); -} diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java deleted file mode 100644 index 7c5a57c004e4..000000000000 --- a/services/backup/java/com/android/server/backup/transport/TransportClient.java +++ /dev/null @@ -1,697 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.transport; - -import static com.android.server.backup.transport.TransportUtils.formatMessage; - -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.annotation.WorkerThread; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.DeadObjectException; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.SystemClock; -import android.os.UserHandle; -import android.text.format.DateFormat; -import android.util.ArrayMap; -import android.util.EventLog; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.backup.IBackupTransport; -import com.android.internal.util.Preconditions; -import com.android.server.EventLogTags; -import com.android.server.backup.TransportManager; -import com.android.server.backup.transport.TransportUtils.Priority; - -import dalvik.system.CloseGuard; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -/** - * A {@link TransportClient} manages the connection to an {@link IBackupTransport} service, obtained - * via the {@param bindIntent} parameter provided in the constructor. A {@link TransportClient} is - * responsible for only one connection to the transport service, not more. - * - * <p>After retrieved using {@link TransportManager#getTransportClient(String, String)}, you can - * call either {@link #connect(String)}, if you can block your thread, or {@link - * #connectAsync(TransportConnectionListener, String)}, otherwise, to obtain a {@link - * IBackupTransport} instance. It's meant to be passed around as a token to a connected transport. - * When the connection is not needed anymore you should call {@link #unbind(String)} or indirectly - * via {@link TransportManager#disposeOfTransportClient(TransportClient, String)}. - * - * <p>DO NOT forget to unbind otherwise there will be dangling connections floating around. - * - * <p>This class is thread-safe. - * - * @see TransportManager - */ -public class TransportClient { - @VisibleForTesting static final String TAG = "TransportClient"; - private static final int LOG_BUFFER_SIZE = 5; - - private final @UserIdInt int mUserId; - private final Context mContext; - private final TransportStats mTransportStats; - private final Intent mBindIntent; - private final ServiceConnection mConnection; - private final String mIdentifier; - private final String mCreatorLogString; - private final ComponentName mTransportComponent; - private final Handler mListenerHandler; - private final String mPrefixForLog; - private final Object mStateLock = new Object(); - private final Object mLogBufferLock = new Object(); - private final CloseGuard mCloseGuard = CloseGuard.get(); - - @GuardedBy("mLogBufferLock") - private final List<String> mLogBuffer = new LinkedList<>(); - - @GuardedBy("mStateLock") - private final Map<TransportConnectionListener, String> mListeners = new ArrayMap<>(); - - @GuardedBy("mStateLock") - @State - private int mState = State.IDLE; - - @GuardedBy("mStateLock") - private volatile IBackupTransport mTransport; - - TransportClient( - @UserIdInt int userId, - Context context, - TransportStats transportStats, - Intent bindIntent, - ComponentName transportComponent, - String identifier, - String caller) { - this( - userId, - context, - transportStats, - bindIntent, - transportComponent, - identifier, - caller, - new Handler(Looper.getMainLooper())); - } - - @VisibleForTesting - TransportClient( - @UserIdInt int userId, - Context context, - TransportStats transportStats, - Intent bindIntent, - ComponentName transportComponent, - String identifier, - String caller, - Handler listenerHandler) { - mUserId = userId; - mContext = context; - mTransportStats = transportStats; - mTransportComponent = transportComponent; - mBindIntent = bindIntent; - mIdentifier = identifier; - mCreatorLogString = caller; - mListenerHandler = listenerHandler; - mConnection = new TransportConnection(context, this); - - // For logging - String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", ""); - mPrefixForLog = classNameForLog + "#" + mIdentifier + ":"; - - mCloseGuard.open("markAsDisposed"); - } - - public ComponentName getTransportComponent() { - return mTransportComponent; - } - - /** - * Attempts to connect to the transport (if needed). - * - * <p>Note that being bound is not the same as connected. To be connected you also need to be - * bound. You go from nothing to bound, then to bound and connected. To have a usable transport - * binder instance you need to be connected. This method will attempt to connect and return an - * usable transport binder regardless of the state of the object, it may already be connected, - * or bound but not connected, not bound at all or even unusable. - * - * <p>So, a {@link Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)} (or - * one of its variants) can be called or not depending on the inner state. However, it won't be - * called again if we're already bound. For example, if one was already requested but the - * framework has not yet returned (meaning we're bound but still trying to connect) it won't - * trigger another one, just piggyback on the original request. - * - * <p>It's guaranteed that you are going to get a call back to {@param listener} after this - * call. However, the {@param IBackupTransport} parameter, the transport binder, is not - * guaranteed to be non-null, or if it's non-null it's not guaranteed to be usable - i.e. it can - * throw {@link DeadObjectException}s on method calls. You should check for both in your code. - * The reasons for a null transport binder are: - * - * <ul> - * <li>Some code called {@link #unbind(String)} before you got a callback. - * <li>The framework had already called {@link - * ServiceConnection#onServiceDisconnected(ComponentName)} or {@link - * ServiceConnection#onBindingDied(ComponentName)} on this object's connection before. - * Check the documentation of those methods for when that happens. - * <li>The framework returns false for {@link Context#bindServiceAsUser(Intent, - * ServiceConnection, int, UserHandle)} (or one of its variants). Check documentation for - * when this happens. - * </ul> - * - * For unusable transport binders check {@link DeadObjectException}. - * - * @param listener The listener that will be called with the (possibly null or unusable) {@link - * IBackupTransport} instance and this {@link TransportClient} object. - * @param caller A {@link String} identifying the caller for logging/debugging purposes. This - * should be a human-readable short string that is easily identifiable in the logs. Ideally - * TAG.methodName(), where TAG is the one used in logcat. In cases where this is is not very - * descriptive like MyHandler.handleMessage() you should put something that someone reading - * the code would understand, like MyHandler/MSG_FOO. - * @see #connect(String) - * @see DeadObjectException - * @see ServiceConnection#onServiceConnected(ComponentName, IBinder) - * @see ServiceConnection#onServiceDisconnected(ComponentName) - * @see Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle) - */ - public void connectAsync(TransportConnectionListener listener, String caller) { - synchronized (mStateLock) { - checkStateIntegrityLocked(); - - switch (mState) { - case State.UNUSABLE: - log(Priority.WARN, caller, "Async connect: UNUSABLE client"); - notifyListener(listener, null, caller); - break; - case State.IDLE: - boolean hasBound = - mContext.bindServiceAsUser( - mBindIntent, - mConnection, - Context.BIND_AUTO_CREATE, - UserHandle.of(mUserId)); - if (hasBound) { - // We don't need to set a time-out because we are guaranteed to get a call - // back in ServiceConnection, either an onServiceConnected() or - // onBindingDied(). - log(Priority.DEBUG, caller, "Async connect: service bound, connecting"); - setStateLocked(State.BOUND_AND_CONNECTING, null); - mListeners.put(listener, caller); - } else { - log(Priority.ERROR, "Async connect: bindService returned false"); - // mState remains State.IDLE - mContext.unbindService(mConnection); - notifyListener(listener, null, caller); - } - break; - case State.BOUND_AND_CONNECTING: - log( - Priority.DEBUG, - caller, - "Async connect: already connecting, adding listener"); - mListeners.put(listener, caller); - break; - case State.CONNECTED: - log(Priority.DEBUG, caller, "Async connect: reusing transport"); - notifyListener(listener, mTransport, caller); - break; - } - } - } - - /** - * Removes the transport binding. - * - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link #connectAsync(TransportConnectionListener, String)} for more details. - */ - public void unbind(String caller) { - synchronized (mStateLock) { - checkStateIntegrityLocked(); - - log(Priority.DEBUG, caller, "Unbind requested (was " + stateToString(mState) + ")"); - switch (mState) { - case State.UNUSABLE: - case State.IDLE: - break; - case State.BOUND_AND_CONNECTING: - setStateLocked(State.IDLE, null); - // After unbindService() no calls back to mConnection - mContext.unbindService(mConnection); - notifyListenersAndClearLocked(null); - break; - case State.CONNECTED: - setStateLocked(State.IDLE, null); - mContext.unbindService(mConnection); - break; - } - } - } - - /** Marks this TransportClient as disposed, allowing it to be GC'ed without warnings. */ - public void markAsDisposed() { - synchronized (mStateLock) { - Preconditions.checkState( - mState < State.BOUND_AND_CONNECTING, "Can't mark as disposed if still bound"); - mCloseGuard.close(); - } - } - - /** - * Attempts to connect to the transport (if needed) and returns it. - * - * <p>Synchronous version of {@link #connectAsync(TransportConnectionListener, String)}. The - * same observations about state are valid here. Also, what was said about the {@link - * IBackupTransport} parameter of {@link TransportConnectionListener} now apply to the return - * value of this method. - * - * <p>This is a potentially blocking operation, so be sure to call this carefully on the correct - * threads. You can't call this from the process main-thread (it throws an exception if you do - * so). - * - * <p>In most cases only the first call to this method will block, the following calls should - * return instantly. However, this is not guaranteed. - * - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link #connectAsync(TransportConnectionListener, String)} for more details. - * @return A {@link IBackupTransport} transport binder instance or null. If it's non-null it can - * still be unusable - throws {@link DeadObjectException} on method calls - */ - @WorkerThread - @Nullable - public IBackupTransport connect(String caller) { - // If called on the main-thread this could deadlock waiting because calls to - // ServiceConnection are on the main-thread as well - Preconditions.checkState( - !Looper.getMainLooper().isCurrentThread(), "Can't call connect() on main thread"); - - IBackupTransport transport = mTransport; - if (transport != null) { - log(Priority.DEBUG, caller, "Sync connect: reusing transport"); - return transport; - } - - // If it's already UNUSABLE we return straight away, no need to go to main-thread - synchronized (mStateLock) { - if (mState == State.UNUSABLE) { - log(Priority.WARN, caller, "Sync connect: UNUSABLE client"); - return null; - } - } - - CompletableFuture<IBackupTransport> transportFuture = new CompletableFuture<>(); - TransportConnectionListener requestListener = - (requestedTransport, transportClient) -> - transportFuture.complete(requestedTransport); - - long requestTime = SystemClock.elapsedRealtime(); - log(Priority.DEBUG, caller, "Sync connect: calling async"); - connectAsync(requestListener, caller); - - try { - transport = transportFuture.get(); - long time = SystemClock.elapsedRealtime() - requestTime; - mTransportStats.registerConnectionTime(mTransportComponent, time); - log(Priority.DEBUG, caller, String.format(Locale.US, "Connect took %d ms", time)); - return transport; - } catch (InterruptedException | ExecutionException e) { - String error = e.getClass().getSimpleName(); - log(Priority.ERROR, caller, error + " while waiting for transport: " + e.getMessage()); - return null; - } - } - - /** - * Tries to connect to the transport, if it fails throws {@link TransportNotAvailableException}. - * - * <p>Same as {@link #connect(String)} except it throws instead of returning null. - * - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link #connectAsync(TransportConnectionListener, String)} for more details. - * @return A {@link IBackupTransport} transport binder instance. - * @see #connect(String) - * @throws TransportNotAvailableException if connection attempt fails. - */ - @WorkerThread - public IBackupTransport connectOrThrow(String caller) throws TransportNotAvailableException { - IBackupTransport transport = connect(caller); - if (transport == null) { - log(Priority.ERROR, caller, "Transport connection failed"); - throw new TransportNotAvailableException(); - } - return transport; - } - - /** - * If the {@link TransportClient} is already connected to the transport, returns the transport, - * otherwise throws {@link TransportNotAvailableException}. - * - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link #connectAsync(TransportConnectionListener, String)} for more details. - * @return A {@link IBackupTransport} transport binder instance. - * @throws TransportNotAvailableException if not connected. - */ - public IBackupTransport getConnectedTransport(String caller) - throws TransportNotAvailableException { - IBackupTransport transport = mTransport; - if (transport == null) { - log(Priority.ERROR, caller, "Transport not connected"); - throw new TransportNotAvailableException(); - } - return transport; - } - - @Override - public String toString() { - return "TransportClient{" - + mTransportComponent.flattenToShortString() - + "#" - + mIdentifier - + "}"; - } - - @Override - protected void finalize() throws Throwable { - synchronized (mStateLock) { - mCloseGuard.warnIfOpen(); - if (mState >= State.BOUND_AND_CONNECTING) { - String callerLogString = "TransportClient.finalize()"; - log( - Priority.ERROR, - callerLogString, - "Dangling TransportClient created in [" + mCreatorLogString + "] being " - + "GC'ed. Left bound, unbinding..."); - try { - unbind(callerLogString); - } catch (IllegalStateException e) { - // May throw because there may be a race between this method being called and - // the framework calling any method on the connection with the weak reference - // there already cleared. In this case the connection will unbind before this - // is called. This is fine. - } - } - } - } - - private void onServiceConnected(IBinder binder) { - IBackupTransport transport = IBackupTransport.Stub.asInterface(binder); - synchronized (mStateLock) { - checkStateIntegrityLocked(); - - if (mState != State.UNUSABLE) { - log(Priority.DEBUG, "Transport connected"); - setStateLocked(State.CONNECTED, transport); - notifyListenersAndClearLocked(transport); - } - } - } - - /** - * If we are called here the TransportClient becomes UNUSABLE. After one of these calls, if a - * binding happen again the new service can be a different instance. Since transports are - * stateful, we don't want a new instance responding for an old instance's state. - */ - private void onServiceDisconnected() { - synchronized (mStateLock) { - log(Priority.ERROR, "Service disconnected: client UNUSABLE"); - setStateLocked(State.UNUSABLE, null); - try { - // After unbindService() no calls back to mConnection - mContext.unbindService(mConnection); - } catch (IllegalArgumentException e) { - // TODO: Investigate why this is happening - // We're UNUSABLE, so any calls to mConnection will be no-op, so it's safe to - // swallow this one - log( - Priority.WARN, - "Exception trying to unbind onServiceDisconnected(): " + e.getMessage()); - } - } - } - - /** - * If we are called here the TransportClient becomes UNUSABLE for the same reason as in {@link - * #onServiceDisconnected()}. - */ - private void onBindingDied() { - synchronized (mStateLock) { - checkStateIntegrityLocked(); - - log(Priority.ERROR, "Binding died: client UNUSABLE"); - // After unbindService() no calls back to mConnection - switch (mState) { - case State.UNUSABLE: - break; - case State.IDLE: - log(Priority.ERROR, "Unexpected state transition IDLE => UNUSABLE"); - setStateLocked(State.UNUSABLE, null); - break; - case State.BOUND_AND_CONNECTING: - setStateLocked(State.UNUSABLE, null); - mContext.unbindService(mConnection); - notifyListenersAndClearLocked(null); - break; - case State.CONNECTED: - setStateLocked(State.UNUSABLE, null); - mContext.unbindService(mConnection); - break; - } - } - } - - private void notifyListener( - TransportConnectionListener listener, - @Nullable IBackupTransport transport, - String caller) { - String transportString = (transport != null) ? "IBackupTransport" : "null"; - log(Priority.INFO, "Notifying [" + caller + "] transport = " + transportString); - mListenerHandler.post(() -> listener.onTransportConnectionResult(transport, this)); - } - - @GuardedBy("mStateLock") - private void notifyListenersAndClearLocked(@Nullable IBackupTransport transport) { - for (Map.Entry<TransportConnectionListener, String> entry : mListeners.entrySet()) { - TransportConnectionListener listener = entry.getKey(); - String caller = entry.getValue(); - notifyListener(listener, transport, caller); - } - mListeners.clear(); - } - - @GuardedBy("mStateLock") - private void setStateLocked(@State int state, @Nullable IBackupTransport transport) { - log(Priority.VERBOSE, "State: " + stateToString(mState) + " => " + stateToString(state)); - onStateTransition(mState, state); - mState = state; - mTransport = transport; - } - - private void onStateTransition(int oldState, int newState) { - String transport = mTransportComponent.flattenToShortString(); - int bound = transitionThroughState(oldState, newState, State.BOUND_AND_CONNECTING); - int connected = transitionThroughState(oldState, newState, State.CONNECTED); - if (bound != Transition.NO_TRANSITION) { - int value = (bound == Transition.UP) ? 1 : 0; // 1 is bound, 0 is not bound - EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transport, value); - } - if (connected != Transition.NO_TRANSITION) { - int value = (connected == Transition.UP) ? 1 : 0; // 1 is connected, 0 is not connected - EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_CONNECTION, transport, value); - } - } - - /** - * Returns: - * - * <ul> - * <li>{@link Transition#UP}, if oldState < stateReference <= newState - * <li>{@link Transition#DOWN}, if oldState >= stateReference > newState - * <li>{@link Transition#NO_TRANSITION}, otherwise - */ - @Transition - private int transitionThroughState( - @State int oldState, @State int newState, @State int stateReference) { - if (oldState < stateReference && stateReference <= newState) { - return Transition.UP; - } - if (oldState >= stateReference && stateReference > newState) { - return Transition.DOWN; - } - return Transition.NO_TRANSITION; - } - - @GuardedBy("mStateLock") - private void checkStateIntegrityLocked() { - switch (mState) { - case State.UNUSABLE: - checkState(mListeners.isEmpty(), "Unexpected listeners when state = UNUSABLE"); - checkState( - mTransport == null, "Transport expected to be null when state = UNUSABLE"); - case State.IDLE: - checkState(mListeners.isEmpty(), "Unexpected listeners when state = IDLE"); - checkState(mTransport == null, "Transport expected to be null when state = IDLE"); - break; - case State.BOUND_AND_CONNECTING: - checkState( - mTransport == null, - "Transport expected to be null when state = BOUND_AND_CONNECTING"); - break; - case State.CONNECTED: - checkState(mListeners.isEmpty(), "Unexpected listeners when state = CONNECTED"); - checkState( - mTransport != null, - "Transport expected to be non-null when state = CONNECTED"); - break; - default: - checkState(false, "Unexpected state = " + stateToString(mState)); - } - } - - private void checkState(boolean assertion, String message) { - if (!assertion) { - log(Priority.ERROR, message); - } - } - - private String stateToString(@State int state) { - switch (state) { - case State.UNUSABLE: - return "UNUSABLE"; - case State.IDLE: - return "IDLE"; - case State.BOUND_AND_CONNECTING: - return "BOUND_AND_CONNECTING"; - case State.CONNECTED: - return "CONNECTED"; - default: - return "<UNKNOWN = " + state + ">"; - } - } - - private void log(int priority, String message) { - TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, null, message)); - saveLogEntry(formatMessage(null, null, message)); - } - - private void log(int priority, String caller, String message) { - TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, caller, message)); - saveLogEntry(formatMessage(null, caller, message)); - } - - private void saveLogEntry(String message) { - CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis()); - message = time + " " + message; - synchronized (mLogBufferLock) { - if (mLogBuffer.size() == LOG_BUFFER_SIZE) { - mLogBuffer.remove(mLogBuffer.size() - 1); - } - mLogBuffer.add(0, message); - } - } - - List<String> getLogBuffer() { - synchronized (mLogBufferLock) { - return Collections.unmodifiableList(mLogBuffer); - } - } - - @IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP}) - @Retention(RetentionPolicy.SOURCE) - private @interface Transition { - int DOWN = -1; - int NO_TRANSITION = 0; - int UP = 1; - } - - @IntDef({State.UNUSABLE, State.IDLE, State.BOUND_AND_CONNECTING, State.CONNECTED}) - @Retention(RetentionPolicy.SOURCE) - private @interface State { - // Constant values MUST be in order - int UNUSABLE = 0; - int IDLE = 1; - int BOUND_AND_CONNECTING = 2; - int CONNECTED = 3; - } - - /** - * This class is a proxy to TransportClient methods that doesn't hold a strong reference to the - * TransportClient, allowing it to be GC'ed. If the reference was lost it logs a message. - */ - private static class TransportConnection implements ServiceConnection { - private final Context mContext; - private final WeakReference<TransportClient> mTransportClientRef; - - private TransportConnection(Context context, TransportClient transportClient) { - mContext = context; - mTransportClientRef = new WeakReference<>(transportClient); - } - - @Override - public void onServiceConnected(ComponentName transportComponent, IBinder binder) { - TransportClient transportClient = mTransportClientRef.get(); - if (transportClient == null) { - referenceLost("TransportConnection.onServiceConnected()"); - return; - } - transportClient.onServiceConnected(binder); - } - - @Override - public void onServiceDisconnected(ComponentName transportComponent) { - TransportClient transportClient = mTransportClientRef.get(); - if (transportClient == null) { - referenceLost("TransportConnection.onServiceDisconnected()"); - return; - } - transportClient.onServiceDisconnected(); - } - - @Override - public void onBindingDied(ComponentName transportComponent) { - TransportClient transportClient = mTransportClientRef.get(); - if (transportClient == null) { - referenceLost("TransportConnection.onBindingDied()"); - return; - } - transportClient.onBindingDied(); - } - - /** @see TransportClient#finalize() */ - private void referenceLost(String caller) { - mContext.unbindService(this); - TransportUtils.log( - Priority.INFO, - TAG, - caller + " called but TransportClient reference has been GC'ed"); - } - } -} diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java deleted file mode 100644 index a4e9b1091bed..000000000000 --- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.transport; - -import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST; -import static com.android.server.backup.transport.TransportUtils.formatMessage; - -import android.annotation.UserIdInt; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -import com.android.server.backup.TransportManager; -import com.android.server.backup.transport.TransportUtils.Priority; - -import java.io.PrintWriter; -import java.util.Map; -import java.util.WeakHashMap; - -/** - * Manages the creation and disposal of {@link TransportClient}s. The only class that should use - * this is {@link TransportManager}, all the other usages should go to {@link TransportManager}. - */ -public class TransportClientManager { - private static final String TAG = "TransportClientManager"; - - private final @UserIdInt int mUserId; - private final Context mContext; - private final TransportStats mTransportStats; - private final Object mTransportClientsLock = new Object(); - private int mTransportClientsCreated = 0; - private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>(); - - public TransportClientManager(@UserIdInt int userId, Context context, - TransportStats transportStats) { - mUserId = userId; - mContext = context; - mTransportStats = transportStats; - } - - /** - * Retrieves a {@link TransportClient} for the transport identified by {@param - * transportComponent}. - * - * @param transportComponent The {@link ComponentName} of the transport. - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more - * details. - * @return A {@link TransportClient}. - */ - public TransportClient getTransportClient(ComponentName transportComponent, String caller) { - Intent bindIntent = - new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent); - - return getTransportClient(transportComponent, caller, bindIntent); - } - - /** - * Retrieves a {@link TransportClient} for the transport identified by {@param - * transportComponent} whose binding intent will have the {@param extras} extras. - * - * @param transportComponent The {@link ComponentName} of the transport. - * @param extras A {@link Bundle} of extras to pass to the binding intent. - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more - * details. - * @return A {@link TransportClient}. - */ - public TransportClient getTransportClient( - ComponentName transportComponent, Bundle extras, String caller) { - Intent bindIntent = - new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent); - bindIntent.putExtras(extras); - - return getTransportClient(transportComponent, caller, bindIntent); - } - - private TransportClient getTransportClient( - ComponentName transportComponent, String caller, Intent bindIntent) { - synchronized (mTransportClientsLock) { - TransportClient transportClient = - new TransportClient( - mUserId, - mContext, - mTransportStats, - bindIntent, - transportComponent, - Integer.toString(mTransportClientsCreated), - caller); - mTransportClientsCallerMap.put(transportClient, caller); - mTransportClientsCreated++; - TransportUtils.log( - Priority.DEBUG, - TAG, - formatMessage(null, caller, "Retrieving " + transportClient)); - return transportClient; - } - } - - /** - * Disposes of the {@link TransportClient}. - * - * @param transportClient The {@link TransportClient} to be disposed of. - * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check - * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more - * details. - */ - public void disposeOfTransportClient(TransportClient transportClient, String caller) { - transportClient.unbind(caller); - transportClient.markAsDisposed(); - synchronized (mTransportClientsLock) { - TransportUtils.log( - Priority.DEBUG, - TAG, - formatMessage(null, caller, "Disposing of " + transportClient)); - mTransportClientsCallerMap.remove(transportClient); - } - } - - public void dump(PrintWriter pw) { - pw.println("Transport clients created: " + mTransportClientsCreated); - synchronized (mTransportClientsLock) { - pw.println("Current transport clients: " + mTransportClientsCallerMap.size()); - for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) { - String caller = mTransportClientsCallerMap.get(transportClient); - pw.println(" " + transportClient + " [" + caller + "]"); - for (String logEntry : transportClient.getLogBuffer()) { - pw.println(" " + logEntry); - } - } - } - } -} diff --git a/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java b/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java deleted file mode 100644 index 1ccffd01d12c..000000000000 --- a/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.transport; - -import android.annotation.Nullable; - -import com.android.internal.backup.IBackupTransport; - -/** - * Listener to be called by {@link TransportClient#connectAsync(TransportConnectionListener, - * String)}. - */ -public interface TransportConnectionListener { - /** - * Called when {@link TransportClient} has a transport binder available or that it decided it - * couldn't obtain one, in which case {@param transport} is null. - * - * @param transport A {@link IBackupTransport} transport binder or null. - * @param transportClient The {@link TransportClient} used to retrieve this transport binder. - */ - void onTransportConnectionResult( - @Nullable IBackupTransport transport, TransportClient transportClient); -} diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java b/services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java deleted file mode 100644 index c08eb7f4a54e..000000000000 --- a/services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.transport; - -import android.util.AndroidException; - -import com.android.internal.backup.IBackupTransport; - -/** - * Exception thrown when the {@link IBackupTransport} is not available. This happen when a {@link - * TransportClient} connection attempt fails. Check {@link - * TransportClient#connectAsync(TransportConnectionListener, String)} for when that happens. - * - * @see TransportClient#connectAsync(TransportConnectionListener, String) - */ -public class TransportNotAvailableException extends AndroidException { - TransportNotAvailableException() { - super("Transport not available"); - } -} diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java deleted file mode 100644 index 02766deeb7e2..000000000000 --- a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.transport; - -import android.content.ComponentName; -import android.util.AndroidException; - -import com.android.server.backup.TransportManager; - -/** - * Exception thrown when the transport is not registered. - * - * @see TransportManager#getTransportDirName(String) - * @see TransportManager#getTransportConfigurationIntent(String) - * @see TransportManager#getTransportDataManagementIntent(String) - * @see TransportManager#getTransportDataManagementLabel(String) - */ -public class TransportNotRegisteredException extends AndroidException { - public TransportNotRegisteredException(String transportName) { - super("Transport " + transportName + " not registered"); - } - - public TransportNotRegisteredException(ComponentName transportComponent) { - super("Transport for host " + transportComponent + " not registered"); - } -} diff --git a/services/backup/java/com/android/server/backup/transport/TransportStats.java b/services/backup/java/com/android/server/backup/transport/TransportStats.java deleted file mode 100644 index bd84782122ad..000000000000 --- a/services/backup/java/com/android/server/backup/transport/TransportStats.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.transport; - -import android.annotation.Nullable; -import android.content.ComponentName; - -import java.io.PrintWriter; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; - -/** Responsible for aggregating {@link TransportClient} relevant times. */ -public class TransportStats { - private final Object mStatsLock = new Object(); - private final Map<ComponentName, Stats> mTransportStats = new HashMap<>(); - - void registerConnectionTime(ComponentName transportComponent, long timeMs) { - synchronized (mStatsLock) { - Stats stats = mTransportStats.get(transportComponent); - if (stats == null) { - stats = new Stats(); - mTransportStats.put(transportComponent, stats); - } - stats.register(timeMs); - } - } - - /** Returns {@link Stats} for transport whose host service is {@code transportComponent}. */ - @Nullable - public Stats getStatsForTransport(ComponentName transportComponent) { - synchronized (mStatsLock) { - Stats stats = mTransportStats.get(transportComponent); - if (stats == null) { - return null; - } - return new Stats(stats); - } - } - - public void dump(PrintWriter pw) { - synchronized (mStatsLock) { - Optional<Stats> aggregatedStats = - mTransportStats.values().stream().reduce(Stats::merge); - if (aggregatedStats.isPresent()) { - dumpStats(pw, "", aggregatedStats.get()); - } - if (!mTransportStats.isEmpty()) { - pw.println("Per transport:"); - for (ComponentName transportComponent : mTransportStats.keySet()) { - Stats stats = mTransportStats.get(transportComponent); - pw.println(" " + transportComponent.flattenToShortString()); - dumpStats(pw, " ", stats); - } - } - } - } - - private static void dumpStats(PrintWriter pw, String prefix, Stats stats) { - pw.println( - String.format( - Locale.US, "%sAverage connection time: %.2f ms", prefix, stats.average)); - pw.println(String.format(Locale.US, "%sMax connection time: %d ms", prefix, stats.max)); - pw.println(String.format(Locale.US, "%sMin connection time: %d ms", prefix, stats.min)); - pw.println(String.format(Locale.US, "%sNumber of connections: %d ", prefix, stats.n)); - } - - public static final class Stats { - public static Stats merge(Stats a, Stats b) { - return new Stats( - a.n + b.n, - (a.average * a.n + b.average * b.n) / (a.n + b.n), - Math.max(a.max, b.max), - Math.min(a.min, b.min)); - } - - public int n; - public double average; - public long max; - public long min; - - public Stats() { - n = 0; - average = 0; - max = 0; - min = Long.MAX_VALUE; - } - - private Stats(int n, double average, long max, long min) { - this.n = n; - this.average = average; - this.max = max; - this.min = min; - } - - private Stats(Stats original) { - this(original.n, original.average, original.max, original.min); - } - - private void register(long sample) { - average = (average * n + sample) / (n + 1); - n++; - max = Math.max(max, sample); - min = Math.min(min, sample); - } - } -} diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java deleted file mode 100644 index 766d77bd639c..000000000000 --- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.transport; - -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.os.DeadObjectException; -import android.util.Log; -import android.util.Slog; - -import com.android.internal.backup.IBackupTransport; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** Utility methods for transport-related operations. */ -public class TransportUtils { - private static final String TAG = "TransportUtils"; - - /** - * Throws {@link TransportNotAvailableException} if {@param transport} is null. The semantics is - * similar to a {@link DeadObjectException} coming from a dead transport binder. - */ - public static IBackupTransport checkTransportNotNull(@Nullable IBackupTransport transport) - throws TransportNotAvailableException { - if (transport == null) { - log(Priority.ERROR, TAG, "Transport not available"); - throw new TransportNotAvailableException(); - } - return transport; - } - - static void log(@Priority int priority, String tag, String message) { - if (priority == Priority.WTF) { - Slog.wtf(tag, message); - } else if (Log.isLoggable(tag, priority)) { - Slog.println(priority, tag, message); - } - } - - static String formatMessage(@Nullable String prefix, @Nullable String caller, String message) { - StringBuilder string = new StringBuilder(); - if (prefix != null) { - string.append(prefix).append(" "); - } - if (caller != null) { - string.append("[").append(caller).append("] "); - } - return string.append(message).toString(); - } - - /** - * Create our own constants so we can log WTF using the same APIs. Except for {@link - * Priority#WTF} all the others have the same value, so can be used directly - */ - @IntDef({Priority.VERBOSE, Priority.DEBUG, Priority.INFO, Priority.WARN, Priority.WTF}) - @Retention(RetentionPolicy.SOURCE) - @interface Priority { - int VERBOSE = Log.VERBOSE; - int DEBUG = Log.DEBUG; - int INFO = Log.INFO; - int WARN = Log.WARN; - int ERROR = Log.ERROR; - int WTF = -1; - } - - private TransportUtils() {} -} |