Merge "Reboot less in FuseDaemonHostTest"
diff --git a/tests/jni/FuseDaemonTest/FuseDaemonHostTest.xml b/tests/jni/FuseDaemonTest/FuseDaemonHostTest.xml
index 12f464f..2974c31 100644
--- a/tests/jni/FuseDaemonTest/FuseDaemonHostTest.xml
+++ b/tests/jni/FuseDaemonTest/FuseDaemonHostTest.xml
@@ -20,6 +20,7 @@
         <option name="test-file-name" value="FuseDaemonTest.apk" />
         <option name="test-file-name" value="FuseDaemonLegacyTest.apk" />
     </target_preparer>
+    <target_preparer class="com.android.tests.fused.host.FuseTargetPreparer"/>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.tests.fused.host.LegacyAccessHostTest" />
         <option name="class" value="com.android.tests.fused.host.FuseDaemonHostTest" />
diff --git a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonBaseHostTest.java b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonBaseHostTest.java
deleted file mode 100644
index 282abd6..0000000
--- a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonBaseHostTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2019 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.tests.fused.host;
-
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Base class for host tests that run FuseDaemon tests.
- * The main utility of this class is that it toggles FUSE and restarts the device if it's not
- * already on, then restores the original settings after the test has finished.
- *
- * @see {@link #enableFuseIfNecessary()}
- * @see {@link #restoreFuseSettings()}
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-abstract public class FuseDaemonBaseHostTest extends BaseHostJUnit4Test {
-    private static final String PROP_FUSE = "persist.sys.fuse";
-    private static final String PROP_SETTINGS_FUSE = "persist.sys.fflag.override.settings_fuse";
-    private static ITestDevice sDevice = null;
-    // Flag to determine whether FUSE was initially enabled, so we know what state to restore the
-    // device to when we're done. 0 means we haven't checked yet, 1 is enabled and -1 is disabled.
-    private static int sFuseInitialState = 0;
-
-    private boolean isFuseEnabled() throws Exception {
-        String enabled = getDevice().getProperty(PROP_FUSE);
-        if (enabled == null) {
-            return false; // current default value of PROP_FUSE
-        }
-        return enabled.contains("true") || enabled.contains("1");
-    }
-
-    /* {@link #sDevice} must be initialized before calling this method */
-    private static void toggleFuse(boolean enable) throws Exception {
-        final String strEnable = enable ? "true" : "false";
-        if (strEnable.equals(sDevice.getProperty(PROP_SETTINGS_FUSE))) {
-            return;
-        }
-        // root settings will be reset once device has rebooted
-        sDevice.enableAdbRoot();
-        sDevice.setProperty(PROP_SETTINGS_FUSE, (enable ? "true" : "false"));
-        Thread.sleep(TimeUnit.SECONDS.toMillis(1));
-        sDevice.reboot();
-    }
-
-    /**
-     * Snapshots {@link #sFuseInitialState} only once - if called while {@link #sFuseInitialState}
-     * is not {@code 0}, it will be a no-op.
-     */
-    private static void snapshotFuseInitialState(boolean enabled) {
-        if (sFuseInitialState != 0) {
-            return;
-        }
-        sFuseInitialState = enabled ? 1 : -1;
-    }
-
-    private static boolean getFuseInitialState() {
-        return sFuseInitialState == 1;
-    }
-
-    private void enableFuseAndRebootDeviceIfNecessary() throws Exception {
-        if (isFuseEnabled()) {
-            snapshotFuseInitialState(true);
-            return;
-        }
-        snapshotFuseInitialState(false);
-        sDevice = getDevice();
-        toggleFuse(true);
-    }
-
-    @Before
-    public void enableFuseIfNecessary() throws Exception {
-        enableFuseAndRebootDeviceIfNecessary();
-    }
-
-    @AfterClass
-    public static void restoreFuseSettings() throws Exception {
-        if (sDevice != null) {
-            toggleFuse(getFuseInitialState());
-        }
-    }
-
-    protected String executeShellCommand(String cmd) throws Exception {
-        return getDevice().executeShellCommand(cmd);
-    }
-}
\ No newline at end of file
diff --git a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonHostTest.java b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonHostTest.java
index 6bedbce..e174655 100644
--- a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonHostTest.java
+++ b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseDaemonHostTest.java
@@ -20,10 +20,10 @@
 
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -31,7 +31,7 @@
  * Runs the FuseDaemon tests.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class FuseDaemonHostTest extends FuseDaemonBaseHostTest {
+public class FuseDaemonHostTest extends BaseHostJUnit4Test {
     /**
      * Runs the given phase of FilePathAccessTest by calling into the device.
      * Throws an exception if the test phase fails.
@@ -42,6 +42,10 @@
                 phase));
     }
 
+    private String executeShellCommand(String cmd) throws Exception {
+        return getDevice().executeShellCommand(cmd);
+    }
+
     @Before
     public void setup() throws Exception {
         executeShellCommand("mkdir /sdcard/Android/data/com.android.shell");
diff --git a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseTargetPreparer.java b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseTargetPreparer.java
new file mode 100644
index 0000000..35be0c2
--- /dev/null
+++ b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/FuseTargetPreparer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.tests.fused.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.targetprep.BaseTargetPreparer;
+
+public class FuseTargetPreparer extends BaseTargetPreparer {
+    private static final String PROP_FUSE = "persist.sys.fuse";
+    private static final String PROP_SETTINGS_FUSE = "persist.sys.fflag.override.settings_fuse";
+    private static boolean sFuseInitialState = false;
+
+    private static boolean isFuseEnabled(ITestDevice device) throws DeviceNotAvailableException {
+        String enabled = device.getProperty(PROP_FUSE);
+        if (enabled == null) {
+            return false; // current default value of PROP_FUSE
+        }
+        return enabled.contains("true") || enabled.contains("1");
+    }
+
+    private static void toggleFuse(boolean enable, ITestDevice device)
+            throws DeviceNotAvailableException {
+        final String strEnable = Boolean.toString(enable);
+        if (strEnable.equals(device.getProperty(PROP_SETTINGS_FUSE))) {
+            return;
+        }
+        // root settings will be reset once device has rebooted
+        device.enableAdbRoot();
+        device.setProperty(PROP_SETTINGS_FUSE, strEnable);
+        device.reboot();
+    }
+
+    private static void enableFuseAndRebootDeviceIfNecessary(ITestDevice device)
+            throws DeviceNotAvailableException {
+        sFuseInitialState = isFuseEnabled(device);
+        if (sFuseInitialState) {
+            // Nothing to do if it's enabled
+            return;
+        }
+        toggleFuse(true, device);
+    }
+
+    public static void restoreFuseSettings(ITestDevice device) throws DeviceNotAvailableException {
+        toggleFuse(sFuseInitialState, device);
+    }
+
+    @Override
+    public void setUp(TestInformation testInformation) throws DeviceNotAvailableException {
+        enableFuseAndRebootDeviceIfNecessary(testInformation.getDevice());
+    }
+
+    @Override
+    public void tearDown(TestInformation testInformation, Throwable e)
+            throws DeviceNotAvailableException {
+        restoreFuseSettings(testInformation.getDevice());
+    }
+}
diff --git a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/LegacyAccessHostTest.java b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/LegacyAccessHostTest.java
index cfc69d8..f88deb3 100644
--- a/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/LegacyAccessHostTest.java
+++ b/tests/jni/FuseDaemonTest/host/src/com/android/tests/fused/host/LegacyAccessHostTest.java
@@ -20,10 +20,10 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -31,10 +31,14 @@
  * Runs the legacy file path access tests.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class LegacyAccessHostTest extends FuseDaemonBaseHostTest {
+public class LegacyAccessHostTest extends BaseHostJUnit4Test {
 
     public static final String SHELL_FILE = "/sdcard/LegacyAccessHostTest_shell";
 
+    private String executeShellCommand(String cmd) throws Exception {
+        return getDevice().executeShellCommand(cmd);
+    }
+
     /**
      * Runs the given phase of LegacyFileAccessTest by calling into the device.
      * Throws an exception if the test phase fails.
@@ -84,17 +88,18 @@
     @Test
     public void testCreateFilesInRandomPlaces_hasW() throws Exception {
         revokePermissions("android.permission.READ_EXTERNAL_STORAGE");
+        executeShellCommand("mkdir -p /sdcard/Android/data/com.android.shell");
         runDeviceTest("testCreateFilesInRandomPlaces_hasW");
     }
 
     @Test
     public void testMkdirInRandomPlaces_hasW() throws Exception {
         revokePermissions("android.permission.READ_EXTERNAL_STORAGE");
+        executeShellCommand("mkdir -p /sdcard/Android/data/com.android.shell");
         runDeviceTest("testMkdirInRandomPlaces_hasW");
     }
 
     @Test
-    @Ignore("b/146189163")
     public void testReadOnlyExternalStorage_hasR() throws Exception {
         revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE");
         createFile(SHELL_FILE);
@@ -106,7 +111,6 @@
     }
 
     @Test
-    @Ignore("b/146189163")
     public void testCantAccessExternalStorage() throws Exception {
         revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE",
                 "android.permission.READ_EXTERNAL_STORAGE");
diff --git a/tests/jni/FuseDaemonTest/legacy/src/com/android/tests/fused/legacy/LegacyFileAccessTest.java b/tests/jni/FuseDaemonTest/legacy/src/com/android/tests/fused/legacy/LegacyFileAccessTest.java
index 500a7e9..39efda1 100644
--- a/tests/jni/FuseDaemonTest/legacy/src/com/android/tests/fused/legacy/LegacyFileAccessTest.java
+++ b/tests/jni/FuseDaemonTest/legacy/src/com/android/tests/fused/legacy/LegacyFileAccessTest.java
@@ -77,15 +77,16 @@
                 "LegacyFileAccessTest");
         assertCanCreateFile(file);
 
-        // Can even create another app's external storage dir
+        // However, even legacy apps can't create files under other app's directories
         final File otherAppDir = new File(Environment.getExternalStorageDirectory(),
                 "Android/data/com.android.shell");
         file = new File(otherAppDir, "LegacyFileAccessTest.txt");
+
+        // otherAppDir was already created by the host test
         try {
-            otherAppDir.mkdir();
-            assertCanCreateFile(file);
-        } finally {
-            otherAppDir.delete();
+            file.createNewFile();
+            fail("File creation expected to fail: " + file);
+        } catch (IOException expected) {
         }
     }
 
@@ -105,14 +106,10 @@
         final File otherAppDir = new File(Environment.getExternalStorageDirectory(),
                 "Android/data/com.android.shell");
 
-        // Can create a directory under another app's private directory
+        // However, even legacy apps can't create dirs under other app's directories
         final File subDir = new File(otherAppDir, "LegacyFileAccessTest");
-        try {
-            otherAppDir.mkdir();
-            assertCanCreateDir(subDir);
-        } finally {
-            otherAppDir.delete();
-        }
+        // otherAppDir was already created by the host test
+        assertThat(subDir.mkdir()).isFalse();
 
         // Try to list a directory and fail because it requires READ permission
         assertThat(new File(Environment.getExternalStorageDirectory(),