Improve the performance of App Cloning CTS tests

Improved the performance of App Cloning CTS tests by 15 to 20 seconds on
an average by restructuring the setup and tearDown methods along with
bunch of other improvements mentioned in
go/ac-tests-performance-improvement document. This CL incorporates the
code changes related to the performance improvement mentioned in the doc.

Bug: 218154538
Bug: 217440402
Bug: 217637261
Bug: 218254899
Bug: 217354495
Bug: 220868413
Test: atest com.android.cts.appcloning.AppCloningHostTest
Change-Id: Id6f93253ccfb14a0f07af7f6afef97f477ef5232
(cherry picked from commit 701ed5a2f44c658c5a08592b35f8d005896977d6)
diff --git a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningBaseHostTest.java b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningBaseHostTest.java
index 56c4866..f1777fc 100644
--- a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningBaseHostTest.java
+++ b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningBaseHostTest.java
@@ -22,8 +22,6 @@
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
 import com.android.tradefed.util.CommandResult;
 
@@ -34,8 +32,8 @@
 public class AppCloningBaseHostTest extends BaseHostTestCase {
 
     protected static final String APP_A_PACKAGE = "com.android.cts.appcloningtestapp";
+    protected static final String APP_A = "CtsAppCloningTestApp.apk";
 
-    private static final String APP_A = "CtsAppCloningTestApp.apk";
     private static final String TEST_CLASS_A = APP_A_PACKAGE + ".AppCloningDeviceTest";
     private static final long DEFAULT_INSTRUMENTATION_TIMEOUT_MS = 600_000; // 10min
 
@@ -45,11 +43,7 @@
 
     public String mCloneUserId;
 
-    public void baseHostSetup() throws Exception {
-        assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode());
-        assumeTrue(isAtLeastS());
-        assumeFalse("Device uses sdcardfs", usesSdcardFs());
-
+    private void createAndStartCloneUser() throws Exception {
         // create clone user
         String output = executeShellCommand(
                 "pm create-user --profileOf 0 --user-type android.os.usertype.profile.CLONE "
@@ -60,27 +54,25 @@
 
         CommandResult out = executeShellV2Command("am start-user -w %s", mCloneUserId);
         assertThat(isSuccessful(out)).isTrue();
+    }
 
-        // Install the app in both the user spaces
-        installAppAsUser(APP_A, getCurrentUserId());
-        installAppAsUser(APP_A, Integer.valueOf(mCloneUserId));
+    public void baseHostSetup() throws Exception {
+        setDevice();
+
+        assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode());
+        assumeTrue(isAtLeastS());
+        assumeFalse("Device uses sdcardfs", usesSdcardFs());
+
+        createAndStartCloneUser();
     }
 
     public void baseHostTeardown() throws Exception {
         if (isHeadlessSystemUserMode() || !isAtLeastS() || usesSdcardFs()) return;
 
-        // Uninstall the app
-        uninstallPackage(APP_A_PACKAGE);
-
         // remove the clone user
         executeShellCommand("pm remove-user %s", mCloneUserId);
     }
 
-    protected void installAppAsUser(String packageFile, int userId)
-            throws TargetSetupError, DeviceNotAvailableException {
-        installPackageAsUser(packageFile, false, userId, "-t");
-    }
-
     protected CommandResult runContentProviderCommand(String commandType, String userId,
             String provider, String relativePath, String... args) throws Exception {
         String fullUri = provider + relativePath;
diff --git a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java
index ae63583..f2c63cd 100644
--- a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java
+++ b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java
@@ -43,6 +43,7 @@
 
     private static final int CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS = 20000;
     private static final int CLONE_PROFILE_MEDIA_PROVIDER_OPERATION_TIMEOUT_MS = 30000;
+    private static final int CONTENT_PROVIDER_SETUP_TIMEOUT_MS = 50000;
 
     private static final String IMAGE_NAME_TO_BE_CREATED_KEY = "imageNameToBeCreated";
     private static final String IMAGE_NAME_TO_BE_DISPLAYED_KEY = "imageNameToBeDisplayed";
@@ -52,27 +53,46 @@
             "imageNameToBeVerifiedInCloneProfile";
     private static final String CLONE_USER_ID = "cloneUserId";
     private static final String MEDIA_PROVIDER_IMAGES_PATH = "/external/images/media/";
+    private static final String CONTENT_PROVIDER_SETUP_FAILURE =
+            "ContentProviderHandler Setup Failure";
     private ContentProviderHandler mContentProviderHandler;
 
+    private void contentProviderHandlerSetup() throws Exception {
+        mContentProviderHandler = new ContentProviderHandler(mDevice);
+        eventually(() -> mContentProviderHandler.setUp(), CONTENT_PROVIDER_SETUP_TIMEOUT_MS,
+                CONTENT_PROVIDER_SETUP_FAILURE);
+    }
+
     @Before
     public void setup() throws Exception {
         super.baseHostSetup();
-
-        mContentProviderHandler = new ContentProviderHandler(getDevice());
-        mContentProviderHandler.setUp();
     }
 
-    @After
-    public void tearDown() throws Exception {
-        super.baseHostTeardown();
-
+    private void contentProviderHandlerTearDown() throws Exception {
         if (mContentProviderHandler != null) {
             mContentProviderHandler.tearDown();
         }
     }
 
+    @After
+    public void tearDown() throws Exception {
+        super.baseHostTeardown();
+    }
+
     @Test
     public void testCreateCloneUserFile() throws Exception {
+        try {
+            contentProviderHandlerSetup();
+
+            // createCloneUserFile Test Logic
+            createCloneUserFileTest();
+        } finally {
+
+            contentProviderHandlerTearDown();
+        }
+    }
+
+    private void createCloneUserFileTest() throws Exception {
         CommandResult out;
 
         // Check that the clone user directories exist
@@ -163,6 +183,9 @@
 
     @Test
     public void testPrivateAppDataDirectoryForCloneUser() throws Exception {
+        // Install the app in clone user space
+        installPackage(APP_A, "--user " + Integer.valueOf(mCloneUserId));
+
         eventually(() -> {
             // Wait for finish.
             assertThat(isPackageInstalled(APP_A_PACKAGE, mCloneUserId)).isTrue();
@@ -171,13 +194,18 @@
 
     @Test
     public void testCrossUserMediaAccess() throws Exception {
+        // Install the app in both the user spaces
+        installPackage(APP_A, "--user all");
+
+        int currentUserId = getCurrentUserId();
+
         // Run save image test in owner user space
         Map<String, String> ownerArgs = new HashMap<>();
         ownerArgs.put(IMAGE_NAME_TO_BE_DISPLAYED_KEY, "WeirdOwnerProfileImage");
         ownerArgs.put(IMAGE_NAME_TO_BE_CREATED_KEY, "owner_profile_image");
 
         runDeviceTestAsUserInPkgA("testMediaStoreManager_writeImageToSharedStorage",
-                getCurrentUserId(), ownerArgs);
+                currentUserId, ownerArgs);
 
         // Run save image test in clone user space
         Map<String, String> cloneArgs = new HashMap<>();
@@ -195,8 +223,7 @@
 
         // From owner user space
         runDeviceTestAsUserInPkgA(
-                "testMediaStoreManager_verifyCrossUserImagesInSharedStorage",
-                getCurrentUserId(), args);
+                "testMediaStoreManager_verifyCrossUserImagesInSharedStorage", currentUserId, args);
 
         // From clone user space
         runDeviceTestAsUserInPkgA(
@@ -207,9 +234,14 @@
     @Test
     public void testGetStorageVolumesIncludingSharedProfiles() throws Exception {
         assumeTrue(isAtLeastT());
+        int currentUserId = getCurrentUserId();
+
+        // Install the app in owner user space
+        installPackage(APP_A, "--user " + currentUserId);
+
         Map<String, String> args = new HashMap<>();
         args.put(CLONE_USER_ID, mCloneUserId);
         runDeviceTestAsUserInPkgA("testStorageManager_verifyInclusionOfSharedProfileVolumes",
-                getCurrentUserId(), args);
+                currentUserId, args);
     }
 }
diff --git a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java
index 782604a..fdea67b 100644
--- a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java
+++ b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java
@@ -25,40 +25,59 @@
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.CommandStatus;
 
+import java.util.function.BooleanSupplier;
+
 
 abstract class BaseHostTestCase extends BaseHostJUnit4Test {
     private int mCurrentUserId = NativeDevice.INVALID_USER_ID;
     private static final String ERROR_MESSAGE_TAG = "[ERROR]";
+    protected ITestDevice mDevice = null;
+
+    protected void setDevice() {
+        mDevice = getDevice();
+    }
 
     protected String executeShellCommand(String cmd, Object... args) throws Exception {
-        return getDevice().executeShellCommand(String.format(cmd, args));
+        return mDevice.executeShellCommand(String.format(cmd, args));
     }
 
     protected CommandResult executeShellV2Command(String cmd, Object... args) throws Exception {
-        return getDevice().executeShellV2Command(String.format(cmd, args));
+        return mDevice.executeShellV2Command(String.format(cmd, args));
     }
 
     protected boolean isPackageInstalled(String packageName, String userId) throws Exception {
-        return getDevice().isPackageInstalled(packageName, userId);
+        return mDevice.isPackageInstalled(packageName, userId);
     }
 
     // TODO (b/174775905) remove after exposing the check from ITestDevice.
     protected boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException {
-        String result = getDevice()
+        String result = mDevice
                 .executeShellCommand("getprop ro.fw.mu.headless_system_user").trim();
         return "true".equalsIgnoreCase(result);
     }
 
     protected boolean isAtLeastS() throws DeviceNotAvailableException {
-        DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(getDevice());
+        DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(mDevice);
         return deviceSdkLevel.isDeviceAtLeastS();
     }
 
     protected boolean isAtLeastT() throws DeviceNotAvailableException {
-        DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(getDevice());
+        DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(mDevice);
         return deviceSdkLevel.isDeviceAtLeastT();
     }
 
+    protected static void throwExceptionIfTimeout(long start, long timeoutMillis, Throwable e) {
+        if (System.currentTimeMillis() - start < timeoutMillis) {
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ignored) {
+                throw new RuntimeException(e);
+            }
+        } else {
+            throw new RuntimeException(e);
+        }
+    }
+
     protected static void eventually(ThrowingRunnable r, long timeoutMillis) {
         long start = System.currentTimeMillis();
 
@@ -67,15 +86,24 @@
                 r.run();
                 return;
             } catch (Throwable e) {
-                if (System.currentTimeMillis() - start < timeoutMillis) {
-                    try {
-                        Thread.sleep(100);
-                    } catch (InterruptedException ignored) {
-                        throw new RuntimeException(e);
-                    }
-                } else {
-                    throw new RuntimeException(e);
+                throwExceptionIfTimeout(start, timeoutMillis, e);
+            }
+        }
+    }
+
+    protected static void eventually(ThrowingBooleanSupplier booleanSupplier,
+            long timeoutMillis, String failureMessage) {
+        long start = System.currentTimeMillis();
+
+        while (true) {
+            try {
+                if (booleanSupplier.getAsBoolean()) {
+                    return;
                 }
+
+                throw new RuntimeException(failureMessage);
+            } catch (Throwable e) {
+                throwExceptionIfTimeout(start, timeoutMillis, e);
             }
         }
     }
@@ -101,8 +129,7 @@
     private void setCurrentUserId() throws Exception {
         if (mCurrentUserId != NativeDevice.INVALID_USER_ID) return;
 
-        ITestDevice device = getDevice();
-        mCurrentUserId = device.getCurrentUser();
+        mCurrentUserId = mDevice.getCurrentUser();
         CLog.i("Current user: %d");
     }
 
@@ -112,4 +139,11 @@
          */
         void run() throws Exception;
     }
+
+    protected interface ThrowingBooleanSupplier {
+        /**
+         * Similar to {@link BooleanSupplier#getAsBoolean} but has {@code throws Exception}.
+         */
+        boolean getAsBoolean() throws Exception;
+    }
 }
diff --git a/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp b/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
index 6417bd6..ef10a81 100644
--- a/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
+++ b/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
@@ -20,10 +20,6 @@
     name: "CtsAppCloningTestApp",
     defaults: ["cts_defaults"],
     static_libs: [
-        "cts-scopedstorage-lib",
-        "androidx.test.rules",
-        "truth-prebuilt",
-        "androidx.appcompat_appcompat",
         "cts-install-lib",
     ],
     srcs: ["src/**/*.java"],
diff --git a/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/MediaStoreWriteOperation.java b/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/MediaStoreWriteOperation.java
index 8229cec..33e14966 100644
--- a/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/MediaStoreWriteOperation.java
+++ b/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/MediaStoreWriteOperation.java
@@ -26,6 +26,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Calendar;
 
 public class MediaStoreWriteOperation {
 
@@ -47,7 +48,7 @@
         // Publish a new image
         ContentValues newImageDetails = new ContentValues();
         newImageDetails.put(MediaStore.Images.Media.DISPLAY_NAME,
-                displayName + ".jpg");
+                displayName + "_" + Calendar.getInstance().getTime() + ".jpg");
         newImageDetails.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
         newImageDetails.put(MediaStore.Images.Media.WIDTH, bitmap.getWidth());
         newImageDetails.put(MediaStore.Images.Media.HEIGHT, bitmap.getHeight());