CTS tests for MediaProjection related APIs

Introduce tests for new `MediaProjection#Callback`,
`MediaProjectionConfig`, and `MediaProjectionManager` APIs

Bug: 264468358
Bug: 264468268
Test: atest CtsMediaProjectionTestCases:MediaProjectionTest
Test: atest CtsMediaProjectionTestCases:MediaProjectionConfigTest
Test: atest CtsMediaProjectionTestCases:MediaProjectionManagerTest
Change-Id: Ie7be6897f3fc10bf435b13d77a10413975acdec0
diff --git a/tests/tests/media/README.md b/tests/tests/media/README.md
index 026bc4f..6d728ab 100644
--- a/tests/tests/media/README.md
+++ b/tests/tests/media/README.md
@@ -12,6 +12,7 @@
 | CtsMediaExtractorTestCases      | MediaExtractor related tests                                              |
 | CtsMediaMuxerTestCases          | MediaMuxer related tests                                                  |
 | CtsMediaPlayerTestCases         | MediaPlayer related tests                                                 |
+| CtsMediaProjectionTestCases     | MediaProjection related tests                                             |
 | CtsMediaRecorderTestCases       | MediaRecorder related tests                                               |
 | CtsMediaMiscTestCases           | All other media tests                                                     |
 
diff --git a/tests/tests/media/common/src/android/media/cts/MediaProjectionActivity.java b/tests/tests/media/common/src/android/media/cts/MediaProjectionActivity.java
index 4bf1877..64da66d 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaProjectionActivity.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaProjectionActivity.java
@@ -57,7 +57,7 @@
     private final ServiceConnection mConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
-            startActivityForResult(mProjectionManager.createScreenCaptureIntent(), PERMISSION_CODE);
+            startActivityForResult(getScreenCaptureIntent(), PERMISSION_CODE);
             dismissPermissionDialog();
             mProjectionServiceBound = true;
         }
@@ -68,7 +68,6 @@
         }
     };
 
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -88,6 +87,10 @@
         }
     }
 
+    protected Intent getScreenCaptureIntent() {
+        return mProjectionManager.createScreenCaptureIntent();
+    }
+
     private void bindMediaProjectionService() {
         Intent intent = new Intent(this, LocalMediaProjectionService.class);
         startService(intent);
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/MediaProjectionTest.java b/tests/tests/media/misc/src/android/media/misc/cts/MediaProjectionTest.java
deleted file mode 100644
index c2a78d8..0000000
--- a/tests/tests/media/misc/src/android/media/misc/cts/MediaProjectionTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2020 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.media.misc.cts;
-
-import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.media.cts.MediaProjectionActivity;
-import android.media.projection.MediaProjection;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.compatibility.common.util.NonMainlineTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test MediaProjection lifecycle.
- *
- * This test starts and stops a MediaProjection screen capture session using
- * MediaProjectionActivity.
- *
- * Currently we check that we are able to draw overlay windows during the session but not before
- * or after. (We request SYATEM_ALERT_WINDOW permission, but it is not granted, so by default we
- * cannot.)
- *
- * Note that there are other tests verifying that screen capturing actually works correctly in
- * CtsWindowManagerDeviceTestCases.
- */
-@NonMainlineTest
-public class MediaProjectionTest {
-    @Rule
-    public ActivityTestRule<MediaProjectionActivity> mActivityRule =
-            new ActivityTestRule<>(MediaProjectionActivity.class, false, false);
-
-    private MediaProjectionActivity mActivity;
-    private MediaProjection mMediaProjection;
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getContext();
-        runWithShellPermissionIdentity(() -> {
-            mContext.getPackageManager().revokeRuntimePermission(
-                    mContext.getPackageName(),
-                    android.Manifest.permission.SYSTEM_ALERT_WINDOW,
-                    new UserHandle(ActivityManager.getCurrentUser()));
-        });
-    }
-
-    @Test
-    public void testOverlayAllowedDuringScreenCapture() throws Exception {
-        assertFalse(Settings.canDrawOverlays(mContext));
-
-        startMediaProjection();
-        assertTrue(Settings.canDrawOverlays(mContext));
-
-        stopMediaProjection();
-        assertFalse(Settings.canDrawOverlays(mContext));
-    }
-
-    private void startMediaProjection() throws Exception {
-        mActivityRule.launchActivity(null);
-        mActivity = mActivityRule.getActivity();
-        mMediaProjection = mActivity.waitForMediaProjection();
-    }
-
-    private void stopMediaProjection() throws Exception {
-        final int STOP_TIMEOUT_MS = 1000;
-        CountDownLatch stoppedLatch = new CountDownLatch(1);
-
-        mMediaProjection.registerCallback(new MediaProjection.Callback() {
-            public void onStop() {
-                stoppedLatch.countDown();
-            }
-        }, new Handler(Looper.getMainLooper()));
-        mMediaProjection.stop();
-
-        assertTrue("Could not stop the MediaProjection in " + STOP_TIMEOUT_MS + "ms",
-                stoppedLatch.await(STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS));
-    }
-}
diff --git a/tests/tests/media/projection/Android.bp b/tests/tests/media/projection/Android.bp
new file mode 100644
index 0000000..aa6a734
--- /dev/null
+++ b/tests/tests/media/projection/Android.bp
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license", // CC-BY
+    ],
+}
+
+android_test {
+    name: "CtsMediaProjectionTestCases",
+    defaults: ["cts_defaults"],
+    // include both the 32 and 64 bit versions
+    compile_multilib: "both",
+    static_libs: [
+        "ctstestrunner-axt",
+        "cts-media-common",
+        "junit",
+        "junit-params",
+        "testng",
+        "truth-prebuilt",
+    ],
+    aaptflags: [
+        "--auto-add-overlay",
+
+        // Do not compress these files:
+        "-0 .vp9",
+        "-0 .ts",
+        "-0 .heic",
+        "-0 .trp",
+        "-0 .ota",
+        "-0 .mxmf",
+    ],
+    srcs: [
+        "src/**/*.java",
+        "aidl/**/*.aidl",
+    ],
+    // This test uses private APIs
+    platform_apis: true,
+    jni_uses_sdk_apis: true,
+    libs: [
+        "org.apache.http.legacy",
+        "android.test.base",
+        "android.test.runner",
+    ],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+        "mts-media",
+    ],
+    host_required: ["cts-dynamic-config"],
+    min_sdk_version: "33",
+    target_sdk_version: "33",
+}
diff --git a/tests/tests/media/projection/AndroidManifest.xml b/tests/tests/media/projection/AndroidManifest.xml
new file mode 100644
index 0000000..f5a9ee0
--- /dev/null
+++ b/tests/tests/media/projection/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.media.projection.cts"
+     android:targetSandboxVersion="2">
+
+    <uses-sdk android:minSdkVersion="33"
+         android:targetSdkVersion="33"/>
+
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
+    <application
+         android:requestLegacyExternalStorage="true"
+         android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+        <uses-library android:name="org.apache.http.legacy"
+             android:required="false"/>
+        <activity android:name="android.media.projection.cts.MediaProjectionCustomIntentActivity"
+                  android:label="MediaProjectionActivity"
+                  android:screenOrientation="locked"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.media.projection.cts"
+         android:label="CTS tests of android.media.projection">
+        <meta-data android:name="listener"
+             android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/media/projection/AndroidTest.xml b/tests/tests/media/projection/AndroidTest.xml
new file mode 100644
index 0000000..6a3dcb2
--- /dev/null
+++ b/tests/tests/media/projection/AndroidTest.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<configuration description="Config for CTS Media test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="media" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+        <option name="set-test-harness" value="false" />
+        <option name="screen-always-on" value="on" />
+        <option name="screen-adaptive-brightness" value="off" />
+        <option name="disable-audio" value="false"/>
+        <option name="screen-saver" value="off"/>
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="host" />
+        <option name="config-filename" value="CtsMediaProjectionTestCases" />
+        <option name="dynamic-config-name" value="CtsMediaProjectionTestCases" />
+        <option name="version" value="9.0_r1"/>
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="device" />
+        <option name="config-filename" value="CtsMediaProjectionTestCases" />
+        <option name="version" value="1.0"/>
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
+        <option name="push-all" value="true" />
+        <option name="media-folder-name" value="CtsMediaProjectionTestCases-2.1" />
+        <option name="dynamic-config-module" value="CtsMediaProjectionTestCases" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsMediaProjectionTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.media.projection.cts" />
+        <!-- setup can be expensive so limit the number of shards -->
+        <option name="ajur-max-shard" value="5" />
+        <!-- test-timeout unit is ms, value = 30 min -->
+        <option name="test-timeout" value="1800000" />
+        <option name="runtime-hint" value="4h" />
+        <option name="hidden-api-checks" value="true" />
+        <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+        <option name="isolated-storage" value="false" />
+    </test>
+</configuration>
diff --git a/tests/tests/media/projection/DynamicConfig.xml b/tests/tests/media/projection/DynamicConfig.xml
new file mode 100644
index 0000000..9f4f37f
--- /dev/null
+++ b/tests/tests/media/projection/DynamicConfig.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2023 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.
+-->
+
+<dynamicConfig>
+    <entry key="media_files_url">
+    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/misc/CtsMediaMiscTestCases-2.1.zip</value>
+    </entry>
+</dynamicConfig>
diff --git a/tests/tests/media/projection/copy_media.sh b/tests/tests/media/projection/copy_media.sh
new file mode 100755
index 0000000..9e0c744
--- /dev/null
+++ b/tests/tests/media/projection/copy_media.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# Copyright (C) 2023 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.
+#
+## script to install media test files manually
+[ -z "$MEDIA_ROOT_DIR" ] && MEDIA_ROOT_DIR=$(dirname $0)/..
+source $MEDIA_ROOT_DIR/common/copy_media_utils.sh
+get_adb_options "$@"
+copy_media "misc" "CtsMediaMiscTestCases-2.1"
diff --git a/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionConfigTest.java b/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionConfigTest.java
new file mode 100644
index 0000000..09d5e55
--- /dev/null
+++ b/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionConfigTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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.media.projection.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.media.projection.MediaProjectionConfig;
+
+import com.android.compatibility.common.util.ApiTest;
+import com.android.compatibility.common.util.NonMainlineTest;
+
+import org.junit.Test;
+
+/**
+ * Test {@link MediaProjectionConfig}.
+ *
+ * Run with:
+ * atest CtsMediaProjectionTestCases:MediaProjectionConfigTest
+ */
+@NonMainlineTest
+public class MediaProjectionConfigTest {
+
+    @ApiTest(apis = "android.media.projection.MediaProjectionConfig#createConfigForDisplay")
+    @Test
+    public void testCreateConfigForDisplay() {
+        assertThat(MediaProjectionConfig.createConfigForDefaultDisplay()).isNotNull();
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjectionConfig#createConfigForUserChoice")
+    @Test
+    public void testCreateConfigForUserChoice() {
+        assertThat(MediaProjectionConfig.createConfigForUserChoice()).isNotNull();
+    }
+}
diff --git a/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionCustomIntentActivity.java b/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionCustomIntentActivity.java
new file mode 100644
index 0000000..caa4664
--- /dev/null
+++ b/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionCustomIntentActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 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.media.projection.cts;
+
+import android.content.Intent;
+import android.media.cts.MediaProjectionActivity;
+import android.os.Bundle;
+
+import java.util.Optional;
+
+public class MediaProjectionCustomIntentActivity extends MediaProjectionActivity {
+    private static final String TAG = "MediaProjectionCustomIntentActivity";
+    public static final String EXTRA_SCREEN_CAPTURE_INTENT = "extra_screen_capture_intent";
+    private Optional<Intent> mProvidedScreenCaptureIntent;
+
+    @Override
+    protected Intent getScreenCaptureIntent() {
+        return mProvidedScreenCaptureIntent.orElseGet(super::getScreenCaptureIntent);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        // Save intent before invoking super
+        final Intent originalIntent = getIntent();
+        if (originalIntent.hasExtra(EXTRA_SCREEN_CAPTURE_INTENT)) {
+            mProvidedScreenCaptureIntent = Optional.of(
+                    originalIntent.getParcelableExtra(EXTRA_SCREEN_CAPTURE_INTENT, Intent.class));
+        } else {
+            mProvidedScreenCaptureIntent = Optional.empty();
+        }
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionManagerTest.java b/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionManagerTest.java
new file mode 100644
index 0000000..845ddb0
--- /dev/null
+++ b/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionManagerTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2023 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.media.projection.cts;
+
+
+import static android.media.projection.cts.MediaProjectionCustomIntentActivity.EXTRA_SCREEN_CAPTURE_INTENT;
+
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.media.projection.MediaProjection;
+import android.media.projection.MediaProjectionConfig;
+import android.media.projection.MediaProjectionManager;
+import android.os.UserHandle;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ApiTest;
+import com.android.compatibility.common.util.NonMainlineTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test {@link MediaProjectionManager}.
+ *
+ * Run with:
+ * atest CtsMediaProjectionTestCases:MediaProjectionManagerTest
+ */
+@NonMainlineTest
+public class MediaProjectionManagerTest {
+    private Context mContext;
+
+    private MediaProjectionManager mProjectionManager;
+
+    @Rule
+    public ActivityTestRule<MediaProjectionCustomIntentActivity> mActivityRule =
+            new ActivityTestRule<>(MediaProjectionCustomIntentActivity.class, false, false);
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        runWithShellPermissionIdentity(() -> {
+            mContext.getPackageManager().revokeRuntimePermission(
+                    mContext.getPackageName(),
+                    android.Manifest.permission.SYSTEM_ALERT_WINDOW,
+                    new UserHandle(ActivityManager.getCurrentUser()));
+        });
+        mProjectionManager = mContext.getSystemService(MediaProjectionManager.class);
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjectionManager#createScreenCaptureIntent")
+    @Test
+    public void testCreateScreenCaptureIntent() {
+        assertThat(mProjectionManager.createScreenCaptureIntent()).isNotNull();
+        assertThat(mProjectionManager.createScreenCaptureIntent(
+                MediaProjectionConfig.createConfigForDefaultDisplay())).isNotNull();
+        assertThat(mProjectionManager.createScreenCaptureIntent(
+                MediaProjectionConfig.createConfigForUserChoice())).isNotNull();
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjectionManager#getMediaProjection")
+    @Test
+    public void testGetMediaProjection() throws Exception {
+        // Launch the activity.
+        mActivityRule.launchActivity(null);
+        MediaProjectionCustomIntentActivity activity = mActivityRule.getActivity();
+        // Ensure mediaprojection instance is valid.
+        MediaProjection mediaProjection = activity.waitForMediaProjection();
+        assertThat(mediaProjection).isNotNull();
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjectionManager#getMediaProjection")
+    @Test
+    public void testGetMediaProjection_displayConfig() throws Exception {
+        validateValidMediaProjectionFromIntent(
+                MediaProjectionConfig.createConfigForDefaultDisplay());
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjectionManager#getMediaProjection")
+    @Test
+    public void testGetMediaProjection_usersChoiceConfig() throws Exception {
+        validateValidMediaProjectionFromIntent(MediaProjectionConfig.createConfigForUserChoice());
+    }
+
+    private void validateValidMediaProjectionFromIntent(MediaProjectionConfig config)
+            throws Exception {
+        // Build intent to launch test activity.
+        Intent testActivityIntent = new Intent();
+        testActivityIntent.setClass(mContext, MediaProjectionCustomIntentActivity.class);
+        // Ensure test activity uses given intent when requesting permission.
+        testActivityIntent.putExtra(EXTRA_SCREEN_CAPTURE_INTENT,
+                mProjectionManager.createScreenCaptureIntent(config));
+        // Launch the activity.
+        mActivityRule.launchActivity(testActivityIntent);
+        MediaProjectionCustomIntentActivity activity = mActivityRule.getActivity();
+        // Ensure mediaprojection instance is valid.
+        MediaProjection mediaProjection = activity.waitForMediaProjection();
+        assertThat(mediaProjection).isNotNull();
+    }
+}
diff --git a/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionTest.java b/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionTest.java
new file mode 100644
index 0000000..4f2d8ab
--- /dev/null
+++ b/tests/tests/media/projection/src/android/media/projection/cts/MediaProjectionTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2023 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.media.projection.cts;
+
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.media.cts.MediaProjectionActivity;
+import android.media.projection.MediaProjection;
+import android.media.projection.MediaProjectionManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ApiTest;
+import com.android.compatibility.common.util.NonMainlineTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test {@link MediaProjection} lifecycle & callbacks.
+ *
+ * Note that there are other tests verifying that screen capturing actually works correctly in
+ * CtsWindowManagerDeviceTestCases.
+ *
+ * Run with:
+ * atest CtsMediaProjectionTestCases:MediaProjectionTest
+ */
+@NonMainlineTest
+public class MediaProjectionTest {
+    private static final String TAG = "MediaProjectionTest";
+    private static final int STOP_TIMEOUT_MS = 1000;
+    private static final int RECORDING_WIDTH = 500;
+    private static final int RECORDING_HEIGHT = 700;
+    private static final int RECORDING_DENSITY = 200;
+
+    @Rule
+    public ActivityTestRule<MediaProjectionActivity> mActivityRule =
+            new ActivityTestRule<>(MediaProjectionActivity.class, false, false);
+
+    private MediaProjectionActivity mActivity;
+    private MediaProjectionManager mProjectionManager;
+    private MediaProjection mMediaProjection;
+    private MediaProjection.Callback mCallback = null;
+    private ImageReader mImageReader;
+    private VirtualDisplay mVirtualDisplay;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        runWithShellPermissionIdentity(() -> {
+            mContext.getPackageManager().revokeRuntimePermission(
+                    mContext.getPackageName(),
+                    android.Manifest.permission.SYSTEM_ALERT_WINDOW,
+                    new UserHandle(ActivityManager.getCurrentUser()));
+        });
+        mProjectionManager = mContext.getSystemService(MediaProjectionManager.class);
+    }
+
+    @After
+    public void cleanup() {
+        if (mMediaProjection != null) {
+            if (mCallback != null) {
+                mMediaProjection.unregisterCallback(mCallback);
+                mCallback = null;
+            }
+            mMediaProjection.stop();
+            mMediaProjection = null;
+        }
+    }
+
+    /**
+     * This test starts and stops a MediaProjection screen capture session using
+     * MediaProjectionActivity.
+     *
+     * Currently, we check that we are able to draw overlay windows during the session but not
+     * before
+     * or after. (We request SYSTEM_ALERT_WINDOW permission, but it is not granted, so by default
+     * we
+     * cannot).
+     */
+    @Test
+    public void testOverlayAllowedDuringScreenCapture() throws Exception {
+        assertFalse(Settings.canDrawOverlays(mContext));
+
+        startMediaProjection();
+        assertTrue(Settings.canDrawOverlays(mContext));
+
+        CountDownLatch latch = new CountDownLatch(1);
+        mCallback = new MediaProjection.Callback() {
+            @Override
+            public void onStop() {
+                latch.countDown();
+            }
+        };
+        mMediaProjection.registerCallback(mCallback, new Handler(Looper.getMainLooper()));
+        mMediaProjection.stop();
+
+        assertTrue("Could not stop the MediaProjection in " + STOP_TIMEOUT_MS + "ms",
+                latch.await(STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        assertFalse(Settings.canDrawOverlays(mContext));
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjection#createVirtualDisplay")
+    @Test
+    public void testCreateVirtualDisplay() throws Exception {
+        startMediaProjection();
+        createVirtualDisplay();
+
+        assertThat(mVirtualDisplay).isNotNull();
+        Point virtualDisplayDimensions = new Point();
+        mVirtualDisplay.getDisplay().getSize(virtualDisplayDimensions);
+        assertThat(virtualDisplayDimensions).isEqualTo(
+                new Point(RECORDING_WIDTH, RECORDING_HEIGHT));
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjection#unregisterCallback")
+    @Test
+    public void testUnregisterCallback() throws Exception {
+        startMediaProjection();
+
+        CountDownLatch latch = new CountDownLatch(1);
+        mCallback = new MediaProjection.Callback() {
+            @Override
+            public void onStop() {
+                latch.countDown();
+            }
+        };
+        mMediaProjection.registerCallback(mCallback, new Handler(Looper.getMainLooper()));
+        mMediaProjection.unregisterCallback(mCallback);
+
+        createVirtualDisplay();
+        mMediaProjection.stop();
+        assertFalse("Callback is not invoked after " + STOP_TIMEOUT_MS + " ms if unregistere",
+                latch.await(STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @ApiTest(apis = {
+            "android.media.projection.MediaProjection#registerCallback",
+            "android.media.projection.MediaProjection#stop",
+            "android.media.projection.MediaProjection.Callback#onStop"
+    })
+    @Test
+    public void testCallbackOnStop() throws Exception {
+        startMediaProjection();
+
+        CountDownLatch latch = new CountDownLatch(1);
+        mCallback = new MediaProjection.Callback() {
+            @Override
+            public void onStop() {
+                latch.countDown();
+            }
+        };
+        mMediaProjection.registerCallback(mCallback, new Handler(Looper.getMainLooper()));
+        createVirtualDisplay();
+        mMediaProjection.stop();
+
+        assertTrue("Could not stop the MediaProjection in " + STOP_TIMEOUT_MS + "ms",
+                latch.await(STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjection.Callback#onCapturedContentResize")
+    @Test
+    public void testCallbackOnCapturedContentResize() throws Exception {
+        startMediaProjection();
+
+        CountDownLatch latch = new CountDownLatch(1);
+        Point mContentSize = new Point();
+        mCallback = new MediaProjection.Callback() {
+            @Override
+            public void onCapturedContentResize(int width, int height) {
+                mContentSize.x = width;
+                mContentSize.y = height;
+                latch.countDown();
+            }
+        };
+        createVirtualDisplayAndWaitForCallback(latch);
+        Rect maxWindowMetrics = mActivity.getSystemService(
+                WindowManager.class).getMaximumWindowMetrics().getBounds();
+        assertThat(mContentSize).isEqualTo(
+                new Point(maxWindowMetrics.width(), maxWindowMetrics.height()));
+    }
+
+    @ApiTest(apis = "android.media.projection.MediaProjection"
+            + ".Callback#onCapturedContentVisibilityChanged")
+    @Test
+    public void testCallbackOnCapturedContentVisibilityChanged() throws Exception {
+        startMediaProjection();
+        CountDownLatch latch = new CountDownLatch(1);
+        final boolean[] isVisibleUpdate = {false};
+        mCallback = new MediaProjection.Callback() {
+            @Override
+            public void onCapturedContentVisibilityChanged(boolean isVisible) {
+                super.onCapturedContentVisibilityChanged(isVisible);
+                isVisibleUpdate[0] = isVisible;
+                latch.countDown();
+            }
+        };
+        createVirtualDisplayAndWaitForCallback(latch);
+        assertThat(isVisibleUpdate[0]).isTrue();
+    }
+
+    /**
+     * Given mCallback invokes {@link CountDownLatch#countDown()} when triggered, start recording
+     * and wait for the callback to be invoked.
+     */
+    private void createVirtualDisplayAndWaitForCallback(CountDownLatch latch) throws Exception {
+        mMediaProjection.registerCallback(mCallback, new Handler(Looper.getMainLooper()));
+        createVirtualDisplay();
+        assertTrue("Did not get callback after starting recording on the MediaProjection in "
+                        + STOP_TIMEOUT_MS + "ms",
+                latch.await(STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    private void startMediaProjection() throws Exception {
+        mActivityRule.launchActivity(null);
+        mActivity = mActivityRule.getActivity();
+        mMediaProjection = mActivity.waitForMediaProjection();
+    }
+
+    private void createVirtualDisplay() {
+        mImageReader = ImageReader.newInstance(RECORDING_WIDTH, RECORDING_HEIGHT,
+                PixelFormat.RGBA_8888, /* maxImages= */ 1);
+        mVirtualDisplay = mMediaProjection.createVirtualDisplay(TAG + "VirtualDisplay",
+                RECORDING_WIDTH, RECORDING_HEIGHT, RECORDING_DENSITY,
+                VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+                mImageReader.getSurface(),
+                new VirtualDisplay.Callback() {
+                    @Override
+                    public void onStopped() {
+                        super.onStopped();
+                        // VirtualDisplay stopped by the system; no more frames incoming. Must
+                        // release VirtualDisplay
+                        Log.v(TAG, "handleVirtualDisplayStopped");
+                        cleanupVirtualDisplay();
+                    }
+                }, new Handler(Looper.getMainLooper()));
+    }
+
+    private void cleanupVirtualDisplay() {
+        if (mImageReader != null) {
+            mImageReader.close();
+            mImageReader = null;
+        }
+
+        if (mVirtualDisplay != null) {
+            final Surface surface = mVirtualDisplay.getSurface();
+            if (surface != null) {
+                surface.release();
+            }
+            mVirtualDisplay.release();
+            mVirtualDisplay = null;
+        }
+    }
+}