diff options
| author | 2024-09-12 18:04:07 +0000 | |
|---|---|---|
| committer | 2024-09-12 18:04:07 +0000 | |
| commit | 5394369d517609a99bf0e6966deb04757bb75d31 (patch) | |
| tree | 336c5c1982e6ab19f4120ad12832d091975ebeff | |
| parent | d91e612f2edebcebb91de214b70f633953a530b4 (diff) | |
| parent | c429a488ac127cbe6b10d361d42e6ad419a14773 (diff) | |
Merge "Test display event delivery to cached processes." into main
11 files changed, 689 insertions, 0 deletions
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp index fe73025e9736..36ea24195789 100644 --- a/services/tests/displayservicetests/Android.bp +++ b/services/tests/displayservicetests/Android.bp @@ -22,6 +22,7 @@ android_test { static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", + "compatibility-device-util-axt", "flag-junit", "frameworks-base-testutils", "junit", @@ -48,6 +49,10 @@ android_test { "automotive-tests", ], + data: [ + ":DisplayManagerTestApp", + ], + certificate: "platform", dxflags: ["--multi-dex"], diff --git a/services/tests/displayservicetests/AndroidManifest.xml b/services/tests/displayservicetests/AndroidManifest.xml index 74260cdd497c..37a34eeb9724 100644 --- a/services/tests/displayservicetests/AndroidManifest.xml +++ b/services/tests/displayservicetests/AndroidManifest.xml @@ -39,6 +39,10 @@ android:testOnly="true"> <uses-library android:name="android.test.mock" android:required="true" /> <uses-library android:name="android.test.runner" /> + <activity android:name="com.android.server.display.SimpleActivity" + android:exported="true" /> + <activity android:name="com.android.server.display.SimpleActivity2" + android:exported="true" /> </application> <instrumentation diff --git a/services/tests/displayservicetests/AndroidTest.xml b/services/tests/displayservicetests/AndroidTest.xml index 2985f98417df..f3697bbffd5c 100644 --- a/services/tests/displayservicetests/AndroidTest.xml +++ b/services/tests/displayservicetests/AndroidTest.xml @@ -23,6 +23,13 @@ <option name="test-file-name" value="DisplayServiceTests.apk" /> </target_preparer> + <!-- Load additional APKs onto device --> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="DisplayManagerTestApp.apk" /> + </target_preparer> + <option name="test-tag" value="DisplayServiceTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest"> <option name="package" value="com.android.frameworks.displayservicetests" /> diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java new file mode 100644 index 000000000000..90f62577b261 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2024 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.server.display; + +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; +import static android.util.DisplayMetrics.DENSITY_HIGH; +import static android.util.DisplayMetrics.DENSITY_MEDIUM; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import android.app.ActivityManager; +import android.app.Instrumentation; +import android.content.Context; +import android.content.Intent; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.platform.test.annotations.AppModeSdkSandbox; +import android.util.Log; +import android.util.SparseArray; + +import androidx.annotation.GuardedBy; +import androidx.annotation.NonNull; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.SystemUtil; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * Tests that applications can receive display events correctly. + */ +@RunWith(Parameterized.class) +@AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") +public class DisplayEventDeliveryTest { + private static final String TAG = "DisplayEventDeliveryTest"; + + private static final String NAME = TAG; + private static final int WIDTH = 720; + private static final int HEIGHT = 480; + + private static final int MESSAGE_LAUNCHED = 1; + private static final int MESSAGE_CALLBACK = 2; + + private static final int DISPLAY_ADDED = 1; + private static final int DISPLAY_CHANGED = 2; + private static final int DISPLAY_REMOVED = 3; + + private static final long DISPLAY_EVENT_TIMEOUT_MSEC = 100; + private static final long TEST_FAILURE_TIMEOUT_MSEC = 10000; + + private static final String TEST_PACKAGE = + "com.android.servicestests.apps.displaymanagertestapp"; + private static final String TEST_ACTIVITY = TEST_PACKAGE + ".DisplayEventActivity"; + private static final String TEST_DISPLAYS = "DISPLAYS"; + private static final String TEST_MESSENGER = "MESSENGER"; + + private final Object mLock = new Object(); + + private Instrumentation mInstrumentation; + private Context mContext; + private DisplayManager mDisplayManager; + private ActivityManager mActivityManager; + private ActivityManager.OnUidImportanceListener mUidImportanceListener; + private CountDownLatch mLatchActivityLaunch; + private CountDownLatch mLatchActivityCached; + private HandlerThread mHandlerThread; + private Handler mHandler; + private Messenger mMessenger; + private int mPid; + private int mUid; + + /** + * Array of DisplayBundle. The test handler uses it to check if certain display events have + * been sent to DisplayEventActivity. + * Key: displayId of each new VirtualDisplay created by this test + * Value: DisplayBundle, storing the VirtualDisplay and its expected display events + * + * NOTE: The lock is required when adding and removing virtual displays. Otherwise it's not + * necessary to lock mDisplayBundles when accessing it from the test function. + */ + @GuardedBy("mLock") + private SparseArray<DisplayBundle> mDisplayBundles; + + /** + * Helper class to store VirtualDisplay and its corresponding display events expected to be + * sent to DisplayEventActivity. + */ + private static final class DisplayBundle { + private VirtualDisplay mVirtualDisplay; + private final int mDisplayId; + + // Display events we expect to receive before timeout + private final LinkedBlockingQueue<Integer> mExpectations; + + DisplayBundle(VirtualDisplay display) { + mVirtualDisplay = display; + mDisplayId = display.getDisplay().getDisplayId(); + mExpectations = new LinkedBlockingQueue<>(); + } + + public void releaseDisplay() { + if (mVirtualDisplay != null) { + mVirtualDisplay.release(); + } + mVirtualDisplay = null; + } + + /** + * Add the received display event from the test activity to the queue + * + * @param event The corresponding display event + */ + public void addDisplayEvent(int event) { + Log.d(TAG, "Received " + mDisplayId + " " + event); + mExpectations.offer(event); + } + + + /** + * Assert that there isn't any unexpected display event from the test activity + */ + public void assertNoDisplayEvents() { + try { + assertNull(mExpectations.poll(DISPLAY_EVENT_TIMEOUT_MSEC, TimeUnit.MILLISECONDS)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + /** + * Wait for the expected display event from the test activity + * + * @param expect The expected display event + */ + public void waitDisplayEvent(int expect) { + while (true) { + try { + final Integer event; + event = mExpectations.poll(TEST_FAILURE_TIMEOUT_MSEC, TimeUnit.MILLISECONDS); + assertNotNull(event); + if (expect == event) { + Log.d(TAG, "Found " + mDisplayId + " " + event); + return; + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + + /** + * How many virtual displays to create during the test + */ + @Parameter(0) + public int mDisplayCount; + + /** + * True if running the test activity in cached mode + * False if running it in non-cached mode + */ + @Parameter(1) + public boolean mCached; + + @Parameters(name = "#{index}: {0} {1}") + public static Iterable<? extends Object> data() { + return Arrays.asList(new Object[][]{ + {1, false}, {2, false}, {3, false}, {10, false}, + {1, true}, {2, true}, {3, true}, {10, true} + }); + } + + private class TestHandler extends Handler { + TestHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(@NonNull Message msg) { + switch (msg.what) { + case MESSAGE_LAUNCHED: + mPid = msg.arg1; + mUid = msg.arg2; + Log.d(TAG, "Launched " + mPid + " " + mUid); + mLatchActivityLaunch.countDown(); + break; + case MESSAGE_CALLBACK: + Log.d(TAG, "Callback " + msg.arg1 + " " + msg.arg2); + synchronized (mLock) { + // arg1: displayId + DisplayBundle bundle = mDisplayBundles.get(msg.arg1); + if (bundle != null) { + // arg2: display event + bundle.addDisplayEvent(msg.arg2); + } + } + break; + default: + fail("Unexpected value: " + msg.what); + break; + } + } + } + + @Before + public void setUp() throws Exception { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mContext = mInstrumentation.getContext(); + mDisplayManager = mContext.getSystemService(DisplayManager.class); + mLatchActivityLaunch = new CountDownLatch(1); + mLatchActivityCached = new CountDownLatch(1); + mActivityManager = mContext.getSystemService(ActivityManager.class); + mUidImportanceListener = (uid, importance) -> { + if (uid == mUid && importance == IMPORTANCE_CACHED) { + Log.d(TAG, "Listener " + uid + " becomes " + importance); + mLatchActivityCached.countDown(); + } + }; + SystemUtil.runWithShellPermissionIdentity(() -> + mActivityManager.addOnUidImportanceListener(mUidImportanceListener, + IMPORTANCE_CACHED)); + // The lock is not functionally necessary but eliminates lint error messages. + synchronized (mLock) { + mDisplayBundles = new SparseArray<>(); + } + mHandlerThread = new HandlerThread("handler"); + mHandlerThread.start(); + mHandler = new TestHandler(mHandlerThread.getLooper()); + mMessenger = new Messenger(mHandler); + mPid = 0; + } + + @After + public void tearDown() throws Exception { + mActivityManager.removeOnUidImportanceListener(mUidImportanceListener); + mHandlerThread.quitSafely(); + synchronized (mLock) { + for (int i = 0; i < mDisplayBundles.size(); i++) { + DisplayBundle bundle = mDisplayBundles.valueAt(i); + // Clean up unreleased virtual display + bundle.releaseDisplay(); + } + mDisplayBundles.clear(); + } + SystemUtil.runShellCommand(mInstrumentation, "am force-stop " + TEST_PACKAGE); + } + + /** + * Return a display bundle at the stated index. The bundle is retrieved under lock. + */ + private DisplayBundle displayBundleAt(int i) { + synchronized (mLock) { + return mDisplayBundles.valueAt(i); + } + } + + /** + * Create virtual displays, change their configurations and release them + * mDisplays: the amount of virtual displays to be created + * mCached: true to run the test activity in cached mode; false in non-cached mode + */ + @Test + public void testDisplayEvents() { + Log.d(TAG, "Start test testDisplayEvents " + mDisplayCount + " " + mCached); + // Launch DisplayEventActivity and start listening to display events + launchTestActivity(); + + if (mCached) { + // The test activity in cached mode won't receive the pending display events + makeTestActivityCached(); + } + + // Create new virtual displays + for (int i = 0; i < mDisplayCount; i++) { + // Lock is needed here to ensure the handler can query the displays + synchronized (mLock) { + VirtualDisplay display = createVirtualDisplay(NAME + i); + DisplayBundle bundle = new DisplayBundle(display); + mDisplayBundles.put(bundle.mDisplayId, bundle); + } + } + + for (int i = 0; i < mDisplayCount; i++) { + if (mCached) { + // DISPLAY_ADDED should be deferred for cached process + displayBundleAt(i).assertNoDisplayEvents(); + } else { + // DISPLAY_ADDED should arrive immediately for non-cached process + displayBundleAt(i).waitDisplayEvent(DISPLAY_ADDED); + } + } + + // Change the virtual displays + for (int i = 0; i < mDisplayCount; i++) { + DisplayBundle bundle = displayBundleAt(i); + bundle.mVirtualDisplay.resize(WIDTH, HEIGHT, DENSITY_HIGH); + } + + for (int i = 0; i < mDisplayCount; i++) { + if (mCached) { + // DISPLAY_CHANGED should be deferred for cached process + displayBundleAt(i).assertNoDisplayEvents(); + } else { + // DISPLAY_CHANGED should arrive immediately for non-cached process + displayBundleAt(i).waitDisplayEvent(DISPLAY_CHANGED); + } + } + + if (mCached) { + // The test activity becomes non-cached and should receive the pending display events + bringTestActivityTop(); + + for (int i = 0; i < mDisplayCount; i++) { + // The pending DISPLAY_ADDED & DISPLAY_CHANGED should arrive now + displayBundleAt(i).waitDisplayEvent(DISPLAY_ADDED); + displayBundleAt(i).waitDisplayEvent(DISPLAY_CHANGED); + } + } + + // Release the virtual displays + for (int i = 0; i < mDisplayCount; i++) { + displayBundleAt(i).releaseDisplay(); + } + + // DISPLAY_REMOVED should arrive now + for (int i = 0; i < mDisplayCount; i++) { + displayBundleAt(i).waitDisplayEvent(DISPLAY_REMOVED); + } + } + + /** + * Launch the test activity that would listen to display events + */ + private void launchTestActivity() { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(TEST_PACKAGE, TEST_ACTIVITY); + intent.putExtra(TEST_MESSENGER, mMessenger); + intent.putExtra(TEST_DISPLAYS, mDisplayCount); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + SystemUtil.runWithShellPermissionIdentity( + () -> { + mContext.startActivity(intent); + }, + android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); + waitLatch(mLatchActivityLaunch); + } + + /** + * Bring the test activity back to top + */ + private void bringTestActivityTop() { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(TEST_PACKAGE, TEST_ACTIVITY); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + SystemUtil.runWithShellPermissionIdentity( + () -> { + mContext.startActivity(intent); + }, + android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); + } + + /** + * Bring the test activity into cached mode by launching another 2 apps + */ + private void makeTestActivityCached() { + // Launch another activity to bring the test activity into background + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(mContext, SimpleActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + + // Launch another activity to bring the test activity into cached mode + Intent intent2 = new Intent(Intent.ACTION_MAIN); + intent2.setClass(mContext, SimpleActivity2.class); + intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + SystemUtil.runWithShellPermissionIdentity( + () -> { + mInstrumentation.startActivitySync(intent); + mInstrumentation.startActivitySync(intent2); + }, + android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); + waitLatch(mLatchActivityCached); + } + + /** + * Create a virtual display + * + * @param name The name of the new virtual display + * @return The new virtual display + */ + private VirtualDisplay createVirtualDisplay(String name) { + return mDisplayManager.createVirtualDisplay(name, WIDTH, HEIGHT, DENSITY_MEDIUM, + null /* surface: as we don't actually draw anything, null is enough */, + VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + /* flags: a public virtual display that another app can access */); + } + + /** + * Wait for CountDownLatch with timeout + */ + private void waitLatch(CountDownLatch latch) { + try { + latch.await(TEST_FAILURE_TIMEOUT_MSEC, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/SimpleActivity.java b/services/tests/displayservicetests/src/com/android/server/display/SimpleActivity.java new file mode 100644 index 000000000000..c4ebbd9fd876 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/SimpleActivity.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 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.server.display; + +import android.app.Activity; + +/** + * An activity doing nothing + */ +public final class SimpleActivity extends Activity { } diff --git a/services/tests/displayservicetests/src/com/android/server/display/SimpleActivity2.java b/services/tests/displayservicetests/src/com/android/server/display/SimpleActivity2.java new file mode 100644 index 000000000000..a719a57efb2d --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/SimpleActivity2.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 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.server.display; + +import android.app.Activity; + +/** + * Another activity doing nothing + */ +public final class SimpleActivity2 extends Activity { } diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index e55e0f27dfb4..b41b30cf0e2e 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -30,6 +30,7 @@ android_test { "src/**/*.kt", "test-apps/SuspendTestApp/src/**/*.java", + "test-apps/DisplayManagerTestApp/src/**/*.java", ], kotlincflags: [ @@ -134,6 +135,7 @@ android_test { }, data: [ + ":DisplayManagerTestApp", ":SimpleServiceTestApp1", ":SimpleServiceTestApp2", ":SimpleServiceTestApp3", diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml index b56af87ee020..5298251b79f7 100644 --- a/services/tests/servicestests/AndroidTest.xml +++ b/services/tests/servicestests/AndroidTest.xml @@ -35,6 +35,7 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> + <option name="test-file-name" value="DisplayManagerTestApp.apk" /> <option name="test-file-name" value="FrameworksServicesTests.apk" /> <option name="test-file-name" value="SuspendTestApp.apk" /> <option name="test-file-name" value="SimpleServiceTestApp1.apk" /> diff --git a/services/tests/servicestests/test-apps/DisplayManagerTestApp/Android.bp b/services/tests/servicestests/test-apps/DisplayManagerTestApp/Android.bp new file mode 100644 index 000000000000..962ae9be4103 --- /dev/null +++ b/services/tests/servicestests/test-apps/DisplayManagerTestApp/Android.bp @@ -0,0 +1,37 @@ +// Copyright (C) 2024 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 + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "DisplayManagerTestApp", + + sdk_version: "current", + + srcs: ["**/*.java"], + + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, +} diff --git a/services/tests/servicestests/test-apps/DisplayManagerTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/DisplayManagerTestApp/AndroidManifest.xml new file mode 100644 index 000000000000..c0d9d6fd3719 --- /dev/null +++ b/services/tests/servicestests/test-apps/DisplayManagerTestApp/AndroidManifest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 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="com.android.servicestests.apps.displaymanagertestapp"> + + <application android:label="DisplayEventTestApp"> + <activity android:name=".DisplayEventActivity" + android:exported="true" /> + </application> + +</manifest> diff --git a/services/tests/servicestests/test-apps/DisplayManagerTestApp/src/com/android/servicestests/apps/displaymanagertestapp/DisplayEventActivity.java b/services/tests/servicestests/test-apps/DisplayManagerTestApp/src/com/android/servicestests/apps/displaymanagertestapp/DisplayEventActivity.java new file mode 100644 index 000000000000..07754b201758 --- /dev/null +++ b/services/tests/servicestests/test-apps/DisplayManagerTestApp/src/com/android/servicestests/apps/displaymanagertestapp/DisplayEventActivity.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 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.servicestests.apps.displaymanagertestapp; + +import android.app.Activity; +import android.content.Intent; +import android.hardware.display.DisplayManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; + +/** + * A simple activity manipulating displays and listening to corresponding display events + */ +public class DisplayEventActivity extends Activity { + private static final String TAG = DisplayEventActivity.class.getSimpleName(); + + private static final String TEST_DISPLAYS = "DISPLAYS"; + private static final String TEST_MESSENGER = "MESSENGER"; + + private static final int MESSAGE_LAUNCHED = 1; + private static final int MESSAGE_CALLBACK = 2; + + private static final int DISPLAY_ADDED = 1; + private static final int DISPLAY_CHANGED = 2; + private static final int DISPLAY_REMOVED = 3; + + private int mExpectedDisplayCount; + private int mSeenDisplayCount; + private Messenger mMessenger; + private DisplayManager mDisplayManager; + private DisplayManager.DisplayListener mDisplayListener; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + mExpectedDisplayCount = 0; + mSeenDisplayCount = intent.getIntExtra(TEST_DISPLAYS, 0); + mMessenger = intent.getParcelableExtra(TEST_MESSENGER, Messenger.class); + mDisplayManager = getApplicationContext().getSystemService(DisplayManager.class); + mDisplayListener = new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + callback(displayId, DISPLAY_ADDED); + } + + @Override + public void onDisplayRemoved(int displayId) { + callback(displayId, DISPLAY_REMOVED); + } + + @Override + public void onDisplayChanged(int displayId) { + callback(displayId, DISPLAY_CHANGED); + } + }; + Handler handler = new Handler(Looper.getMainLooper()); + mDisplayManager.registerDisplayListener(mDisplayListener, handler); + launched(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mDisplayManager.unregisterDisplayListener(mDisplayListener); + } + + private void launched() { + try { + Message msg = Message.obtain(); + msg.what = MESSAGE_LAUNCHED; + msg.arg1 = Process.myPid(); + msg.arg2 = Process.myUid(); + Log.d(TAG, "Launched " + mSeenDisplayCount); + mMessenger.send(msg); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + private void callback(int displayId, int event) { + try { + Message msg = Message.obtain(); + msg.what = MESSAGE_CALLBACK; + msg.arg1 = displayId; + msg.arg2 = event; + Log.d(TAG, "Msg " + msg.arg1 + " " + msg.arg2); + mMessenger.send(msg); + if (event == DISPLAY_REMOVED) { + mExpectedDisplayCount++; + if (mExpectedDisplayCount >= mSeenDisplayCount) { + finish(); + } + } + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } +} |