Rewrite app-with-activity to match guidelines

Bug: 441727732
Test: manually verified from command line and Android Studio
Change-Id: I1eeb4852da5d6f19b8e24c319bebd24392f79c51
diff --git a/templates/app-with-activity/submission/appTest/src/main/AndroidManifest.xml b/templates/app-with-activity/submission/appTest/src/main/AndroidManifest.xml
index 3f2ec78..bc21fae 100644
--- a/templates/app-with-activity/submission/appTest/src/main/AndroidManifest.xml
+++ b/templates/app-with-activity/submission/appTest/src/main/AndroidManifest.xml
@@ -25,11 +25,6 @@
         android:supportsRtl="true">
         <activity
             android:name=".PocActivity"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
+            android:exported="true" />
     </application>
 </manifest>
diff --git a/templates/app-with-activity/submission/appTest/src/main/java/com/android/security/DeviceTest.java b/templates/app-with-activity/submission/appTest/src/main/java/com/android/security/DeviceTest.java
index 059b0ea..37dd0be 100644
--- a/templates/app-with-activity/submission/appTest/src/main/java/com/android/security/DeviceTest.java
+++ b/templates/app-with-activity/submission/appTest/src/main/java/com/android/security/DeviceTest.java
@@ -17,11 +17,11 @@
 package com.android.security;
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import android.app.Instrumentation;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -29,7 +29,9 @@
 import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -38,63 +40,114 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
- * An example device test that starts an activity and uses broadcasts to wait for the artifact
- * proving vulnerability
+ * ## Test Architecture: Device-Side Logic
+ *
+ * This class runs as an instrumented test on the Android device. It contains a single `@Test`
+ * method that can be run in different "phases" based on arguments from the host.
  */
 @RunWith(AndroidJUnit4.class)
 public class DeviceTest {
     private static final String TAG = DeviceTest.class.getSimpleName();
-    Context mContext;
+    private static final int TIMEOUT_MS = 5000;
 
-    /** Test broadcast action */
-    public static final String ACTION_BROADCAST = "action_security_test_broadcast";
+    /** Test broadcast action sent from the PocActivity with the result. */
+    public static final String ACTION_BROADCAST = "com.android.security.action.RESULT_BROADCAST";
 
-    /** Broadcast intent extra name for artifacts */
+    /** Broadcast intent extra name for artifacts. */
     public static final String INTENT_ARTIFACT = "artifact";
 
-    /** Device test */
-    @Test
-    public void testDeviceSideMethod() throws Exception {
-        mContext = getApplicationContext();
+    private Context mContext;
+    private Instrumentation mInstrumentation;
 
-        AtomicReference<String> actual = new AtomicReference<>();
+    @Before
+    public void setUp() {
+        mContext = getApplicationContext();
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    /**
+     * This is the single entry point for the device-side test. It follows the "Prove the Boundary"
+     * pattern to create a reliable test.
+     */
+    @Test
+    public void testVulnerabilityFailsOnVulnerableDevice() throws Exception {
+        // STEP 1: Assert the Boundary is Intact
+        // Launch the PoC activity without the trigger file present.
+        // The sensitive action should be blocked by the security boundary.
+        Log.d(TAG, "STEP 1: Asserting that the security boundary is intact...");
+        String initialArtifact = launchActivityAndGetResult();
+        assertEquals(
+                "The security boundary should have blocked the action.",
+                "ACCESS_DENIED",
+                initialArtifact);
+        Log.d(TAG, "Success: The security boundary correctly blocked the action.");
+
+        // STEP 2: Trigger the Vulnerability
+        // Create the trigger file that the PoC activity will detect.
+        Log.d(TAG, "STEP 2: Triggering the vulnerability...");
+        mInstrumentation
+                .getUiAutomation()
+                .executeShellCommand("touch " + PocActivity.TRIGGER_FILE_PATH)
+                .close();
+        Log.d(TAG, "Success: Vulnerability trigger created at " + PocActivity.TRIGGER_FILE_PATH);
+
+        // STEP 3: Assert the Boundary is Breached
+        // Relaunch the PoC activity. This time, it should detect the trigger file and the
+        // sensitive action should succeed.
+        Log.d(TAG, "STEP 3: Asserting that the security boundary is breached...");
+        String finalArtifact = launchActivityAndGetResult();
+
+        // The final, critical assertion. If the artifact contains the secret data,
+        // we fail the test and report the artifact as proof. This test SHOULD fail on a
+        // vulnerable device.
+        if ("SECRET_DATA_ACCESSED".equals(finalArtifact)) {
+            fail("Vulnerability proven: Leaked sensitive data: '" + finalArtifact + "'");
+        }
+
+        // If the exploit failed, the artifact will be "ACCESS_DENIED". In this case, the
+        // test will pass, correctly indicating that the device is not vulnerable.
+        assertEquals(
+                "The exploit failed; the security boundary was not bypassed.",
+                "ACCESS_DENIED",
+                finalArtifact);
+        Log.d(
+                TAG,
+                "The device appears to be secure; the exploit did not bypass the security"
+                        + " boundary.");
+    }
+
+    /**
+     * Launches the PocActivity, waits for it to send a result broadcast, and returns the artifact.
+     */
+    private String launchActivityAndGetResult() throws InterruptedException {
+        final AtomicReference<String> artifactRef = new AtomicReference<>();
         final Semaphore broadcastReceived = new Semaphore(0);
+
         BroadcastReceiver broadcastReceiver =
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
-                        try {
-                            if (!intent.getAction().equals(ACTION_BROADCAST)) {
-                                Log.i(
-                                        TAG,
-                                        String.format(
-                                                "got a broadcast that we didn't expect: %s",
-                                                intent.getAction()));
-                            }
-                            actual.set(intent.getStringExtra(INTENT_ARTIFACT));
+                        if (ACTION_BROADCAST.equals(intent.getAction())) {
+                            artifactRef.set(intent.getStringExtra(INTENT_ARTIFACT));
                             broadcastReceived.release();
-                        } catch (Exception e) {
-                            Log.e(TAG, "got an exception when handling broadcast", e);
                         }
                     }
                 };
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ACTION_BROADCAST);
-        mContext.registerReceiver(broadcastReceiver, filter, Context.RECEIVER_EXPORTED);
+        mContext.registerReceiver(
+                broadcastReceiver, new IntentFilter(ACTION_BROADCAST), Context.RECEIVER_EXPORTED);
 
-        // start the target app
-        try {
-            Log.d(TAG, "starting local activity");
-            Intent newActivityIntent = new Intent(mContext, PocActivity.class);
-            newActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            // this could be startActivityForResult, but is generic for illustrative purposes
-            mContext.startActivity(newActivityIntent);
-        } finally {
-            getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+        // Start the target app.
+        Intent newActivityIntent = new Intent(mContext, PocActivity.class);
+        newActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(newActivityIntent);
+
+        // Wait for the result from the activity.
+        if (!broadcastReceived.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Timed out waiting for result from PocActivity.");
         }
-        assertTrue(
-                "Timed out when getting result from other activity",
-                broadcastReceived.tryAcquire(/* TIMEOUT_MS */ 5000, TimeUnit.MILLISECONDS));
-        assertEquals("The target artifact should have been 'secure'", "secure", actual.get());
+
+        mContext.unregisterReceiver(broadcastReceiver);
+
+        return artifactRef.get();
     }
 }
diff --git a/templates/app-with-activity/submission/appTest/src/main/java/com/android/security/PocActivity.java b/templates/app-with-activity/submission/appTest/src/main/java/com/android/security/PocActivity.java
index 113bd2b..e69a1c1 100644
--- a/templates/app-with-activity/submission/appTest/src/main/java/com/android/security/PocActivity.java
+++ b/templates/app-with-activity/submission/appTest/src/main/java/com/android/security/PocActivity.java
@@ -21,22 +21,65 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import java.io.File;
+
+/**
+ * ## Test Architecture: The Proof-of-Concept Application
+ *
+ * This Activity serves as the proof-of-concept (PoC) application. It is **not** the vulnerable
+ * component itself. Instead, it is a tool designed to trigger and demonstrate a vulnerability in the
+ * underlying Android platform or SoC components.
+ *
+ * ### The Mock Vulnerability Trigger
+ * The "vulnerability" this app triggers is simple: if a specific file exists at {@link
+ * #TRIGGER_FILE_PATH}, a sensitive action that should normally be blocked will succeed.
+ *
+ * This simulates a real-world scenario where an attacker-controlled app could perform an action
+ * (like creating a file or sending an intent) that puts a platform component into an insecure
+ * state, allowing the app to bypass a security boundary.
+ *
+ * This Activity is UI-less and designed for automated testing. It performs its check and reports its
+ * result via a broadcast as soon as it is launched, then finishes.
+ */
 public class PocActivity extends Activity {
     private static final String TAG = PocActivity.class.getSimpleName();
 
+    // A file path that the host can write to and the app can read.
+    static final String TRIGGER_FILE_PATH = "/data/local/tmp/exploit_trigger.txt";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-        Log.d(TAG, "poc activity started");
 
-        // Collect the artifact representing vulnerability here.
-        // Change this to whatever type best fits the vulnerable artifact; consider using a bundle
-        // if there are multiple artifacts necessary to prove the security vulnerability.
-        String artifact = "vulnerable";
+        boolean isExploitTriggered = new File(TRIGGER_FILE_PATH).exists();
+        Log.d(TAG, "PocActivity started. Exploit triggered: " + isExploitTriggered);
 
-        Intent vulnerabilityDescriptionIntent = new Intent(DeviceTest.ACTION_BROADCAST);
-        vulnerabilityDescriptionIntent.putExtra(DeviceTest.INTENT_ARTIFACT, artifact);
-        this.sendBroadcast(vulnerabilityDescriptionIntent);
+        // This is the sensitive action that should be protected by a security boundary.
+        // It is triggered automatically when the Activity is created.
+        attemptSensitiveAction(isExploitTriggered);
+    }
+
+    /**
+     * Attempts to perform the sensitive action. The outcome depends on whether the mock exploit has
+     * been triggered.
+     */
+    private void attemptSensitiveAction(boolean isExploitTriggered) {
+        String artifact;
+        if (isExploitTriggered) {
+            // STEP 3: The exploit has been triggered. The sensitive action now succeeds.
+            // We collect an artifact that proves the vulnerability.
+            artifact = "SECRET_DATA_ACCESSED";
+            Log.d(TAG, "Sensitive action succeeded. Captured artifact: " + artifact);
+        } else {
+            // STEP 2: The exploit has not been triggered. The sensitive action is blocked.
+            artifact = "ACCESS_DENIED";
+            Log.d(TAG, "Sensitive action blocked by security boundary.");
+        }
+
+        // Send the result back to the device-side test.
+        Intent resultIntent = new Intent(DeviceTest.ACTION_BROADCAST);
+        resultIntent.putExtra(DeviceTest.INTENT_ARTIFACT, artifact);
+        sendBroadcast(resultIntent);
+        finish(); // Close the activity after the action is performed.
     }
 }
diff --git a/templates/app-with-activity/submission/hostTest/src/main/java/com/android/security/autorepro_placeholder/HostsideTest.java b/templates/app-with-activity/submission/hostTest/src/main/java/com/android/security/autorepro_placeholder/HostsideTest.java
index 2cc1406..496ef32 100644
--- a/templates/app-with-activity/submission/hostTest/src/main/java/com/android/security/autorepro_placeholder/HostsideTest.java
+++ b/templates/app-with-activity/submission/hostTest/src/main/java/com/android/security/autorepro_placeholder/HostsideTest.java
@@ -21,10 +21,21 @@
 import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * ## Test Architecture: Host-Side Launcher
+ *
+ * This host-side test is minimal. Its only purpose is to:
+ * 1. Install the PoC app (`appTest`).
+ * 2. Run a single, designated test method in the device-side test class.
+ * 3. Clean up the app after the test is complete.
+ *
+ * All of the complex PoC logic is contained in the device-side test.
+ */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class HostsideTest extends NonRootSecurityTestCase {
 
@@ -34,15 +45,15 @@
     static final String TEST_PKG = "com.android.security.appTest_AutoReproPlaceholder";
     // The class name will be different from the application ID but will match the source code.
     static final String TEST_CLASS = "com.android.security.DeviceTest";
+    // The single device-side test method that contains the entire PoC.
+    static final String TEST_METHOD = "testVulnerabilityFailsOnVulnerableDevice";
 
-    /** An app test, which uses this host Java test to launch an Android instrumented test */
+    /**
+     * Installs the app and runs the single, self-contained device-side test.
+     */
     @Test
-    public void testWithApp() throws Exception {
-        ITestDevice device = getDevice();
-        assertTrue("could not disable root", device.disableAdbRoot());
-        uninstallPackage(device, TEST_PKG);
-
+    public void testPoc() throws Exception {
         installPackage(TEST_APP);
-        runDeviceTests(TEST_PKG, TEST_CLASS, "testDeviceSideMethod");
+        runDeviceTests(TEST_PKG, TEST_CLASS, TEST_METHOD);
     }
 }