In test helper apps, handle configuration changes

When a configuration change occurs, an activity may be recreated.
Essentially, it's destroyed and a new one is created in its place.
A problem with this is, some of our test helper apps perform non-
idempotent actions, or store state in local variables. Restarting
the activity can lead to hard-to-diagnose test failures.

Of all the test helper activities, there are essentially these categories:

1. The activity already accounts for configuration changes.
2. The activity already finishes itself within onCreate.
3. The activity already appears to be completely idempotent.
4. The activity does not gracefully handle configuration changes, but
   is trivial to fix.
5. The activity does not gracefully handle configuration changes, and
   fixing it is non-trivial.

This change fixes:

- Category #4 activities by detecting, logging, and gracefully handling
  configuration changes.
- Category #5 activities by disabling as many configuration changes as
  possible, then if a configuration change still occurs, detect, log and
  throw an exception. (This still won't reflect as a TestRunner
  exception, but it will show as an error in the logs.)

Fix: 300545016
Fix: 280542662
Test: cd packages/modules/Permission && atest :alltests
Change-Id: I60bb60a9a8fe93ef9d03d8f645c3d79ae787eefa
diff --git a/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt b/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt
index 39183a8..8a23929 100644
--- a/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt
+++ b/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt
@@ -16,10 +16,19 @@
 package android.permission.cts.appthatrequestpermission
 
 import android.app.Activity
+import android.os.Bundle
+import android.util.Log
 
 class RequestPermissions : Activity() {
-    override fun onStart() {
-        super.onStart()
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        if (savedInstanceState != null) {
+            Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+            return
+        }
+
         val permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS)!!
         requestPermissions(permissions, 0)
     }
@@ -35,5 +44,6 @@
     companion object {
         private const val EXTRA_PERMISSIONS =
                 "android.permission.cts.appthatrequestpermission.extra.PERMISSIONS"
+        private val TAG = RequestPermissions::class.simpleName
     }
 }
\ No newline at end of file
diff --git a/tests/cts/permission/AppThatRequestCustomCameraPermission/src/android/permission/cts/appthatrequestcustomcamerapermission/RequestCameraPermission.java b/tests/cts/permission/AppThatRequestCustomCameraPermission/src/android/permission/cts/appthatrequestcustomcamerapermission/RequestCameraPermission.java
index 4bbeb53..288e7e1 100644
--- a/tests/cts/permission/AppThatRequestCustomCameraPermission/src/android/permission/cts/appthatrequestcustomcamerapermission/RequestCameraPermission.java
+++ b/tests/cts/permission/AppThatRequestCustomCameraPermission/src/android/permission/cts/appthatrequestcustomcamerapermission/RequestCameraPermission.java
@@ -25,7 +25,6 @@
 import android.util.Log;
 
 public class RequestCameraPermission extends Activity {
-
     private static final String LOG_TAG = RequestCameraPermission.class.getSimpleName();
 
     public static final String CUSTOM_PERMISSION = "appthatrequestcustomcamerapermission.CUSTOM";
@@ -35,6 +34,11 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        if (savedInstanceState != null) {
+            Log.w(LOG_TAG, "Activity was recreated. (Perhaps due to a configuration change?)");
+            return;
+        }
+
         boolean cameraGranted =
                 checkSelfPermission(CAMERA) == PERMISSION_GRANTED;
         boolean customGranted =
diff --git a/tests/cts/permission/AppToTestRevokeSelfPermission/src/android/permission/cts/apptotestselfrevokepermission/RevokePermission.java b/tests/cts/permission/AppToTestRevokeSelfPermission/src/android/permission/cts/apptotestselfrevokepermission/RevokePermission.java
index a1971f7..b9a0ed7 100644
--- a/tests/cts/permission/AppToTestRevokeSelfPermission/src/android/permission/cts/apptotestselfrevokepermission/RevokePermission.java
+++ b/tests/cts/permission/AppToTestRevokeSelfPermission/src/android/permission/cts/apptotestselfrevokepermission/RevokePermission.java
@@ -19,13 +19,22 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 
 import java.util.Arrays;
 
 public class RevokePermission extends Activity {
+    private static final String TAG = "RevokePermission";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)");
+            return;
+        }
+
         Intent intent = getIntent();
         String[] permissions = intent.getStringArrayExtra("permissions");
         if (permissions == null) {
diff --git a/tests/cts/permissionui/AndroidManifest.xml b/tests/cts/permissionui/AndroidManifest.xml
index 5629366..3b80b8d 100644
--- a/tests/cts/permissionui/AndroidManifest.xml
+++ b/tests/cts/permissionui/AndroidManifest.xml
@@ -39,7 +39,9 @@
         </service>
 
         <activity android:name="com.android.compatibility.common.util.FutureResultActivity" />
-        <activity android:name=".StartForFutureActivity" />
+        <activity
+            android:name=".StartForFutureActivity"
+            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"/>
 
         <activity android:name=".TestInstallerActivity"
                   android:exported="true"
diff --git a/tests/cts/permissionui/AppThatAccessesCameraAndMic/AndroidManifest.xml b/tests/cts/permissionui/AppThatAccessesCameraAndMic/AndroidManifest.xml
index 52a8437..e1130fb 100644
--- a/tests/cts/permissionui/AppThatAccessesCameraAndMic/AndroidManifest.xml
+++ b/tests/cts/permissionui/AppThatAccessesCameraAndMic/AndroidManifest.xml
@@ -24,7 +24,8 @@
 
     <application android:label="CtsCameraMicAccess">
         <activity android:name=".AccessCameraOrMicActivity"
-        android:exported="true">
+                  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+                  android:exported="true">
             <intent-filter>
                 <action android:name="test.action.USE_CAMERA_OR_MIC" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/cts/permissionui/AppThatAccessesCameraAndMic/src/android/permissionui/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt b/tests/cts/permissionui/AppThatAccessesCameraAndMic/src/android/permissionui/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
index 1adc4f0..4dd668a 100644
--- a/tests/cts/permissionui/AppThatAccessesCameraAndMic/src/android/permissionui/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
+++ b/tests/cts/permissionui/AppThatAccessesCameraAndMic/src/android/permissionui/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
@@ -30,6 +30,7 @@
 import android.media.AudioRecord
 import android.media.ImageReader
 import android.media.MediaRecorder.AudioSource.MIC
+import android.os.Bundle
 import android.os.Handler
 import android.os.Process
 import android.util.Log
@@ -65,6 +66,17 @@
     private var runHotword = false
     private var finishEarly = false
 
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        if (savedInstanceState != null) {
+            throw RuntimeException(
+                "Activity was recreated (perhaps due to a configuration change?) " +
+                    "and this activity doesn't currently know how to gracefully handle " +
+                    "configuration changes.")
+        }
+    }
+
     override fun onStart() {
         super.onStart()
         runCamera = intent.getBooleanExtra(USE_CAMERA, false)
diff --git a/tests/cts/permissionui/CreateNotificationChannelsApp31/AndroidManifest.xml b/tests/cts/permissionui/CreateNotificationChannelsApp31/AndroidManifest.xml
index 524dfab..b342319 100644
--- a/tests/cts/permissionui/CreateNotificationChannelsApp31/AndroidManifest.xml
+++ b/tests/cts/permissionui/CreateNotificationChannelsApp31/AndroidManifest.xml
@@ -25,6 +25,7 @@
 
     <application android:label="CreateNotif">
         <activity android:name=".CreateNotificationChannelsActivity"
+                  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                   android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/cts/permissionui/CreateNotificationChannelsApp31/src/android/permissionui/cts/usepermission/CreateNotificationChannelsActivity.kt b/tests/cts/permissionui/CreateNotificationChannelsApp31/src/android/permissionui/cts/usepermission/CreateNotificationChannelsActivity.kt
index 25d5845..4bc394c 100644
--- a/tests/cts/permissionui/CreateNotificationChannelsApp31/src/android/permissionui/cts/usepermission/CreateNotificationChannelsActivity.kt
+++ b/tests/cts/permissionui/CreateNotificationChannelsApp31/src/android/permissionui/cts/usepermission/CreateNotificationChannelsActivity.kt
@@ -52,6 +52,14 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+
+        if (savedInstanceState != null) {
+            throw RuntimeException(
+                "Activity was recreated (perhaps due to a configuration change?) " +
+                "and this activity doesn't currently know how to gracefully handle " +
+                    "configuration changes.")
+        }
+
         registerReceiver(receiver, IntentFilter(BROADCAST_ACTION), RECEIVER_EXPORTED)
         handleIntent(intent)
     }
@@ -157,4 +165,8 @@
             PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantedPerms)
                 .setPackage(TEST_PKG))
     }
+
+    companion object {
+        private val TAG = CreateNotificationChannelsActivity::class.simpleName
+    }
 }
diff --git a/tests/cts/permissionui/UsePermissionApp30WithBackground/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt b/tests/cts/permissionui/UsePermissionApp30WithBackground/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
index c4f8b49..7d07826 100644
--- a/tests/cts/permissionui/UsePermissionApp30WithBackground/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
+++ b/tests/cts/permissionui/UsePermissionApp30WithBackground/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
@@ -19,11 +19,17 @@
 import android.app.Activity
 import android.content.Intent
 import android.os.Bundle
+import android.util.Log
 
 class RequestPermissionsActivity : Activity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        if (savedInstanceState != null) {
+            Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+            return
+        }
+
         val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
         requestPermissions(permissions, 1)
     }
@@ -41,4 +47,8 @@
         })
         finish()
     }
+
+    companion object {
+        private val TAG = RequestPermissionsActivity::class.simpleName
+    }
 }
diff --git a/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt b/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
index 4df0cf1..3eea773 100644
--- a/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
+++ b/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
+import android.util.Log
 import com.android.modules.utils.build.SdkLevel
 
 class RequestPermissionsActivity : Activity() {
@@ -29,17 +30,20 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
-        if (savedInstanceState == null) {
-            val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
-            shouldAskTwice = intent.getBooleanExtra("$packageName.ASK_TWICE", false)
-            if (SdkLevel.isAtLeastV()) {
-                // TODO: make deviceId dynamic
-                requestPermissions(permissions, 1, Context.DEVICE_ID_DEFAULT)
-            } else {
-                requestPermissions(permissions, 1)
-            }
-            timesAsked = 1
+        if (savedInstanceState != null) {
+            Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+            return
         }
+
+        val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
+        shouldAskTwice = intent.getBooleanExtra("$packageName.ASK_TWICE", false)
+        if (SdkLevel.isAtLeastV()) {
+            // TODO: make deviceId dynamic
+            requestPermissions(permissions, 1, Context.DEVICE_ID_DEFAULT)
+        } else {
+            requestPermissions(permissions, 1)
+        }
+        timesAsked = 1
     }
 
     override fun onRequestPermissionsResult(
@@ -82,4 +86,8 @@
         )
         finish()
     }
+
+    companion object {
+        private val TAG = RequestPermissionsActivity::class.simpleName
+    }
 }
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt b/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
index cb0182b..add0df1 100644
--- a/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
@@ -24,6 +24,7 @@
 import android.content.IntentFilter
 import android.os.Bundle
 import android.os.Handler
+import android.util.Log
 
 class RequestPermissionsActivity : Activity() {
 
@@ -31,6 +32,12 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+
+        if (savedInstanceState != null) {
+            Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+            return
+        }
+
         registerReceiver(object : BroadcastReceiver() {
             override fun onReceive(context: Context?, intent: Intent?) {
                 if (intent?.action != ACTION_SHOW_OVERLAY) {
@@ -85,5 +92,6 @@
     companion object {
         const val ACTION_SHOW_OVERLAY = "android.permissionui.cts.usepermission.ACTION_SHOW_OVERLAY"
         const val ACTION_HIDE_OVERLAY = "android.permissionui.cts.usepermission.ACTION_HIDE_OVERLAY"
+        private val TAG = RequestPermissionsActivity::class.simpleName
     }
 }
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/StartForFutureActivity.kt b/tests/cts/permissionui/src/android/permissionui/cts/StartForFutureActivity.kt
index b77da30..643c013 100644
--- a/tests/cts/permissionui/src/android/permissionui/cts/StartForFutureActivity.kt
+++ b/tests/cts/permissionui/src/android/permissionui/cts/StartForFutureActivity.kt
@@ -19,13 +19,28 @@
 import android.app.Activity
 import android.app.Instrumentation
 import android.content.Intent
+import android.os.Bundle
+import android.util.Log
 import java.util.concurrent.CompletableFuture
 
 class StartForFutureActivity : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        if (savedInstanceState != null) {
+            Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+        }
+    }
+
     fun startActivityForFuture(
         intent: Intent,
         future: CompletableFuture<Instrumentation.ActivityResult>
     ) {
+        if (StartForFutureActivity.future != null) {
+            throw RuntimeException("StartForFutureActivity only supports launching one " +
+                "concurrent activity, but more than one was attempted.")
+        }
+
         startActivityForResult(intent, 1)
         StartForFutureActivity.future = future
     }
@@ -39,5 +54,6 @@
 
     companion object {
         private var future: CompletableFuture<Instrumentation.ActivityResult>? = null
+        private val TAG = StartForFutureActivity::class.simpleName
     }
 }
diff --git a/tests/cts/role/AndroidManifest.xml b/tests/cts/role/AndroidManifest.xml
index a69caf1..a8c8c8e 100644
--- a/tests/cts/role/AndroidManifest.xml
+++ b/tests/cts/role/AndroidManifest.xml
@@ -27,7 +27,9 @@
 
         <uses-library android:name="android.test.runner" />
 
-        <activity android:name=".WaitForResultActivity" />
+        <activity
+            android:name=".WaitForResultActivity"
+            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"/>
     </application>
 
     <instrumentation
diff --git a/tests/cts/role/src/android/app/role/cts/WaitForResultActivity.java b/tests/cts/role/src/android/app/role/cts/WaitForResultActivity.java
index 03944a5..fb13423 100644
--- a/tests/cts/role/src/android/app/role/cts/WaitForResultActivity.java
+++ b/tests/cts/role/src/android/app/role/cts/WaitForResultActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.os.Bundle;
 import android.util.Pair;
 
 import androidx.annotation.NonNull;
@@ -37,6 +38,18 @@
     private int mResultCode;
     private Intent mData;
 
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            throw new RuntimeException(
+                    "Activity was recreated (perhaps due to a configuration change?) "
+                          + "and this activity doesn't currently know how to gracefully handle "
+                          + "configuration changes.");
+        }
+    }
+
     public void startActivityToWaitForResult(@NonNull Intent intent) {
         mLatch = new CountDownLatch(1);
         startActivityForResult(intent, REQUEST_CODE_WAIT_FOR_RESULT);