Rewrites CTS test for key/value backup

Previous implementation used logcat as means of communication between
the test app on the device and the host side of the test. Due to the
nature of logcat that proved to be unreliable and flaky.

New implementation uses device-side test routines to save, read and
verify shared preferences and files. The host side is used to drive the
test flow, install and uninstall the test app, run backup and restore commands.

Test: cts-tradefed run cts -m CtsBackupHostTestCases -t android.cts.backup.KeyValueBackupRestoreHostSideTest
Bug: 38331563
Change-Id: I0d3082c7b0088965f9d018aeb35eae6244f80018
diff --git a/hostsidetests/backup/AndroidTest.xml b/hostsidetests/backup/AndroidTest.xml
index 065e78b..314a92e3 100644
--- a/hostsidetests/backup/AndroidTest.xml
+++ b/hostsidetests/backup/AndroidTest.xml
@@ -17,7 +17,6 @@
     <option name="config-descriptor:metadata" key="component" value="backup" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsBackupRestoreDeviceApp.apk" />
         <option name="test-file-name" value="CtsFullbackupApp.apk" />
         <option name="test-file-name" value="CtsIncludeExcludeApp.apk" />
     </target_preparer>
diff --git a/hostsidetests/backup/app/Android.mk b/hostsidetests/backup/KeyValueApp/Android.mk
similarity index 95%
rename from hostsidetests/backup/app/Android.mk
rename to hostsidetests/backup/KeyValueApp/Android.mk
index 248207c..390c6b0 100644
--- a/hostsidetests/backup/app/Android.mk
+++ b/hostsidetests/backup/KeyValueApp/Android.mk
@@ -32,7 +32,7 @@
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts
 
-LOCAL_PACKAGE_NAME := CtsBackupRestoreDeviceApp
+LOCAL_PACKAGE_NAME := CtsKeyValueBackupRestoreApp
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/backup/KeyValueApp/AndroidManifest.xml b/hostsidetests/backup/KeyValueApp/AndroidManifest.xml
new file mode 100644
index 0000000..cca91a5
--- /dev/null
+++ b/hostsidetests/backup/KeyValueApp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.cts.backup.keyvaluerestoreapp">
+
+    <application
+        android:backupAgent=".KeyValueBackupAgent"
+        android:killAfterRestore="false" >
+
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.keyvaluerestoreapp" />
+
+</manifest>
diff --git a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
similarity index 77%
rename from hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java
rename to hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
index ebf9c24..f79baa9 100644
--- a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java
+++ b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
@@ -14,11 +14,11 @@
  * limitations under the License
  */
 
-package android.backup.cts.backuprestoreapp;
+package android.cts.backup.keyvaluerestoreapp;
 
 import android.app.backup.BackupAgentHelper;
 
-public class CtsBackupRestoreBackupAgent extends BackupAgentHelper {
+public class KeyValueBackupAgent extends BackupAgentHelper {
 
     private static final String KEY_BACKUP_TEST_PREFS_PREFIX = "backup-test-prefs";
     private static final String KEY_BACKUP_TEST_FILES_PREFIX = "backup-test-files";
@@ -27,8 +27,8 @@
     public void onCreate() {
         super.onCreate();
         addHelper(KEY_BACKUP_TEST_PREFS_PREFIX,
-                KeyValueBackupRandomDataActivity.getSharedPreferencesBackupHelper(this));
+                KeyValueBackupRestoreTest.getSharedPreferencesBackupHelper(this));
         addHelper(KEY_BACKUP_TEST_FILES_PREFIX,
-                KeyValueBackupRandomDataActivity.getFileBackupHelper(this));
+                KeyValueBackupRestoreTest.getFileBackupHelper(this));
     }
 }
diff --git a/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupRestoreTest.java b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupRestoreTest.java
new file mode 100644
index 0000000..76f3de2
--- /dev/null
+++ b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupRestoreTest.java
@@ -0,0 +1,289 @@
+/*
+ * 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.keyvaluerestoreapp;
+
+import static android.content.Context.MODE_PRIVATE;
+import static android.support.test.InstrumentationRegistry.getTargetContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.backup.BackupManager;
+import android.app.backup.FileBackupHelper;
+import android.app.backup.SharedPreferencesBackupHelper;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.Scanner;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Device side routines to be invoked by the host side KeyValueBackupRestoreHostSideTest. These
+ * are not designed to be called in any other way, as they rely on state set up by the host side
+ * test.
+ *
+ * Some tests invoked by KeyValueBackupRestoreHostSideTest#testSharedPreferencesRestore() are
+ * interacting with SharedPrefsRestoreTestActivity from another package.
+ *
+ */
+@RunWith(AndroidJUnit4.class)
+public class KeyValueBackupRestoreTest {
+    private static final String TAG = "KeyValueBackupRestore";
+
+    // Names and values of the test files for
+    // KeyValueBackupRestoreHostSideTest#testKeyValueBackupAndRestore()
+    private static final String TEST_FILE_1 = "test-file-1";
+    private static final String TEST_FILE_1_DATA = "test-file-1-data";
+
+    private static final String TEST_FILE_2 = "test-file-2";
+    private static final String TEST_FILE_2_DATA = "test-file-2-data";
+
+    private static final String TEST_PREFS_1 = "test-prefs-1";
+    private static final String INT_PREF = "int-pref";
+    private static final int INT_PREF_VALUE = 111;
+    private static final String BOOL_PREF = "bool-pref";
+    private static final boolean BOOL_PREF_VALUE = true;
+
+    private static final String TEST_PREFS_2 = "test-prefs-2";
+    private static final String FLOAT_PREF = "float-pref";
+    private static final float FLOAT_PREF_VALUE = 0.12345f;
+    private static final String LONG_PREF = "long-pref";
+    private static final long LONG_PREF_VALUE = 12345L;
+    private static final String STRING_PREF = "string-pref";
+    private static final String STRING_PREF_VALUE = "string-pref-value";
+
+    private static final int DEFAULT_INT_VALUE = 0;
+    private static final boolean DEFAULT_BOOL_VALUE = false;
+    private static final float DEFAULT_FLOAT_VALUE = 0.0f;
+    private static final long DEFAULT_LONG_VALUE = 0L;
+    private static final String DEFAULT_STRING_VALUE = null;
+    private static final String DEFAULT_FILE_CONTENT = "";
+
+
+    /** Package name of the test app for
+     * KeyValueBackupRestoreHostSideTest#testSharedPreferencesRestore() */
+    private static final String SHARED_PREFERENCES_RESTORE_PACKAGE_NAME =
+            "android.cts.backup.sharedprefrestoreapp";
+
+    /** Test activity for KeyValueBackupRestoreHostSideTest#testSharedPreferencesRestore() */
+    private static final String SHARED_PREFERENCES_RESTORE_ACTIVITY_NAME =
+            SHARED_PREFERENCES_RESTORE_PACKAGE_NAME + ".SharedPrefsRestoreTestActivity";
+
+    // Shared prefs test activity actions
+    private static final String INIT_ACTION = "android.backup.cts.backuprestore.INIT";
+    private static final String UPDATE_ACTION = "android.backup.cts.backuprestore.UPDATE";
+    private static final String TEST_ACTION = "android.backup.cts.backuprestore.TEST";
+
+    // Action for returning the result of shared preference activity's operations
+    private static final String RESULT_ACTION = "android.backup.cts.backuprestore.RESULT";
+    private static final String EXTRA_SUCCESS = "EXTRA_SUCCESS";
+
+    private static final int ACTIVITY_TEST_TIMEOUT_MS = 5000;
+
+    private Context mContext;
+
+    private int mIntValue;
+    private boolean mBoolValue;
+    private float mFloatValue;
+    private long mLongValue;
+    private String mStringValue;
+    private String mFileContent1;
+    private String mFileContent2;
+
+    private boolean mSharedPrefTestSuccess;
+    private CountDownLatch mLatch;
+    private Intent mSharedPrefActivityIntent;
+
+    private final BroadcastReceiver mSharedPrefencesReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i(TAG, "Received shared preference activity result broadcast");
+            mSharedPrefTestSuccess = intent.getBooleanExtra(EXTRA_SUCCESS, false);
+            mLatch.countDown();
+        }
+    };
+
+    @Before
+    public void setUp() {
+        Log.i(TAG, "set up");
+
+        mContext = getTargetContext();
+        mLatch = new CountDownLatch(1);
+        mSharedPrefTestSuccess = false;
+        mSharedPrefActivityIntent = new Intent()
+                .setClassName(SHARED_PREFERENCES_RESTORE_PACKAGE_NAME,
+                        SHARED_PREFERENCES_RESTORE_ACTIVITY_NAME);
+        mContext.registerReceiver(mSharedPrefencesReceiver, new IntentFilter(RESULT_ACTION));
+    }
+
+    @After
+    public void tearDown() {
+        mContext.unregisterReceiver(mSharedPrefencesReceiver);
+    }
+
+    @Test
+    public void saveSharedPreferencesAndNotifyBackupManager() throws Exception {
+        saveSharedPreferencesValues();
+
+        //Let BackupManager know that the data has changed
+        BackupManager backupManager = new BackupManager(mContext);
+        backupManager.dataChanged();
+    }
+
+    @Test
+    public void checkSharedPrefIsEmpty() throws Exception {
+        readSharedPrefValues();
+        assertEquals(DEFAULT_INT_VALUE, mIntValue);
+        assertEquals(DEFAULT_BOOL_VALUE, mBoolValue);
+        assertEquals(DEFAULT_FLOAT_VALUE, mFloatValue, 0.001f);
+        assertEquals(DEFAULT_LONG_VALUE, mLongValue);
+        assertEquals(DEFAULT_STRING_VALUE, mStringValue);
+        assertEquals(DEFAULT_FILE_CONTENT, mFileContent1);
+        assertEquals(DEFAULT_FILE_CONTENT, mFileContent2);
+    }
+
+    @Test
+    public void checkSharedPreferencesAreRestored() throws Exception {
+        readSharedPrefValues();
+        assertEquals(INT_PREF_VALUE, mIntValue);
+        assertEquals(BOOL_PREF_VALUE, mBoolValue);
+        assertEquals(FLOAT_PREF_VALUE, mFloatValue, 0.001f);
+        assertEquals(LONG_PREF_VALUE, mLongValue);
+        assertEquals(STRING_PREF_VALUE, mStringValue);
+        assertEquals(TEST_FILE_1_DATA, mFileContent1);
+        assertEquals(TEST_FILE_2_DATA, mFileContent2);
+    }
+
+    @Test
+    public void launchSharedPrefActivity() throws Exception {
+        mContext.startActivity(mSharedPrefActivityIntent.setAction(INIT_ACTION));
+
+        assertTrue("Activity init timed out",
+                mLatch.await(ACTIVITY_TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertTrue("Saving shared preferences didn't succeed", mSharedPrefTestSuccess);
+    }
+
+    @Test
+    public void updateSharedPrefActivity() throws Exception {
+        mContext.startActivity(mSharedPrefActivityIntent.setAction(UPDATE_ACTION));
+
+        assertTrue("Activity launch timed out",
+                mLatch.await(ACTIVITY_TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertTrue("Saving shared preferences didn't succeed", mSharedPrefTestSuccess);
+    }
+
+    @Test
+    public void checkSharedPrefActivity() throws Exception {
+        mContext.startActivity(mSharedPrefActivityIntent.setAction(TEST_ACTION));
+
+        assertTrue("Activity test timed out",
+                mLatch.await(ACTIVITY_TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertTrue("Shared preference value wasn't updated from the restore set",
+                mSharedPrefTestSuccess);
+    }
+
+    /** Saves predefined constant values to shared preferences and files. */
+    private void saveSharedPreferencesValues() throws FileNotFoundException {
+        SharedPreferences prefs = mContext.getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putInt(INT_PREF, INT_PREF_VALUE);
+        editor.putBoolean(BOOL_PREF, BOOL_PREF_VALUE);
+        assertTrue("Error committing shared preference 1 value", editor.commit());
+
+        prefs = mContext.getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
+        editor = prefs.edit();
+        editor.putFloat(FLOAT_PREF, FLOAT_PREF_VALUE);
+        editor.putLong(LONG_PREF, LONG_PREF_VALUE);
+        editor.putString(STRING_PREF, STRING_PREF_VALUE);
+        assertTrue("Error committing shared preference 2 value", editor.commit());
+
+        File file = new File(mContext.getFilesDir(), TEST_FILE_1);
+        PrintWriter writer = new PrintWriter(file);
+        writer.write(TEST_FILE_1_DATA);
+        writer.close();
+        assertTrue("Error writing file 1 data", file.exists());
+
+        file = new File(mContext.getFilesDir(), TEST_FILE_2);
+        writer = new PrintWriter(file);
+        writer.write(TEST_FILE_2_DATA);
+        writer.close();
+        assertTrue("Error writing file 2 data", file.exists());
+    }
+
+    private void readSharedPrefValues() throws InterruptedException {
+        SharedPreferences prefs = mContext.getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
+        mIntValue = prefs.getInt(INT_PREF, DEFAULT_INT_VALUE);
+        mBoolValue = prefs.getBoolean(BOOL_PREF, DEFAULT_BOOL_VALUE);
+
+        prefs = mContext.getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
+        mFloatValue = prefs.getFloat(FLOAT_PREF, DEFAULT_FLOAT_VALUE);
+        mLongValue = prefs.getLong(LONG_PREF, DEFAULT_LONG_VALUE);
+        mStringValue = prefs.getString(STRING_PREF, DEFAULT_STRING_VALUE);
+
+        mFileContent1 = readFileContent(TEST_FILE_1);
+        mFileContent2 = readFileContent(TEST_FILE_2);
+
+        Log.i(TAG, INT_PREF + ":" + mIntValue + "\n"
+                + BOOL_PREF + ":" + mBoolValue + "\n"
+                + FLOAT_PREF + ":" + mFloatValue + "\n"
+                + LONG_PREF + ":" + mLongValue + "\n"
+                + STRING_PREF + ":" + mStringValue + "\n"
+                + TEST_FILE_1 + ":" + mFileContent1 + "\n"
+                + TEST_FILE_2 + ":" + mFileContent2 + "\n");
+    }
+
+    private String readFileContent(String fileName) {
+        StringBuilder contents = new StringBuilder();
+        Scanner scanner = null;
+        try {
+            scanner = new Scanner(new File(mContext.getFilesDir(), fileName));
+            while (scanner.hasNext()) {
+                contents.append(scanner.nextLine());
+            }
+            scanner.close();
+        } catch (FileNotFoundException e) {
+            Log.i(TAG, "Couldn't find test file but this may be fine...");
+        } finally {
+            if (scanner != null) {
+                scanner.close();
+            }
+        }
+        return contents.toString();
+    }
+
+    public static SharedPreferencesBackupHelper getSharedPreferencesBackupHelper(Context context) {
+        return new SharedPreferencesBackupHelper(context, TEST_PREFS_1, TEST_PREFS_2);
+    }
+
+    public static FileBackupHelper getFileBackupHelper(Context context) {
+        return new FileBackupHelper(context, TEST_FILE_1, TEST_FILE_2);
+    }
+}
diff --git a/hostsidetests/backup/app/Android.mk b/hostsidetests/backup/SharedPreferencesRestoreApp/Android.mk
similarity index 95%
copy from hostsidetests/backup/app/Android.mk
copy to hostsidetests/backup/SharedPreferencesRestoreApp/Android.mk
index 248207c..bd6929a 100644
--- a/hostsidetests/backup/app/Android.mk
+++ b/hostsidetests/backup/SharedPreferencesRestoreApp/Android.mk
@@ -32,7 +32,7 @@
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts
 
-LOCAL_PACKAGE_NAME := CtsBackupRestoreDeviceApp
+LOCAL_PACKAGE_NAME := CtsSharedPreferencesRestoreApp
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/backup/app/AndroidManifest.xml b/hostsidetests/backup/SharedPreferencesRestoreApp/AndroidManifest.xml
similarity index 72%
rename from hostsidetests/backup/app/AndroidManifest.xml
rename to hostsidetests/backup/SharedPreferencesRestoreApp/AndroidManifest.xml
index 8730385..1cf6da5 100644
--- a/hostsidetests/backup/app/AndroidManifest.xml
+++ b/hostsidetests/backup/SharedPreferencesRestoreApp/AndroidManifest.xml
@@ -16,23 +16,12 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.backup.cts.backuprestoreapp">
+    package="android.cts.backup.sharedprefrestoreapp">
 
     <application
-        android:backupAgent="CtsBackupRestoreBackupAgent"
+        android:backupAgent=".SharedPreferencesBackupAgent"
         android:killAfterRestore="false" >
 
-        <uses-library android:name="android.test.runner" />
-
-        <activity
-            android:name=".KeyValueBackupRandomDataActivity"
-            android:launchMode="singleInstance">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
         <activity android:name=".SharedPrefsRestoreTestActivity"
             android:launchMode="singleInstance">
             <intent-filter>
@@ -42,10 +31,11 @@
             </intent-filter>
         </activity>
 
+        <uses-library android:name="android.test.runner" />
     </application>
 
     <instrumentation
         android:name="android.support.test.runner.AndroidJUnitRunner"
-        android:targetPackage="android.backup.cts.backuprestoreapp" />
+        android:targetPackage="android.cts.backup.sharedprefrestoreapp" />
 
 </manifest>
diff --git a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java b/hostsidetests/backup/SharedPreferencesRestoreApp/src/android/cts/backup/sharedprefrestoreapp/SharedPreferencesBackupAgent.java
similarity index 67%
copy from hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java
copy to hostsidetests/backup/SharedPreferencesRestoreApp/src/android/cts/backup/sharedprefrestoreapp/SharedPreferencesBackupAgent.java
index ebf9c24..e5a3948 100644
--- a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/CtsBackupRestoreBackupAgent.java
+++ b/hostsidetests/backup/SharedPreferencesRestoreApp/src/android/cts/backup/sharedprefrestoreapp/SharedPreferencesBackupAgent.java
@@ -14,21 +14,20 @@
  * limitations under the License
  */
 
-package android.backup.cts.backuprestoreapp;
+package android.cts.backup.sharedprefrestoreapp;
 
 import android.app.backup.BackupAgentHelper;
+import android.app.backup.SharedPreferencesBackupHelper;
 
-public class CtsBackupRestoreBackupAgent extends BackupAgentHelper {
+public class SharedPreferencesBackupAgent extends BackupAgentHelper {
 
     private static final String KEY_BACKUP_TEST_PREFS_PREFIX = "backup-test-prefs";
-    private static final String KEY_BACKUP_TEST_FILES_PREFIX = "backup-test-files";
+    private static final String TEST_PREFS_1 = "test-prefs-1";
 
     @Override
     public void onCreate() {
         super.onCreate();
         addHelper(KEY_BACKUP_TEST_PREFS_PREFIX,
-                KeyValueBackupRandomDataActivity.getSharedPreferencesBackupHelper(this));
-        addHelper(KEY_BACKUP_TEST_FILES_PREFIX,
-                KeyValueBackupRandomDataActivity.getFileBackupHelper(this));
+                new SharedPreferencesBackupHelper(this, TEST_PREFS_1));
     }
 }
diff --git a/hostsidetests/backup/SharedPreferencesRestoreApp/src/android/cts/backup/sharedprefrestoreapp/SharedPrefsRestoreTestActivity.java b/hostsidetests/backup/SharedPreferencesRestoreApp/src/android/cts/backup/sharedprefrestoreapp/SharedPrefsRestoreTestActivity.java
new file mode 100644
index 0000000..2f61c65
--- /dev/null
+++ b/hostsidetests/backup/SharedPreferencesRestoreApp/src/android/cts/backup/sharedprefrestoreapp/SharedPrefsRestoreTestActivity.java
@@ -0,0 +1,103 @@
+package android.cts.backup.sharedprefrestoreapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Test activity that verifies SharedPreference restore behavior.
+ * The activity lifecycle is driven by KeyValueBackupRestoreTest and is roughly the following:
+ *
+ * 1. This activity is launched; it creates a new SharedPreferences instance and writes
+ *       a known value to the INT_PREF element's via that instance.  The instance is
+ *       kept live.
+ *   2. The app is backed up, storing this known value in the backup dataset.
+ *   3. Next, the activity is instructed to write a different value to the INT_PREF
+ *       shared preferences element.  At this point, the app's current on-disk state
+ *       and the live shared preferences instance are in agreement, holding a value
+ *       different from that in the backup.
+ *   4. The runner triggers a restore for this app.  This will rewrite the shared prefs
+ *       file itself with the backed-up content (i.e. different from what was just
+ *       committed from this activity).
+ *   5. Finally, the runner instructs the activity to compare the value of its existing
+ *       shared prefs instance's INT_PREF element with what was previously written.
+ *       The test passes if these differ, i.e. if the live shared prefs instance picked
+ *       up the newly-restored data.
+ *
+ */
+public class SharedPrefsRestoreTestActivity extends Activity {
+    static final String TAG = "SharedPrefsTest";
+
+    // Shared prefs test activity actions
+    static final String INIT_ACTION = "android.backup.cts.backuprestore.INIT";
+    static final String UPDATE_ACTION = "android.backup.cts.backuprestore.UPDATE";
+    static final String TEST_ACTION = "android.backup.cts.backuprestore.TEST";
+
+    static final String RESULT_ACTION = "android.backup.cts.backuprestore.RESULT";
+
+    private static final String TEST_PREFS_1 = "test-prefs-1";
+    private static final String INT_PREF = "int-pref";
+    private static final int INT_PREF_VALUE = 1;
+    private static final int INT_PREF_MODIFIED_VALUE = 99999;
+    private static final int INT_PREF_DEFAULT_VALUE = 0;
+    private static final String EXTRA_SUCCESS = "EXTRA_SUCCESS";
+
+    SharedPreferences mPrefs;
+    int mLastValue;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mPrefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
+        mLastValue = 0;
+
+        processLaunchCommand(getIntent());
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        processLaunchCommand(intent);
+    }
+
+    private void processLaunchCommand(Intent intent) {
+        final String action = intent.getAction();
+        Log.i(TAG, "processLaunchCommand: " + action);
+
+        boolean success = false;
+
+        if (INIT_ACTION.equals(action)) {
+            // We'll issue a backup after setting this value in shared prefs
+            success = setPrefValue(INT_PREF_VALUE);
+        } else if (UPDATE_ACTION.equals(action)) {
+            // We'll issue a *restore* after setting this value, which will send a broadcast
+            // to our receiver to read from the live instance and ensure that the value is
+            // different from what was just written.
+            success = setPrefValue(INT_PREF_MODIFIED_VALUE);
+        } else if (TEST_ACTION.equals(action)) {
+            final int currentValue = mPrefs.getInt(INT_PREF, INT_PREF_DEFAULT_VALUE);
+            Log.i(TAG, "current value: " + currentValue + " last value : " + mLastValue);
+
+            if (currentValue != mLastValue && currentValue != INT_PREF_DEFAULT_VALUE) {
+                success = true;
+            }
+        } else {
+            // Should never happen
+            Log.e(TAG, "Unexpected intent action");
+            return;
+        }
+
+        sendBroadcast(new Intent().setAction(RESULT_ACTION).putExtra(EXTRA_SUCCESS, success));
+    }
+
+    // Write a known value prior to backup
+    private boolean setPrefValue(int value) {
+        Log.i(TAG, "mLastValue = " + mLastValue + " setPrefValue: " + value );
+        mLastValue = value;
+        boolean success = mPrefs.edit().putInt(INT_PREF, value).commit();
+        Log.i(TAG, "setPrefValue success: " + success);
+        return success;
+    }
+}
diff --git a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/Constants.java b/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/Constants.java
deleted file mode 100644
index 08f5854..0000000
--- a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/Constants.java
+++ /dev/null
@@ -1,44 +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 android.backup.cts.backuprestoreapp;
-
-final class Constants {
-    // Names of raw data files used by the tests
-    static final String TEST_FILE_1 = "test-file-1";
-    static final String TEST_FILE_2 = "test-file-2";
-
-    static final String TEST_PREFS_1 = "test-prefs-1";
-    static final String INT_PREF = "int-pref";
-    static final String BOOL_PREF = "bool-pref";
-
-    static final String TEST_PREFS_2 = "test-prefs-2";
-    static final String FLOAT_PREF = "float-pref";
-    static final String LONG_PREF = "long-pref";
-    static final String STRING_PREF = "string-pref";
-
-    static final int DEFAULT_INT_VALUE = 0;
-    static final boolean DEFAULT_BOOL_VALUE = false;
-
-    static final float DEFAULT_FLOAT_VALUE = 0.0f;
-    static final long DEFAULT_LONG_VALUE = 0L;
-    static final String DEFAULT_STRING_VALUE = null;
-
-    // Shared prefs test activity actions
-    static final String INIT_ACTION = "android.backup.cts.backuprestore.INIT";
-    static final String UPDATE_ACTION = "android.backup.cts.backuprestore.UPDATE";
-    static final String TEST_ACTION = "android.backup.cts.backuprestore.TEST";
-}
diff --git a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/KeyValueBackupRandomDataActivity.java b/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/KeyValueBackupRandomDataActivity.java
deleted file mode 100644
index 9a874db..0000000
--- a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/KeyValueBackupRandomDataActivity.java
+++ /dev/null
@@ -1,212 +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 android.backup.cts.backuprestoreapp;
-
-import android.app.Activity;
-import android.app.backup.BackupManager;
-import android.app.backup.FileBackupHelper;
-import android.app.backup.SharedPreferencesBackupHelper;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.PrintWriter;
-import java.util.Random;
-import java.util.Scanner;
-
-import static android.backup.cts.backuprestoreapp.Constants.*;
-
-/**
- * Test activity that reads/writes to shared preferences and files.
- *
- * It uses logcat messages to send the data to the host side of the test.
- * The format of logcat messages: "DATA_PREF: VALUE".
- * VALUES_LOADED_MESSAGE is logged after all the values.
- *
- * Workflow onCreate:
- * - Read shared preferences and files
- * - If the values are default ones:
- *      - Randomly generate new values
- *      - Save new values to shared preferences and files
- *      - Load the new values.
- *
- * Migrated from BackupTestActivity in former BackupTest CTS Verfifier test.
- */
-public class KeyValueBackupRandomDataActivity extends Activity {
-    static final String TAG = KeyValueBackupRandomDataActivity.class.getSimpleName();
-
-    private static final String VALUES_LOADED_MESSAGE = "ValuesLoaded";
-    private static final String EMPTY_STRING_LOG = "empty";
-
-    private boolean mDefaultValues = true;
-    private boolean mValuesWereGenerated;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        new LoadBackupItemsTask().execute();
-    }
-
-    public static SharedPreferencesBackupHelper getSharedPreferencesBackupHelper(Context context) {
-        return new SharedPreferencesBackupHelper(context, TEST_PREFS_1, TEST_PREFS_2);
-    }
-
-    public static FileBackupHelper getFileBackupHelper(Context context) {
-        return new FileBackupHelper(context, TEST_FILE_1, TEST_FILE_2);
-    }
-
-    class LoadBackupItemsTask extends AsyncTask<Void, Void, Void> {
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            loadPreferenceGroup1();
-            loadPreferenceGroup2();
-            loadFile(TEST_FILE_1);
-            loadFile(TEST_FILE_2);
-            return null;
-        }
-
-        private void loadPreferenceGroup1() {
-            SharedPreferences prefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
-
-            int intValue = prefs.getInt(INT_PREF, DEFAULT_INT_VALUE);
-            Log.i(TAG, INT_PREF + ":" + intValue);
-
-            boolean boolValue = prefs.getBoolean(BOOL_PREF, DEFAULT_BOOL_VALUE);
-            Log.i(TAG, BOOL_PREF + ":" + boolValue);
-
-            mDefaultValues = mDefaultValues
-                    && intValue == DEFAULT_INT_VALUE
-                    && boolValue == DEFAULT_BOOL_VALUE;
-        }
-
-        private void loadPreferenceGroup2() {
-            SharedPreferences prefs = getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
-
-            float floatValue = prefs.getFloat(FLOAT_PREF, DEFAULT_FLOAT_VALUE);
-            Log.i(TAG, FLOAT_PREF + ":" + floatValue);
-
-            long longValue = prefs.getLong(LONG_PREF, DEFAULT_LONG_VALUE);
-            Log.i(TAG, LONG_PREF + ":" + longValue);
-
-            String stringValue = prefs.getString(STRING_PREF, DEFAULT_STRING_VALUE);
-            Log.i(TAG, STRING_PREF + ":" + stringValue);
-
-            mDefaultValues = mDefaultValues
-                            && floatValue == DEFAULT_FLOAT_VALUE
-                            && longValue == DEFAULT_LONG_VALUE
-                            && stringValue == DEFAULT_STRING_VALUE;
-        }
-
-        private void loadFile(String fileName) {
-            StringBuilder contents = new StringBuilder();
-            Scanner scanner = null;
-            try {
-                scanner = new Scanner(new File(getFilesDir(), fileName));
-                while (scanner.hasNext()) {
-                    contents.append(scanner.nextLine());
-                }
-                scanner.close();
-            } catch (FileNotFoundException e) {
-                Log.i(TAG, "Couldn't find test file but this may be fine...");
-            } finally {
-                if (scanner != null) {
-                    scanner.close();
-                }
-            }
-            String logString = contents.toString();
-            logString = logString.isEmpty() ? EMPTY_STRING_LOG : logString;
-            Log.i(TAG, fileName + ":" + logString);
-
-            mDefaultValues = mDefaultValues && contents.toString().isEmpty();
-        }
-
-        @Override
-        protected void onPostExecute(Void param) {
-            super.onPostExecute(param);
-
-            if (mDefaultValues && !mValuesWereGenerated) {
-                new GenerateValuesTask().execute();
-            } else {
-                Log.i(TAG, VALUES_LOADED_MESSAGE);
-            }
-        }
-    }
-
-    class GenerateValuesTask extends AsyncTask<Void, Void, Exception> {
-
-        @Override
-        protected Exception doInBackground(Void... params) {
-            Random random = new Random();
-            generatePreferenceGroup1(random);
-            generatePreferenceGroup2(random);
-            try {
-                generateTestFile(TEST_FILE_1, random);
-                generateTestFile(TEST_FILE_2, random);
-            } catch (FileNotFoundException e) {
-                return e;
-            }
-            return null;
-        }
-
-        private void generatePreferenceGroup1(Random random) {
-            SharedPreferences prefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
-            SharedPreferences.Editor editor = prefs.edit();
-            editor.putInt(INT_PREF, (random.nextInt(100) + 1));
-            editor.putBoolean(BOOL_PREF, random.nextBoolean());
-            editor.commit();
-        }
-
-        private void generatePreferenceGroup2(Random random) {
-            SharedPreferences prefs = getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
-            SharedPreferences.Editor editor = prefs.edit();
-            editor.putFloat(FLOAT_PREF, random.nextFloat());
-            editor.putLong(LONG_PREF, random.nextLong());
-            editor.putString(STRING_PREF, "Random number " + (random.nextInt(100) + 1));
-            editor.commit();
-        }
-
-        private void generateTestFile(String fileName, Random random)
-                throws FileNotFoundException {
-            File file = new File(getFilesDir(), fileName);
-            PrintWriter writer = new PrintWriter(file);
-            writer.write("Random number " + (random.nextInt(100) + 1));
-            writer.close();
-        }
-
-        @Override
-        protected void onPostExecute(Exception exception) {
-            super.onPostExecute(exception);
-            mValuesWereGenerated = true;
-
-            if (exception != null) {
-                Log.e(TAG, "Couldn't generate test data...", exception);
-            } else {
-                BackupManager backupManager = new BackupManager(
-                        KeyValueBackupRandomDataActivity.this);
-                backupManager.dataChanged();
-
-                new LoadBackupItemsTask().execute();
-            }
-        }
-    }
-}
diff --git a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/SharedPrefsRestoreTestActivity.java b/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/SharedPrefsRestoreTestActivity.java
deleted file mode 100644
index ed06f33..0000000
--- a/hostsidetests/backup/app/src/android/backup/cts/backuprestoreapp/SharedPrefsRestoreTestActivity.java
+++ /dev/null
@@ -1,91 +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 android.backup.cts.backuprestoreapp;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.util.Log;
-
-import static android.backup.cts.backuprestoreapp.Constants.*;
-
-/**
- * Test activity that verifies SharedPreference restore behavior.
- *
- * Test logic:
- *   1. This activity is launched; it creates a new SharedPreferences instance and writes
- *       a known value to the INT_PREF element's via that instance.  The instance is
- *       kept live.
- *   2. The app is backed up, storing this known value in the backup dataset.
- *   3. Next, the activity is instructed to write a different value to the INT_PREF
- *       shared preferences element.  At this point, the app's current on-disk state
- *       and the live shared preferences instance are in agreement, holding a value
- *       different from that in the backup.
- *   4. The runner triggers a restore for this app.  This will rewrite the shared prefs
- *       file itself with the backed-up content (i.e. different from what was just
- *       committed from this activity).
- *   5. Finally, the runner instructs the activity to compare the value of its existing
- *       shared prefs instance's INT_PREF element with what was previously written.
- *       The test passes if these differ, i.e. if the live shared prefs instance picked
- *       up the newly-restored data.
- */
-public class SharedPrefsRestoreTestActivity extends Activity {
-    static final String TAG = "SharedPrefsTest";
-
-    SharedPreferences mPrefs;
-    int mLastValue;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mPrefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
-        mLastValue = 0;
-
-        processLaunchCommand(getIntent());
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        processLaunchCommand(intent);
-    }
-
-    private void processLaunchCommand(Intent intent) {
-        final String action = intent.getAction();
-        Log.i(TAG, "processLaunchCommand: " + action);
-        if (INIT_ACTION.equals(action)) {
-            // We'll issue a backup after setting this value in shared prefs
-            setPrefValue(55);
-        } else if (UPDATE_ACTION.equals(action)) {
-            // We'll issue a *restore* after setting this value, which will send a broadcast
-            // to our receiver to read from the live instance and ensure that the value is
-            // different from what was just written.
-            setPrefValue(999);
-        } else if (TEST_ACTION.equals(action)) {
-            final int currentValue = mPrefs.getInt(INT_PREF, mLastValue);
-            Log.i(TAG, "Shared prefs changed: " + (currentValue != mLastValue));
-        }
-    }
-
-    // Write a known value prior to backup
-    private void setPrefValue(int value) {
-        mLastValue = value;
-        mPrefs.edit().putInt(INT_PREF, value).commit();
-    }
-}
diff --git a/hostsidetests/backup/src/android/cts/backup/BackupRestoreHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BackupRestoreHostSideTest.java
deleted file mode 100644
index a2f6714..0000000
--- a/hostsidetests/backup/src/android/cts/backup/BackupRestoreHostSideTest.java
+++ /dev/null
@@ -1,332 +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 android.cts.backup;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test for checking that key/value backup and restore works correctly.
- * It interacts with the app that generates random values and saves them in different shared
- * preferences and files. The app uses BackupAgentHelper to do key/value backup of those values.
- * The tests verifies that the values are restored after the app is uninstalled and reinstalled.
- *
- * NB: The tests uses "bmgr backupnow" for backup, which works on N+ devices.
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class BackupRestoreHostSideTest extends BaseBackupHostSideTest {
-    /** The name of the APK of the app under test */
-    private static final String TEST_APP_APK = "CtsBackupRestoreDeviceApp.apk";
-
-    /** The package name of the APK */
-    private static final String PACKAGE_UNDER_TEST = "android.backup.cts.backuprestoreapp";
-
-    /** The class name of the main activity in the APK */
-    private static final String RANDOM_DATA_ACTIVITY = "KeyValueBackupRandomDataActivity";
-
-    /** Class name of the shared preferences test activity */
-    private static final String SHARED_PREFS_ACTIVITY = "SharedPrefsRestoreTestActivity";
-
-    /** Shell commands to launch the shared prefs restore test activity */
-    private static final String CMD_START_SHARED_PREFS_ACTIVITY = String.format(
-            "am start -W -n %s/%s.%s", PACKAGE_UNDER_TEST, PACKAGE_UNDER_TEST,
-            SHARED_PREFS_ACTIVITY);
-
-    /** Shared prefs restore test logging tag */
-    private static final String SHARED_PREFS_TAG = "SharedPrefsTest";
-
-    /** The command to clear the user data of the package */
-    private static final String CMD_CLEAR_DATA_IN_PACKAGE = String.format(
-            "pm clear %s", PACKAGE_UNDER_TEST);
-
-    /**
-     * Time we wait before reading the logcat again if the message we want is not logged by the
-     * app yet.
-     */
-    private static final int SMALL_LOGCAT_DELAY_MS = 1000;
-
-    /**
-     * Message logged by the app after all the values were loaded from SharedPreferences and files.
-     */
-    private static final String VALUES_LOADED_MESSAGE = "ValuesLoaded";
-
-    /**
-     * Keys for various shared preferences and files saved/read by the app.
-     */
-    private static final String INT_PREF = "int-pref";
-    private static final String BOOL_PREF = "bool-pref";
-    private static final String FLOAT_PREF = "float-pref";
-    private static final String LONG_PREF = "long-pref";
-    private static final String STRING_PREF = "string-pref";
-    private static final String TEST_FILE_1 = "test-file-1";
-    private static final String TEST_FILE_2 = "test-file-2";
-
-    /** Number of the values saved/restored by the app (keys listed above) */
-    private static final int NUMBER_OF_VALUES = 7;
-
-    /**
-     * String equivalents of the default values of the shared preferences logged by the app.
-     * These values are logged by the app by default if it fails to generate or restore values.
-     */
-    private static final String DEFAULT_INT_STRING = Integer.toString(0);
-    private static final String DEFAULT_BOOL_STRING = Boolean.toString(false);
-    private static final String DEFAULT_FLOAT_STRING = Float.toString(0.0f);
-    private static final String DEFAULT_LONG_STRING = Long.toString(0L);
-    private static final String DEFAULT_STRING_STRING = "null";
-    private static final String DEFAULT_FILE_STRING = "empty";
-
-    /*
-     *  Shared prefs test activity actions
-     */
-    static final String INIT_ACTION = "android.backup.cts.backuprestore.INIT";
-    static final String UPDATE_ACTION = "android.backup.cts.backuprestore.UPDATE";
-    static final String TEST_ACTION = "android.backup.cts.backuprestore.TEST";
-
-    private boolean mIsBackupSupported;
-    private boolean mWasBackupEnabled;
-    private String mOldTransport;
-
-    /**
-     * Map of the shared preferences/files values reported by the app.
-     * Format example: INT_PREF -> 17 (string, as found in the logcat).
-     */
-    private Map<String, String> mSavedValues;
-
-    @Test
-    public void testKeyValueBackupAndRestore() throws Exception {
-        // Clear app data if any
-        mDevice.executeShellCommand(CMD_CLEAR_DATA_IN_PACKAGE);
-
-        clearLogcat();
-
-        // Start the main activity of the app
-        startActivityInPackageAndWait(PACKAGE_UNDER_TEST, RANDOM_DATA_ACTIVITY);
-
-        // The app will generate some random values onCreate. Save them to mSavedValues
-        saveDataValuesReportedByApp();
-
-        // If all the values are default, there is something wrong with the app
-        assertNotAllValuesAreDefault();
-
-        // Run backup
-        // TODO: make this compatible with N-, potentially by replacing 'backupnow' with 'run'.
-        String backupnowOutput = backupNow(PACKAGE_UNDER_TEST);
-
-        assertBackupIsSuccessful(PACKAGE_UNDER_TEST, backupnowOutput);
-
-        assertNull(uninstallPackage(PACKAGE_UNDER_TEST));
-
-        installPackage(TEST_APP_APK);
-
-        clearLogcat();
-
-        // Start the reinstalled app
-        startActivityInPackageAndWait(PACKAGE_UNDER_TEST, RANDOM_DATA_ACTIVITY);
-
-        // If the app data was restored successfully, the app should not generate new values and
-        // the values reported by the app should match values saved in mSavedValues
-        assertValuesAreRestored();
-    }
-
-    /**
-     * Saves the data values reported by the app in {@code mSavedValues}.
-     */
-    private void saveDataValuesReportedByApp()
-            throws InterruptedException, DeviceNotAvailableException {
-        mSavedValues = readDataValuesFromLogcat();
-        assertEquals(NUMBER_OF_VALUES, mSavedValues.size());
-    }
-
-    /**
-     * Checks that at least some values in {@code mSavedValues} are different from corresponding
-     * default values.
-     */
-    private void assertNotAllValuesAreDefault() {
-        boolean allValuesAreDefault = mSavedValues.get(INT_PREF).equals(DEFAULT_INT_STRING)
-                && mSavedValues.get(BOOL_PREF).equals(DEFAULT_BOOL_STRING)
-                && mSavedValues.get(FLOAT_PREF).equals(DEFAULT_FLOAT_STRING)
-                && mSavedValues.get(LONG_PREF).equals(DEFAULT_LONG_STRING)
-                && mSavedValues.get(STRING_PREF).equals(DEFAULT_STRING_STRING)
-                && mSavedValues.get(TEST_FILE_1).equals(DEFAULT_FILE_STRING)
-                && mSavedValues.get(TEST_FILE_2).equals(DEFAULT_FILE_STRING);
-
-        assertFalse("The values were not changed from default.", allValuesAreDefault);
-    }
-
-    /**
-     * Reads the values logged by the app and verifies that they are the same as the ones we saved
-     * in {@code mSavedValues}.
-     */
-    private void assertValuesAreRestored()
-            throws InterruptedException, DeviceNotAvailableException {
-        Map<String, String> restoredValues = readDataValuesFromLogcat();
-
-        // Iterating through mSavedValues (vs. restoredValues) keyset to make sure all of the
-        // keys are reported in restored data
-        for (String dataType : mSavedValues.keySet()) {
-            assertEquals(mSavedValues.get(dataType), restoredValues.get(dataType));
-        }
-    }
-
-    /**
-     * Reads the values that app has reported via logcat and saves them in a map.
-     *
-     * The app logs the values once they are read from shared preferences or a file.
-     * If the values are default ones (i.e., it's the first run of the application), the app then
-     * generates random values and saves them in shared preferences or a file.
-     * Finally, the app reads the values from shared preferences or a file again and logs them.
-     * We are only interested in the final (generated or restored) values.
-     * The format of the log messages is "INT_PREF:17".
-     *
-     * @return Map of the values found in logcat.
-     */
-    private Map<String, String> readDataValuesFromLogcat()
-            throws InterruptedException, DeviceNotAvailableException {
-        Map<String, String> result = new HashMap<>();
-
-        long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30);
-
-        // The app generates reads, generates and reads values in async tasks fired onCreate.
-        // It may take some time for all tasks to finish and for logs to appear, so we check logcat
-        // repeatedly until we read VALUES_LOADED_MESSAGE, which is the last message the app logs.
-        search:
-        while (timeout >= System.currentTimeMillis()) {
-            String logs = getLogcatForClass(RANDOM_DATA_ACTIVITY);
-
-            Scanner in = new Scanner(logs);
-            while (in.hasNextLine()) {
-                String line = in.nextLine();
-                // Filter by TAG.
-                if (line.startsWith("I/" + RANDOM_DATA_ACTIVITY)) {
-                    // Get rid of the TAG.
-                    String message = line.split(":", 2)[1].trim();
-
-                    // VALUES_LOADED_MESSAGE is logged by the app when all the values are loaded and
-                    // logged so we can stop expecting more lines at this point.
-                    if (message.equals(VALUES_LOADED_MESSAGE)) {
-                        break search;
-                    }
-
-                    // Values are logged by the app in the format "INT_PREF:17".
-                    String[] values = message.split(":");
-                    if (values.length == 2) {
-                        result.put(values[0], values[1]);
-                    }
-                }
-            }
-            in.close();
-
-            // In case the key has not been found, wait for the log to update before
-            // performing the next search.
-            Thread.sleep(SMALL_LOGCAT_DELAY_MS);
-        }
-        assertTrue("Timeout while reading the app values", timeout > System.currentTimeMillis());
-        return result;
-    }
-
-    @Test
-    public void testSharedPreferencesRestore() throws Exception {
-        // Clear app data if any
-        mDevice.executeShellCommand(CMD_CLEAR_DATA_IN_PACKAGE);
-        // Clear logcat
-        mDevice.executeAdbCommand("logcat", "-c");
-
-        // Start the main test activity and generate some data in shared prefs.
-        mDevice.executeShellCommand(
-                CMD_START_SHARED_PREFS_ACTIVITY + " -a " + INIT_ACTION);
-        waitForLogcat(SHARED_PREFS_TAG, "processLaunchCommand: " + INIT_ACTION);
-
-        // Back up that shared prefs state
-        backupNow(PACKAGE_UNDER_TEST);
-
-        // Update the shared-prefs contents via the activity, post-backup
-        mDevice.executeAdbCommand("logcat", "-c");
-        mDevice.executeShellCommand(
-                CMD_START_SHARED_PREFS_ACTIVITY + " -a " + UPDATE_ACTION);
-        waitForLogcat(SHARED_PREFS_TAG, "processLaunchCommand: " + UPDATE_ACTION);
-
-        // Issue a restore operation for the package, which will rewrite shared prefs
-        // out from under the activity's live SharedPreferences instance
-        restore(PACKAGE_UNDER_TEST);
-
-        // Tell the activity to report its shared prefs state, and evaluate.
-        mDevice.executeAdbCommand("logcat", "-c");
-        mDevice.executeShellCommand(
-                CMD_START_SHARED_PREFS_ACTIVITY + " -a " + TEST_ACTION);
-        final String result = waitForLogcat(SHARED_PREFS_TAG, "Shared prefs changed:");
-        assertTrue("Shared prefs instance not reinitialized from disk", result.contains("true"));
-    }
-
-    /**
-     * Watch logcat until we see a string from Log.i() under the given tag that contains
-     * the stated string, and return the line.
-     */
-    private String waitForLogcat(String tag, String contents)
-            throws InterruptedException, DeviceNotAvailableException {
-        final long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30);
-
-        // Read the shared prefs restore result from the activity
-        while (timeout >= System.currentTimeMillis()) {
-            String logs = getLogcatForClass(tag);
-
-            Scanner in = new Scanner(logs);
-            try {
-                while (in.hasNextLine()) {
-                    String line = in.nextLine();
-                    // Filter by TAG.
-                    if (line.startsWith("I/" + tag)) {
-                        // Get rid of the TAG.
-                        line = line.split(":", 2)[1].trim();
-                        if (line.contains(contents)) {
-                            return line;
-                        }
-                    }
-                }
-            } finally {
-                in.close();
-            }
-
-            // In case the key has not been found, wait for the log to update before
-            // performing the next search.
-            Thread.sleep(SMALL_LOGCAT_DELAY_MS);
-        }
-        assertTrue("Timeout while waiting for logged string: I/" + tag + " : " + contents,
-                timeout > System.currentTimeMillis());
-        return null;
-    }
-
-    /**
-     * Returns the logcat string with the tag {@param className} and clears everything else.
-     */
-    private String getLogcatForClass(String className) throws DeviceNotAvailableException {
-        return mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", className + ":I", "*:S");
-    }
-}
diff --git a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
index 8ed6059..44d1aa6 100644
--- a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
@@ -72,6 +72,14 @@
     }
 
     /**
+     * Execute shell command "bmgr restore <packageName>" and assert success.
+     */
+    protected void restoreAndAssertSuccess(String packageName) throws DeviceNotAvailableException {
+        String restoreOutput = restore(packageName);
+        assertRestoreIsSuccessful(restoreOutput);
+    }
+
+    /**
      * Execute shell command "bmgr restore <packageName>" and return output from this command.
      */
     protected String restore(String packageName) throws DeviceNotAvailableException {
diff --git a/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
new file mode 100644
index 0000000..cbccd7a
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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 static junit.framework.Assert.assertNull;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileNotFoundException;
+
+/**
+ * Test for checking that key/value backup and restore works correctly.
+ * It interacts with the app that saves values in different shared preferences and files.
+ * The app uses BackupAgentHelper to do key/value backup of those values.
+ *
+ * NB: The tests use "bmgr backupnow" for backup, which works on N+ devices.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class KeyValueBackupRestoreHostSideTest extends BaseBackupHostSideTest {
+
+    /** The name of the package of the app under test */
+    private static final String KEY_VALUE_RESTORE_APP_PACKAGE =
+            "android.cts.backup.keyvaluerestoreapp";
+
+    /** The name of the package with the activity testing shared preference restore. */
+    private static final String SHARED_PREFERENCES_RESTORE_APP_PACKAGE =
+            "android.cts.backup.sharedprefrestoreapp";
+
+    /** The name of the device side test class */
+    private static final String KEY_VALUE_RESTORE_DEVICE_TEST_NAME =
+            KEY_VALUE_RESTORE_APP_PACKAGE + ".KeyValueBackupRestoreTest";
+
+    /** The name of the apk of the app under test */
+    private static final String KEY_VALUE_RESTORE_APP_APK = "CtsKeyValueBackupRestoreApp.apk";
+
+    /** The name of the apk with the activity testing shared preference restore. */
+    private static final String SHARED_PREFERENCES_RESTORE_APP_APK =
+            "CtsSharedPreferencesRestoreApp.apk";
+
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        installPackage(KEY_VALUE_RESTORE_APP_APK);
+        clearPackageData(KEY_VALUE_RESTORE_APP_PACKAGE);
+
+        installPackage(SHARED_PREFERENCES_RESTORE_APP_APK);
+        clearPackageData(SHARED_PREFERENCES_RESTORE_APP_APK);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        // Clear backup data and uninstall the package (in that order!)
+        clearBackupDataInLocalTransport(KEY_VALUE_RESTORE_APP_PACKAGE);
+        assertNull(uninstallPackage(KEY_VALUE_RESTORE_APP_PACKAGE));
+
+        clearBackupDataInLocalTransport(SHARED_PREFERENCES_RESTORE_APP_PACKAGE);
+        assertNull(uninstallPackage(SHARED_PREFERENCES_RESTORE_APP_PACKAGE));
+    }
+
+    /**
+     * Test that verifies key/value backup and restore.
+     *
+     * The flow of the test:
+     * 1. Check that app has no saved data
+     * 2. App saves the predefined values to shared preferences and files.
+     * 3. Backup the app's data
+     * 4. Uninstall the app
+     * 5. Install the app back
+     * 6. Check that all the shared preferences and files were restored.
+     */
+    @Test
+    public void testKeyValueBackupAndRestore() throws Exception {
+        checkDeviceTest("checkSharedPrefIsEmpty");
+
+        checkDeviceTest("saveSharedPreferencesAndNotifyBackupManager");
+
+        backupNowAndAssertSuccess(KEY_VALUE_RESTORE_APP_PACKAGE);
+
+        assertNull(uninstallPackage(KEY_VALUE_RESTORE_APP_PACKAGE));
+
+        installPackage(KEY_VALUE_RESTORE_APP_APK);
+
+        // Shared preference should be restored
+        checkDeviceTest("checkSharedPreferencesAreRestored");
+    }
+
+    /**
+     * Test that verifies SharedPreference restore behavior.
+     *
+     * The tests uses device-side test routines and a test activity in *another* package, since
+     * the app containing the instrumented tests is killed after each test.
+     *
+     * Test logic:
+     *   1. The activity is launched; it creates a new SharedPreferences instance and writes
+     *       a known value to the INT_PREF element's via that instance.  The instance is
+     *       kept live.
+     *   2. The app is backed up, storing this known value in the backup dataset.
+     *   3. Next, the activity is instructed to write a different value to the INT_PREF
+     *       shared preferences element.  At this point, the app's current on-disk state
+     *       and the live shared preferences instance are in agreement, holding a value
+     *       different from that in the backup.
+     *   4. The runner triggers a restore for this app.  This will rewrite the shared prefs
+     *       file itself with the backed-up content (i.e. different from what was just
+     *       committed from this activity).
+     *   5. Finally, the runner instructs the activity to compare the value of its existing
+     *       shared prefs instance's INT_PREF element with what was previously written.
+     *       The test passes if these differ, i.e. if the live shared prefs instance picked
+     *       up the newly-restored data.
+     */
+    @Test
+    public void testSharedPreferencesRestore() throws Exception {
+        checkDeviceTest("launchSharedPrefActivity");
+
+        backupNowAndAssertSuccess(SHARED_PREFERENCES_RESTORE_APP_PACKAGE);
+
+        checkDeviceTest("updateSharedPrefActivity");
+
+        restoreAndAssertSuccess(SHARED_PREFERENCES_RESTORE_APP_PACKAGE);
+
+        checkDeviceTest("checkSharedPrefActivity");
+    }
+
+    private void checkDeviceTest(String methodName)
+            throws DeviceNotAvailableException {
+        super.checkDeviceTest(KEY_VALUE_RESTORE_APP_PACKAGE, KEY_VALUE_RESTORE_DEVICE_TEST_NAME,
+                methodName);
+    }
+}