AccessibilityMenuService test class setup & initial test.
Bug: 265173123
Test: atest AccessibilityMenuServiceTests
Change-Id: I388e02b58bf582ae6b481804c5f34883e8692e5f
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index 140c10d..f358417 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -18,6 +18,15 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// This filegroup is used by menu tests.
+filegroup {
+ name: "AccessibilityMenuSource",
+ srcs: [
+ "src/**/AccessibilityMenuService.java",
+ "src/**/A11yMenuShortcut.java",
+ ],
+}
+
android_app {
name: "AccessibilityMenu",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index 39e5a8c..a902c5b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/shortcutItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/grid_item_padding"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index c1f2aa8..8ca64d2 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.accessibilitymenu;
+import android.Manifest;
import android.accessibilityservice.AccessibilityButtonController;
import android.accessibilityservice.AccessibilityService;
import android.content.BroadcastReceiver;
@@ -51,8 +52,12 @@
/** @hide */
public class AccessibilityMenuService extends AccessibilityService
implements View.OnTouchListener {
- private static final String TAG = "A11yMenuService";
+ public static final String PACKAGE_NAME = AccessibilityMenuService.class.getPackageName();
+ public static final String INTENT_TOGGLE_MENU = ".toggle_menu";
+ public static final String INTENT_HIDE_MENU = ".hide_menu";
+
+ private static final String TAG = "A11yMenuService";
private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
private static final int BRIGHTNESS_UP_INCREMENT_GAMMA =
@@ -74,7 +79,8 @@
// TODO(b/136716947): Support multi-display once a11y framework side is ready.
private DisplayManager mDisplayManager;
- final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
int mRotation;
@Override
@@ -95,13 +101,20 @@
}
};
- final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mHideMenuReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mA11yMenuLayout.hideMenu();
}
};
+ private final BroadcastReceiver mToggleMenuReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mA11yMenuLayout.toggleVisibility();
+ }
+ };
+
/**
* Update a11y menu layout when large button setting is changed.
*/
@@ -172,7 +185,19 @@
protected void onServiceConnected() {
mA11yMenuLayout = new A11yMenuOverlayLayout(this);
- registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ IntentFilter hideMenuFilter = new IntentFilter();
+ hideMenuFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ hideMenuFilter.addAction(PACKAGE_NAME + INTENT_HIDE_MENU);
+
+ // Including WRITE_SECURE_SETTINGS enforces that we only listen to apps
+ // with the restricted WRITE_SECURE_SETTINGS permission who broadcast this intent.
+ registerReceiver(mHideMenuReceiver, hideMenuFilter,
+ Manifest.permission.WRITE_SECURE_SETTINGS, null,
+ Context.RECEIVER_EXPORTED);
+ registerReceiver(mToggleMenuReceiver,
+ new IntentFilter(PACKAGE_NAME + INTENT_TOGGLE_MENU),
+ Manifest.permission.WRITE_SECURE_SETTINGS, null,
+ Context.RECEIVER_EXPORTED);
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mPrefs.registerOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
@@ -260,7 +285,8 @@
* @param increment The increment amount in gamma-space
*/
private void adjustBrightness(int increment) {
- BrightnessInfo info = getDisplay().getBrightnessInfo();
+ Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ BrightnessInfo info = display.getBrightnessInfo();
int gamma = BrightnessUtils.convertLinearToGammaFloat(
info.brightness,
info.brightnessMinimum,
@@ -275,7 +301,7 @@
info.brightnessMinimum,
info.brightnessMaximum
);
- mDisplayManager.setBrightness(getDisplayId(), brightness);
+ mDisplayManager.setBrightness(display.getDisplayId(), brightness);
mA11yMenuLayout.showSnackbar(
getString(R.string.brightness_percentage_label,
(gamma / (BrightnessUtils.GAMMA_SPACE_MAX / 100))));
@@ -310,7 +336,8 @@
@Override
public boolean onUnbind(Intent intent) {
- unregisterReceiver(mBroadcastReceiver);
+ unregisterReceiver(mHideMenuReceiver);
+ unregisterReceiver(mToggleMenuReceiver);
mPrefs.unregisterOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
sInitialized = false;
return super.onUnbind(intent);
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
index 6f0fe37..6ae65cb 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
@@ -21,6 +21,7 @@
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.TextView;
@@ -146,6 +147,15 @@
shortcutIconButton.setBackground(
mShortcutDrawableUtils.createAdaptiveIconDrawable(shortcutItem.imageColor));
+
+ shortcutIconButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(
+ View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setUniqueId(host.getTag().toString());
+ }
+ });
}
}
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
new file mode 100644
index 0000000..1757dda
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "AccessibilityMenuServiceTests",
+ certificate: "platform",
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "compatibility-device-util-axt",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ":AccessibilityMenuSource",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ instrumentation_for: "AccessibilityMenu",
+}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml
new file mode 100644
index 0000000..7be6ca7
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.systemui.accessibility.accessibilitymenu.tests">
+
+ <!-- Needed to write to Settings.Secure to enable and disable the service under test. -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.systemui.accessibility.accessibilitymenu.tests"
+ android:label="AccessibilityMenu Test Cases">
+ </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml
new file mode 100644
index 0000000..39bee53
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<configuration description="Runs AccessibilityMenu Test Cases.">
+ <option name="test-tag" value="AccessibilityMenuServiceTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="AccessibilityMenuServiceTests.apk" />
+ <option name="aapt-version" value="AAPT2" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.systemui.accessibility.accessibilitymenu.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING b/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING
new file mode 100644
index 0000000..2bd52b5
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "AccessibilityMenuServiceTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
new file mode 100644
index 0000000..529a70c
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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 com.android.systemui.accessibility.accessibilitymenu.tests;
+
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU;
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_TOGGLE_MENU;
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.PACKAGE_NAME;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManager;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.TestUtils;
+import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityMenuServiceTest {
+ private static final String TAG = "A11yMenuServiceTest";
+
+ private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5;
+ private static final int TIMEOUT_UI_CHANGE_S = 5;
+
+ private static Instrumentation sInstrumentation;
+ private static UiAutomation sUiAutomation;
+
+ private static AccessibilityManager sAccessibilityManager;
+
+ @BeforeClass
+ public static void classSetup() throws Throwable {
+ final String serviceName = PACKAGE_NAME + "/.AccessibilityMenuService";
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sUiAutomation = sInstrumentation.getUiAutomation(
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ final Context context = sInstrumentation.getContext();
+ sAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+
+ // Disable all a11yServices if any are active.
+ if (!sAccessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+ TestUtils.waitUntil("Failed to disable all services",
+ TIMEOUT_SERVICE_STATUS_CHANGE_S,
+ () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty());
+ }
+
+ // Enable a11yMenu service.
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, serviceName);
+
+ TestUtils.waitUntil("Failed to enable service",
+ TIMEOUT_SERVICE_STATUS_CHANGE_S,
+ () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(
+ info -> info.getId().contains(serviceName)).count() == 1);
+ }
+
+ @AfterClass
+ public static void classTeardown() throws Throwable {
+ Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+ }
+
+ private boolean isMenuVisible() {
+ return sUiAutomation.getRootInActiveWindow() != null
+ && sUiAutomation.getRootInActiveWindow().getPackageName().toString().equals(
+ PACKAGE_NAME);
+ }
+
+ private void openMenu() throws Throwable {
+ if (isMenuVisible()) {
+ return;
+ }
+ Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU);
+ sInstrumentation.getContext().sendBroadcast(intent);
+ TestUtils.waitUntil("Timed out before menu could appear.",
+ TIMEOUT_UI_CHANGE_S, () -> isMenuVisible());
+ }
+
+ private void closeMenu() throws Throwable {
+ if (!isMenuVisible()) {
+ return;
+ }
+ Intent intent = new Intent(PACKAGE_NAME + INTENT_HIDE_MENU);
+ sInstrumentation.getContext().sendBroadcast(intent);
+ TestUtils.waitUntil("Timed out before menu could close.",
+ TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible());
+ }
+
+ private List<AccessibilityNodeInfo> getGridButtonList() {
+ return sUiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByViewId(PACKAGE_NAME + ":id/shortcutIconBtn");
+ }
+
+ private AccessibilityNodeInfo findGridButtonInfo(
+ List<AccessibilityNodeInfo> buttons, String text) {
+ for (AccessibilityNodeInfo button: buttons) {
+ if (button.getUniqueId().equals(text)) {
+ return button;
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void testAdjustBrightness() throws Throwable {
+ openMenu();
+
+ Context context = sInstrumentation.getTargetContext();
+ DisplayManager displayManager = context.getSystemService(
+ DisplayManager.class);
+ float resetBrightness = displayManager.getBrightness(context.getDisplayId());
+
+ List<AccessibilityNodeInfo> buttons = getGridButtonList();
+ AccessibilityNodeInfo brightnessUpButton = findGridButtonInfo(buttons,
+ String.valueOf(ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()));
+ AccessibilityNodeInfo brightnessDownButton = findGridButtonInfo(buttons,
+ String.valueOf(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()));
+
+ int clickId = AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.getId();
+ BrightnessInfo brightnessInfo = displayManager.getDisplay(
+ context.getDisplayId()).getBrightnessInfo();
+
+ try {
+ displayManager.setBrightness(context.getDisplayId(), brightnessInfo.brightnessMinimum);
+ TestUtils.waitUntil("Could not change to minimum brightness",
+ TIMEOUT_UI_CHANGE_S,
+ () -> displayManager.getBrightness(context.getDisplayId())
+ == brightnessInfo.brightnessMinimum);
+ brightnessUpButton.performAction(clickId);
+ TestUtils.waitUntil("Did not detect an increase in brightness.",
+ TIMEOUT_UI_CHANGE_S,
+ () -> displayManager.getBrightness(context.getDisplayId())
+ > brightnessInfo.brightnessMinimum);
+
+ displayManager.setBrightness(context.getDisplayId(), brightnessInfo.brightnessMaximum);
+ TestUtils.waitUntil("Could not change to maximum brightness",
+ TIMEOUT_UI_CHANGE_S,
+ () -> displayManager.getBrightness(context.getDisplayId())
+ == brightnessInfo.brightnessMaximum);
+ brightnessDownButton.performAction(clickId);
+ TestUtils.waitUntil("Did not detect a decrease in brightness.",
+ TIMEOUT_UI_CHANGE_S,
+ () -> displayManager.getBrightness(context.getDisplayId())
+ < brightnessInfo.brightnessMaximum);
+ } finally {
+ displayManager.setBrightness(context.getDisplayId(), resetBrightness);
+ closeMenu();
+ }
+ }
+}