From 41349c02a8867b956ff48752c2d3dffc124fc0fc Mon Sep 17 00:00:00 2001 From: Bernardo Rufino Date: Wed, 10 Jan 2018 21:09:28 +0000 Subject: [PATCH] Binding on-demand #10: Remove permanent binding and add registration This CL is the last of the P1 CLs for binding on-demand. During transport-usage migration to binding on-demand the permanent-bound code was still there and we piggybacked on it to register the transports. Now that everything is migrated we removed the permanent binding and used the registration code created in the CL #9 (selectTransport) for registering all the transports. I put a 3s delay on registration after bring-up. Any operation that uses the transport before that will gracefully fail. The TransportBoundListener does not return a boolean anymore because it can't fail anymore (we pass it the data it needs so that it doesn't need to be exposed to TransportNotRegistered exceptions). It's now called OnTransportRegisteredListener. Eligible transports were a similar thing to valid transports from the permanent binding code, whose only need came from rebinding from what I could tell. I saw no need for it anymore and removed it. If I missed something please shout :) I shuffled methods a bit in TransportManager. There were a lot of changes to robo test infra to bring together TransportManager tests and the rest and re-use mocking infra. Change-Id: If61268228dd0bb724b718abd3dcafdad50e8b3dc Ref: http://go/br-binding-on-demand Bug: 17140907 Test: m -j RunFrameworksServicesRoboTest Test: runtest -p com.android.server.backup frameworks-services Test: gts-tradefed run commandAndExit gts-dev -m GtsBackupTestCases Test: gts-tradefed run commandAndExit gts-dev -m GtsBackupHostTestCases Test: cts-tradefed run commandAndExit cts-dev -m CtsBackupTestCases Test: adb shell bmgr enable true/false Test: adb shell bmgr list transports [-c] Test: adb shell bmgr transport /-c Test: adb shell bmgr restore / / Test: adb shell bmgr backup Test: adb shell bmgr run Test: adb shell bmgr wipe Test: adb shell bmgr fullbackup Test: adb shell bmgr backupnow --all/ Test: adb shell cmd jobscheduler run -f android Test: adb shell dumpsys backup Test: D2D scenario --- .../RefactoredBackupManagerService.java | 82 +- .../server/backup/TransportManager.java | 867 ++++++----------- .../OnTransportRegisteredListener.java | 33 + .../backup/transport/TransportClient.java | 2 +- .../backup/BackupManagerServiceRoboTest.java | 185 ++-- .../server/backup/TransportManagerTest.java | 906 ++++++++---------- .../internal/PerformInitializeTaskTest.java | 222 +++-- .../server/backup/testing/TestUtils.java | 68 ++ .../testing/TransportBoundListenerStub.java | 64 -- .../server/backup/testing/TransportData.java | 149 +++ .../backup/testing/TransportTestUtils.java | 198 ++-- .../shadows/FrameworkShadowContextImpl.java | 37 + .../FrameworkShadowPackageManager.java} | 12 +- .../backup/BackupManagerServiceTest.java | 2 +- 14 files changed, 1375 insertions(+), 1452 deletions(-) create mode 100644 services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java create mode 100644 services/robotests/src/com/android/server/backup/testing/TestUtils.java delete mode 100644 services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java create mode 100644 services/robotests/src/com/android/server/backup/testing/TransportData.java create mode 100644 services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java rename services/robotests/src/com/android/server/{backup/testing/ShadowPackageManagerForBackup.java => testing/shadows/FrameworkShadowPackageManager.java} (84%) diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 03591a812bddd..350d7af78a4ca 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -207,6 +207,10 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED"; public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName"; + // Time delay for initialization operations that can be delayed so as not to consume too much CPU + // on bring-up and increase time-to-UI. + private static final long INITIALIZATION_DELAY_MILLIS = 3000; + // Timeout interval for deciding that a bind or clear-data has taken too long private static final long TIMEOUT_INTERVAL = 10 * 1000; @@ -282,6 +286,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter private final BackupPasswordManager mBackupPasswordManager; + // Time when we post the transport registration operation + private final long mRegisterTransportsRequestedTime; + @GuardedBy("mPendingRestores") private boolean mIsRestoreInProgress; @GuardedBy("mPendingRestores") @@ -735,6 +742,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // Set up our transport options and initialize the default transport SystemConfig systemConfig = SystemConfig.getInstance(); Set transportWhitelist = systemConfig.getBackupTransportWhitelist(); + if (transportWhitelist == null) { + transportWhitelist = Collections.emptySet(); + } String transport = Settings.Secure.getString( @@ -749,8 +759,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter new TransportManager( context, transportWhitelist, - transport, - backupThread.getLooper()); + transport); // If encrypted file systems is enabled or disabled, this call will return the // correct directory. @@ -852,12 +861,14 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } mTransportManager = transportManager; - mTransportManager.setTransportBoundListener(mTransportBoundListener); - mTransportManager.registerAllTransports(); + mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered); + mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime(); + mBackupHandler.postDelayed( + mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS); - // Now that we know about valid backup participants, parse any - // leftover journal files into the pending backup set - mBackupHandler.post(this::parseLeftoverJournals); + // Now that we know about valid backup participants, parse any leftover journal files into + // the pending backup set + mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS); // Power management mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"); @@ -1151,39 +1162,28 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } - private TransportManager.TransportBoundListener mTransportBoundListener = - new TransportManager.TransportBoundListener() { - @Override - public boolean onTransportBound(IBackupTransport transport) { - // If the init sentinel file exists, we need to be sure to perform the init - // as soon as practical. We also create the state directory at registration - // time to ensure it's present from the outset. - String name = null; - try { - name = transport.name(); - String transportDirName = transport.transportDirName(); - File stateDir = new File(mBaseStateDir, transportDirName); - stateDir.mkdirs(); + private void onTransportRegistered(String transportName, String transportDirName) { + if (DEBUG) { + long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime; + Slog.d(TAG, "Transport " + transportName + " registered " + timeMs + + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)"); + } - File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME); - if (initSentinel.exists()) { - synchronized (mQueueLock) { - mPendingInits.add(name); + File stateDir = new File(mBaseStateDir, transportDirName); + stateDir.mkdirs(); - // TODO: pick a better starting time than now + 1 minute - long delay = 1000 * 60; // one minute, in milliseconds - mAlarmManager.set(AlarmManager.RTC_WAKEUP, - System.currentTimeMillis() + delay, mRunInitIntent); - } - } - return true; - } catch (Exception e) { - // the transport threw when asked its file naming prefs; declare it invalid - Slog.w(TAG, "Failed to regiser transport: " + name); - return false; - } - } - }; + File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME); + if (initSentinel.exists()) { + synchronized (mQueueLock) { + mPendingInits.add(transportName); + + // TODO: pick a better starting time than now + 1 minute + long delay = 1000 * 60; // one minute, in milliseconds + mAlarmManager.set(AlarmManager.RTC_WAKEUP, + System.currentTimeMillis() + delay, mRunInitIntent); + } + } + } // ----- Track installation/removal of packages ----- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -2891,14 +2891,14 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); - return mTransportManager.getBoundTransportNames(); + return mTransportManager.getRegisteredTransportNames(); } @Override public ComponentName[] listAllTransportComponents() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransportComponents"); - return mTransportManager.getAllTransportComponents(); + return mTransportManager.getRegisteredTransportComponents(); } @Override @@ -3003,6 +3003,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter /** Selects transport {@code transportName} and returns previous selected transport. */ @Override + @Deprecated + @Nullable public String selectBackupTransport(String transportName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BACKUP, "selectBackupTransport"); diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java index 34b8935939ff7..09456b68e1c0f 100644 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ b/services/backup/java/com/android/server/backup/TransportManager.java @@ -16,93 +16,56 @@ package com.android.server.backup; -import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; - import android.annotation.Nullable; +import android.annotation.WorkerThread; import android.app.backup.BackupManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; -import android.provider.Settings; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.EventLog; -import android.util.Log; 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.EventLogTags; +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 java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; -/** - * Handles in-memory bookkeeping of all BackupTransport objects. - */ +/** 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 static final long REBINDING_TIMEOUT_UNPROVISIONED_MS = 30 * 1000; // 30 sec - private static final long REBINDING_TIMEOUT_PROVISIONED_MS = 5 * 60 * 1000; // 5 mins - private static final int REBINDING_TIMEOUT_MSG = 1; - private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST); private final Context mContext; private final PackageManager mPackageManager; private final Set mTransportWhitelist; - private final Handler mHandler; private final TransportClientManager mTransportClientManager; - - /** - * This listener is called after we bind to any transport. If it returns true, this is a valid - * transport. - */ - private TransportBoundListener mTransportBoundListener; - private final Object mTransportLock = new Object(); - - /** - * We have detected these transports on the device. Unless in exceptional cases, we are also - * bound to all of these. - */ - @GuardedBy("mTransportLock") - private final Map mValidTransports = new ArrayMap<>(); - - /** We are currently bound to these transports. */ - @GuardedBy("mTransportLock") - private final Map mBoundTransports = new ArrayMap<>(); - - /** @see #getEligibleTransportComponents() */ - @GuardedBy("mTransportLock") - private final Set mEligibleTransports = new ArraySet<>(); + private OnTransportRegisteredListener mOnTransportRegisteredListener = (c, n) -> {}; /** @see #getRegisteredTransportNames() */ @GuardedBy("mTransportLock") @@ -110,120 +73,98 @@ public class TransportManager { new ArrayMap<>(); @GuardedBy("mTransportLock") + @Nullable private volatile String mCurrentTransportName; - TransportManager( - Context context, - Set whitelist, - String defaultTransport, - TransportBoundListener listener, - Looper looper) { - this(context, whitelist, defaultTransport, looper); - mTransportBoundListener = listener; + TransportManager(Context context, Set whitelist, String selectedTransport) { + this(context, whitelist, selectedTransport, new TransportClientManager(context)); } + @VisibleForTesting TransportManager( Context context, Set whitelist, - String defaultTransport, - Looper looper) { + String selectedTransport, + TransportClientManager transportClientManager) { mContext = context; mPackageManager = context.getPackageManager(); - if (whitelist != null) { - mTransportWhitelist = whitelist; - } else { - mTransportWhitelist = new ArraySet<>(); - } - mCurrentTransportName = defaultTransport; - mHandler = new RebindOnTimeoutHandler(looper); - mTransportClientManager = new TransportClientManager(context); + mTransportWhitelist = Preconditions.checkNotNull(whitelist); + mCurrentTransportName = selectedTransport; + mTransportClientManager = transportClientManager; } - public void setTransportBoundListener(TransportBoundListener transportBoundListener) { - mTransportBoundListener = transportBoundListener; + /* Sets a listener to be called whenever a transport is registered. */ + public void setOnTransportRegisteredListener(OnTransportRegisteredListener listener) { + mOnTransportRegisteredListener = listener; } + @WorkerThread void onPackageAdded(String packageName) { - // New package added. Bind to all transports it contains. - synchronized (mTransportLock) { - log_verbose("Package added. Binding to all transports. " + packageName); - bindToAllInternal(packageName, null /* all components */); - } + registerTransportsFromPackage(packageName, transportComponent -> true); } void onPackageRemoved(String packageName) { - // Package removed. Remove all its transports from our list. These transports have already - // been removed from mBoundTransports because onServiceDisconnected would already been - // called on TransportConnection objects. synchronized (mTransportLock) { - Iterator> iter = - mValidTransports.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry validTransport = iter.next(); - ComponentName componentName = validTransport.getKey(); - if (componentName.getPackageName().equals(packageName)) { - TransportConnection transportConnection = validTransport.getValue(); - iter.remove(); - if (transportConnection != null) { - mContext.unbindService(transportConnection); - log_verbose("Package removed, removing transport: " - + componentName.flattenToShortString()); - } - } - } - removeTransportsIfLocked( - componentName -> packageName.equals(componentName.getPackageName())); + mRegisteredTransportsDescriptionMap.keySet().removeIf(fromPackageFilter(packageName)); } } - void onPackageChanged(String packageName, String[] components) { + @WorkerThread + void onPackageChanged(String packageName, String... components) { synchronized (mTransportLock) { - // Remove all changed components from mValidTransports. We'll bind to them again - // and re-add them if still valid. - Set transportsToBeRemoved = new ArraySet<>(); - for (String component : components) { - ComponentName componentName = new ComponentName(packageName, component); - transportsToBeRemoved.add(componentName); - TransportConnection removed = mValidTransports.remove(componentName); - if (removed != null) { - mContext.unbindService(removed); - log_verbose("Package changed. Removing transport: " + - componentName.flattenToShortString()); - } - } - removeTransportsIfLocked(transportsToBeRemoved::contains); - bindToAllInternal(packageName, components); + Set transportComponents = + Stream.of(components) + .map(component -> new ComponentName(packageName, component)) + .collect(Collectors.toSet()); + + mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains); + registerTransportsFromPackage(packageName, transportComponents::contains); } } - @GuardedBy("mTransportLock") - private void removeTransportsIfLocked(Predicate filter) { - mEligibleTransports.removeIf(filter); - mRegisteredTransportsDescriptionMap.keySet().removeIf(filter); - } - - public IBackupTransport getTransportBinder(String transportName) { + /** + * Returns the {@link ComponentName}s of the registered transports. + * + *

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) { - ComponentName component = mBoundTransports.get(transportName); - if (component == null) { - Slog.w(TAG, "Transport " + transportName + " not bound."); - return null; - } - TransportConnection conn = mValidTransports.get(component); - if (conn == null) { - Slog.w(TAG, "Transport " + transportName + " not valid."); - return null; - } - return conn.getBinder(); + return mRegisteredTransportsDescriptionMap + .keySet() + .toArray(new ComponentName[mRegisteredTransportsDescriptionMap.size()]); } } - public IBackupTransport getCurrentTransportBinder() { - return getTransportBinder(mCurrentTransportName); + /** + * Returns the names of the registered transports. + * + * @see #getRegisteredTransportComponents() + */ + String[] getRegisteredTransportNames() { + synchronized (mTransportLock) { + return mRegisteredTransportsDescriptionMap + .values() + .stream() + .map(transportDescription -> transportDescription.name) + .toArray(String[]::new); + } + } + + /** Returns a set with the whitelisted transports. */ + Set getTransportWhitelist() { + return mTransportWhitelist; + } + + @Nullable + String getCurrentTransportName() { + return mCurrentTransportName; } /** * Returns the transport name associated with {@code transportComponent}. + * * @throws TransportNotRegisteredException if the transport is not registered. */ public String getTransportName(ComponentName transportComponent) @@ -234,7 +175,32 @@ public class TransportManager { } /** - * Retrieve the configuration intent of {@code transportName}. + * 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 @@ -247,7 +213,21 @@ public class TransportManager { } /** - * Retrieve the data management intent of {@code transportName}. + * 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 @@ -260,19 +240,8 @@ public class TransportManager { } /** - * Retrieve 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; - } - } - - /** - * Retrieve the data management label of {@code transportName}. + * Retrieves the data management label of {@code transportName}. + * * @throws TransportNotRegisteredException if the transport is not registered. */ @Nullable @@ -284,54 +253,74 @@ public class TransportManager { } } - /** - * Retrieve the transport dir name of {@code transportName}. - * @throws TransportNotRegisteredException if the transport is not registered. - */ - public String getTransportDirName(String transportName) - throws TransportNotRegisteredException { + /* Returns true if the transport identified by {@code transportName} is registered. */ + public boolean isTransportRegistered(String transportName) { synchronized (mTransportLock) { - return getRegisteredTransportDescriptionOrThrowLocked(transportName) - .transportDirName; - } - } - - /** - * Retrieve 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; + 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}. + * while {@code transportConsumer} is being executed. Don't do heavy operations in {@code + * transportConsumer}. */ public void forEachRegisteredTransport(Consumer transportConsumer) { synchronized (mTransportLock) { - for (TransportDescription transportDescription - : mRegisteredTransportsDescriptionMap.values()) { + for (TransportDescription transportDescription : + mRegisteredTransportsDescriptionMap.values()) { transportConsumer.accept(transportDescription.name); } } } - public String getTransportName(IBackupTransport binder) { + /** + * 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 String dataManagementLabel) { synchronized (mTransportLock) { - for (TransportConnection conn : mValidTransports.values()) { - if (conn.getBinder() == binder) { - return conn.getName(); - } + 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"); } - return null; + } + + @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") @@ -350,22 +339,12 @@ public class TransportManager { return (entry == null) ? null : entry.getValue(); } - @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 Map.Entry getRegisteredTransportEntryLocked( String transportName) { - for (Map.Entry entry - : mRegisteredTransportsDescriptionMap.entrySet()) { + for (Map.Entry entry : + mRegisteredTransportsDescriptionMap.entrySet()) { TransportDescription description = entry.getValue(); if (transportName.equals(description.name)) { return entry; @@ -374,17 +353,16 @@ public class TransportManager { return null; } - @GuardedBy("mTransportLock") - private TransportDescription getRegisteredTransportDescriptionOrThrowLocked( - ComponentName transportComponent) throws TransportNotRegisteredException { - TransportDescription description = - mRegisteredTransportsDescriptionMap.get(transportComponent); - if (description == null) { - throw new TransportNotRegisteredException(transportComponent); - } - return description; - } - + /** + * 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 { @@ -395,6 +373,16 @@ public class TransportManager { } } + /** + * 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) { @@ -406,19 +394,14 @@ public class TransportManager { } } - public boolean isTransportRegistered(String transportName) { - synchronized (mTransportLock) { - return getRegisteredTransportEntryLocked(transportName) != null; - } - } - /** - * Returns a {@link TransportClient} for the current transport or null if not found. + * 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 found. + * @return A {@link TransportClient} or null if not registered. */ @Nullable public TransportClient getCurrentTransportClient(String caller) { @@ -455,130 +438,88 @@ public class TransportManager { mTransportClientManager.disposeOfTransportClient(transportClient, caller); } - String[] getBoundTransportNames() { - synchronized (mTransportLock) { - return mBoundTransports.keySet().toArray(new String[mBoundTransports.size()]); - } - } - - ComponentName[] getAllTransportComponents() { - synchronized (mTransportLock) { - return mValidTransports.keySet().toArray(new ComponentName[mValidTransports.size()]); - } - } - /** - * An *eligible* transport is a service component that satisfies intent with action - * android.backup.TRANSPORT_HOST and returns true for - * {@link #isTransportTrusted(ComponentName)}. It may be registered or not registered. - * This method returns the {@link ComponentName}s of those transports. - */ - ComponentName[] getEligibleTransportComponents() { - synchronized (mTransportLock) { - return mEligibleTransports.toArray(new ComponentName[mEligibleTransports.size()]); - } - } - - Set getTransportWhitelist() { - return mTransportWhitelist; - } - - /** - * A *registered* transport is an eligible transport that has been successfully connected and - * that returned true for method - * {@link TransportBoundListener#onTransportBound(IBackupTransport)} of TransportBoundListener - * provided in the constructor. This method returns the names of the registered transports. - */ - String[] getRegisteredTransportNames() { - synchronized (mTransportLock) { - return mRegisteredTransportsDescriptionMap.values().stream() - .map(transportDescription -> transportDescription.name) - .toArray(String[]::new); - } - } - - /** - * 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 String 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"); - } - } - - @Nullable - String getCurrentTransportName() { - return mCurrentTransportName; - } - - // This is for mocking, Mockito can't mock if package-protected and in the same package but - // different class loaders. Checked with the debugger and class loaders are different - // See https://github.com/mockito/mockito/issues/796 - @VisibleForTesting(visibility = PACKAGE) - public void registerAllTransports() { - bindToAllInternal(null /* all packages */, null /* all components */); - } - - /** - * Bind to all transports belonging to the given package and the given component list. - * null acts a wildcard. + * Sets {@code transportName} as selected transport and returns previously selected transport + * name. If there was no previous transport it returns null. * - * If packageName is null, bind to all transports in all packages. - * If components is null, bind to all transports in the given package. + *

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. + * + *

{@link Deprecated} as public, this method can be used as private. */ - private void bindToAllInternal(String packageName, String[] components) { - PackageInfo pkgInfo = null; - if (packageName != null) { + @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) { + synchronized (mTransportLock) { + if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) { + int result = registerTransport(transportComponent); + if (result != BackupManager.SUCCESS) { + return result; + } + } + try { - pkgInfo = mPackageManager.getPackageInfo(packageName, 0); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Package not found: " + packageName); - return; + selectTransport(getTransportName(transportComponent)); + return BackupManager.SUCCESS; + } catch (TransportNotRegisteredException e) { + // Shouldn't happen because we are holding the lock + Slog.wtf(TAG, "Transport unexpectedly not registered"); + return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } } + } - Intent intent = new Intent(mTransportServiceIntent); - if (packageName != null) { - intent.setPackage(packageName); + @WorkerThread + public void registerTransports() { + registerTransportsForIntent(mTransportServiceIntent, transportComponent -> true); + } + + @WorkerThread + private void registerTransportsFromPackage( + String packageName, Predicate transportComponentFilter) { + try { + mPackageManager.getPackageInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Trying to register transports from package not found " + packageName); + return; } - List hosts = mPackageManager.queryIntentServicesAsUser( - intent, 0, UserHandle.USER_SYSTEM); - if (hosts != null) { + registerTransportsForIntent( + new Intent(mTransportServiceIntent).setPackage(packageName), + transportComponentFilter.and(fromPackageFilter(packageName))); + } + + @WorkerThread + private void registerTransportsForIntent( + Intent intent, Predicate transportComponentFilter) { + List hosts = + mPackageManager.queryIntentServicesAsUser(intent, 0, UserHandle.USER_SYSTEM); + if (hosts == null) { + return; + } + synchronized (mTransportLock) { for (ResolveInfo host : hosts) { - final ComponentName infoComponentName = getComponentName(host.serviceInfo); - boolean shouldBind = false; - if (components != null && packageName != null) { - for (String component : components) { - ComponentName cn = new ComponentName(pkgInfo.packageName, component); - if (infoComponentName.equals(cn)) { - shouldBind = true; - break; - } - } - } else { - shouldBind = true; - } - if (shouldBind && isTransportTrusted(infoComponentName)) { - tryBindTransport(infoComponentName); + ComponentName transportComponent = host.serviceInfo.getComponentName(); + if (transportComponentFilter.test(transportComponent) + && isTransportTrusted(transportComponent)) { + registerTransport(transportComponent); } } } @@ -605,64 +546,6 @@ public class TransportManager { return true; } - private void tryBindTransport(ComponentName transportComponentName) { - Slog.d(TAG, "Binding to transport: " + transportComponentName.flattenToShortString()); - // TODO: b/22388012 (Multi user backup and restore) - TransportConnection connection = new TransportConnection(transportComponentName); - synchronized (mTransportLock) { - mEligibleTransports.add(transportComponentName); - } - if (bindToTransport(transportComponentName, connection)) { - synchronized (mTransportLock) { - mValidTransports.put(transportComponentName, connection); - } - } else { - Slog.w(TAG, "Couldn't bind to transport " + transportComponentName); - } - } - - private boolean bindToTransport(ComponentName componentName, ServiceConnection connection) { - Intent intent = new Intent(mTransportServiceIntent) - .setComponent(componentName); - return mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE, - createSystemUserHandle()); - } - - 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}. - */ - public int registerAndSelectTransport(ComponentName transportComponent) { - synchronized (mTransportLock) { - if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) { - int result = registerTransport(transportComponent); - if (result != BackupManager.SUCCESS) { - return result; - } - } - - try { - selectTransport(getTransportName(transportComponent)); - return BackupManager.SUCCESS; - } catch (TransportNotRegisteredException e) { - // Shouldn't happen because we are holding the lock - Slog.wtf(TAG, "Transport unexpectedly not registered"); - return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; - } - } - } - /** * Tries to register transport represented by {@code transportComponent}. * @@ -670,7 +553,12 @@ public class TransportManager { * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. */ + @WorkerThread private int registerTransport(ComponentName transportComponent) { + if (!isTransportTrusted(transportComponent)) { + return BackupManager.ERROR_TRANSPORT_INVALID; + } + String transportString = transportComponent.flattenToShortString(); String callerLogString = "TransportManager.registerTransport()"; @@ -689,26 +577,21 @@ public class TransportManager { EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 1); int result; - if (isTransportValid(transport)) { - try { - registerTransport(transportComponent, transport); - // If registerTransport() hasn't thrown... - result = BackupManager.SUCCESS; - } catch (RemoteException e) { - Slog.e(TAG, "Transport " + transportString + " died while registering"); - result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE; - } - } else { - Slog.w(TAG, "Can't register invalid transport " + transportString); - result = BackupManager.ERROR_TRANSPORT_INVALID; + 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"); + EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0); + result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString); - if (result == BackupManager.SUCCESS) { - Slog.d(TAG, "Transport " + transportString + " registered"); - } else { - EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0); - } return result; } @@ -717,204 +600,20 @@ public class TransportManager { throws RemoteException { synchronized (mTransportLock) { String name = transport.name(); - TransportDescription description = new TransportDescription( - name, - transport.transportDirName(), - transport.configurationIntent(), - transport.currentDestinationString(), - transport.dataManagementIntent(), - transport.dataManagementLabel()); + TransportDescription description = + new TransportDescription( + name, + transport.transportDirName(), + transport.configurationIntent(), + transport.currentDestinationString(), + transport.dataManagementIntent(), + transport.dataManagementLabel()); mRegisteredTransportsDescriptionMap.put(transportComponent, description); } } - private boolean isTransportValid(IBackupTransport transport) { - if (mTransportBoundListener == null) { - Slog.w(TAG, "setTransportBoundListener() not called, assuming transport invalid"); - return false; - } - return mTransportBoundListener.onTransportBound(transport); - } - - private class TransportConnection implements ServiceConnection { - - // Hold mTransportLock to access these fields so as to provide a consistent view of them. - private volatile IBackupTransport mBinder; - private volatile String mTransportName; - - private final ComponentName mTransportComponent; - - private TransportConnection(ComponentName transportComponent) { - mTransportComponent = transportComponent; - } - - @Override - public void onServiceConnected(ComponentName component, IBinder binder) { - synchronized (mTransportLock) { - mBinder = IBackupTransport.Stub.asInterface(binder); - boolean success = false; - - EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, - component.flattenToShortString(), 1); - - try { - mTransportName = mBinder.name(); - // BackupManager requests some fields from the transport. If they are - // invalid, throw away this transport. - if (isTransportValid(mBinder)) { - // We're now using the always-bound connection to do the registration but - // when we remove the always-bound code this will be in the first binding - // TODO: Move registration to first binding - registerTransport(component, mBinder); - // If registerTransport() hasn't thrown... - success = true; - } - } catch (RemoteException e) { - success = false; - Slog.e(TAG, "Couldn't get transport name.", e); - } finally { - // we need to intern() the String of the component, so that we can use it with - // Handler's removeMessages(), which uses == operator to compare the tokens - String componentShortString = component.flattenToShortString().intern(); - if (success) { - Slog.d(TAG, "Bound to transport: " + componentShortString); - mBoundTransports.put(mTransportName, component); - // cancel rebinding on timeout for this component as we've already connected - mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString); - } else { - Slog.w(TAG, "Bound to transport " + componentShortString + - " but it is invalid"); - EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, - componentShortString, 0); - mContext.unbindService(this); - mValidTransports.remove(component); - mEligibleTransports.remove(component); - mBinder = null; - } - } - } - } - - @Override - public void onServiceDisconnected(ComponentName component) { - synchronized (mTransportLock) { - mBinder = null; - mBoundTransports.remove(mTransportName); - } - String componentShortString = component.flattenToShortString(); - EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, componentShortString, 0); - Slog.w(TAG, "Disconnected from transport " + componentShortString); - scheduleRebindTimeout(component); - } - - /** - * We'll attempt to explicitly rebind to a transport if it hasn't happened automatically - * for a few minutes after the binding went away. - */ - private void scheduleRebindTimeout(ComponentName component) { - // we need to intern() the String of the component, so that we can use it with Handler's - // removeMessages(), which uses == operator to compare the tokens - final String componentShortString = component.flattenToShortString().intern(); - final long rebindTimeout = getRebindTimeout(); - mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString); - Message msg = mHandler.obtainMessage(REBINDING_TIMEOUT_MSG); - msg.obj = componentShortString; - mHandler.sendMessageDelayed(msg, rebindTimeout); - Slog.d(TAG, "Scheduled explicit rebinding for " + componentShortString + " in " - + rebindTimeout + "ms"); - } - - // Intentionally not synchronized -- the variable is volatile and changes to its value - // are inside synchronized blocks, providing a memory sync barrier; and this method - // does not touch any other state protected by that lock. - private IBackupTransport getBinder() { - return mBinder; - } - - // Intentionally not synchronized; same as getBinder() - private String getName() { - return mTransportName; - } - - // Intentionally not synchronized; same as getBinder() - private void bindIfUnbound() { - if (mBinder == null) { - Slog.d(TAG, - "Rebinding to transport " + mTransportComponent.flattenToShortString()); - bindToTransport(mTransportComponent, this); - } - } - - private long getRebindTimeout() { - final boolean isDeviceProvisioned = Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 0) != 0; - return isDeviceProvisioned - ? REBINDING_TIMEOUT_PROVISIONED_MS - : REBINDING_TIMEOUT_UNPROVISIONED_MS; - } - } - - public interface TransportBoundListener { - /** Should return true if this is a valid transport. */ - boolean onTransportBound(IBackupTransport binder); - } - - private class RebindOnTimeoutHandler extends Handler { - - RebindOnTimeoutHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (msg.what == REBINDING_TIMEOUT_MSG) { - String componentShortString = (String) msg.obj; - ComponentName transportComponent = - ComponentName.unflattenFromString(componentShortString); - synchronized (mTransportLock) { - if (mBoundTransports.containsValue(transportComponent)) { - Slog.d(TAG, "Explicit rebinding timeout passed, but already bound to " - + componentShortString + " so not attempting to rebind"); - return; - } - Slog.d(TAG, "Explicit rebinding timeout passed, attempting rebinding to: " - + componentShortString); - // unbind the existing (broken) connection - TransportConnection conn = mValidTransports.get(transportComponent); - if (conn != null) { - mContext.unbindService(conn); - Slog.d(TAG, "Unbinding the existing (broken) connection to transport: " - + componentShortString); - } - } - // rebind to transport - tryBindTransport(transportComponent); - } else { - Slog.e(TAG, "Unknown message sent to RebindOnTimeoutHandler, msg.what: " - + msg.what); - } - } - } - - private static void log_verbose(String message) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Slog.v(TAG, message); - } - } - - // These only exists to make it testable with Robolectric, which is not updated to API level 24 - // yet. - // TODO: Get rid of this once Robolectric is updated. - private static ComponentName getComponentName(ServiceInfo serviceInfo) { - return new ComponentName(serviceInfo.packageName, serviceInfo.name); - } - - // These only exists to make it testable with Robolectric, which is not updated to API level 24 - // yet. - // TODO: Get rid of this once Robolectric is updated. - public static UserHandle createSystemUserHandle() { - return new UserHandle(UserHandle.USER_SYSTEM); + private static Predicate fromPackageFilter(String packageName) { + return transportComponent -> packageName.equals(transportComponent.getPackageName()); } private static class TransportDescription { diff --git a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java b/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java new file mode 100644 index 0000000000000..391ec2d7f2945 --- /dev/null +++ b/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java @@ -0,0 +1,33 @@ +/* + * 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 index 7bd9111028cbe..bd4a0bb570728 100644 --- a/services/backup/java/com/android/server/backup/transport/TransportClient.java +++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java @@ -236,7 +236,7 @@ public class TransportClient { mBindIntent, mConnection, Context.BIND_AUTO_CREATE, - TransportManager.createSystemUserHandle()); + UserHandle.SYSTEM); 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 diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java index f9ebd28418cd0..bf58224637463 100644 --- a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java +++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java @@ -16,7 +16,11 @@ package com.android.server.backup; -import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAMES; +import static com.android.server.backup.testing.TransportData.backupTransport; +import static com.android.server.backup.testing.TransportData.d2dTransport; +import static com.android.server.backup.testing.TransportData.localTransport; +import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport; +import static com.android.server.backup.testing.TransportTestUtils.setUpTransports; import static com.google.common.truth.Truth.assertThat; @@ -24,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -39,8 +44,8 @@ import android.platform.test.annotations.Presubmit; import android.provider.Settings; import com.android.server.backup.testing.ShadowAppBackupUtils; -import com.android.server.backup.testing.TransportTestUtils; -import com.android.server.backup.testing.TransportTestUtils.TransportData; +import com.android.server.backup.testing.TransportData; +import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; @@ -57,10 +62,10 @@ import org.robolectric.shadows.ShadowContextWrapper; import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowSettings; +import org.robolectric.shadows.ShadowSystemClock; import java.io.File; import java.util.HashMap; -import java.util.List; import java.util.Map; @RunWith(FrameworkRobolectricTestRunner.class) @@ -73,22 +78,24 @@ import java.util.Map; @Presubmit public class BackupManagerServiceRoboTest { private static final String TAG = "BMSTest"; - private static final String TRANSPORT_NAME = - "com.google.android.gms/.backup.BackupTransportService"; @Mock private TransportManager mTransportManager; private HandlerThread mBackupThread; private ShadowLooper mShadowBackupLooper; private File mBaseStateDir; private File mDataDir; - private RefactoredBackupManagerService mBackupManagerService; private ShadowContextWrapper mShadowContext; private Context mContext; + private TransportData mTransport; + private String mTransportName; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mTransport = backupTransport(); + mTransportName = mTransport.transportName; + mBackupThread = new HandlerThread("backup-test"); mBackupThread.setUncaughtExceptionHandler( (t, e) -> ShadowLog.e(TAG, "Uncaught exception in test thread " + t.getName(), e)); @@ -102,15 +109,6 @@ public class BackupManagerServiceRoboTest { File cacheDir = mContext.getCacheDir(); mBaseStateDir = new File(cacheDir, "base_state_dir"); mDataDir = new File(cacheDir, "data_dir"); - - mBackupManagerService = - new RefactoredBackupManagerService( - mContext, - new Trampoline(mContext), - mBackupThread, - mBaseStateDir, - mDataDir, - mTransportManager); } @After @@ -124,10 +122,12 @@ public class BackupManagerServiceRoboTest { @Test public void testDestinationString() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); - when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME))) + when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName))) .thenReturn("destinationString"); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); - String destination = mBackupManagerService.getDestinationString(TRANSPORT_NAME); + String destination = backupManagerService.getDestinationString(mTransportName); assertThat(destination).isEqualTo("destinationString"); } @@ -135,10 +135,12 @@ public class BackupManagerServiceRoboTest { @Test public void testDestinationString_whenTransportNotRegistered() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); - when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME))) + when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName))) .thenThrow(TransportNotRegisteredException.class); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); - String destination = mBackupManagerService.getDestinationString(TRANSPORT_NAME); + String destination = backupManagerService.getDestinationString(mTransportName); assertThat(destination).isNull(); } @@ -146,12 +148,14 @@ public class BackupManagerServiceRoboTest { @Test public void testDestinationString_withoutPermission() throws Exception { mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); - when(mTransportManager.getTransportCurrentDestinationString(eq(TRANSPORT_NAME))) + when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName))) .thenThrow(TransportNotRegisteredException.class); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); expectThrows( SecurityException.class, - () -> mBackupManagerService.getDestinationString(TRANSPORT_NAME)); + () -> backupManagerService.getDestinationString(mTransportName)); } /* Tests for app eligibility */ @@ -159,24 +163,28 @@ public class BackupManagerServiceRoboTest { @Test public void testIsAppEligibleForBackup_whenAppEligible() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); - TransportData transport = - TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME); + TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport()); ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> true; + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); - boolean result = mBackupManagerService.isAppEligibleForBackup("app.package"); + boolean result = backupManagerService.isAppEligibleForBackup("app.package"); assertThat(result).isTrue(); + verify(mTransportManager) - .disposeOfTransportClient(eq(transport.transportClientMock), any()); + .disposeOfTransportClient(eq(transportMock.transportClient), any()); } @Test public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); - TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME); + setUpCurrentTransport(mTransportManager, mTransport); ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false; + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); - boolean result = mBackupManagerService.isAppEligibleForBackup("app.package"); + boolean result = backupManagerService.isAppEligibleForBackup("app.package"); assertThat(result).isFalse(); } @@ -184,38 +192,43 @@ public class BackupManagerServiceRoboTest { @Test public void testIsAppEligibleForBackup_withoutPermission() throws Exception { mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); - TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME); + setUpCurrentTransport(mTransportManager, mTransport); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); expectThrows( SecurityException.class, - () -> mBackupManagerService.isAppEligibleForBackup("app.package")); + () -> backupManagerService.isAppEligibleForBackup("app.package")); } @Test public void testFilterAppsEligibleForBackup() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); - TransportData transport = - TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME); + TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport); Map packagesMap = new HashMap<>(); packagesMap.put("package.a", true); packagesMap.put("package.b", false); ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = packagesMap::get; + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); String[] packages = packagesMap.keySet().toArray(new String[packagesMap.size()]); - String[] filtered = mBackupManagerService.filterAppsEligibleForBackup(packages); + String[] filtered = backupManagerService.filterAppsEligibleForBackup(packages); assertThat(filtered).asList().containsExactly("package.a"); verify(mTransportManager) - .disposeOfTransportClient(eq(transport.transportClientMock), any()); + .disposeOfTransportClient(eq(transportMock.transportClient), any()); } @Test public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); ShadowAppBackupUtils.sAppIsRunningAndEligibleForBackupWithTransport = p -> false; + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); String[] filtered = - mBackupManagerService.filterAppsEligibleForBackup( + backupManagerService.filterAppsEligibleForBackup( new String[] {"package.a", "package.b"}); assertThat(filtered).isEmpty(); @@ -224,12 +237,14 @@ public class BackupManagerServiceRoboTest { @Test public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception { mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); - TransportTestUtils.setUpCurrentTransport(mTransportManager, TRANSPORT_NAME); + setUpCurrentTransport(mTransportManager, mTransport); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); expectThrows( SecurityException.class, () -> - mBackupManagerService.filterAppsEligibleForBackup( + backupManagerService.filterAppsEligibleForBackup( new String[] {"package.a", "package.b"})); } @@ -238,14 +253,12 @@ public class BackupManagerServiceRoboTest { private TransportData mNewTransport; private TransportData mOldTransport; private ComponentName mNewTransportComponent; - private ISelectBackupTransportCallback mCallback; private void setUpForSelectTransport() throws Exception { - List transports = - TransportTestUtils.setUpTransports(mTransportManager, TRANSPORT_NAMES); - mNewTransport = transports.get(0); - mNewTransportComponent = mNewTransport.transportClientMock.getTransportComponent(); - mOldTransport = transports.get(1); + mNewTransport = backupTransport(); + mNewTransportComponent = mNewTransport.getTransportComponent(); + mOldTransport = d2dTransport(); + setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport()); when(mTransportManager.selectTransport(eq(mNewTransport.transportName))) .thenReturn(mOldTransport.transportName); } @@ -254,9 +267,11 @@ public class BackupManagerServiceRoboTest { public void testSelectBackupTransport() throws Exception { setUpForSelectTransport(); mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); String oldTransport = - mBackupManagerService.selectBackupTransport(mNewTransport.transportName); + backupManagerService.selectBackupTransport(mNewTransport.transportName); assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName); assertThat(oldTransport).isEqualTo(mOldTransport.transportName); @@ -266,10 +281,12 @@ public class BackupManagerServiceRoboTest { public void testSelectBackupTransport_withoutPermission() throws Exception { setUpForSelectTransport(); mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); expectThrows( SecurityException.class, - () -> mBackupManagerService.selectBackupTransport(mNewTransport.transportName)); + () -> backupManagerService.selectBackupTransport(mNewTransport.transportName)); } @Test @@ -278,9 +295,11 @@ public class BackupManagerServiceRoboTest { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent))) .thenReturn(BackupManager.SUCCESS); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); - mBackupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback); + backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback); mShadowBackupLooper.runToEndOfTasks(); assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName); @@ -293,9 +312,11 @@ public class BackupManagerServiceRoboTest { mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent))) .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); - mBackupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback); + backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback); mShadowBackupLooper.runToEndOfTasks(); assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName); @@ -304,19 +325,19 @@ public class BackupManagerServiceRoboTest { @Test public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception { - TransportTestUtils.setUpTransports( - mTransportManager, new TransportData(TRANSPORT_NAME, null, null)); - ComponentName newTransportComponent = - TransportTestUtils.transportComponentName(TRANSPORT_NAME); + setUpTransports(mTransportManager, mTransport.unregistered()); + ComponentName newTransportComponent = mTransport.getTransportComponent(); mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent))) .thenReturn(BackupManager.SUCCESS); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); - mBackupManagerService.selectBackupTransportAsync(newTransportComponent, callback); + backupManagerService.selectBackupTransportAsync(newTransportComponent, callback); mShadowBackupLooper.runToEndOfTasks(); - assertThat(getSettingsTransport()).isNotEqualTo(TRANSPORT_NAME); + assertThat(getSettingsTransport()).isNotEqualTo(mTransportName); verify(callback).onFailure(anyInt()); } @@ -324,13 +345,14 @@ public class BackupManagerServiceRoboTest { public void testSelectBackupTransportAsync_withoutPermission() throws Exception { setUpForSelectTransport(); mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); - ComponentName newTransportComponent = - mNewTransport.transportClientMock.getTransportComponent(); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + ComponentName newTransportComponent = mNewTransport.getTransportComponent(); expectThrows( SecurityException.class, () -> - mBackupManagerService.selectBackupTransportAsync( + backupManagerService.selectBackupTransportAsync( newTransportComponent, mock(ISelectBackupTransportCallback.class))); } @@ -338,4 +360,55 @@ public class BackupManagerServiceRoboTest { return ShadowSettings.ShadowSecure.getString( mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT); } + + /* Miscellaneous tests */ + + @Test + public void testConstructor_postRegisterTransports() { + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + + createBackupManagerService(); + + mShadowBackupLooper.runToEndOfTasks(); + verify(mTransportManager).registerTransports(); + } + + @Test + public void testConstructor_doesNotRegisterTransportsSynchronously() { + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + + createBackupManagerService(); + + // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks() + verify(mTransportManager, never()).registerTransports(); + } + + private RefactoredBackupManagerService createBackupManagerService() { + return new RefactoredBackupManagerService( + mContext, + new Trampoline(mContext), + mBackupThread, + mBaseStateDir, + mDataDir, + mTransportManager); + } + + private RefactoredBackupManagerService createInitializedBackupManagerService() { + RefactoredBackupManagerService backupManagerService = + new RefactoredBackupManagerService( + mContext, + new Trampoline(mContext), + mBackupThread, + mBaseStateDir, + mDataDir, + mTransportManager); + mShadowBackupLooper.runToEndOfTasks(); + // Handler instances have their own clock, so advancing looper (with runToEndOfTasks()) + // above does NOT advance the handlers' clock, hence whenever a handler post messages with + // specific time to the looper the time of those messages will be before the looper's time. + // To fix this we advance SystemClock as well since that is from where the handlers read + // time. + ShadowSystemClock.setCurrentTimeMillis(mShadowBackupLooper.getScheduler().getCurrentTime()); + return backupManagerService; + } } diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java index acd670f6748c4..cf0bc235dc10c 100644 --- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java +++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java @@ -16,108 +16,97 @@ package com.android.server.backup; +import static com.android.server.backup.testing.TransportData.genericTransport; +import static com.android.server.backup.testing.TransportTestUtils.mockTransport; +import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager; + import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.mock; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.shadow.api.Shadow.extract; import static org.testng.Assert.expectThrows; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import static java.util.stream.Stream.concat; + import android.annotation.Nullable; 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.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.os.IBinder; -import android.os.RemoteException; import android.platform.test.annotations.Presubmit; -import com.android.internal.backup.IBackupTransport; -import com.android.server.backup.testing.ShadowBackupTransportStub; import com.android.server.backup.testing.ShadowContextImplForBackup; -import com.android.server.backup.testing.ShadowPackageManagerForBackup; -import com.android.server.backup.testing.TransportBoundListenerStub; +import com.android.server.testing.shadows.FrameworkShadowPackageManager; +import com.android.server.backup.testing.TransportData; +import com.android.server.backup.testing.TransportTestUtils.TransportMock; +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.TransportNotRegisteredException; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; +import com.android.server.testing.shadows.FrameworkShadowContextImpl; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLog; -import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowPackageManager; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; @RunWith(FrameworkRobolectricTestRunner.class) @Config( - manifest = Config.NONE, - sdk = 26, - shadows = { - ShadowContextImplForBackup.class, - ShadowBackupTransportStub.class, - ShadowPackageManagerForBackup.class - } + manifest = Config.NONE, + sdk = 26, + shadows = {FrameworkShadowPackageManager.class, FrameworkShadowContextImpl.class} ) @SystemLoaderClasses({TransportManager.class}) @Presubmit public class TransportManagerTest { - private static final String PACKAGE_NAME = "some.package.name"; - private static final String ANOTHER_PACKAGE_NAME = "another.package.name"; + private static final String PACKAGE_A = "some.package.a"; + private static final String PACKAGE_B = "some.package.b"; - private TransportInfo mTransport1; - private TransportInfo mTransport2; + @Mock private OnTransportRegisteredListener mListener; + @Mock private TransportClientManager mTransportClientManager; + private TransportData mTransportA1; + private TransportData mTransportA2; + private TransportData mTransportB1; - private ShadowPackageManager mPackageManagerShadow; - - private final TransportBoundListenerStub mTransportBoundListenerStub = - new TransportBoundListenerStub(true); + private ShadowPackageManager mShadowPackageManager; + private Context mContext; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - ShadowLog.stream = System.out; - - mPackageManagerShadow = - (ShadowPackageManagerForBackup) + mShadowPackageManager = + (FrameworkShadowPackageManager) extract(RuntimeEnvironment.application.getPackageManager()); + mContext = RuntimeEnvironment.application.getApplicationContext(); - mTransport1 = new TransportInfo( - PACKAGE_NAME, - "transport1.name", - new Intent(), - "currentDestinationString", - new Intent(), - "dataManagementLabel"); - mTransport2 = new TransportInfo( - PACKAGE_NAME, - "transport2.name", - new Intent(), - "currentDestinationString", - new Intent(), - "dataManagementLabel"); - - ShadowContextImplForBackup.sComponentBinderMap.put(mTransport1.componentName, - mTransport1.binder); - ShadowContextImplForBackup.sComponentBinderMap.put(mTransport2.componentName, - mTransport2.binder); - ShadowBackupTransportStub.sBinderTransportMap.put( - mTransport1.binder, mTransport1.binderInterface); - ShadowBackupTransportStub.sBinderTransportMap.put( - mTransport2.binder, mTransport2.binderInterface); + mTransportA1 = genericTransport(PACKAGE_A, "TransportFoo"); + mTransportA2 = genericTransport(PACKAGE_A, "TransportBar"); + mTransportB1 = genericTransport(PACKAGE_B, "TransportBaz"); } @After @@ -126,560 +115,441 @@ public class TransportManagerTest { } @Test - public void onPackageAdded_bindsToAllTransports() throws Exception { - setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), - ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); - - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - new HashSet<>(Arrays.asList( - mTransport1.componentName, mTransport2.componentName)), - null /* defaultTransport */, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); - transportManager.onPackageAdded(PACKAGE_NAME); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.componentName, mTransport2.componentName)); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) - .isTrue(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) - .isTrue(); - } - - @Test - public void onPackageAdded_oneTransportUnavailable_bindsToOnlyOneTransport() throws Exception { - setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), - ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); - - ShadowContextImplForBackup.sUnbindableComponents.add(mTransport1.componentName); - - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - new HashSet<>(Arrays.asList( - mTransport1.componentName, mTransport2.componentName)), - null /* defaultTransport */, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); - transportManager.onPackageAdded(PACKAGE_NAME); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - Collections.singleton(mTransport2.componentName)); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - Collections.singleton(mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) - .isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) - .isTrue(); - } - - @Test - public void onPackageAdded_whitelistIsNull_doesNotBindToTransports() throws Exception { - setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), - ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); - - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - null /* whitelist */, - null /* defaultTransport */, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); - transportManager.onPackageAdded(PACKAGE_NAME); - - assertThat(transportManager.getAllTransportComponents()).isEmpty(); - assertThat(transportManager.getBoundTransportNames()).isEmpty(); - assertThat(mTransportBoundListenerStub.isCalled()).isFalse(); - } - - @Test - public void onPackageAdded_onlyOneTransportWhitelisted_onlyConnectsToWhitelistedTransport() - throws Exception { - setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), - ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); - - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - new HashSet<>(Collections.singleton(mTransport2.componentName)), - null /* defaultTransport */, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); - transportManager.onPackageAdded(PACKAGE_NAME); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - Collections.singleton(mTransport2.componentName)); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - Collections.singleton(mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) - .isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) - .isTrue(); - } - - @Test - public void onPackageAdded_appIsNotPrivileged_doesNotBindToTransports() throws Exception { - setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), 0); - - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - new HashSet<>(Arrays.asList( - mTransport1.componentName, mTransport2.componentName)), - null /* defaultTransport */, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); - transportManager.onPackageAdded(PACKAGE_NAME); - - assertThat(transportManager.getAllTransportComponents()).isEmpty(); - assertThat(transportManager.getBoundTransportNames()).isEmpty(); - assertThat(mTransportBoundListenerStub.isCalled()).isFalse(); - } - - @Test - public void onPackageRemoved_transportsUnbound() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - transportManager.onPackageRemoved(PACKAGE_NAME); - - assertThat(transportManager.getAllTransportComponents()).isEmpty(); - assertThat(transportManager.getBoundTransportNames()).isEmpty(); - } - - @Test - public void onPackageRemoved_incorrectPackageName_nothingHappens() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - transportManager.onPackageRemoved(ANOTHER_PACKAGE_NAME); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.componentName, mTransport2.componentName)); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.name, mTransport2.name)); - } - - @Test - public void onPackageChanged_oneComponentChanged_onlyOneTransportRebound() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - transportManager.onPackageChanged(PACKAGE_NAME, new String[]{mTransport2.name}); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.componentName, mTransport2.componentName)); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) - .isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) - .isTrue(); - } - - @Test - public void onPackageChanged_nothingChanged_noTransportsRebound() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - transportManager.onPackageChanged(PACKAGE_NAME, new String[0]); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.componentName, mTransport2.componentName)); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) - .isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) - .isFalse(); - } - - @Test - public void onPackageChanged_unexpectedComponentChanged_noTransportsRebound() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - transportManager.onPackageChanged(PACKAGE_NAME, new String[]{"unexpected.component"}); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.componentName, mTransport2.componentName)); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) - .isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) - .isFalse(); - } - - @Test - public void onPackageChanged_transportsRebound() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - transportManager.onPackageChanged(PACKAGE_NAME, new String[]{mTransport2.name}); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.componentName, mTransport2.componentName)); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - Arrays.asList(mTransport1.name, mTransport2.name)); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface)) - .isFalse(); - assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface)) - .isTrue(); - } - - @Test - public void getTransportBinder_returnsCorrectBinder() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - assertThat(transportManager.getTransportBinder(mTransport1.name)).isEqualTo( - mTransport1.binderInterface); - assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo( - mTransport2.binderInterface); - } - - @Test - public void getTransportBinder_incorrectTransportName_returnsNull() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - assertThat(transportManager.getTransportBinder("incorrect.transport")).isNull(); - } - - @Test - public void getTransportBinder_oneTransportUnavailable_returnsCorrectBinder() throws Exception { + public void testRegisterTransports() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpPackage(PACKAGE_B, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2, mTransportB1); TransportManager transportManager = - createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), - Collections.singletonList(mTransport1), mTransport1.name); + createTransportManager(mTransportA1, mTransportA2, mTransportB1); - assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull(); - assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo( - mTransport2.binderInterface); + transportManager.registerTransports(); + + assertRegisteredTransports( + transportManager, asList(mTransportA1, mTransportA2, mTransportB1)); + + verify(mListener) + .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName); + verify(mListener) + .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName); + verify(mListener) + .onTransportRegistered(mTransportB1.transportName, mTransportB1.transportDirName); } @Test - public void getCurrentTransport_selectTransportNotCalled_returnsDefaultTransport() + public void + testRegisterTransports_whenOneTransportUnavailable_doesNotRegisterUnavailableTransport() + throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + TransportData transport1 = mTransportA1.unavailable(); + TransportData transport2 = mTransportA2; + setUpTransports(transport1, transport2); + TransportManager transportManager = createTransportManager(transport1, transport2); + + transportManager.registerTransports(); + + assertRegisteredTransports(transportManager, singletonList(transport2)); + verify(mListener, never()) + .onTransportRegistered(transport1.transportName, transport1.transportDirName); + verify(mListener) + .onTransportRegistered(transport2.transportName, transport2.transportDirName); + } + + @Test + public void testRegisterTransports_whenWhitelistIsEmpty_doesNotRegisterTransports() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(null); - assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport1.name); + transportManager.registerTransports(); + + assertRegisteredTransports(transportManager, emptyList()); + verify(mListener, never()).onTransportRegistered(any(), any()); } @Test - public void getCurrentTransport_selectTransportCalled_returnsCorrectTransport() + public void + testRegisterTransports_whenOnlyOneTransportWhitelisted_onlyRegistersWhitelistedTransport() + throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(null, mTransportA1); + + transportManager.registerTransports(); + + assertRegisteredTransports(transportManager, singletonList(mTransportA1)); + verify(mListener) + .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName); + verify(mListener, never()) + .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName); + } + + @Test + public void testRegisterTransports_whenAppIsNotPrivileged_doesNotRegisterTransports() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport1.name); - - transportManager.selectTransport(mTransport2.name); - - assertThat(transportManager.getCurrentTransportName()).isEqualTo(mTransport2.name); - } - - @Test - public void getCurrentTransportBinder_returnsCorrectBinder() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); - - assertThat(transportManager.getCurrentTransportBinder()) - .isEqualTo(mTransport1.binderInterface); - } - - @Test - public void getCurrentTransportBinder_transportNotBound_returnsNull() throws Exception { + // Note ApplicationInfo.PRIVATE_FLAG_PRIVILEGED is missing from flags + setUpPackage(PACKAGE_A, 0); + setUpTransports(mTransportA1, mTransportA2); TransportManager transportManager = - createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), - Collections.singletonList(mTransport1), mTransport2.name); + createTransportManager(null, mTransportA1, mTransportA2); - transportManager.selectTransport(mTransport1.name); + transportManager.registerTransports(); - assertThat(transportManager.getCurrentTransportBinder()).isNull(); + assertRegisteredTransports(transportManager, emptyList()); + verify(mListener, never()).onTransportRegistered(any(), any()); } @Test - public void getTransportName_returnsCorrectTransportName() throws Exception { - TransportManager transportManager = createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); + public void testOnPackageAdded_registerTransports() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(mTransportA1); - assertThat(transportManager.getTransportName(mTransport1.binderInterface)) - .isEqualTo(mTransport1.name); - assertThat(transportManager.getTransportName(mTransport2.binderInterface)) - .isEqualTo(mTransport2.name); + transportManager.onPackageAdded(PACKAGE_A); + + assertRegisteredTransports(transportManager, asList(mTransportA1)); + verify(mListener) + .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName); } @Test - public void getTransportName_transportNotBound_returnsNull() throws Exception { - TransportManager transportManager = - createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2), - Collections.singletonList(mTransport1), mTransport1.name); + public void testOnPackageRemoved_unregisterTransports() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpPackage(PACKAGE_B, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportB1); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportB1); + transportManager.registerTransports(); - assertThat(transportManager.getTransportName(mTransport1.binderInterface)).isNull(); - assertThat(transportManager.getTransportName(mTransport2.binderInterface)) - .isEqualTo(mTransport2.name); + transportManager.onPackageRemoved(PACKAGE_A); + + assertRegisteredTransports(transportManager, singletonList(mTransportB1)); } @Test - public void getTransportWhitelist_returnsCorrectWhiteList() throws Exception { - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - new HashSet<>(Arrays.asList(mTransport1.componentName, mTransport2.componentName)), - mTransport1.name, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); + public void testOnPackageRemoved_whenUnknownPackage_nothingHappens() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(mTransportA1); + transportManager.registerTransports(); - assertThat(transportManager.getTransportWhitelist()).containsExactlyElementsIn( - Arrays.asList(mTransport1.componentName, mTransport2.componentName)); + transportManager.onPackageRemoved(PACKAGE_A + "unknown"); + + assertRegisteredTransports(transportManager, singletonList(mTransportA1)); } @Test - public void getTransportWhitelist_whiteListIsNull_returnsEmptyArray() throws Exception { - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - null /* whitelist */, - mTransport1.name, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); - - assertThat(transportManager.getTransportWhitelist()).isEmpty(); - } - - @Test - public void selectTransport_setsTransportCorrectlyAndReturnsPreviousTransport() + public void testOnPackageChanged_whenOneComponentChanged_onlyOneTransportReRegistered() throws Exception { - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - null /* whitelist */, - mTransport1.name, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); + // Reset listener to verify calls after registerTransports() above + reset(mListener); - assertThat(transportManager.selectTransport(mTransport2.name)).isEqualTo(mTransport1.name); - assertThat(transportManager.selectTransport(mTransport1.name)).isEqualTo(mTransport2.name); + transportManager.onPackageChanged( + PACKAGE_A, mTransportA1.getTransportComponent().getClassName()); + + assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportA2)); + verify(mListener) + .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName); + verify(mListener, never()) + .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName); } @Test - public void getTransportClient_forRegisteredTransport_returnCorrectly() throws Exception { + public void testOnPackageChanged_whenNoComponentsChanged_doesNotRegisterTransports() + throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(mTransportA1); + transportManager.registerTransports(); + reset(mListener); + + transportManager.onPackageChanged(PACKAGE_A); + + assertRegisteredTransports(transportManager, singletonList(mTransportA1)); + verify(mListener, never()).onTransportRegistered(any(), any()); + } + + @Test + public void testOnPackageChanged_whenUnknownComponentChanged_noTransportsRegistered() + throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(mTransportA1); + transportManager.registerTransports(); + reset(mListener); + + transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A + ".UnknownComponent"); + + assertRegisteredTransports(transportManager, singletonList(mTransportA1)); + verify(mListener, never()).onTransportRegistered(any(), any()); + } + + @Test + public void testOnPackageChanged_reRegisterTransports() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); + reset(mListener); + + transportManager.onPackageChanged( + PACKAGE_A, + mTransportA1.getTransportComponent().getClassName(), + mTransportA2.getTransportComponent().getClassName()); + + assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportA2)); + verify(mListener) + .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName); + verify(mListener) + .onTransportRegistered(mTransportA2.transportName, mTransportA2.transportDirName); + } + + @Test + public void testGetCurrentTransportName_whenSelectTransportNotCalled_returnsDefaultTransport() + throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); + + String currentTransportName = transportManager.getCurrentTransportName(); + + assertThat(currentTransportName).isEqualTo(mTransportA1.transportName); + } + + @Test + public void testGetCurrentTransport_whenSelectTransportCalled_returnsSelectedTransport() + throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); + transportManager.selectTransport(mTransportA2.transportName); + + String currentTransportName = transportManager.getCurrentTransportName(); + + assertThat(currentTransportName).isEqualTo(mTransportA2.transportName); + } + + @Test + public void testGetTransportWhitelist() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + + Set transportWhitelist = transportManager.getTransportWhitelist(); + + assertThat(transportWhitelist) + .containsExactlyElementsIn( + asList( + mTransportA1.getTransportComponent(), + mTransportA2.getTransportComponent())); + } + + @Test + public void testSelectTransport() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); TransportManager transportManager = - createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); + createTransportManager(null, mTransportA1, mTransportA2); + + String transport1 = transportManager.selectTransport(mTransportA1.transportName); + String transport2 = transportManager.selectTransport(mTransportA2.transportName); + + assertThat(transport1).isNull(); + assertThat(transport2).isEqualTo(mTransportA1.transportName); + } + + @Test + public void testGetTransportClient_forRegisteredTransport() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); TransportClient transportClient = - transportManager.getTransportClient(mTransport1.name, "caller"); + transportManager.getTransportClient(mTransportA1.transportName, "caller"); - assertThat(transportClient.getTransportComponent()).isEqualTo(mTransport1.componentName); + assertThat(transportClient.getTransportComponent()) + .isEqualTo(mTransportA1.getTransportComponent()); } @Test - public void getTransportClient_forOldNameOfTransportThatChangedName_returnsNull() + public void testGetTransportClient_forOldNameOfTransportThatChangedName_returnsNull() throws Exception { - TransportManager transportManager = - createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); transportManager.updateTransportAttributes( - mTransport1.componentName, "newName", null, "destinationString", null, null); + mTransportA1.getTransportComponent(), + "newName", + null, + "destinationString", + null, + null); TransportClient transportClient = - transportManager.getTransportClient(mTransport1.name, "caller"); + transportManager.getTransportClient(mTransportA1.transportName, "caller"); assertThat(transportClient).isNull(); } @Test - public void getTransportClient_forNewNameOfTransportThatChangedName_returnsCorrectly() + public void testGetTransportClient_forNewNameOfTransportThatChangedName_returnsCorrectly() throws Exception { - TransportManager transportManager = - createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); transportManager.updateTransportAttributes( - mTransport1.componentName, "newName", null, "destinationString", null, null); + mTransportA1.getTransportComponent(), + "newName", + null, + "destinationString", + null, + null); - TransportClient transportClient = - transportManager.getTransportClient("newName", "caller"); + TransportClient transportClient = transportManager.getTransportClient("newName", "caller"); - assertThat(transportClient.getTransportComponent()).isEqualTo(mTransport1.componentName); + assertThat(transportClient.getTransportComponent()) + .isEqualTo(mTransportA1.getTransportComponent()); } @Test - public void getTransportName_forTransportThatChangedName_returnsNewName() - throws Exception { - TransportManager transportManager = - createTransportManagerAndSetUpTransports( - Arrays.asList(mTransport1, mTransport2), mTransport1.name); + public void testGetTransportName_forTransportThatChangedName_returnsNewName() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1, mTransportA2); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); transportManager.updateTransportAttributes( - mTransport1.componentName, "newName", null, "destinationString", null, null); + mTransportA1.getTransportComponent(), + "newName", + null, + "destinationString", + null, + null); - String transportName = transportManager.getTransportName(mTransport1.componentName); + String transportName = + transportManager.getTransportName(mTransportA1.getTransportComponent()); assertThat(transportName).isEqualTo("newName"); } @Test - public void isTransportRegistered_returnsCorrectly() throws Exception { - TransportManager transportManager = - createTransportManagerAndSetUpTransports( - Collections.singletonList(mTransport1), - Collections.singletonList(mTransport2), - mTransport1.name); + public void testIsTransportRegistered() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(mTransportA1, mTransportA2); + transportManager.registerTransports(); - assertThat(transportManager.isTransportRegistered(mTransport1.name)).isTrue(); - assertThat(transportManager.isTransportRegistered(mTransport2.name)).isFalse(); + boolean isTransportA1Registered = + transportManager.isTransportRegistered(mTransportA1.transportName); + boolean isTransportA2Registered = + transportManager.isTransportRegistered(mTransportA2.transportName); + + assertThat(isTransportA1Registered).isTrue(); + assertThat(isTransportA2Registered).isFalse(); } @Test - public void getTransportAttributes_forRegisteredTransport_returnsCorrectValues() + public void testGetTransportAttributes_forRegisteredTransport_returnsCorrectValues() throws Exception { - TransportManager transportManager = - createTransportManagerAndSetUpTransports( - Collections.singletonList(mTransport1), - mTransport1.name); + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(mTransportA1); + transportManager.registerTransports(); - assertThat(transportManager.getTransportConfigurationIntent(mTransport1.name)) - .isEqualTo(mTransport1.binderInterface.configurationIntent()); - assertThat(transportManager.getTransportDataManagementIntent(mTransport1.name)) - .isEqualTo(mTransport1.binderInterface.dataManagementIntent()); - assertThat(transportManager.getTransportDataManagementLabel(mTransport1.name)) - .isEqualTo(mTransport1.binderInterface.dataManagementLabel()); - assertThat(transportManager.getTransportDirName(mTransport1.name)) - .isEqualTo(mTransport1.binderInterface.transportDirName()); + Intent configurationIntent = + transportManager.getTransportConfigurationIntent(mTransportA1.transportName); + Intent dataManagementIntent = + transportManager.getTransportDataManagementIntent(mTransportA1.transportName); + String dataManagementLabel = + transportManager.getTransportDataManagementLabel(mTransportA1.transportName); + String transportDirName = transportManager.getTransportDirName(mTransportA1.transportName); + + assertThat(configurationIntent).isEqualTo(mTransportA1.configurationIntent); + assertThat(dataManagementIntent).isEqualTo(mTransportA1.dataManagementIntent); + assertThat(dataManagementLabel).isEqualTo(mTransportA1.dataManagementLabel); + assertThat(transportDirName).isEqualTo(mTransportA1.transportDirName); } @Test - public void getTransportAttributes_forUnregisteredTransport_throws() - throws Exception { - TransportManager transportManager = - createTransportManagerAndSetUpTransports( - Collections.singletonList(mTransport1), - Collections.singletonList(mTransport2), - mTransport1.name); + public void testGetTransportAttributes_forUnregisteredTransport_throws() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(mTransportA1); + transportManager.registerTransports(); expectThrows( TransportNotRegisteredException.class, - () -> transportManager.getTransportConfigurationIntent(mTransport2.name)); + () -> transportManager.getTransportConfigurationIntent(mTransportA2.transportName)); expectThrows( TransportNotRegisteredException.class, - () -> transportManager.getTransportDataManagementIntent( - mTransport2.name)); + () -> + transportManager.getTransportDataManagementIntent( + mTransportA2.transportName)); expectThrows( TransportNotRegisteredException.class, - () -> transportManager.getTransportDataManagementLabel(mTransport2.name)); + () -> transportManager.getTransportDataManagementLabel(mTransportA2.transportName)); expectThrows( TransportNotRegisteredException.class, - () -> transportManager.getTransportDirName(mTransport2.name)); + () -> transportManager.getTransportDirName(mTransportA2.transportName)); } - private void setUpPackageWithTransports(String packageName, List transports, - int flags) throws Exception { + private List setUpTransports(TransportData... transports) throws Exception { + setUpTransportsForTransportManager(mShadowPackageManager, transports); + List transportMocks = new ArrayList<>(transports.length); + for (TransportData transport : transports) { + TransportMock transportMock = mockTransport(transport); + when(mTransportClientManager.getTransportClient( + eq(transport.getTransportComponent()), any())) + .thenReturn(transportMock.transportClient); + transportMocks.add(transportMock); + } + return transportMocks; + } + + private void setUpPackage(String packageName, int flags) { PackageInfo packageInfo = new PackageInfo(); packageInfo.packageName = packageName; packageInfo.applicationInfo = new ApplicationInfo(); packageInfo.applicationInfo.privateFlags = flags; - - mPackageManagerShadow.addPackage(packageInfo); - - List transportsInfo = new ArrayList<>(); - for (TransportInfo transport : transports) { - ResolveInfo info = new ResolveInfo(); - info.serviceInfo = new ServiceInfo(); - info.serviceInfo.packageName = packageName; - info.serviceInfo.name = transport.name; - transportsInfo.add(info); - } - - Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST); - intent.setPackage(packageName); - - mPackageManagerShadow.addResolveInfoForIntent(intent, transportsInfo); + mShadowPackageManager.addPackage(packageInfo); } - private TransportManager createTransportManagerAndSetUpTransports( - List availableTransports, String defaultTransportName) throws Exception { - return createTransportManagerAndSetUpTransports(availableTransports, - Collections.emptyList(), defaultTransportName); - } - - private TransportManager createTransportManagerAndSetUpTransports( - List availableTransports, List unavailableTransports, - String defaultTransportName) - throws Exception { - List availableTransportsNames = new ArrayList<>(); - List availableTransportsComponentNames = new ArrayList<>(); - for (TransportInfo transport : availableTransports) { - availableTransportsNames.add(transport.name); - availableTransportsComponentNames.add(transport.componentName); - } - - List allTransportsComponentNames = new ArrayList<>(); - allTransportsComponentNames.addAll(availableTransportsComponentNames); - for (TransportInfo transport : unavailableTransports) { - allTransportsComponentNames.add(transport.componentName); - } - - for (TransportInfo transport : unavailableTransports) { - ShadowContextImplForBackup.sUnbindableComponents.add(transport.componentName); - } - - setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2), - ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); - - TransportManager transportManager = new TransportManager( - RuntimeEnvironment.application.getApplicationContext(), - new HashSet<>(allTransportsComponentNames), - defaultTransportName, - mTransportBoundListenerStub, - ShadowLooper.getMainLooper()); - transportManager.onPackageAdded(PACKAGE_NAME); - - assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn( - availableTransportsComponentNames); - assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn( - availableTransportsNames); - for (TransportInfo transport : availableTransports) { - assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface)) - .isTrue(); - } - for (TransportInfo transport : unavailableTransports) { - assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface)) - .isFalse(); - } - - mTransportBoundListenerStub.resetState(); - + private TransportManager createTransportManager( + @Nullable TransportData selectedTransport, TransportData... transports) { + Set whitelist = + concat(Stream.of(selectedTransport), Stream.of(transports)) + .filter(Objects::nonNull) + .map(TransportData::getTransportComponent) + .collect(toSet()); + TransportManager transportManager = + new TransportManager( + mContext, + whitelist, + selectedTransport != null ? selectedTransport.transportName : null, + mTransportClientManager); + transportManager.setOnTransportRegisteredListener(mListener); return transportManager; } - private static class TransportInfo { - public final String packageName; - public final String name; - public final ComponentName componentName; - public final IBackupTransport binderInterface; - public final IBinder binder; - - TransportInfo( - String packageName, - String name, - @Nullable Intent configurationIntent, - String currentDestinationString, - @Nullable Intent dataManagementIntent, - String dataManagementLabel) { - this.packageName = packageName; - this.name = name; - this.componentName = new ComponentName(packageName, name); - this.binder = mock(IBinder.class); - IBackupTransport transport = mock(IBackupTransport.class); - try { - when(transport.name()).thenReturn(name); - when(transport.configurationIntent()).thenReturn(configurationIntent); - when(transport.currentDestinationString()).thenReturn(currentDestinationString); - when(transport.dataManagementIntent()).thenReturn(dataManagementIntent); - when(transport.dataManagementLabel()).thenReturn(dataManagementLabel); - } catch (RemoteException e) { - // Only here to mock methods that throw RemoteException - } - this.binderInterface = transport; - } + private void assertRegisteredTransports( + TransportManager transportManager, List transports) { + assertThat(transportManager.getRegisteredTransportComponents()) + .asList() + .containsExactlyElementsIn( + transports + .stream() + .map(TransportData::getTransportComponent) + .collect(toList())); + assertThat(transportManager.getRegisteredTransportNames()) + .asList() + .containsExactlyElementsIn( + transports.stream().map(t -> t.transportName).collect(toList())); } - } diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java index dfca9010130f2..ace0441c8a4a7 100644 --- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java +++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java @@ -19,8 +19,10 @@ package com.android.server.backup.internal; import static android.app.backup.BackupTransport.TRANSPORT_ERROR; import static android.app.backup.BackupTransport.TRANSPORT_OK; -import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAME; -import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAMES; +import static com.android.server.backup.testing.TransportData.backupTransport; +import static com.android.server.backup.testing.TransportData.d2dTransport; +import static com.android.server.backup.testing.TransportData.localTransport; +import static com.android.server.backup.testing.TransportTestUtils.setUpTransports; import static com.google.common.truth.Truth.assertThat; @@ -28,7 +30,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -44,7 +45,8 @@ import com.android.internal.backup.IBackupTransport; import com.android.server.backup.RefactoredBackupManagerService; import com.android.server.backup.TransportManager; import com.android.server.backup.testing.TransportTestUtils; -import com.android.server.backup.testing.TransportTestUtils.TransportData; +import com.android.server.backup.testing.TransportData; +import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.backup.transport.TransportClient; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; @@ -58,7 +60,10 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.io.File; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import java.util.stream.Stream; @RunWith(FrameworkRobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 26) @@ -68,16 +73,21 @@ public class PerformInitializeTaskTest { @Mock private RefactoredBackupManagerService mBackupManagerService; @Mock private TransportManager mTransportManager; @Mock private OnTaskFinishedListener mListener; - @Mock private IBackupTransport mTransport; + @Mock private IBackupTransport mTransportBinder; @Mock private IBackupObserver mObserver; @Mock private AlarmManager mAlarmManager; @Mock private PendingIntent mRunInitIntent; private File mBaseStateDir; + private TransportData mTransport; + private String mTransportName; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mTransport = backupTransport(); + mTransportName = mTransport.transportName; + Application context = RuntimeEnvironment.application; mBaseStateDir = new File(context.getCacheDir(), "base_state_dir"); assertThat(mBaseStateDir.mkdir()).isTrue(); @@ -88,82 +98,76 @@ public class PerformInitializeTaskTest { @Test public void testRun_callsTransportCorrectly() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); - verify(mTransport).initializeDevice(); - verify(mTransport).finishBackup(); + verify(mTransportBinder).initializeDevice(); + verify(mTransportBinder).finishBackup(); } @Test public void testRun_callsBackupManagerCorrectly() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); verify(mBackupManagerService) - .recordInitPending( - false, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME)); + .recordInitPending(false, mTransportName, mTransport.transportDirName); verify(mBackupManagerService) - .resetBackupState( - eq( - new File( - mBaseStateDir, - TransportTestUtils.transportDirName(TRANSPORT_NAME)))); + .resetBackupState(eq(new File(mBaseStateDir, mTransport.transportDirName))); } @Test public void testRun_callsObserverAndListenerCorrectly() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_OK); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_OK); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); - verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_OK)); + verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_OK)); verify(mObserver).backupFinished(eq(TRANSPORT_OK)); verify(mListener).onFinished(any()); } @Test public void testRun_whenInitializeDeviceFails() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_ERROR, 0); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_ERROR, 0); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); - verify(mTransport).initializeDevice(); - verify(mTransport, never()).finishBackup(); + verify(mTransportBinder).initializeDevice(); + verify(mTransportBinder, never()).finishBackup(); verify(mBackupManagerService) - .recordInitPending( - true, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME)); + .recordInitPending(true, mTransportName, mTransport.transportDirName); } @Test public void testRun_whenInitializeDeviceFails_callsObserverAndListenerCorrectly() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_ERROR, 0); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_ERROR, 0); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); - verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_ERROR)); + verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR)); verify(mObserver).backupFinished(eq(TRANSPORT_ERROR)); verify(mListener).onFinished(any()); } @Test public void testRun_whenInitializeDeviceFails_schedulesAlarm() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_ERROR, 0); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_ERROR, 0); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); @@ -172,37 +176,36 @@ public class PerformInitializeTaskTest { @Test public void testRun_whenFinishBackupFails() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); - verify(mTransport).initializeDevice(); - verify(mTransport).finishBackup(); + verify(mTransportBinder).initializeDevice(); + verify(mTransportBinder).finishBackup(); verify(mBackupManagerService) - .recordInitPending( - true, TRANSPORT_NAME, TransportTestUtils.transportDirName(TRANSPORT_NAME)); + .recordInitPending(true, mTransportName, mTransport.transportDirName); } @Test public void testRun_whenFinishBackupFails_callsObserverAndListenerCorrectly() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); - verify(mObserver).onResult(eq(TRANSPORT_NAME), eq(TRANSPORT_ERROR)); + verify(mObserver).onResult(eq(mTransportName), eq(TRANSPORT_ERROR)); verify(mObserver).backupFinished(eq(TRANSPORT_ERROR)); verify(mListener).onFinished(any()); } @Test public void testRun_whenFinishBackupFails_schedulesAlarm() throws Exception { - setUpTransport(TRANSPORT_NAME); - configureTransport(mTransport, TRANSPORT_OK, TRANSPORT_ERROR); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransport(mTransport); + configureTransport(mTransportBinder, TRANSPORT_OK, TRANSPORT_ERROR); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); @@ -211,64 +214,76 @@ public class PerformInitializeTaskTest { @Test public void testRun_whenOnlyOneTransportFails() throws Exception { - List transports = - TransportTestUtils.setUpTransports( - mTransportManager, TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]); - configureTransport(transports.get(0).transportMock, TRANSPORT_ERROR, 0); - configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK); + TransportData transport1 = backupTransport(); + TransportData transport2 = d2dTransport(); + List transportMocks = + setUpTransports(mTransportManager, transport1, transport2); + configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0); + configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK); PerformInitializeTask performInitializeTask = - createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]); + createPerformInitializeTask(transport1.transportName, transport2.transportName); performInitializeTask.run(); - verify(transports.get(1).transportMock).initializeDevice(); - verify(mObserver).onResult(eq(TRANSPORT_NAMES[0]), eq(TRANSPORT_ERROR)); - verify(mObserver).onResult(eq(TRANSPORT_NAMES[1]), eq(TRANSPORT_OK)); + verify(transportMocks.get(1).transport).initializeDevice(); + verify(mObserver).onResult(eq(transport1.transportName), eq(TRANSPORT_ERROR)); + verify(mObserver).onResult(eq(transport2.transportName), eq(TRANSPORT_OK)); verify(mObserver).backupFinished(eq(TRANSPORT_ERROR)); } @Test public void testRun_withMultipleTransports() throws Exception { - List transports = - TransportTestUtils.setUpTransports(mTransportManager, TRANSPORT_NAMES); - configureTransport(transports.get(0).transportMock, TRANSPORT_OK, TRANSPORT_OK); - configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK); - configureTransport(transports.get(2).transportMock, TRANSPORT_OK, TRANSPORT_OK); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAMES); + List transportMocks = + setUpTransports( + mTransportManager, backupTransport(), d2dTransport(), localTransport()); + configureTransport(transportMocks.get(0).transport, TRANSPORT_OK, TRANSPORT_OK); + configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK); + configureTransport(transportMocks.get(2).transport, TRANSPORT_OK, TRANSPORT_OK); + String[] transportNames = + Stream.of(new TransportData[] {backupTransport(), d2dTransport(), localTransport()}) + .map(t -> t.transportName) + .toArray(String[]::new); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(transportNames); performInitializeTask.run(); - for (TransportData transport : transports) { + Iterator transportsIterator = + Arrays.asList( + new TransportData[] { + backupTransport(), d2dTransport(), localTransport() + }) + .iterator(); + for (TransportMock transportMock : transportMocks) { + TransportData transport = transportsIterator.next(); verify(mTransportManager).getTransportClient(eq(transport.transportName), any()); verify(mTransportManager) - .disposeOfTransportClient(eq(transport.transportClientMock), any()); + .disposeOfTransportClient(eq(transportMock.transportClient), any()); } } @Test public void testRun_whenOnlyOneTransportFails_disposesAllTransports() throws Exception { - List transports = - TransportTestUtils.setUpTransports( - mTransportManager, TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]); - configureTransport(transports.get(0).transportMock, TRANSPORT_ERROR, 0); - configureTransport(transports.get(1).transportMock, TRANSPORT_OK, TRANSPORT_OK); + TransportData transport1 = backupTransport(); + TransportData transport2 = d2dTransport(); + List transportMocks = + setUpTransports(mTransportManager, transport1, transport2); + configureTransport(transportMocks.get(0).transport, TRANSPORT_ERROR, 0); + configureTransport(transportMocks.get(1).transport, TRANSPORT_OK, TRANSPORT_OK); PerformInitializeTask performInitializeTask = - createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]); + createPerformInitializeTask(transport1.transportName, transport2.transportName); performInitializeTask.run(); verify(mTransportManager) - .disposeOfTransportClient(eq(transports.get(0).transportClientMock), any()); + .disposeOfTransportClient(eq(transportMocks.get(0).transportClient), any()); verify(mTransportManager) - .disposeOfTransportClient(eq(transports.get(1).transportClientMock), any()); + .disposeOfTransportClient(eq(transportMocks.get(1).transportClient), any()); } @Test public void testRun_whenTransportNotRegistered() throws Exception { - TransportTestUtils.setUpTransports( - mTransportManager, new TransportData(TRANSPORT_NAME, null, null)); - - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + setUpTransports(mTransportManager, mTransport.unregistered()); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); @@ -279,16 +294,15 @@ public class PerformInitializeTaskTest { @Test public void testRun_whenOnlyOneTransportNotRegistered() throws Exception { - List transports = - TransportTestUtils.setUpTransports( - mTransportManager, - new TransportData(TRANSPORT_NAMES[0], null, null), - new TransportData(TRANSPORT_NAMES[1])); - String registeredTransportName = transports.get(1).transportName; - IBackupTransport registeredTransport = transports.get(1).transportMock; - TransportClient registeredTransportClient = transports.get(1).transportClientMock; + TransportData transport1 = backupTransport().unregistered(); + TransportData transport2 = d2dTransport(); + List transportMocks = + setUpTransports(mTransportManager, transport1, transport2); + String registeredTransportName = transport2.transportName; + IBackupTransport registeredTransport = transportMocks.get(1).transport; + TransportClient registeredTransportClient = transportMocks.get(1).transportClient; PerformInitializeTask performInitializeTask = - createPerformInitializeTask(TRANSPORT_NAMES[0], TRANSPORT_NAMES[1]); + createPerformInitializeTask(transport1.transportName, transport2.transportName); performInitializeTask.run(); @@ -299,25 +313,24 @@ public class PerformInitializeTaskTest { @Test public void testRun_whenTransportNotAvailable() throws Exception { - TransportClient transportClient = mock(TransportClient.class); - TransportTestUtils.setUpTransports( - mTransportManager, new TransportData(TRANSPORT_NAME, null, transportClient)); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + TransportMock transportMock = setUpTransport(mTransport.unavailable()); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); - verify(mTransportManager).disposeOfTransportClient(eq(transportClient), any()); + verify(mTransportManager) + .disposeOfTransportClient(eq(transportMock.transportClient), any()); verify(mObserver).backupFinished(eq(TRANSPORT_ERROR)); verify(mListener).onFinished(any()); } @Test public void testRun_whenTransportThrowsDeadObjectException() throws Exception { - TransportClient transportClient = mock(TransportClient.class); - TransportTestUtils.setUpTransports( - mTransportManager, new TransportData(TRANSPORT_NAME, mTransport, transportClient)); - when(mTransport.initializeDevice()).thenThrow(DeadObjectException.class); - PerformInitializeTask performInitializeTask = createPerformInitializeTask(TRANSPORT_NAME); + TransportMock transportMock = setUpTransport(mTransport); + IBackupTransport transport = transportMock.transport; + TransportClient transportClient = transportMock.transportClient; + when(transport.initializeDevice()).thenThrow(DeadObjectException.class); + PerformInitializeTask performInitializeTask = createPerformInitializeTask(mTransportName); performInitializeTask.run(); @@ -343,9 +356,10 @@ public class PerformInitializeTaskTest { when(transportMock.finishBackup()).thenReturn(finishBackupStatus); } - private void setUpTransport(String transportName) throws Exception { - TransportTestUtils.setUpTransport( - mTransportManager, - new TransportData(transportName, mTransport, mock(TransportClient.class))); + private TransportMock setUpTransport(TransportData transport) throws Exception { + TransportMock transportMock = + TransportTestUtils.setUpTransport(mTransportManager, transport); + mTransportBinder = transportMock.transport; + return transportMock; } } diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/src/com/android/server/backup/testing/TestUtils.java new file mode 100644 index 0000000000000..1be298d2c8c55 --- /dev/null +++ b/services/robotests/src/com/android/server/backup/testing/TestUtils.java @@ -0,0 +1,68 @@ +/* + * 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.testing; + +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; + +import java.util.concurrent.Callable; + +public class TestUtils { + /** + * Calls {@link Runnable#run()} and returns if no exception is thrown. Otherwise, if the + * exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException} and + * throw. + * + *

Warning:DON'T use this outside tests. A wrapped checked exception is just a failure + * in a test. + */ + public static void uncheck(ThrowingRunnable runnable) { + try { + runnable.runOrThrow(); + } catch (Exception e) { + throw wrapIfChecked(e); + } + } + + /** + * Calls {@link Callable#call()} and returns the value if no exception is thrown. Otherwise, if + * the exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException} + * and throw. + * + *

Warning:DON'T use this outside tests. A wrapped checked exception is just a failure + * in a test. + */ + public static T uncheck(Callable callable) { + try { + return callable.call(); + } catch (Exception e) { + throw wrapIfChecked(e); + } + } + + /** + * Wrap {@code e} in a {@link RuntimeException} only if it's not one already, in which case it's + * returned. + */ + public static RuntimeException wrapIfChecked(Exception e) { + if (e instanceof RuntimeException) { + return (RuntimeException) e; + } + return new RuntimeException(e); + } + + private TestUtils() {} +} diff --git a/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java b/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java deleted file mode 100644 index 84ac2c212854d..0000000000000 --- a/services/robotests/src/com/android/server/backup/testing/TransportBoundListenerStub.java +++ /dev/null @@ -1,64 +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.testing; - -import com.android.internal.backup.IBackupTransport; -import com.android.server.backup.TransportManager; - -import java.util.HashSet; -import java.util.Set; - -/** - * Stub implementation of TransportBoundListener, which returns given result and can tell whether - * it was called for given transport. - */ -public class TransportBoundListenerStub implements - TransportManager.TransportBoundListener { - private boolean mAlwaysReturnSuccess; - private Set mTransportsCalledFor = new HashSet<>(); - - public TransportBoundListenerStub(boolean alwaysReturnSuccess) { - this.mAlwaysReturnSuccess = alwaysReturnSuccess; - } - - @Override - public boolean onTransportBound(IBackupTransport binder) { - mTransportsCalledFor.add(binder); - return mAlwaysReturnSuccess; - } - - /** - * Returns whether the listener was called for the specified transport at least once. - */ - public boolean isCalledForTransport(IBackupTransport binder) { - return mTransportsCalledFor.contains(binder); - } - - /** - * Returns whether the listener was called at least once. - */ - public boolean isCalled() { - return !mTransportsCalledFor.isEmpty(); - } - - /** - * Resets listener calls. - */ - public void resetState() { - mTransportsCalledFor.clear(); - } -} diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/src/com/android/server/backup/testing/TransportData.java new file mode 100644 index 0000000000000..9feaa8efcf3d4 --- /dev/null +++ b/services/robotests/src/com/android/server/backup/testing/TransportData.java @@ -0,0 +1,149 @@ +/* + * 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.testing; + +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Intent; + +public class TransportData { + // No constants since new Intent() can't be called in static context because of Robolectric + public static TransportData backupTransport() { + return new TransportData( + "com.google.android.gms/.backup.BackupTransportService", + "com.google.android.gms/.backup.BackupTransportService", + "com.google.android.gms.backup.BackupTransportService", + new Intent(), + "user@gmail.com", + new Intent(), + "Google Account"); + } + + public static TransportData d2dTransport() { + return new TransportData( + "com.google.android.gms/.backup.migrate.service.D2dTransport", + "com.google.android.gms/.backup.component.D2dTransportService", + "d2dMigrateTransport", + null, + "Moving data to new device", + null, + ""); + } + + public static TransportData localTransport() { + return new TransportData( + "android/com.android.internal.backup.LocalTransport", + "android/com.android.internal.backup.LocalTransportService", + "com.android.internal.backup.LocalTransport", + null, + "Backing up to debug-only private cache", + null, + ""); + } + + public static TransportData genericTransport(String packageName, String className) { + return new TransportData( + packageName + "/." + className, + packageName + "/." + className + "Service", + packageName + "." + className, + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel"); + } + + @TransportTestUtils.TransportStatus + public int transportStatus; + public final String transportName; + private final String transportComponentShort; + @Nullable + public String transportDirName; + @Nullable public Intent configurationIntent; + @Nullable public String currentDestinationString; + @Nullable public Intent dataManagementIntent; + @Nullable public String dataManagementLabel; + + private TransportData( + @TransportTestUtils.TransportStatus int transportStatus, + String transportName, + String transportComponentShort, + String transportDirName, + Intent configurationIntent, + String currentDestinationString, + Intent dataManagementIntent, + String dataManagementLabel) { + this.transportStatus = transportStatus; + this.transportName = transportName; + this.transportComponentShort = transportComponentShort; + this.transportDirName = transportDirName; + this.configurationIntent = configurationIntent; + this.currentDestinationString = currentDestinationString; + this.dataManagementIntent = dataManagementIntent; + this.dataManagementLabel = dataManagementLabel; + } + + public TransportData( + String transportName, + String transportComponentShort, + String transportDirName, + Intent configurationIntent, + String currentDestinationString, + Intent dataManagementIntent, + String dataManagementLabel) { + this( + TransportTestUtils.TransportStatus.REGISTERED_AVAILABLE, + transportName, + transportComponentShort, + transportDirName, + configurationIntent, + currentDestinationString, + dataManagementIntent, + dataManagementLabel); + } + + /** + * Not field because otherwise we'd have to call ComponentName::new in static context and + * Robolectric does not like this. + */ + public ComponentName getTransportComponent() { + return ComponentName.unflattenFromString(transportComponentShort); + } + + public TransportData unavailable() { + return new TransportData( + TransportTestUtils.TransportStatus.REGISTERED_UNAVAILABLE, + transportName, + transportComponentShort, + transportDirName, + configurationIntent, + currentDestinationString, + dataManagementIntent, + dataManagementLabel); + } + + public TransportData unregistered() { + return new TransportData( + TransportTestUtils.TransportStatus.UNREGISTERED, + transportName, + transportComponentShort, + transportDirName, + configurationIntent, + currentDestinationString, + dataManagementIntent, + dataManagementLabel); + } +} diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java index 9770e407ec721..e1dc7b5e151e2 100644 --- a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java +++ b/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java @@ -16,13 +16,23 @@ package com.android.server.backup.testing; +import static com.android.server.backup.testing.TestUtils.uncheck; + +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static java.util.stream.Collectors.toList; + import android.annotation.Nullable; import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; +import android.support.annotation.IntDef; import com.android.internal.backup.IBackupTransport; import com.android.server.backup.TransportManager; @@ -30,85 +40,82 @@ import com.android.server.backup.transport.TransportClient; import com.android.server.backup.transport.TransportNotAvailableException; import com.android.server.backup.transport.TransportNotRegisteredException; -import java.util.Arrays; +import org.robolectric.shadows.ShadowPackageManager; + import java.util.List; +import java.util.stream.Stream; public class TransportTestUtils { - public static final String[] TRANSPORT_NAMES = { - "android/com.android.internal.backup.LocalTransport", - "com.google.android.gms/.backup.migrate.service.D2dTransport", - "com.google.android.gms/.backup.BackupTransportService" - }; + /** + * Differently from {@link #setUpTransports(TransportManager, TransportData...)}, which + * configures {@link TransportManager}, this is meant to mock the environment for a real + * TransportManager. + */ + public static void setUpTransportsForTransportManager( + ShadowPackageManager shadowPackageManager, TransportData... transports) + throws Exception { + for (TransportData transport : transports) { + ComponentName transportComponent = transport.getTransportComponent(); + String packageName = transportComponent.getPackageName(); + ResolveInfo resolveInfo = resolveInfo(transportComponent); + shadowPackageManager.addResolveInfoForIntent(transportIntent(), resolveInfo); + shadowPackageManager.addResolveInfoForIntent( + transportIntent().setPackage(packageName), resolveInfo); + } + } - public static final String TRANSPORT_NAME = TRANSPORT_NAMES[0]; + private static Intent transportIntent() { + return new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST); + } - /** {@code transportName} has to be in the {@link ComponentName} format (with '/') */ - public static TransportData setUpCurrentTransport( - TransportManager transportManager, String transportName) throws Exception { - TransportData transport = setUpTransports(transportManager, transportName).get(0); - when(transportManager.getCurrentTransportClient(any())) - .thenReturn(transport.transportClientMock); - return transport; + private static ResolveInfo resolveInfo(ComponentName transportComponent) { + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.serviceInfo = new ServiceInfo(); + resolveInfo.serviceInfo.packageName = transportComponent.getPackageName(); + resolveInfo.serviceInfo.name = transportComponent.getClassName(); + return resolveInfo; } /** {@code transportName} has to be in the {@link ComponentName} format (with '/') */ - public static List setUpTransports( - TransportManager transportManager, String... transportNames) throws Exception { - return setUpTransports( - transportManager, - Arrays.stream(transportNames) - .map(TransportData::new) - .toArray(TransportData[]::new)); + public static TransportMock setUpCurrentTransport( + TransportManager transportManager, TransportData transport) throws Exception { + TransportMock transportMock = setUpTransports(transportManager, transport).get(0); + if (transportMock.transportClient != null) { + when(transportManager.getCurrentTransportClient(any())) + .thenReturn(transportMock.transportClient); + } + return transportMock; } /** @see #setUpTransport(TransportManager, TransportData) */ - public static List setUpTransports( + public static List setUpTransports( TransportManager transportManager, TransportData... transports) throws Exception { - for (TransportData transport : transports) { - setUpTransport(transportManager, transport); - } - return Arrays.asList(transports); + return Stream.of(transports) + .map(transport -> uncheck(() -> setUpTransport(transportManager, transport))) + .collect(toList()); } - /** - * Configures transport according to {@link TransportData}: - * - *

    - *
  • {@link TransportData#transportMock} {@code null} means transport not available. - *
  • {@link TransportData#transportClientMock} {@code null} means transport not registered. - *
- */ - public static void setUpTransport(TransportManager transportManager, TransportData transport) - throws Exception { + public static TransportMock setUpTransport( + TransportManager transportManager, TransportData transport) throws Exception { + int status = transport.transportStatus; String transportName = transport.transportName; - String transportDirName = transportDirName(transportName); - ComponentName transportComponent = transportComponentName(transportName); - IBackupTransport transportMock = transport.transportMock; - TransportClient transportClientMock = transport.transportClientMock; + ComponentName transportComponent = transport.getTransportComponent(); + String transportDirName = transport.transportDirName; - if (transportClientMock != null) { + TransportMock transportMock = mockTransport(transport); + if (status == TransportStatus.REGISTERED_AVAILABLE + || status == TransportStatus.REGISTERED_UNAVAILABLE) { // Transport registered when(transportManager.getTransportClient(eq(transportName), any())) - .thenReturn(transportClientMock); + .thenReturn(transportMock.transportClient); when(transportManager.getTransportClientOrThrow(eq(transportName), any())) - .thenReturn(transportClientMock); + .thenReturn(transportMock.transportClient); when(transportManager.getTransportName(transportComponent)).thenReturn(transportName); when(transportManager.getTransportDirName(eq(transportName))) .thenReturn(transportDirName); when(transportManager.getTransportDirName(eq(transportComponent))) .thenReturn(transportDirName); - when(transportClientMock.getTransportComponent()).thenReturn(transportComponent); - - if (transportMock != null) { - // Transport registered and available - when(transportClientMock.connectOrThrow(any())).thenReturn(transportMock); - when(transportMock.name()).thenReturn(transportName); - when(transportMock.transportDirName()).thenReturn(transportDirName); - } else { - // Transport registered but unavailable - when(transportClientMock.connectOrThrow(any())) - .thenThrow(TransportNotAvailableException.class); - } + // TODO: Mock rest of description methods } else { // Transport not registered when(transportManager.getTransportClient(eq(transportName), any())).thenReturn(null); @@ -121,34 +128,73 @@ public class TransportTestUtils { when(transportManager.getTransportDirName(eq(transportComponent))) .thenThrow(TransportNotRegisteredException.class); } + return transportMock; } - /** {@code transportName} has to be in the {@link ComponentName} format (with '/') */ - public static ComponentName transportComponentName(String transportName) { - return ComponentName.unflattenFromString(transportName); - } + public static TransportMock mockTransport(TransportData transport) throws Exception { + final TransportClient transportClientMock; + int status = transport.transportStatus; + ComponentName transportComponent = transport.getTransportComponent(); + if (status == TransportStatus.REGISTERED_AVAILABLE + || status == TransportStatus.REGISTERED_UNAVAILABLE) { + // Transport registered + transportClientMock = mock(TransportClient.class); + when(transportClientMock.getTransportComponent()).thenReturn(transportComponent); + if (status == TransportStatus.REGISTERED_AVAILABLE) { + // Transport registered and available + IBackupTransport transportMock = mockTransportBinder(transport); + when(transportClientMock.connectOrThrow(any())).thenReturn(transportMock); - public static String transportDirName(String transportName) { - return transportName + "_dir_name"; - } + return new TransportMock(transportClientMock, transportMock); + } else { + // Transport registered but unavailable + when(transportClientMock.connectOrThrow(any())) + .thenThrow(TransportNotAvailableException.class); - public static class TransportData { - public final String transportName; - @Nullable public final IBackupTransport transportMock; - @Nullable public final TransportClient transportClientMock; - - public TransportData( - String transportName, - @Nullable IBackupTransport transportMock, - @Nullable TransportClient transportClientMock) { - this.transportName = transportName; - this.transportMock = transportMock; - this.transportClientMock = transportClientMock; + return new TransportMock(transportClientMock, null); + } + } else { + // Transport not registered + return new TransportMock(null, null); } + } - public TransportData(String transportName) { - this(transportName, mock(IBackupTransport.class), mock(TransportClient.class)); + private static IBackupTransport mockTransportBinder(TransportData transport) throws Exception { + IBackupTransport transportBinder = mock(IBackupTransport.class); + try { + when(transportBinder.name()).thenReturn(transport.transportName); + when(transportBinder.transportDirName()).thenReturn(transport.transportDirName); + when(transportBinder.configurationIntent()).thenReturn(transport.configurationIntent); + when(transportBinder.currentDestinationString()) + .thenReturn(transport.currentDestinationString); + when(transportBinder.dataManagementIntent()).thenReturn(transport.dataManagementIntent); + when(transportBinder.dataManagementLabel()).thenReturn(transport.dataManagementLabel); + } catch (RemoteException e) { + fail("RemoteException?"); } + return transportBinder; + } + + public static class TransportMock { + @Nullable public final TransportClient transportClient; + @Nullable public final IBackupTransport transport; + + private TransportMock( + @Nullable TransportClient transportClient, @Nullable IBackupTransport transport) { + this.transportClient = transportClient; + this.transport = transport; + } + } + + @IntDef({ + TransportStatus.REGISTERED_AVAILABLE, + TransportStatus.REGISTERED_UNAVAILABLE, + TransportStatus.UNREGISTERED + }) + public @interface TransportStatus { + int REGISTERED_AVAILABLE = 0; + int REGISTERED_UNAVAILABLE = 1; + int UNREGISTERED = 2; } private TransportTestUtils() {} diff --git a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java new file mode 100644 index 0000000000000..6d220737f2fbd --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowContextImpl.java @@ -0,0 +1,37 @@ +/* + * 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.testing.shadows; + +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.UserHandle; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowContextImpl; + +@Implements(className = ShadowContextImpl.CLASS_NAME, inheritImplementationMethods = true) +public class FrameworkShadowContextImpl extends ShadowContextImpl { + @Implementation + public boolean bindServiceAsUser( + Intent service, + ServiceConnection connection, + int flags, + UserHandle user) { + return bindService(service, connection, flags); + } +} diff --git a/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java similarity index 84% rename from services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java rename to services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java index b64b59d238d16..5cdbe7ff09ba9 100644 --- a/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java +++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowPackageManager.java @@ -14,22 +14,18 @@ * limitations under the License */ -package com.android.server.backup.testing; +package com.android.server.testing.shadows; import android.app.ApplicationPackageManager; import android.content.Intent; import android.content.pm.ResolveInfo; - +import java.util.List; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowApplicationPackageManager; -import java.util.List; - -/** - * Implementation of PackageManager for Robolectric which handles queryIntentServicesAsUser(). - */ +/** Extension of ShadowApplicationPackageManager */ @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true) -public class ShadowPackageManagerForBackup extends ShadowApplicationPackageManager { +public class FrameworkShadowPackageManager extends ShadowApplicationPackageManager { @Override public List queryIntentServicesAsUser(Intent intent, int flags, int userId) { return queryIntentServices(intent, flags); diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java index 766d30d9f9a5c..7b4441ae30e60 100644 --- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -96,7 +96,7 @@ public class BackupManagerServiceTest { createBackupManagerService(); verify(mTransportManager) - .setTransportBoundListener(any(TransportManager.TransportBoundListener.class)); + .setOnTransportRegisteredListener(any()); } @Test