Add CTS tradefed target preparer for backup tests.

BackupPreparer is used to enable backup and select LocalTransport
on the testdevice before all the tests and return the device
to the original state after all tests are finished.

This is better than the current way of implementing it in the
base test class, which is enabling/disabling backup before/after
each test, which can trigger the device init operation
that can disrupt the execution of the next test since it's run
async and there is no guarantee on when it's going to be executed.

Bug: 62253989
Test: make cts -j32 && cts-tradefed run cts-dev -m
CtsBackupHostTestCases

Change-Id: Iaeb9c62dc77ffce4d96bf992692c7ec9e9caffb3
(cherry picked from commit 8b71b0ca70f4c17048c7fcebd13588ffd8dcee99)
diff --git a/hostsidetests/backup/AndroidTest.xml b/hostsidetests/backup/AndroidTest.xml
index 2c4641b..065e78b 100644
--- a/hostsidetests/backup/AndroidTest.xml
+++ b/hostsidetests/backup/AndroidTest.xml
@@ -21,6 +21,10 @@
         <option name="test-file-name" value="CtsFullbackupApp.apk" />
         <option name="test-file-name" value="CtsIncludeExcludeApp.apk" />
     </target_preparer>
+    <target_preparer class="android.cts.backup.BackupPreparer">
+        <option name="enable-backup-if-needed" value="true" />
+        <option name="select-local-transport" value="true" />
+    </target_preparer>
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsBackupHostTestCases.jar" />
     </test>
diff --git a/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java b/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
new file mode 100644
index 0000000..1d98021
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
@@ -0,0 +1,141 @@
+/*
+ * 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 android.cts.backup;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.targetprep.BuildError;
+import com.android.tradefed.targetprep.ITargetCleaner;
+import com.android.tradefed.targetprep.TargetSetupError;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Tradedfed target preparer for the backup tests.
+ * Enables backup before all the tests and selects local transport.
+ * Reverts to the original state after all the tests are executed.
+ */
+@OptionClass(alias = "backup-preparer")
+public class BackupPreparer implements ITargetCleaner {
+    @Option(name="enable-backup-if-needed", description=
+            "Enable backup before all the tests and return to the original state after.")
+    private boolean mEnableBackup = true;
+
+    @Option(name="select-local-transport", description=
+            "Select local transport before all the tests and return to the original transport "
+                    + "after.")
+    private boolean mSelectLocalTransport = true;
+
+    /** Value of PackageManager.FEATURE_BACKUP */
+    private static final String FEATURE_BACKUP = "android.software.backup";
+
+    private static final String LOCAL_TRANSPORT =
+            "android/com.android.internal.backup.LocalTransport";
+
+    private boolean mIsBackupSupported;
+    private boolean mWasBackupEnabled;
+    private String mOldTransport;
+    private ITestDevice mDevice;
+
+    @Override
+    public void setUp(ITestDevice device, IBuildInfo buildInfo)
+            throws TargetSetupError, BuildError, DeviceNotAvailableException {
+        mDevice = device;
+
+        mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP);
+
+        if (mIsBackupSupported) {
+            // Enable backup and select local backup transport
+            if (!hasBackupTransport(LOCAL_TRANSPORT)) {
+                throw new TargetSetupError("Device should have LocalTransport available",
+                        device.getDeviceDescriptor());
+            }
+            if (mEnableBackup) {
+                CLog.i("Enabling backup on %s", mDevice.getSerialNumber());
+                mWasBackupEnabled = enableBackup(true);
+                CLog.d("Backup was enabled? : %s", mWasBackupEnabled);
+                if (mSelectLocalTransport) {
+                    CLog.i("Selecting local transport on %s", mDevice.getSerialNumber());
+                    mOldTransport = setBackupTransport(LOCAL_TRANSPORT);
+                    CLog.d("Old transport : %s", mOldTransport);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
+            throws DeviceNotAvailableException {
+        mDevice = device;
+
+        if (mIsBackupSupported) {
+            if (mEnableBackup) {
+                CLog.i("Returning backup to it's previous state on %s", mDevice.getSerialNumber());
+                enableBackup(mWasBackupEnabled);
+                if (mSelectLocalTransport) {
+                    CLog.i("Returning selected transport to it's previous value on %s",
+                            mDevice.getSerialNumber());
+                    setBackupTransport(mOldTransport);
+                }
+            }
+        }
+    }
+
+    // Copied over from BackupQuotaTest
+    private boolean hasBackupTransport(String transport) throws DeviceNotAvailableException {
+        String output = mDevice.executeShellCommand("bmgr list transports");
+        for (String t : output.split(" ")) {
+            if (transport.equals(t.trim())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Copied over from BackupQuotaTest
+    private boolean enableBackup(boolean enable) throws DeviceNotAvailableException {
+        boolean previouslyEnabled;
+        String output = mDevice.executeShellCommand("bmgr enabled");
+        Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$");
+        Matcher matcher = pattern.matcher(output.trim());
+        if (matcher.find()) {
+            previouslyEnabled = "enabled".equals(matcher.group(1));
+        } else {
+            throw new RuntimeException("non-parsable output setting bmgr enabled: " + output);
+        }
+
+        mDevice.executeShellCommand("bmgr enable " + enable);
+        return previouslyEnabled;
+    }
+
+    // Copied over from BackupQuotaTest
+    private String setBackupTransport(String transport) throws DeviceNotAvailableException {
+        String output = mDevice.executeShellCommand("bmgr transport " + transport);
+        Pattern pattern = Pattern.compile("\\(formerly (.*)\\)$");
+        Matcher matcher = pattern.matcher(output);
+        if (matcher.find()) {
+            return matcher.group(1);
+        } else {
+            throw new RuntimeException("non-parsable output setting bmgr transport: " + output);
+        }
+    }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
index 8380184..9d182c6 100644
--- a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
@@ -16,7 +16,6 @@
 
 package android.cts.backup;
 
-import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assume.assumeTrue;
@@ -29,8 +28,6 @@
 import org.junit.Before;
 import org.junit.runner.RunWith;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 import java.util.Scanner;
 
 /**
@@ -38,7 +35,6 @@
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public abstract class BaseBackupHostSideTest extends CompatibilityHostTestBase {
-
     /** Value of PackageManager.FEATURE_BACKUP */
     private static final String FEATURE_BACKUP = "android.software.backup";
 
@@ -46,25 +42,16 @@
             "android/com.android.internal.backup.LocalTransport";
 
     private boolean mIsBackupSupported;
-    private boolean mWasBackupEnabled;
-    private String mOldTransport;
 
     @Before
     public void setUp() throws DeviceNotAvailableException, Exception {
         mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP);
         assumeTrue(mIsBackupSupported);
-        // Enable backup and select local backup transport
-        assertTrue("LocalTransport should be available.", hasBackupTransport(LOCAL_TRANSPORT));
-        mWasBackupEnabled = enableBackup(true);
-        mOldTransport = setBackupTransport(LOCAL_TRANSPORT);
     }
 
     @After
     public void tearDown() throws Exception {
-        if (mIsBackupSupported) {
-            setBackupTransport(mOldTransport);
-            enableBackup(mWasBackupEnabled);
-        }
+        // Not deleting to avoid breaking the tests calling super.tearDown()
     }
 
     /**
@@ -125,42 +112,28 @@
         assertTrue("Restore not successful", restoreOutput.contains("restoreFinished: 0"));
     }
 
-    // Copied over from BackupQuotaTest
-    private boolean enableBackup(boolean enable) throws Exception {
-        boolean previouslyEnabled;
-        String output = mDevice.executeShellCommand("bmgr enabled");
-        Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$");
-        Matcher matcher = pattern.matcher(output.trim());
-        if (matcher.find()) {
-            previouslyEnabled = "enabled".equals(matcher.group(1));
-        } else {
-            throw new RuntimeException("non-parsable output setting bmgr enabled: " + output);
-        }
-
-        mDevice.executeShellCommand("bmgr enable " + enable);
-        return previouslyEnabled;
+    protected void startActivityInPackageAndWait(String packageName, String className)
+            throws DeviceNotAvailableException {
+        mDevice.executeShellCommand(String.format(
+                "am start -W -a android.intent.action.MAIN -n %s/%s.%s", packageName,
+                packageName,
+                className));
     }
 
-    // Copied over from BackupQuotaTest
-    private String setBackupTransport(String transport) throws Exception {
-        String output = mDevice.executeShellCommand("bmgr transport " + transport);
-        Pattern pattern = Pattern.compile("\\(formerly (.*)\\)$");
-        Matcher matcher = pattern.matcher(output);
-        if (matcher.find()) {
-            return matcher.group(1);
-        } else {
-            throw new RuntimeException("non-parsable output setting bmgr transport: " + output);
-        }
+    /**
+     * Clears backup data stored in Local Transport for a package.
+     * NB: 'bmgr wipe' does not produce any useful output if the package or transport not found,
+     * so we cannot really check the success of the operation
+      */
+    protected void clearBackupDataInLocalTransport(String packageName)
+            throws DeviceNotAvailableException {
+        mDevice.executeShellCommand(String.format("bmgr wipe %s %s", LOCAL_TRANSPORT, packageName));
     }
 
-    // Copied over from BackupQuotaTest
-    private boolean hasBackupTransport(String transport) throws Exception {
-        String output = mDevice.executeShellCommand("bmgr list transports");
-        for (String t : output.split(" ")) {
-            if (transport.equals(t.trim())) {
-                return true;
-            }
-        }
-        return false;
+    /**
+     * Clears package data
+     */
+    protected void clearPackageData(String packageName) throws DeviceNotAvailableException {
+        mDevice.executeShellCommand(String.format("pm clear %s", packageName));
     }
 }