Merge "ITS: Check the video profile file format." into tm-dev
diff --git a/apps/CameraITS/utils/lighting_control_utils.py b/apps/CameraITS/utils/lighting_control_utils.py
index a78c658..ad5c521 100644
--- a/apps/CameraITS/utils/lighting_control_utils.py
+++ b/apps/CameraITS/utils/lighting_control_utils.py
@@ -15,7 +15,9 @@
import logging
+import select
import struct
+import sys
import time
import sensor_fusion_utils
@@ -24,6 +26,8 @@
ARDUINO_BRIGHTNESS_MIN = 0
ARDUINO_LIGHT_START_BYTE = 254
+KEYBOARD_ENTRY_WAIT_TIME = 20 # seconds to wait for keyboard entry
+
def set_light_brightness(ch, brightness, serial_port, delay=0):
"""Turn on light to specified brightness.
@@ -96,5 +100,6 @@
if arduino_serial_port:
set_light_brightness(lighting_ch, level, arduino_serial_port, delay=1)
else:
- _ = input(f'Turn {state} lights in rig and hit ENTER to continue.')
+ print(f'Turn {state} lights in rig and hit <ENTER> to continue.')
+ _, _, _ = select.select([sys.stdin], [], [], KEYBOARD_ENTRY_WAIT_TIME)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java
index d3b1866..cfd7b07 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BluetoothToggleActivity.java
@@ -16,11 +16,6 @@
package com.android.cts.verifier.bluetooth;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -32,6 +27,9 @@
import android.view.View.OnClickListener;
import android.widget.ToggleButton;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
/**
* Activity for testing that Bluetooth can be disabled and enabled properly. The activity shows
* a button that toggles Bluetooth by disabling it via {@link BluetoothAdapter#disable()} and
@@ -42,13 +40,12 @@
private static final String TAG = BluetoothToggleActivity.class.getName();
private static final int START_ENABLE_BLUETOOTH_REQUEST = 1;
+ private static final int START_DISABLE_BLUETOOTH_REQUEST = 2;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothBroadcastReceiver mReceiver;
- private ProgressDialog mDisablingDialog;
-
private ToggleButton mToggleButton;
private int mNumDisabledTimes = 0;
@@ -65,9 +62,6 @@
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mReceiver, filter);
- mDisablingDialog = new ProgressDialog(this);
- mDisablingDialog.setMessage(getString(R.string.bt_disabling));
-
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
getPassButton().setEnabled(false);
@@ -87,12 +81,17 @@
}
private void enableBluetooth() {
- mDisablingDialog.dismiss();
mToggleButton.setEnabled(false);
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, START_ENABLE_BLUETOOTH_REQUEST);
}
+ private void disableBluetooth() {
+ mToggleButton.setEnabled(false);
+ Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISABLE);
+ startActivityForResult(intent, START_DISABLE_BLUETOOTH_REQUEST);
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
@@ -102,22 +101,14 @@
mToggleButton.setChecked(enabledBluetooth);
mToggleButton.setEnabled(true);
break;
+ case START_DISABLE_BLUETOOTH_REQUEST:
+ boolean disabledBluetooth = RESULT_OK == resultCode;
+ mToggleButton.setChecked(!disabledBluetooth);
+ mToggleButton.setEnabled(true);
+ break;
}
}
- private void disableBluetooth() {
- mDisablingDialog.show();
- mToggleButton.setEnabled(false);
- if (!mBluetoothAdapter.disable()) {
- mDisablingDialog.dismiss();
- mToggleButton.setEnabled(true);
- new AlertDialog.Builder(this)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.bt_disabling_error)
- .setPositiveButton(android.R.string.ok, null)
- .show();
- }
- }
class BluetoothBroadcastReceiver extends BroadcastReceiver {
@Override
@@ -138,11 +129,6 @@
mNumEnabledTimes++;
}
- if (BluetoothAdapter.STATE_OFF == newState) {
- mDisablingDialog.dismiss();
- mToggleButton.setEnabled(true);
- }
-
mToggleButton.setChecked(mBluetoothAdapter.isEnabled());
getPassButton().setEnabled(mNumDisabledTimes > 0 && mNumEnabledTimes > 0);
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
index 203dc68..f8ec3108 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
@@ -197,14 +197,18 @@
private static final String TV_PROFILE_TYPE_NAME = "com.android.tv.profile";
- // We are allowed 11 minutes before the entire test run fails
- private static final Duration MAX_TEST_DURATION = Duration.ofMinutes(10);
+ // We timeout 10 seconds before the infra would timeout
+ private static final Duration MAX_TEST_DURATION =
+ Duration.ofMillis(
+ Long.parseLong(TestApis.instrumentation().arguments().getString(
+ "timeout_msec", "600000")) - 10000);
private final ExecutorService mTestExecutor = Executors.newSingleThreadExecutor();
private Thread mTestThread;
private final Logger mLogger = Logger.forInstance(this);
public DeviceState() {
+
mLogger.constructor(() -> {
Future<Thread> testThreadFuture = mTestExecutor.submit(Thread::currentThread);
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/device/Device.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/device/Device.java
index b2530e8..79eb2b8 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/device/Device.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/device/Device.java
@@ -43,6 +43,7 @@
try {
ShellCommand.builder("input keyevent")
.addOperand("KEYCODE_WAKEUP")
+ .allowEmptyOutput(true)
.validate(String::isEmpty)
.execute();
} catch (AdbException e) {
diff --git a/hostsidetests/angle/OWNERS b/hostsidetests/angle/OWNERS
index 949f3a7..21522ca 100644
--- a/hostsidetests/angle/OWNERS
+++ b/hostsidetests/angle/OWNERS
@@ -1,7 +1,10 @@
# Bug component: 452972
-timvp@google.com
-alanward@google.com
+abdolrashidi@google.com
+cclao@google.com
+chrisforbes@google.com
cnorthrop@google.com
-courtneygo@google.com
ianelliott@google.com
-tobine@google.com
+lfy@google.com
+romanl@google.com
+vantablack@google.com
+yuxinhu@google.com
diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
index deb05ed..8720426 100644
--- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
+++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
@@ -951,6 +951,11 @@
Classpaths.getClassDefsFromJar(getDevice(), apk).stream()
.map(ClassDef::getType)
.collect(ImmutableSet.toImmutableSet());
+ // b/226559955: The directory paths containing APKs contain the build ID,
+ // so strip out the @BUILD_ID portion.
+ // e.g. /apex/com.android.bluetooth/app/Bluetooth@SC-DEV/Bluetooth.apk ->
+ // /apex/com.android.bluetooth/app/Bluetooth/Bluetooth.apk
+ apk = apk.replaceFirst("@[^/]*", "");
final ImmutableSet<String> burndownClasses =
FULL_APK_IN_APEX_BURNDOWN.getOrDefault(apk, ImmutableSet.of());
final Multimap<String, String> duplicates =
diff --git a/hostsidetests/car_builtin/src/android/car/cts/builtin/ActivityManagerHelperHostTest.java b/hostsidetests/car_builtin/src/android/car/cts/builtin/ActivityManagerHelperHostTest.java
index e41bbc0..8f5c3ed 100644
--- a/hostsidetests/car_builtin/src/android/car/cts/builtin/ActivityManagerHelperHostTest.java
+++ b/hostsidetests/car_builtin/src/android/car/cts/builtin/ActivityManagerHelperHostTest.java
@@ -16,33 +16,47 @@
package android.car.cts.builtin;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.cts.builtin.user.CarInitialUserCommand;
+import android.car.cts.builtin.user.InitializedUsersCommand;
+
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(DeviceJUnit4ClassRunner.class)
public final class ActivityManagerHelperHostTest extends CarBuiltinApiHostCtsBase {
+ private static final int SYSTEM_USER = 0;
+ // The startUserInForeground, startUserInBackground and unlockUser ActivityManagerHelper
+ // APIs are called by InitialUserSetter during device boot.Checking the default users
+ // are properly initialized covers the API testing.
@Test
- public void testStartUser() throws Exception {
- // TODO (b/201005730): implement the test case to test the startUserInBackground()
- // and startUserInForeground() APIs
+ public void testDefaultUserInitialization() throws Exception {
+ InitializedUsersCommand initUsersCommand = new InitializedUsersCommand(getDevice());
+ initUsersCommand.executeWith();
+ List<Integer> initUsers = initUsersCommand.getInitializedUsers();
+
+ ArrayList<Integer> defaultUsers = new ArrayList<>();
+ defaultUsers.add(SYSTEM_USER);
+ if (initUsersCommand.hasHeadlessUser()) {
+ CarInitialUserCommand initialUserCommand = new CarInitialUserCommand(getDevice());
+ initialUserCommand.executeWith();
+ int initialUser = initialUserCommand.getCarInitialUser();
+ defaultUsers.add(initialUser);
+ }
+
+ assertThat(initUsers).containsAtLeastElementsIn(defaultUsers);
}
@Test
public void testStopUserWithDelayedLocking() throws Exception {
- // TODO (b/201005730): implement the test case to test
- // the stopUserWithDelayedLocking() API.
- }
-
- @Test
- public void testUnlockUser() throws Exception {
- // TODO (b/201005730): implement the test case to test the unlockUser() API.
- }
-
- @Test
- public void testStopAllTaskForUser() throws Exception {
- // TODO (b/201005730): implement the test case to test the stopAllTasksForUser() API.
+ // CtsCarHostTestCases:CarGarageModeAtomTests covers the stopUserWithDelayedLocking
+ // API call.
}
}
diff --git a/hostsidetests/car_builtin/src/android/car/cts/builtin/user/CarInitialUserCommand.java b/hostsidetests/car_builtin/src/android/car/cts/builtin/user/CarInitialUserCommand.java
new file mode 100644
index 0000000..f96f04e
--- /dev/null
+++ b/hostsidetests/car_builtin/src/android/car/cts/builtin/user/CarInitialUserCommand.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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.car.cts.builtin.user;
+
+import android.car.cts.builtin.CtsCarShellCommand;
+
+import com.android.tradefed.device.ITestDevice;
+
+public final class CarInitialUserCommand extends CtsCarShellCommand {
+
+ private static final String COMMAND_NAME = "cmd car_service get-initial-user";
+
+ // the value from UserHandler.USER_NULL
+ private static final int INVALID_USER_ID = -10_000;
+
+ private int mCarInitialUser = INVALID_USER_ID;
+
+ public CarInitialUserCommand(ITestDevice device) {
+ super(COMMAND_NAME, device);
+ }
+
+ public int getCarInitialUser() {
+ return mCarInitialUser;
+ }
+
+ @Override
+ protected void parseCommandReturn() throws Exception {
+ mCarInitialUser = Integer.parseInt(mCommandReturn.trim());
+ }
+}
diff --git a/hostsidetests/car_builtin/src/android/car/cts/builtin/user/InitializedUsersCommand.java b/hostsidetests/car_builtin/src/android/car/cts/builtin/user/InitializedUsersCommand.java
new file mode 100644
index 0000000..bb79b35
--- /dev/null
+++ b/hostsidetests/car_builtin/src/android/car/cts/builtin/user/InitializedUsersCommand.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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.car.cts.builtin.user;
+
+import static org.junit.Assert.fail;
+
+import android.car.cts.builtin.CtsCarShellCommand;
+
+import com.android.tradefed.device.ITestDevice;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class InitializedUsersCommand extends CtsCarShellCommand {
+
+ private static final String COMMAND_NAME = "cmd user list -v ";
+ private static final int USER_INFO_MIN_STRING_LENGTH = 20;
+
+ private static final Pattern USER_PATTERN =
+ Pattern.compile(".*id=(\\d+).*type=([^\\s]+).*");
+ private static final int USER_PATTERN_GROUP_ID = 1;
+ private static final int USER_PATTERN_GROUP_TYPE = 2;
+
+ private List<Integer> mInitializedUsers;
+ private boolean mHasHeadlessUser;
+
+ public InitializedUsersCommand(ITestDevice device) {
+ super(COMMAND_NAME, device);
+ }
+
+ public List<Integer> getInitializedUsers() {
+ return mInitializedUsers;
+ }
+
+ public boolean hasHeadlessUser() {
+ return mHasHeadlessUser;
+ }
+
+ @Override
+ protected void parseCommandReturn() throws Exception {
+ mInitializedUsers = new ArrayList<Integer>();
+ mHasHeadlessUser = false;
+ String[] userInfoList = mCommandReturn.split("\n");
+ for (int i = 0; i < userInfoList.length; i++) {
+ if (userInfoList[i].length() > USER_INFO_MIN_STRING_LENGTH) {
+ if (userInfoList[i].contains("INITIALIZED")) {
+ Matcher matcher = USER_PATTERN.matcher(userInfoList[i]);
+ if (!matcher.find()) {
+ fail("parseCommandReturn: no match was found in: " + userInfoList[i]);
+ }
+ int userId = Integer.parseInt(matcher.group(USER_PATTERN_GROUP_ID));
+ mInitializedUsers.add(userId);
+ String type = matcher.group(USER_PATTERN_GROUP_TYPE);
+ if (!mHasHeadlessUser && type.contains("system.HEADLESS")) {
+ mHasHeadlessUser = true;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
index 3c4f75b..319a138 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
@@ -102,10 +102,8 @@
.add("setDefaultSmsApplication")
.add("getPermittedInputMethods")
.add("setPermittedInputMethods")
- .add("getDrawable")
- .add("getDrawableForDensity")
.add("getDevicePolicyManagementRoleHolderPackage")
- .add("getDrawableAsIcon")
+ .add("getResources")
.build();
private static final String LOG_TAG = "ParentProfileTest";
diff --git a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
index 8167e5f..29d9132 100644
--- a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
+++ b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
@@ -74,6 +74,14 @@
}
Log.i(TAG, "Volume at " + percentageVolume + "%");
break;
+ case "android.hdmicec.app.RAISE_VOLUME":
+ audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
+ AudioManager.ADJUST_RAISE, 0);
+ break;
+ case "android.hdmicec.app.LOWER_VOLUME":
+ audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
+ AudioManager.ADJUST_LOWER, 0);
+ break;
case "android.hdmicec.app.SET_VOLUME":
int percentVolume = getIntent().getIntExtra("volumePercent", 50);
int volume = minVolume + ((maxVolume - minVolume) * percentVolume / 100);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/AudioManagerHelper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/AudioManagerHelper.java
index 88091c6..49b7964 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/AudioManagerHelper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/AudioManagerHelper.java
@@ -16,13 +16,13 @@
package android.hdmicec.cts;
+import com.android.tradefed.device.ITestDevice;
+
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import com.android.tradefed.device.ITestDevice;
-
/** Helper class to get DUT audio status using Audio manager app */
public final class AudioManagerHelper {
@@ -66,6 +66,14 @@
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.REPORT_AUDIO_STATUS);
}
+ public static void unmuteDevice(ITestDevice device)
+ throws Exception {
+ // Clear activity
+ device.executeShellCommand(CLEAR_COMMAND);
+ // Start the APK and wait for it to complete.
+ device.executeShellCommand(START_COMMAND + "android.hdmicec.app.UNMUTE");
+ }
+
public static boolean isDeviceMuted(ITestDevice device) throws Exception {
// Clear activity
device.executeShellCommand(CLEAR_COMMAND);
@@ -76,6 +84,20 @@
return (LogHelper.parseDutVolume(device, CLASS) >= 128);
}
+ public static void lowerVolume(ITestDevice device) throws Exception {
+ // Clear activity
+ device.executeShellCommand(CLEAR_COMMAND);
+ // Start the APK and wait for it to complete
+ device.executeShellCommand(START_COMMAND + "android.hdmicec.app.LOWER_VOLUME");
+ }
+
+ public static void raiseVolume(ITestDevice device) throws Exception {
+ // Clear activity
+ device.executeShellCommand(CLEAR_COMMAND);
+ // Start the APK and wait for it to complete
+ device.executeShellCommand(START_COMMAND + "android.hdmicec.app.RAISE_VOLUME");
+ }
+
public static void setDeviceVolume(ITestDevice device, int percentVolume) throws Exception {
// Clear activity
device.executeShellCommand(CLEAR_COMMAND);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecAbsoluteVolumeControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecAbsoluteVolumeControlTest.java
new file mode 100644
index 0000000..c8235d0
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecAbsoluteVolumeControlTest.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2022 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.hdmicec.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Abstract base class for tests for Absolute Volume Control (AVC). Subclasses must call
+ * this class's constructor to specify the device type of the DUT and the System Audio device.
+ * The three valid pairs of (DUT type, System Audio device type) for AVC are as follows:
+ * (Playback, TV); (Playback, Audio System); (TV, Audio System).
+ *
+ * Currently, it is only feasible to test the case where the DUT is a playback device and the
+ * System Audio device is a TV. This is because the CEC adapter responds <Feature Abort> to
+ * <Set Audio Volume Level> when it is started as an Audio System.
+ */
+public abstract class BaseHdmiCecAbsoluteVolumeControlTest extends BaseHdmiCecCtsTest {
+
+ /**
+ * Constructor. The test device type and client params (determining the client's device type)
+ * passed in here determine the behavior of the tests.
+ */
+ public BaseHdmiCecAbsoluteVolumeControlTest(@HdmiCecConstants.CecDeviceType int testDeviceType,
+ String... clientParams) {
+ super(testDeviceType, clientParams);
+ }
+
+ /**
+ * Returns the audio output device being used.
+ */
+ public int getAudioOutputDevice() {
+ if (mTestDeviceType == HdmiCecConstants.CEC_DEVICE_TYPE_TV) {
+ return HdmiCecConstants.DEVICE_OUT_HDMI_ARC;
+ } else {
+ return HdmiCecConstants.DEVICE_OUT_HDMI;
+ }
+ }
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // This setting must be enabled to use AVC. Start with it disabled to ensure that we can
+ // control when the AVC initiation process starts.
+ setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+ HdmiCecConstants.VOLUME_CONTROL_DISABLED);
+
+ // Disable and enable CEC on the DUT to clear its knowledge of device feature support.
+ // If the DUT isn't a TV, simulate a connected sink as well.
+ if (mTestDeviceType == HdmiCecConstants.CEC_DEVICE_TYPE_TV) {
+ getDevice().executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
+ waitForCondition(() -> !isCecAvailable(getDevice()), "Could not disable CEC");
+ getDevice().executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
+ waitForCondition(() -> isCecAvailable(getDevice()), "Could not enable CEC");
+ } else {
+ simulateCecSinkConnected(getDevice(), getTargetLogicalAddress());
+ }
+
+ // Full volume behavior is a prerequisite for AVC. However, we cannot control this
+ // condition from CTS tests or shell due to missing permissions. Therefore, we run these
+ // tests only if it is already true.
+ assumeTrue(isFullVolumeDevice(getAudioOutputDevice()));
+ }
+
+ /**
+ * Requires the device to be able to adopt CEC 2.0 so that it sends <Give Features>.
+ *
+ * Tests that the DUT enables and disables AVC in response to changes in the System Audio
+ * device's support for <Set Audio Volume Level>. In this test, this support status is
+ * communicated through <Report Features> messages.
+ */
+ @Test
+ public void testEnableDisableAvc_cec20_triggeredByReportFeatures() throws Exception {
+ // Enable CEC 2.0
+ setCec20();
+
+ // Enable CEC volume
+ setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+ HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+
+ // Enable System Audio Mode if the System Audio device is an Audio System
+ enableSystemAudioModeIfApplicable();
+
+ // Since CEC 2.0 is enabled, DUT should also use <Give Features> to query AVC support
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.GIVE_FEATURES);
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.SET_AUDIO_VOLUME_LEVEL);
+
+ // System Audio device reports support for <Set Audio Volume Level> via <Report Features>
+ sendReportFeatures(true);
+
+ // DUT queries audio status
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.GIVE_AUDIO_STATUS);
+ checkAbsoluteVolumeControlStatus(false);
+
+ // DUT receives audio status
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+ CecMessage.formatParams(50));
+ checkAbsoluteVolumeControlStatus(true);
+
+ // System Audio device reports no support for <Set Audio Volume Level>
+ sendReportFeatures(false);
+ checkAbsoluteVolumeControlStatus(false);
+ }
+
+ /**
+ * Tests that the DUT enables and disables AVC in response to changes in the System Audio
+ * device's support for <Set Audio Volume Level>. In this test, this support status is
+ * communicated through (the lack of) <Feature Abort> responses to <Set Audio Volume Level>.
+ */
+ @Test
+ public void testEnableDisableAvc_triggeredByAvcSupportChanged() throws Exception {
+ setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+ HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+
+ enableSystemAudioModeIfApplicable();
+
+ // DUT queries AVC support by sending <Set Audio Volume Level>
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.SET_AUDIO_VOLUME_LEVEL);
+
+ // System Audio device does not respond with <Feature Abort>. DUT queries audio status
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.GIVE_AUDIO_STATUS);
+ checkAbsoluteVolumeControlStatus(false);
+
+ // DUT receives audio status
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+ CecMessage.formatParams(50));
+ checkAbsoluteVolumeControlStatus(true);
+
+ // System Audio device responds to <Set Audio Volume Level> with
+ // <Feature Abort>[Unrecognized opcode]
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.FEATURE_ABORT,
+ CecMessage.formatParams(CecOperand.SET_AUDIO_VOLUME_LEVEL + "00"));
+ checkAbsoluteVolumeControlStatus(false);
+ }
+
+ /**
+ * Tests that the DUT enables and disables AVC in response to CEC volume control being
+ * enabled or disabled.
+ */
+ @Test
+ public void testEnableAndDisableAVC_triggeredByVolumeControlSettingChange() throws Exception {
+ enableSystemAudioModeIfApplicable();
+
+ // System audio device reports support for <Set Audio Volume Level>
+ sendReportFeatures(true);
+
+ // Enable CEC volume
+ setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+ HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+
+ // DUT queries audio status
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.GIVE_AUDIO_STATUS);
+ checkAbsoluteVolumeControlStatus(false);
+
+ // DUT receives audio status
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+ CecMessage.formatParams(50));
+ checkAbsoluteVolumeControlStatus(true);
+
+ // CEC volume control is disabled on the DUT
+ setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+ HdmiCecConstants.VOLUME_CONTROL_DISABLED);
+ checkAbsoluteVolumeControlStatus(false);
+ }
+
+ /**
+ * Tests that the DUT enables and disables AVC in response to System Audio mode being
+ * enabled or disabled.
+ *
+ * Only valid when the System Audio device is an Audio System.
+ */
+ @Test
+ public void testEnableDisableAvc_triggeredBySystemAudioModeChange() throws Exception {
+ assumeTrue("Skipping this test for this setup because the System Audio device "
+ + "is not an Audio System.",
+ hdmiCecClient.getSelfDevice().getDeviceType()
+ == HdmiCecConstants.CEC_DEVICE_TYPE_AUDIO_SYSTEM);
+
+ // System audio device reports support for <Set Audio Volume Level>
+ sendReportFeatures(true);
+
+ // CEC volume control is enabled on the DUT
+ setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+ HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+
+ // Enable System Audio Mode
+ broadcastSystemAudioModeMessage(true);
+
+ // DUT queries audio status
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.GIVE_AUDIO_STATUS);
+ checkAbsoluteVolumeControlStatus(false);
+
+ // DUT receives audio status
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+ CecMessage.formatParams(50));
+ checkAbsoluteVolumeControlStatus(true);
+
+ // System Audio Mode is disabled
+ broadcastSystemAudioModeMessage(false);
+ checkAbsoluteVolumeControlStatus(false);
+ }
+
+ /**
+ * Tests that the DUT sends the correct CEC messages when AVC is enabled and Android
+ * initiates volume changes.
+ */
+ @Test
+ public void testOutgoingVolumeUpdates() throws Exception {
+ // Enable AVC
+ setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+ HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+ enableSystemAudioModeIfApplicable();
+ sendReportFeatures(true);
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.GIVE_AUDIO_STATUS);
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+ CecMessage.formatParams(50));
+ checkAbsoluteVolumeControlStatus(true);
+
+ // Calling AudioManager#setStreamVolume should cause the DUT to send
+ // <Set Audio Volume Level> with the new volume level as a parameter
+ AudioManagerHelper.setDeviceVolume(getDevice(), 80);
+ String setAudioVolumeLevelMessage = hdmiCecClient.checkExpectedOutput(
+ hdmiCecClient.getSelfDevice(), CecOperand.SET_AUDIO_VOLUME_LEVEL);
+ assertThat(CecMessage.getParams(setAudioVolumeLevelMessage)).isEqualTo(80);
+
+ // Calling AudioManager#adjustStreamVolume should cause the DUT to send
+ // <User Control Pressed>, <User Control Released>, and <Give Audio Status>
+ AudioManagerHelper.raiseVolume(getDevice());
+ String userControlPressedMessage = hdmiCecClient.checkExpectedOutput(
+ hdmiCecClient.getSelfDevice(), CecOperand.USER_CONTROL_PRESSED);
+ assertThat(CecMessage.getParams(userControlPressedMessage))
+ .isEqualTo(HdmiCecConstants.CEC_KEYCODE_VOLUME_UP);
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.USER_CONTROL_RELEASED);
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.GIVE_AUDIO_STATUS);
+ }
+
+ /**
+ * Tests that the DUT notifies AudioManager when it receives <Report Audio Status> from the
+ * System Audio device.
+ */
+ @Test
+ public void testIncomingVolumeUpdates() throws Exception {
+ // Enable AVC
+ setSettingsValue(HdmiCecConstants.SETTING_VOLUME_CONTROL_ENABLED,
+ HdmiCecConstants.VOLUME_CONTROL_ENABLED);
+ enableSystemAudioModeIfApplicable();
+ sendReportFeatures(true);
+ hdmiCecClient.checkExpectedOutput(hdmiCecClient.getSelfDevice(),
+ CecOperand.GIVE_AUDIO_STATUS);
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+ CecMessage.formatParams(50)); // Volume 50, mute off
+ checkAbsoluteVolumeControlStatus(true);
+
+ // Volume and mute status should match the initial <Report Audio Status>
+ assertApproximateDeviceVolumeAndMute(50, false);
+
+ // Test an incoming <Report Audio Status> that does not mute the device
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+ CecMessage.formatParams(90)); // Volume 90, mute off
+ assertApproximateDeviceVolumeAndMute(90, false);
+
+ // Test an incoming <Report Audio Status> that mutes the device
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_AUDIO_STATUS,
+ CecMessage.formatParams(70 + 0b1000_0000)); // Volume 70, mute on
+ assertApproximateDeviceVolumeAndMute(0, true);
+ }
+
+ /**
+ * Enables System Audio Mode if the System Audio device is an Audio System.
+ */
+ private void enableSystemAudioModeIfApplicable() throws Exception {
+ if (hdmiCecClient.getSelfDevice() == LogicalAddress.AUDIO_SYSTEM) {
+ broadcastSystemAudioModeMessage(true);
+ }
+ }
+
+ /**
+ * Has the CEC client broadcast a message enabling or disabling System Audio Mode.
+ */
+ private void broadcastSystemAudioModeMessage(boolean val) throws Exception {
+ hdmiCecClient.sendCecMessage(
+ hdmiCecClient.getSelfDevice(),
+ LogicalAddress.BROADCAST,
+ CecOperand.SET_SYSTEM_AUDIO_MODE,
+ CecMessage.formatParams(val ? 1 : 0));
+ }
+
+ /**
+ * Has the CEC client send a <Report Features> message expressing support or lack of support for
+ * <Set Audio Volume Level>.
+ */
+ private void sendReportFeatures(boolean setAudioVolumeLevelSupport) throws Exception {
+ String deviceTypeNibble = hdmiCecClient.getSelfDevice() == LogicalAddress.TV
+ ? "80" : "08";
+ String featureSupportNibble = setAudioVolumeLevelSupport ? "01" : "00";
+ hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.REPORT_FEATURES,
+ "06:" + deviceTypeNibble + ":00:" + featureSupportNibble);
+ }
+
+ /**
+ * Checks that the status of Absolute Volume Control on the DUT to match the expected status.
+ * Waits and rechecks a limited number of times if the status does not currently match.
+ */
+ private void checkAbsoluteVolumeControlStatus(boolean enabledStatus) throws Exception {
+ String expectedStatus = enabledStatus ? "enabled" : "disabled";
+ waitForCondition(() -> getAbsoluteVolumeControlStatus() == enabledStatus,
+ "Absolute Volume Control was not " + expectedStatus + " when expected");
+ }
+
+ /**
+ * Returns the state of Absolute Volume Control on the DUT. This is determined by the
+ * volume behavior of the audio output device being used for HDMI audio.
+ */
+ private boolean getAbsoluteVolumeControlStatus() throws Exception {
+ return getDevice()
+ .executeShellCommand("dumpsys hdmi_control | grep mIsAbsoluteVolumeControlEnabled:")
+ .replace("mIsAbsoluteVolumeControlEnabled:", "").trim()
+ .equals("true");
+ }
+
+ /**
+ * Asserts that the DUT's volume (scale: [0, 100]) is within 5 points of an expected volume.
+ * This accounts for small differences due to rounding when converting between volume scales.
+ * Also asserts that the DUT's mute status is equal to {@code expectedMute}.
+ *
+ * Asserting both volume and mute at the same time saves a shell command because both are
+ * conveyed in a single log message.
+ */
+ private void assertApproximateDeviceVolumeAndMute(int expectedVolume, boolean expectedMute)
+ throws Exception {
+ // Raw output is equal to volume out of 100, plus 128 if muted
+ // In practice, if the stream is muted, volume equals 0, so this will be at most 128
+ int rawOutput = AudioManagerHelper.getDutAudioVolume(getDevice());
+
+ int actualVolume = rawOutput % 128;
+ assertWithMessage("Expected DUT to have volume " + expectedVolume
+ + " but was actually " + actualVolume)
+ .that(Math.abs(expectedVolume - actualVolume) <= 5)
+ .isTrue();
+
+ boolean actualMute = rawOutput >= 128;
+ String expectedMuteString = expectedMute ? "muted" : "unmuted";
+ String actualMuteString = actualMute ? "muted" : "unmuted";
+ assertWithMessage("Expected DUT to be " + expectedMuteString
+ + "but was actually " + actualMuteString)
+ .that(expectedMute)
+ .isEqualTo(actualMute);
+ }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
index 2bd8281..83525e9 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
@@ -37,6 +37,7 @@
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -503,6 +504,25 @@
.isEqualTo(wakefulness);
}
+ /**
+ * Checks a given condition once every {@link HdmiCecConstants.SLEEP_TIMESTEP_SECONDS} seconds
+ * until it is true, or {@link HdmiCecConstants.MAX_SLEEP_TIME_SECONDS} seconds have passed.
+ * Triggers an assertion failure if the condition remains false after the time limit.
+ * @param condition Callable that returns whether the condition is met
+ * @param errorMessage The message to print if the condition is false
+ */
+ public void waitForCondition(Callable<Boolean> condition, String errorMessage)
+ throws Exception {
+ int waitTimeSeconds = 0;
+ boolean conditionState;
+ do {
+ TimeUnit.SECONDS.sleep(HdmiCecConstants.SLEEP_TIMESTEP_SECONDS);
+ waitTimeSeconds += HdmiCecConstants.SLEEP_TIMESTEP_SECONDS;
+ conditionState = condition.call();
+ } while (!conditionState && waitTimeSeconds <= HdmiCecConstants.MAX_SLEEP_TIME_SECONDS);
+ assertWithMessage(errorMessage).that(conditionState).isTrue();
+ }
+
public void sendOtp() throws Exception {
ITestDevice device = getDevice();
device.executeShellCommand("cmd hdmi_control onetouchplay");
@@ -551,6 +571,7 @@
throws Exception {
hdmiCecClient.clearClientOutput();
device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
+ waitForCondition(() -> !isCecAvailable(device), "Could not disable CEC");
device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
// When a CEC device has just become available, the CEC adapter isn't able to send it
// messages right away. Therefore we let the first <Give Power Status> message time-out, and
@@ -560,20 +581,36 @@
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
hdmiCecClient.sendCecMessage(LogicalAddress.TV, source, CecOperand.REPORT_POWER_STATUS,
CecMessage.formatParams(HdmiCecConstants.CEC_POWER_STATUS_STANDBY));
- checkIsCecAvailable(device);
+ waitForCondition(() -> isCecAvailable(device),
+ "Simulating that a sink is connected, failed.");
}
- private void checkIsCecAvailable(ITestDevice device) throws Exception {
- boolean isCecAvailable;
- int waitTimeSeconds = 0;
- do {
- TimeUnit.SECONDS.sleep(HdmiCecConstants.SLEEP_TIMESTEP_SECONDS);
- waitTimeSeconds += HdmiCecConstants.SLEEP_TIMESTEP_SECONDS;
- isCecAvailable =
- device.executeShellCommand("dumpsys hdmi_control | grep mIsCecAvailable:")
- .replace("mIsCecAvailable:", "").trim().equals("true");
- } while (!isCecAvailable && waitTimeSeconds <= HdmiCecConstants.MAX_SLEEP_TIME_SECONDS);
- assertWithMessage("Simulating that a sink is connected, failed.")
- .that(isCecAvailable).isTrue();
+ boolean isCecAvailable(ITestDevice device) throws Exception {
+ return device.executeShellCommand("dumpsys hdmi_control | grep mIsCecAvailable:")
+ .replace("mIsCecAvailable:", "").trim().equals("true");
+ }
+
+ /**
+ * Returns whether an audio output device is using full volume behavior by checking if it is in
+ * the "mFullVolumeDevices" line in audio dumpsys. Example: "mFullVolumeDevices=0x400,0x40001".
+ */
+ public boolean isFullVolumeDevice(int audioOutputDevice) throws Exception {
+ String[] splitLine = getDevice().executeShellCommand(
+ "dumpsys audio | grep mFullVolumeDevices").split("=");
+ if (splitLine.length < 2) {
+ // No full volume devices
+ return false;
+ }
+ String[] deviceStrings = splitLine[1].trim().split(",");
+ for (String deviceString : deviceStrings) {
+ try {
+ if (Integer.decode(deviceString) == audioOutputDevice) {
+ return true;
+ }
+ } catch (NumberFormatException e) {
+ // Ignore this device and continue
+ }
+ }
+ return false;
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
index 88362d8..a5be8bf 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
@@ -42,6 +42,7 @@
SYSTEM_AUDIO_MODE_REQUEST(0x70),
GIVE_AUDIO_STATUS(0x71),
SET_SYSTEM_AUDIO_MODE(0x72),
+ SET_AUDIO_VOLUME_LEVEL(0x73),
REPORT_AUDIO_STATUS(0x7a),
GIVE_SYSTEM_AUDIO_MODE_STATUS(0x7d),
SYSTEM_AUDIO_MODE_STATUS(0x7e),
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
index f7c8e14..f1b22f9 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
@@ -156,6 +156,7 @@
// CEC Settings Values
public static final String VOLUME_CONTROL_ENABLED = "1";
+ public static final String VOLUME_CONTROL_DISABLED = "0";
// Power Control Modes for source devices
public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
@@ -208,4 +209,15 @@
// CEC 2.0 Report Feature Bits
public static final int FEATURES_SINK_SUPPORTS_ARC_TX_BIT = 0x4;
public static final int FEATURES_SINK_SUPPORTS_ARC_RX_BIT = 0x2;
+
+ // Audio device types from AudioDeviceInfo
+ public static final int DEVICE_OUT_HDMI = 0x400;
+ public static final int DEVICE_OUT_HDMI_ARC = 0x40000;
+ public static final int DEVICE_OUT_HDMI_EARC = 0x40001;
+
+ // Volume behavior constants from AudioManager
+ public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
+ public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
+ public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecAvcToTvTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecAvcToTvTest.java
new file mode 100644
index 0000000..f601312
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecAvcToTvTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.hdmicec.cts.playback;
+
+import android.hdmicec.cts.BaseHdmiCecAbsoluteVolumeControlTest;
+import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.HdmiCecConstants;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for Absolute Volume Control where the DUT is a Playback device and the
+ * System Audio device is a TV.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class HdmiCecAvcToTvTest extends BaseHdmiCecAbsoluteVolumeControlTest {
+
+ /**
+ * No need to pass in client parameters because the client is started as TV as long as the
+ * DUT is not a TV.
+ */
+ public HdmiCecAvcToTvTest() {
+ super(HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
+ }
+
+ @Rule
+ public RuleChain ruleChain =
+ RuleChain.outerRule(BaseHdmiCecCtsTest.CecRules.requiresCec(this))
+ .around(BaseHdmiCecCtsTest.CecRules.requiresLeanback(this))
+ .around(
+ BaseHdmiCecCtsTest.CecRules.requiresDeviceType(
+ this, HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE))
+ .around(hdmiCecClient);
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAbsoluteVolumeControlFollowerTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAbsoluteVolumeControlFollowerTest.java
new file mode 100644
index 0000000..77dbcb2
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAbsoluteVolumeControlFollowerTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 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.hdmicec.cts.tv;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.hdmicec.cts.AudioManagerHelper;
+import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests TV behavior when it receives <Set Audio Volume Level>.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class HdmiCecAbsoluteVolumeControlFollowerTest extends BaseHdmiCecCtsTest {
+ public HdmiCecAbsoluteVolumeControlFollowerTest() {
+ super(HdmiCecConstants.CEC_DEVICE_TYPE_TV, "-t", "p", "-t", "a");
+ }
+
+ @Rule
+ public RuleChain ruleChain =
+ RuleChain.outerRule(CecRules.requiresCec(this))
+ .around(CecRules.requiresLeanback(this))
+ .around(CecRules.requiresDeviceType(this, HdmiCecConstants.CEC_DEVICE_TYPE_TV))
+ .around(hdmiCecClient);
+
+ /**
+ * Tests that if System Audio Mode is enabled, the TV responds to <Set Audio Volume Level>
+ * with <Feature Abort>[Not in correct mode to respond]
+ */
+ @Test
+ public void testSystemAudioModeOn_respondsFeatureAbort() throws Exception {
+ AudioManagerHelper.unmuteDevice(getDevice());
+
+ int initialDeviceVolume = AudioManagerHelper.getDutAudioVolume(getDevice());
+
+ getDevice().executeShellCommand("cmd hdmi_control setsam on");
+
+ hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_1,
+ CecOperand.SET_AUDIO_VOLUME_LEVEL,
+ CecMessage.formatParams((initialDeviceVolume + 50) % 101));
+
+ // Check that the DUT sent
+ // <Feature Abort>[Set Audio Volume Level, Not in correct mode to respond]
+ String featureAbort = hdmiCecClient.checkExpectedOutput(
+ LogicalAddress.PLAYBACK_1, CecOperand.FEATURE_ABORT);
+ assertThat(CecOperand.getOperand(CecMessage.getParams(featureAbort, 0, 2)))
+ .isEqualTo(CecOperand.SET_AUDIO_VOLUME_LEVEL);
+ assertThat(CecMessage.getParams(featureAbort, 2, 4)).isEqualTo(1);
+
+ // Check that volume did not change
+ assertThat(AudioManagerHelper.getDutAudioVolume(getDevice()))
+ .isEqualTo(initialDeviceVolume);
+ }
+
+ /**
+ * Tests that if System Audio Mode is disabled, the TV updates its volume after receiving
+ * <Set Audio Volume Level>
+ */
+ @Test
+ public void testSystemAudioModeOff_updatesVolume() throws Exception {
+ // Wait for CEC adapter to enable System Audio Mode before turning it off
+ hdmiCecClient.checkExpectedMessageFromClient(LogicalAddress.AUDIO_SYSTEM,
+ LogicalAddress.TV, CecOperand.SYSTEM_AUDIO_MODE_STATUS);
+
+ getDevice().executeShellCommand("cmd hdmi_control setsam off");
+
+ AudioManagerHelper.unmuteDevice(getDevice());
+
+ int initialDeviceVolume = AudioManagerHelper.getDutAudioVolume(getDevice());
+ try {
+ int volumeToSet = (initialDeviceVolume + 50) % 101;
+ hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_1,
+ CecOperand.SET_AUDIO_VOLUME_LEVEL,
+ CecMessage.formatParams(volumeToSet));
+
+ // Check that no <Feature Abort> was sent
+ hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.PLAYBACK_1,
+ CecOperand.FEATURE_ABORT);
+
+ // Check that device volume is within 5 points of the expected volume.
+ // This accounts for rounding errors due to volume scale conversions.
+ int deviceVolume = AudioManagerHelper.getDutAudioVolume(getDevice());
+ assertWithMessage("Expected DUT to have volume " + volumeToSet
+ + " but was actually " + deviceVolume)
+ .that(Math.abs(volumeToSet - deviceVolume) <= 5)
+ .isTrue();
+ } finally {
+ AudioManagerHelper.setDeviceVolume(getDevice(), initialDeviceVolume);
+ }
+ }
+}
diff --git a/tests/ServiceKillTest/Android.bp b/tests/ServiceKillTest/Android.bp
new file mode 100644
index 0000000..a9f32c5
--- /dev/null
+++ b/tests/ServiceKillTest/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2021 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.
+
+android_test {
+ name: "CtsServiceKillTestCases",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.rules",
+ "ub-uiautomator",
+ "compatibility-device-util-axt",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "app/src/**/*.java",
+ ],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ platform_apis: true,
+}
diff --git a/tests/ServiceKillTest/AndroidManifest.xml b/tests/ServiceKillTest/AndroidManifest.xml
new file mode 100644
index 0000000..e32d385
--- /dev/null
+++ b/tests/ServiceKillTest/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.cts.servicekilltest" >
+
+ <queries>
+ <package android:name="com.android.cts.servicekilltestapp" />
+ </queries>
+
+ <application android:label="Cts Service Kill Test">
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:functionalTest="true"
+ android:targetPackage="com.android.cts.servicekilltest"
+ android:label="Service Kill Tests"/>
+</manifest>
diff --git a/tests/ServiceKillTest/AndroidTest.xml b/tests/ServiceKillTest/AndroidTest.xml
new file mode 100644
index 0000000..29247c4
--- /dev/null
+++ b/tests/ServiceKillTest/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 Service Kill test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsServiceKillTestApp.apk" />
+ <option name="test-file-name" value="CtsServiceKillTestCases.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.cts.servicekilltest" />
+ <option name="runtime-hint" value="180m" />
+ <option name="test-timeout" value="14400000" />
+ </test>
+
+</configuration>
diff --git a/tests/ServiceKillTest/OWNERS b/tests/ServiceKillTest/OWNERS
new file mode 100644
index 0000000..553f8d0
--- /dev/null
+++ b/tests/ServiceKillTest/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316234
+suprabh@google.com
diff --git a/tests/ServiceKillTest/app/Android.bp b/tests/ServiceKillTest/app/Android.bp
new file mode 100644
index 0000000..834ce40
--- /dev/null
+++ b/tests/ServiceKillTest/app/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 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.
+
+android_test_helper_app {
+ name: "CtsServiceKillTestApp",
+ defaults: ["cts_support_defaults"],
+ sdk_version: "current",
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ srcs: ["src/**/*.java"],
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/tests/ServiceKillTest/app/AndroidManifest.xml b/tests/ServiceKillTest/app/AndroidManifest.xml
new file mode 100644
index 0000000..e3f45df
--- /dev/null
+++ b/tests/ServiceKillTest/app/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.cts.servicekilltestapp">
+
+ <queries>
+ <package android:name="com.android.cts.servicekilltest" />
+ </queries>
+
+ <application>
+ <service android:name=".ServiceKillTestService" android:exported="true"/>
+ </application>
+
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java b/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java
new file mode 100644
index 0000000..56384ad
--- /dev/null
+++ b/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2021 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.cts.servicekilltestapp;
+
+import android.annotation.SuppressLint;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.util.Log;
+import android.os.SystemClock;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public class ServiceKillTestService extends Service {
+
+ /**
+ * Execution times for each measure
+ */
+ public static final long HOUR_IN_MS = TimeUnit.HOURS.toMillis(1);
+ public static final long ALARM_REPEAT_MS = TimeUnit.MINUTES.toMillis(10);
+ public static final long ALARM_REPEAT_MARGIN_MS = TimeUnit.SECONDS.toMillis(30);
+ public static final long PERSIST_BENCHMARK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(30);
+ public static final long WORK_REPEAT_MS = TimeUnit.SECONDS.toMillis(10);
+ public static final long MAIN_REPEAT_MS = TimeUnit.SECONDS.toMillis(10);
+
+ /**
+ * Actions and extras
+ */
+ public static final String TEST_CASE_PACKAGE_NAME = "com.android.cts.servicekilltest";
+ public static final String TEST_APP_PACKAGE_NAME = TEST_CASE_PACKAGE_NAME + "app";
+ public static final String ACTION_START = TEST_CASE_PACKAGE_NAME + ".ACTION_START";
+ public static final String ACTION_STOP = TEST_CASE_PACKAGE_NAME + ".ACTION_STOP";
+ public static final String ACTION_RESULT = TEST_CASE_PACKAGE_NAME + ".ACTION_RESULT";
+ private static final String ACTION_ALARM = TEST_CASE_PACKAGE_NAME + ".ACTION_ALARM";
+ public static final String EXTRA_TEST_ID = "test_id";
+
+
+ public static final String APP = "CTSServiceKillTest";
+ public static final String TAG = "ServiceKillTest";
+
+ public static String NOTIFICATION_CHANNEL_FOREGROUND = "foreground";
+
+ private PowerManager.WakeLock mWakeLock;
+ private Handler mHandler;
+ private ScheduledExecutorService mScheduledExecutor;
+ private ExecutorService mExecutor;
+
+
+ private Benchmark mCurrentBenchmark;
+
+ private boolean mStarted = false;
+
+ private ScheduledFuture<?> mScheduledFuture;
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private BroadcastReceiver mAlarmReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null && ACTION_ALARM.equals(intent.getAction())) {
+ logDebug("Alarm");
+ mCurrentBenchmark.addEvent(Benchmark.Measure.ALARM, SystemClock.elapsedRealtime());
+ scheduleAlarm();
+ saveBenchmarkIfRequired(mCurrentBenchmark);
+ }
+
+ }
+ };
+
+ private Runnable mMainRunnable = new Runnable() {
+ @Override
+ public void run() {
+ logDebug("Main");
+ mCurrentBenchmark.addEvent(Benchmark.Measure.MAIN, SystemClock.elapsedRealtime());
+ mHandler.postDelayed(this, MAIN_REPEAT_MS);
+ saveBenchmarkIfRequired(mCurrentBenchmark);
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ logDebug("onCreate()");
+ PowerManager powerManager = getSystemService(PowerManager.class);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, APP + "::" + TAG);
+ mWakeLock.acquire();
+ mExecutor = Executors.newSingleThreadExecutor();
+ mHandler = new Handler();
+ startForeground();
+ loadBenchmarkAsync(benchmark -> {
+ logDebug("Loading Benchmark " + benchmark);
+
+ if (benchmark == null || !benchmark.isRunning()) {
+ mCurrentBenchmark = new Benchmark();
+ logDebug("New Benchmark " + mCurrentBenchmark);
+ } else {
+ mCurrentBenchmark = benchmark;
+ }
+ startBenchmark();
+ });
+
+ }
+
+ private String getTestId(Intent i) {
+ return i == null ? null : i.getStringExtra(EXTRA_TEST_ID);
+ }
+
+ private boolean isAction(Intent i, String action) {
+ if (i != null && action != null) {
+ return action.equals(i.getAction());
+ }
+ return i == null && action == null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ startForeground();
+
+ final String id = getTestId(intent);
+ if (id != null) {
+ logDebug("onStartCommand TEST " + id + " action " + intent.getAction());
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mCurrentBenchmark != null) {
+ if (isAction(intent, ACTION_START)) {
+ logDebug("Starting TEST " + id);
+ mCurrentBenchmark.startTest(id);
+ } else if (isAction(intent, ACTION_STOP)) {
+ logDebug("Stopping TEST " + id);
+ sendResult(id, mCurrentBenchmark.finishTest(id));
+
+ if (!mCurrentBenchmark.isRunning()) {
+ logDebug("No TEST running, stopping benchmark");
+ saveBenchmarkAsync(mCurrentBenchmark, () -> {
+ logDebug("No TEST running, stopping service");
+ stopSelf();
+ });
+ }
+ } else if (isAction(intent, ACTION_RESULT)) {
+ logDebug("Getting results for TEST " + id);
+ sendResult(id, mCurrentBenchmark.getAllResults(id));
+ }
+ } else {
+ mHandler.postDelayed(this, 1000);
+ }
+ }
+ });
+ } else {
+ Log.w(TAG, "Ignoring start request without test ID");
+ }
+ return START_STICKY;
+ }
+
+ private void sendResult(String testId, Map<Benchmark.Measure, Float> result) {
+ logDebug("Sending result");
+ Intent intent = new Intent(ACTION_RESULT);
+ intent.putExtra(EXTRA_TEST_ID, testId);
+ intent.setPackage(TEST_CASE_PACKAGE_NAME);
+ if (result != null) {
+ for (Benchmark.Measure measure : result.keySet()) {
+ intent.putExtra(measure.name(), result.get(measure));
+ logDebug("Result " + measure.name() + "=" + result.get(measure));
+ }
+ }
+ sendBroadcast(intent);
+ }
+
+ private void startForeground() {
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+
+ NotificationChannel notificationChannel =
+ new NotificationChannel(NOTIFICATION_CHANNEL_FOREGROUND, TAG,
+ NotificationManager.IMPORTANCE_LOW);
+ notificationManager.createNotificationChannel(notificationChannel);
+ startForeground(12, new Notification.Builder(this, NOTIFICATION_CHANNEL_FOREGROUND)
+ .setSmallIcon(android.R.drawable.ic_media_play)
+ .setChannelId(NOTIFICATION_CHANNEL_FOREGROUND)
+ .setContentText("Foreground Service Kill Test Running").build());
+ }
+
+ private PendingIntent getAlarmIntent() {
+ Intent i = new Intent(ACTION_ALARM);
+ i.setPackage(getPackageName());
+ return PendingIntent.getBroadcast(this, 0, i,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ }
+
+ private void scheduleAlarm() {
+ long alarmTime = SystemClock.elapsedRealtime() + ALARM_REPEAT_MS;
+ logDebug(String.format("Scheduling alarm at %d", alarmTime));
+ AlarmManager alarmManager = getSystemService(AlarmManager.class);
+ if (alarmManager.canScheduleExactAlarms()) {
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ alarmTime,
+ getAlarmIntent()
+ );
+ } else {
+ Log.w(TAG, "Cannot schedule exact alarms");
+ }
+ }
+
+ private void cancelAlarm() {
+ logDebug("Cancel alarm ");
+ AlarmManager alarmManager = getSystemService(AlarmManager.class);
+ alarmManager.cancel(getAlarmIntent());
+ }
+
+
+ private void startBenchmark() {
+ mHandler.post(mMainRunnable);
+ scheduleAlarm();
+ registerReceiver(mAlarmReceiver, new IntentFilter(ACTION_ALARM));
+ mScheduledExecutor = Executors.newScheduledThreadPool(1);
+ mScheduledFuture = mScheduledExecutor.scheduleAtFixedRate(() -> {
+ try {
+ logDebug("Work");
+ long now = SystemClock.elapsedRealtime();
+ mHandler.post(() -> {
+ mCurrentBenchmark.addEvent(Benchmark.Measure.WORK, now);
+ saveBenchmarkIfRequired(mCurrentBenchmark);
+ });
+ } catch (Throwable t) {
+ Log.e(TAG, "Error in scheduled execution ", t);
+ }
+ }, WORK_REPEAT_MS, WORK_REPEAT_MS, TimeUnit.MILLISECONDS);
+
+ mStarted = true;
+ }
+
+ private void stopBenchmark() {
+ try {
+ unregisterReceiver(mAlarmReceiver);
+ } catch (Exception e) {
+ Log.w(TAG, "Receiver not registered", e);
+ }
+ cancelAlarm();
+ mHandler.removeCallbacks(mMainRunnable);
+ if (mScheduledExecutor != null) {
+ mScheduledExecutor.shutdown();
+ if (mScheduledFuture.isDone()) {
+ try {
+ mScheduledFuture.get();
+ } catch (CancellationException e) {
+ } catch (Exception e) {
+ Log.e(TAG, "Error in scheduled execution ", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ logDebug("onDestroy()");
+ if (mStarted) {
+ stopBenchmark();
+ }
+ mExecutor.shutdown();
+ mWakeLock.release();
+ }
+
+ private static Intent getServiceIntent(Context context) {
+ return new Intent(context, ServiceKillTestService.class);
+ }
+
+ private void loadBenchmarkAsync(Consumer<Benchmark> consumer) {
+ mExecutor.execute(() -> {
+ final Benchmark benchmark = loadBenchmark();
+ mHandler.post(() -> {
+ consumer.accept(benchmark);
+ });
+ });
+ }
+
+ public interface Consumer<T> {
+ void accept(T consumable);
+ }
+
+ private synchronized Benchmark loadBenchmark() {
+ ObjectInputStream in = null;
+ try {
+ in = new ObjectInputStream(openFileInput(TAG));
+ return (Benchmark) in.readObject();
+ } catch (FileNotFoundException e) {
+ logDebug("File not found");
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "Class no found", e);
+ } catch (IOException e) {
+ Log.e(TAG, "I/O error", e);
+ } finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot close benchmark file", e);
+ }
+ }
+ return null;
+ }
+
+
+ private synchronized void clearBenchmark() {
+ deleteFile(TAG);
+ }
+
+ private void saveBenchmarkIfRequired(Benchmark benchmark) {
+ if (SystemClock.elapsedRealtime() - benchmark.lastPersisted > PERSIST_BENCHMARK_TIMEOUT_MS) {
+ saveBenchmarkAsync(benchmark, null);
+ }
+ }
+
+
+ private void saveBenchmarkAsync(Benchmark benchmark, Runnable runnable) {
+ final byte[] bytes = benchmark.toBytes();
+
+ mExecutor.execute(() -> {
+ save(bytes);
+ mHandler.post(() -> {
+ logDebug("SAVED " + benchmark);
+ benchmark.setPersisted();
+ if (runnable != null) {
+ runnable.run();
+ }
+ });
+ });
+ }
+
+ private synchronized void save(byte[] bytes) {
+ FileOutputStream fileOut = null;
+ try {
+ fileOut = openFileOutput(TAG, MODE_PRIVATE);
+ fileOut.write(bytes);
+ fileOut.flush();
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "File not found", e);
+ } catch (IOException e) {
+ Log.e(TAG, "I/O error", e);
+ } finally {
+ try {
+ if (fileOut != null) {
+ fileOut.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot close benchmark file", e);
+ }
+ }
+ }
+
+ private static class Range {
+ public final long from;
+ public final long to;
+
+ public Range(long from, long to) {
+ if (to < from || to < 0 || from < 0) {
+ throw new IllegalArgumentException("FROM: " + from + " before TO: " + to);
+ }
+ this.from = from;
+ this.to = to;
+ }
+
+ public boolean inRange(long timestamp) {
+ return timestamp >= from && timestamp <= to;
+ }
+
+ public long getDuration() {
+ return to - from + 1;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[%d-%d]", from, to);
+ }
+ }
+
+ public static class Benchmark implements Serializable {
+
+ public enum Measure {
+ TOTAL,
+ WORK(WORK_REPEAT_MS),
+ MAIN(MAIN_REPEAT_MS),
+ ALARM(ALARM_REPEAT_MS + ALARM_REPEAT_MARGIN_MS);
+
+ private final long interval;
+
+ Measure() {
+ interval = -1;
+ }
+
+ Measure(long interval) {
+ this.interval = interval;
+ }
+ }
+
+ private static final long serialVersionUID = -2939643983335136263L;
+
+ private long lastPersisted = -1;
+
+ private long startTime;
+
+ public Benchmark() {
+ startTime = SystemClock.elapsedRealtime();
+ }
+
+ private final Map<Measure, List<Long>> eventMap = new HashMap<>();
+ private final Map<String, Long> tests = new HashMap<>();
+
+ public boolean isRunning() {
+ return tests.size() > 0;
+ }
+
+ public void startTest(String id) {
+ if (!tests.containsKey(id)) {
+ tests.put(id, SystemClock.elapsedRealtime());
+ }
+ }
+
+ public Map<Measure, Float> finishTest(String id) {
+ if (tests.containsKey(id)) {
+ Long startTime = tests.remove(id);
+ return getAllResults(new Range(startTime, SystemClock.elapsedRealtime()));
+ }
+ Log.w(TAG, "Missing results for test " + id);
+ return null;
+ }
+
+ public Map<Measure, Float> getAllResults(String id) {
+ if (tests.containsKey(id)) {
+ Long startTime = tests.get(id);
+ return getAllResults(new Range(startTime, SystemClock.elapsedRealtime()));
+ }
+ return null;
+ }
+
+ private Map<Measure, Float> getAllResults(Range range) {
+ Map<Measure, Float> results = new HashMap<>();
+ for (Measure measure : Measure.values()) {
+ results.put(measure, getResult(measure, range));
+ }
+ return results;
+ }
+
+ public long getLastPersisted() {
+ return lastPersisted;
+ }
+
+ public void setPersisted() {
+ this.lastPersisted = SystemClock.elapsedRealtime();
+ }
+
+ private List<Long> filter(List<Long> source, Range range) {
+ List<Long> result = new ArrayList<>(source);
+
+ if (range == null) {
+ return source;
+ }
+
+ Iterator<Long> i = result.iterator();
+ while (i.hasNext()) {
+ if (!range.inRange(i.next())) {
+ i.remove();
+ }
+ }
+ return result;
+ }
+
+ public void addEvent(Measure measure, long timestamp) {
+ List<Long> events = getEvents(measure);
+ events.add(timestamp);
+ if (!eventMap.containsKey(measure)) {
+ eventMap.put(measure, events);
+ }
+ }
+
+ public void addEvent(Measure measure) {
+ addEvent(measure, SystemClock.elapsedRealtime());
+ }
+
+ private List<Long> getEvents(Measure measure) {
+ List<Long> events = eventMap.get(measure);
+ return events == null ? new ArrayList<>() : events;
+ }
+
+ public float getResult(Measure measure) {
+ return getResult(measure, null);
+ }
+
+ public float getResult(Measure measure, Range range) {
+
+ if (measure == Measure.TOTAL) {
+ return (getResult(Measure.WORK, range) + (2 * getResult(Measure.ALARM, range)) +
+ getResult(Measure.MAIN, range)) / 4f;
+ }
+
+ List<Long> events = filter(getEvents(measure), range);
+
+ return Math
+ .min(1, events.size() / (getDuration(range) / (float) measure.interval));
+ }
+
+ private long getDuration() {
+ return SystemClock.elapsedRealtime() - startTime;
+ }
+
+ private long getDuration(Range range) {
+ if (range == null) {
+ return getDuration();
+ }
+ return range.getDuration();
+ }
+
+ private byte[] toBytes() {
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(bos);
+ out.writeObject(this);
+ out.flush();
+ out.close();
+ bos.close();
+ return bos.toByteArray();
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot serialize benchmark: " + this, e);
+ return null;
+ }
+ }
+
+ @SuppressLint("DefaultLocale")
+ @Override
+ public String toString() {
+ return toReportString().replaceAll("\n", "");
+ }
+
+ @SuppressLint("DefaultLocale")
+ public String toReportString() {
+ return String
+ .format("Benchmark TIME: %tT TESTS: %d \n\nMAIN:\n%.1f%% %d \n\nWORK:\n%.1f%%" +
+ " %d \n\nALARM:\n%.1f%% %d \n\n%s",
+ getDuration() - TimeZone.getDefault().getOffset(0),
+ tests.size(), getResult(Measure.MAIN) * 100,
+ getEvents(Measure.MAIN).size(), getResult(Measure.WORK) * 100,
+ getEvents(Measure.WORK).size(), getResult(Measure.ALARM) * 100,
+ getEvents(Measure.ALARM).size(), isRunning() ? "RUNNING..." :
+ getResult(Measure.TOTAL) >= 0.9f ? "TEST PASSED!" :
+ "TEST FAILED!");
+ }
+ }
+
+ public static void logDebug(String s) {
+ if (DEBUG) {
+ Log.d(TAG, s);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ServiceKillTest/src/com/android/cts/servicekilltest/ServiceKillTests.java b/tests/ServiceKillTest/src/com/android/cts/servicekilltest/ServiceKillTests.java
new file mode 100644
index 0000000..91922dd
--- /dev/null
+++ b/tests/ServiceKillTest/src/com/android/cts/servicekilltest/ServiceKillTests.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2021 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.cts.servicekilltest;
+
+import static com.android.cts.servicekilltestapp.ServiceKillTestService.Benchmark.Measure.*;
+import static com.android.cts.servicekilltestapp.ServiceKillTestService.logDebug;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.runner.RunWith;
+import org.junit.After;
+
+import com.android.cts.servicekilltestapp.ServiceKillTestService;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+import android.os.BatteryManager;
+import android.os.SystemClock;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.Before;
+
+import android.support.test.uiautomator.UiDevice;
+
+@AppModeFull
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class ServiceKillTests {
+
+ private static final boolean REQUIRE_UNPLUGGED = true;
+ private static final float DEFAULT_THRESHOLD = 0.9f;
+ private static final long DEFAULT_DURATION = TimeUnit.HOURS.toMinutes(3);
+
+ @Before
+ public void setUp() throws Exception {
+ if (REQUIRE_UNPLUGGED) {
+ setChargingAndCheck(false);
+ } else {
+ setCharging(false);
+ }
+ }
+
+ @Test
+ public void testAll() throws Exception {
+ test("testAll", DEFAULT_DURATION, TOTAL);
+ }
+
+ /**
+ * Examples on how you can configure the tests for different purposes
+ */
+ @Test
+ @Ignore
+ public void testAlarmStrict() throws Exception {
+ test("testAlarm", DEFAULT_DURATION, ALARM, 0.99f);
+ }
+
+ @Test
+ @Ignore
+ public void testMainLong() throws Exception {
+ test("testMain", DEFAULT_DURATION * 2, MAIN);
+ }
+
+ @Test
+ @Ignore
+ public void testWork() throws Exception {
+ test("testWork", DEFAULT_DURATION, WORK);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ setCharging(true);
+ }
+
+
+ private void test(String testName, long runTimeMinutes,
+ ServiceKillTestService.Benchmark.Measure measure) throws Exception {
+ test(testName, runTimeMinutes, measure, DEFAULT_THRESHOLD);
+ }
+
+ private void test(String testName, long runTimeMinutes,
+ ServiceKillTestService.Benchmark.Measure measure, float passThreshold)
+ throws Exception {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ final long startTime = SystemClock.elapsedRealtime();
+
+ final String testId = generateTestId(testName);
+
+ logDebug("Testing " + testId + " " + runTimeMinutes + "min " + measure + " th " +
+ passThreshold);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null) {
+ logDebug("onReceive testId " +
+ intent.getStringExtra(ServiceKillTestService.EXTRA_TEST_ID) + " " +
+ intent.getAction());
+ if (intent.hasExtra(ServiceKillTestService.EXTRA_TEST_ID) && testId.equals(
+ intent.getStringExtra(ServiceKillTestService.EXTRA_TEST_ID))) {
+ float result = intent.getFloatExtra(measure.name(), 0);
+ logDebug("result " + result);
+ assertTrue("Test '" + testName + "' for '" + measure + "' did not reach " +
+ passThreshold, result >= passThreshold);
+ latch.countDown();
+ }
+ }
+ }
+ };
+ context.registerReceiver(receiver, new IntentFilter(ServiceKillTestService.ACTION_RESULT));
+
+ startTest(context, testId);
+
+ try {
+ logDebug("waiting " + runTimeMinutes + "min ");
+ while (SystemClock.elapsedRealtime() - startTime <
+ TimeUnit.MINUTES.toMillis(runTimeMinutes)) {
+ latch.await(5, TimeUnit.MINUTES);
+ if (REQUIRE_UNPLUGGED) {
+ setChargingAndCheck(false);
+ }
+ }
+ } catch (InterruptedException e) {
+ stopTest(context, testId);
+ throw e;
+ }
+
+ stopTest(context, testId);
+
+ logDebug("Waiting max 1 more minutes");
+ latch.await(1, TimeUnit.MINUTES);
+
+ if (latch.getCount() > 0) {
+ fail("Did not get test result from ServiceKillTestService");
+ }
+
+ context.unregisterReceiver(receiver);
+ }
+
+ private Intent getServiceIntent(String action, String testId) {
+ Intent i = new Intent(action);
+ i.putExtra(ServiceKillTestService.EXTRA_TEST_ID, testId);
+ i.setComponent(new ComponentName(ServiceKillTestService.TEST_APP_PACKAGE_NAME,
+ ServiceKillTestService.class.getName()));
+ return i;
+ }
+
+ private void startService(Context context, String testId, String action) {
+ logDebug("service " + testId + " " + action);
+ context.startForegroundService(getServiceIntent(action, testId));
+ }
+
+ private void startTest(Context context, String testId) {
+ startService(context, testId, ServiceKillTestService.ACTION_START);
+ }
+
+ private void stopTest(Context context, String testId) {
+ startService(context, testId, ServiceKillTestService.ACTION_STOP);
+ }
+
+ private String generateTestId(String testName) {
+ return testName + ":" + this.hashCode() + ":" + SystemClock.elapsedRealtime();
+ }
+
+
+ private void setChargingAndCheck(final boolean charging) throws Exception {
+ final BatteryManager bm =
+ InstrumentationRegistry.getTargetContext().getSystemService(BatteryManager.class);
+ logDebug("isCharging " + bm.isCharging() + " target " + charging);
+
+ if (charging != bm.isCharging()) {
+ setCharging(charging);
+ PollingCheck.waitFor(TimeUnit.SECONDS.toMillis(20),
+ () -> charging == bm.isCharging(), "Setting charging timeout");
+ }
+ }
+
+ private void setCharging(final boolean charging) throws Exception {
+ if (charging) {
+ logDebug("Setting CHARGING ");
+ executeAndLog("dumpsys battery reset");
+ } else {
+ logDebug("Setting UNPLUG ");
+ executeAndLog("dumpsys battery unplug");
+ executeAndLog("dumpsys battery set status "
+ + BatteryManager.BATTERY_STATUS_DISCHARGING);
+ }
+ }
+
+ private String executeAndLog(String command) throws IOException {
+ final String output =
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .executeShellCommand(command).trim();
+ logDebug("command: '" + command + "', output: '" + output + "'");
+ return output;
+ }
+
+}
\ No newline at end of file
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
index 4fc8ca2..e9fb255 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
@@ -151,6 +151,9 @@
return editorInfo != null ? editorInfo.privateImeOptions : null;
})));
+ assertTrue(RunOnMainUtils.getOnMain(sInstrumentation,
+ inputMethod::getCurrentInputStarted));
+
final InputMethod.AccessibilityInputConnection connection =
inputMethod.getCurrentInputConnection();
assertNotNull(connection);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityInputConnectionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityInputConnectionTest.java
new file mode 100644
index 0000000..ea512f3
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityInputConnectionTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts;
+
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+
+import static org.junit.Assert.assertTrue;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibilityservice.InputMethod;
+import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
+import android.accessibilityservice.cts.utils.AsyncUtils;
+import android.accessibilityservice.cts.utils.InputConnectionSplitter;
+import android.accessibilityservice.cts.utils.NoOpInputConnection;
+import android.accessibilityservice.cts.utils.RunOnMainUtils;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.LargeTest;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests for {@link InputMethod.AccessibilityInputConnection}.
+ */
+@LargeTest
+@AppModeFull
+@RunWith(AndroidJUnit4.class)
+public final class AccessibilityInputConnectionTest {
+ private static Instrumentation sInstrumentation;
+ private static UiAutomation sUiAutomation;
+
+ private static StubImeAccessibilityService sStubImeAccessibilityService;
+
+ private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+ new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ private AtomicReference<InputConnection> mLastInputConnectionSpy = new AtomicReference<>();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
+ @BeforeClass
+ public static void oneTimeSetup() throws Exception {
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sUiAutomation = sInstrumentation.getUiAutomation();
+ sInstrumentation
+ .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ sStubImeAccessibilityService = enableService(StubImeAccessibilityService.class);
+ }
+
+ @AfterClass
+ public static void postTestTearDown() {
+ sStubImeAccessibilityService.disableSelfAndRemove();
+ sUiAutomation.destroy();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ final String markerValue = "Test-" + SystemClock.elapsedRealtimeNanos();
+ final CountDownLatch startInputLatch = new CountDownLatch(1);
+ sStubImeAccessibilityService.setOnStartInputCallback(((editorInfo, restarting) -> {
+ if (editorInfo != null && TextUtils.equals(markerValue, editorInfo.privateImeOptions)) {
+ startInputLatch.countDown();
+ }
+ }));
+
+ final AccessibilityEndToEndActivity activity = launchActivityAndWaitForItToBeOnscreen(
+ sInstrumentation, sUiAutomation, mActivityRule);
+
+ final LinearLayout layout = (LinearLayout) activity.findViewById(R.id.edittext).getParent();
+ sInstrumentation.runOnMainSync(() -> {
+ final EditText editText = new EditText(activity) {
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+ final InputConnection ic = super.onCreateInputConnection(editorInfo);
+ // For some reasons, Mockito.spy() for real Framework classes did not work...
+ // Use NoOpInputConnection/InputConnectionSplitter instead.
+ final InputConnection spy = Mockito.spy(new NoOpInputConnection());
+ mLastInputConnectionSpy.set(spy);
+ return new InputConnectionSplitter(ic, spy);
+ }
+ };
+ editText.setPrivateImeOptions(markerValue);
+ layout.addView(editText);
+ editText.requestFocus();
+ });
+
+ // Wait until EditorInfo#privateImeOptions becomes the expected marker value.
+ assertTrue("time out waiting for input to start",
+ startInputLatch.await(AsyncUtils.DEFAULT_TIMEOUT_MS, MILLISECONDS));
+ }
+
+ private InputMethod.AccessibilityInputConnection getInputConnection() {
+ return RunOnMainUtils.getOnMain(
+ sInstrumentation,
+ () -> sStubImeAccessibilityService.getInputMethod().getCurrentInputConnection());
+ }
+
+ private InputConnection resetAndGetLastInputConnectionSpy() {
+ final InputConnection spy = mLastInputConnectionSpy.get();
+ Mockito.reset(spy);
+ return spy;
+ }
+
+ @Test
+ public void testCommitText() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ ic.commitText("test", 1, null);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+ .commitText("test", 1, null);
+ }
+
+ @Test
+ public void testSetSelection() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ ic.setSelection(1, 2);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS)).setSelection(1, 2);
+ }
+
+ @Test
+ public void testGetSurroundingText() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ ic.getSurroundingText(1, 2, InputConnection.GET_TEXT_WITH_STYLES);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+ .getSurroundingText(1, 2, InputConnection.GET_TEXT_WITH_STYLES);
+ }
+
+ @Test
+ public void testDeleteSurroundingText() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ ic.deleteSurroundingText(2, 1);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+ .deleteSurroundingText(2, 1);
+ }
+
+ @Test
+ public void testSendKeyEvent() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ final long eventTime = SystemClock.uptimeMillis();
+ final KeyEvent keyEvent = new KeyEvent(eventTime, eventTime,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
+
+ ic.sendKeyEvent(keyEvent);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+ .sendKeyEvent(keyEvent);
+ }
+
+ @Test
+ public void testPerformEditorAction() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ ic.performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+ .performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
+ }
+
+ @Test
+ public void testPerformContextMenuAction() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ ic.performContextMenuAction(android.R.id.selectAll);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+ .performContextMenuAction(android.R.id.selectAll);
+ }
+
+ @Test
+ public void testGetCursorCapsMode() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ ic.getCursorCapsMode(InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+ .getCursorCapsMode(InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
+ }
+
+ @Test
+ public void testClearMetaKeyStates() {
+ final InputMethod.AccessibilityInputConnection ic = getInputConnection();
+ final InputConnection spy = resetAndGetLastInputConnectionSpy();
+
+ ic.clearMetaKeyStates(KeyEvent.META_SHIFT_ON);
+ Mockito.verify(spy, Mockito.timeout(AsyncUtils.DEFAULT_TIMEOUT_MS))
+ .clearMetaKeyStates(KeyEvent.META_SHIFT_ON);
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java
index 6bfc66b..77e194e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubImeAccessibilityService.java
@@ -38,6 +38,17 @@
private CountDownLatch mSelectionChangeLatch = null;
private int mSelectionTarget = INVALID;
+ @FunctionalInterface
+ public interface OnStartInputCallback {
+ void onStartInput(EditorInfo editorInfo, boolean restarting);
+ }
+
+ private OnStartInputCallback mOnStartInputCallback;
+
+ public void setOnStartInputCallback(OnStartInputCallback callback) {
+ mOnStartInputCallback = callback;
+ }
+
public void setStartInputCountDownLatch(CountDownLatch latch) {
mStartInputLatch = latch;
}
@@ -77,6 +88,9 @@
mStartInputLatch.countDown();
mStartInputLatch = null;
}
+ if (mOnStartInputCallback != null) {
+ mOnStartInputCallback.onStartInput(attribute, restarting);
+ }
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/InputConnectionSplitter.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/InputConnectionSplitter.java
new file mode 100644
index 0000000..4e45522
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/InputConnectionSplitter.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts.utils;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
+
+/**
+ * A special version of {@link InputConnectionWrapper} so that you can forward method invocations
+ * to another {@link InputConnection}.
+ *
+ * <p>Useful for mocking/spying.</p>
+ */
+public final class InputConnectionSplitter extends InputConnectionWrapper {
+ private final InputConnection mForNotify;
+
+ public InputConnectionSplitter(InputConnection target, InputConnection forNotify) {
+ super(target, false);
+ mForNotify = forNotify;
+ }
+
+ @Override
+ public CharSequence getTextBeforeCursor(int n, int flags) {
+ mForNotify.getTextBeforeCursor(n, flags);
+ return super.getTextBeforeCursor(n, flags);
+ }
+
+ @Override
+ public CharSequence getTextAfterCursor(int n, int flags) {
+ mForNotify.getTextBeforeCursor(n, flags);
+ return super.getTextBeforeCursor(n, flags);
+ }
+
+ @Override
+ public CharSequence getSelectedText(int flags) {
+ mForNotify.getSelectedText(flags);
+ return super.getSelectedText(flags);
+ }
+
+ @Override
+ public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ mForNotify.getSurroundingText(beforeLength, afterLength, flags);
+ return super.getSurroundingText(beforeLength, afterLength, flags);
+ }
+
+ @Override
+ public int getCursorCapsMode(int reqModes) {
+ mForNotify.getCursorCapsMode(reqModes);
+ return super.getCursorCapsMode(reqModes);
+ }
+
+ @Override
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ mForNotify.getExtractedText(request, flags);
+ return super.getExtractedText(request, flags);
+ }
+
+ @Override
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ mForNotify.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ return super.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ }
+
+ @Override
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ mForNotify.deleteSurroundingText(beforeLength, afterLength);
+ return super.deleteSurroundingText(beforeLength, afterLength);
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ mForNotify.setComposingText(text, newCursorPosition);
+ return super.setComposingText(text, newCursorPosition);
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence text,
+ int newCursorPosition, TextAttribute textAttribute) {
+ mForNotify.setComposingText(text, newCursorPosition, textAttribute);
+ return super.setComposingText(text, newCursorPosition, textAttribute);
+ }
+
+ @Override
+ public boolean setComposingRegion(int start, int end) {
+ mForNotify.setComposingRegion(start, end);
+ return super.setComposingRegion(start, end);
+ }
+
+ @Override
+ public boolean setComposingRegion(int start, int end, TextAttribute textAttribute) {
+ mForNotify.setComposingRegion(start, end, textAttribute);
+ return super.setComposingRegion(start, end, textAttribute);
+ }
+
+ @Override
+ public boolean finishComposingText() {
+ mForNotify.finishComposingText();
+ return super.finishComposingText();
+ }
+
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ mForNotify.commitText(text, newCursorPosition);
+ return super.commitText(text, newCursorPosition);
+ }
+
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition,
+ TextAttribute textAttribute) {
+ mForNotify.commitText(text, newCursorPosition, textAttribute);
+ return super.commitText(text, newCursorPosition, textAttribute);
+ }
+
+ @Override
+ public boolean commitCompletion(CompletionInfo text) {
+ mForNotify.commitCompletion(text);
+ return super.commitCompletion(text);
+ }
+
+ @Override
+ public boolean commitCorrection(CorrectionInfo correctionInfo) {
+ mForNotify.commitCorrection(correctionInfo);
+ return super.commitCorrection(correctionInfo);
+ }
+
+ @Override
+ public boolean setSelection(int start, int end) {
+ mForNotify.setSelection(start, end);
+ return super.setSelection(start, end);
+ }
+
+ @Override
+ public boolean performEditorAction(int editorAction) {
+ mForNotify.performEditorAction(editorAction);
+ return super.performEditorAction(editorAction);
+ }
+
+ @Override
+ public boolean performContextMenuAction(int id) {
+ mForNotify.performContextMenuAction(id);
+ return super.performContextMenuAction(id);
+ }
+
+ @Override
+ public boolean beginBatchEdit() {
+ mForNotify.beginBatchEdit();
+ return super.beginBatchEdit();
+ }
+
+ @Override
+ public boolean endBatchEdit() {
+ mForNotify.endBatchEdit();
+ return super.endBatchEdit();
+ }
+
+ @Override
+ public boolean sendKeyEvent(KeyEvent event) {
+ mForNotify.sendKeyEvent(event);
+ return super.sendKeyEvent(event);
+
+ }
+
+ @Override
+ public boolean clearMetaKeyStates(int states) {
+ mForNotify.clearMetaKeyStates(states);
+ return super.clearMetaKeyStates(states);
+ }
+
+ @Override
+ public boolean reportFullscreenMode(boolean enabled) {
+ mForNotify.reportFullscreenMode(enabled);
+ return super.reportFullscreenMode(enabled);
+ }
+
+ @Override
+ public boolean performSpellCheck() {
+ mForNotify.performSpellCheck();
+ return super.performSpellCheck();
+ }
+
+ @Override
+ public boolean performPrivateCommand(String action, Bundle data) {
+ mForNotify.performPrivateCommand(action, data);
+ return super.performPrivateCommand(action, data);
+ }
+
+ @Override
+ public boolean requestCursorUpdates(int cursorUpdateMode) {
+ mForNotify.requestCursorUpdates(cursorUpdateMode);
+ return super.requestCursorUpdates(cursorUpdateMode);
+ }
+
+ @Override
+ public Handler getHandler() {
+ mForNotify.getHandler();
+ return super.getHandler();
+ }
+
+ @Override
+ public void closeConnection() {
+ mForNotify.closeConnection();
+ super.closeConnection();
+ }
+
+ @Override
+ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ mForNotify.commitContent(inputContentInfo, flags, opts);
+ return super.commitContent(inputContentInfo, flags, opts);
+ }
+
+ @Override
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
+ mForNotify.setImeConsumesInput(imeConsumesInput);
+ return super.setImeConsumesInput(imeConsumesInput);
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/NoOpInputConnection.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/NoOpInputConnection.java
new file mode 100644
index 0000000..c79827c
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/NoOpInputConnection.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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.accessibilityservice.cts.utils;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
+
+/**
+ * A no-op implementation of {@link InputConnection}.
+ *
+ * <p>Useful for mocking/spying.</p>
+ */
+public class NoOpInputConnection implements InputConnection {
+
+ @Override
+ public CharSequence getTextBeforeCursor(int n, int flags) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTextAfterCursor(int n, int flags) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getSelectedText(int flags) {
+ return null;
+ }
+
+ @Override
+ public int getCursorCapsMode(int reqModes) {
+ return 0;
+ }
+
+ @Override
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ return null;
+ }
+
+ @Override
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ return false;
+ }
+
+ @Override
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ return false;
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ return false;
+ }
+
+ @Override
+ public boolean setComposingRegion(int start, int end) {
+ return false;
+ }
+
+ @Override
+ public boolean finishComposingText() {
+ return false;
+ }
+
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ return false;
+ }
+
+ @Override
+ public boolean commitCompletion(CompletionInfo text) {
+ return false;
+ }
+
+ @Override
+ public boolean commitCorrection(CorrectionInfo correctionInfo) {
+ return false;
+ }
+
+ @Override
+ public boolean setSelection(int start, int end) {
+ return false;
+ }
+
+ @Override
+ public boolean performEditorAction(int editorAction) {
+ return false;
+ }
+
+ @Override
+ public boolean performContextMenuAction(int id) {
+ return false;
+ }
+
+ @Override
+ public boolean beginBatchEdit() {
+ return false;
+ }
+
+ @Override
+ public boolean endBatchEdit() {
+ return false;
+ }
+
+ @Override
+ public boolean sendKeyEvent(KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean clearMetaKeyStates(int states) {
+ return false;
+ }
+
+ @Override
+ public boolean reportFullscreenMode(boolean enabled) {
+ return false;
+ }
+
+ @Override
+ public boolean performPrivateCommand(String action, Bundle data) {
+ return false;
+ }
+
+ @Override
+ public boolean requestCursorUpdates(int cursorUpdateMode) {
+ return false;
+ }
+
+ @Override
+ public boolean requestCursorUpdates(int cursorUpdateMode, int cursorUpdateFilter) {
+ return false;
+ }
+
+ @Override
+ public Handler getHandler() {
+ return null;
+ }
+
+ @Override
+ public void closeConnection() {
+ }
+
+ @Override
+ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ return false;
+ }
+}
diff --git a/tests/appsearch/aidl/com/android/cts/appsearch/ICommandReceiver.aidl b/tests/appsearch/aidl/com/android/cts/appsearch/ICommandReceiver.aidl
index 25a202e..fdbc84f 100644
--- a/tests/appsearch/aidl/com/android/cts/appsearch/ICommandReceiver.aidl
+++ b/tests/appsearch/aidl/com/android/cts/appsearch/ICommandReceiver.aidl
@@ -24,6 +24,8 @@
List<String> globalGet(in String packageName, in String databaseName, in String namespace,
in String id);
+ List<String> globalGetSchema(String packageName, String databaseName);
+
boolean indexGloballySearchableDocument(in String databaseName, in String namespace,
in String id, in List<Bundle> permissionBundles);
diff --git a/tests/appsearch/helper-app/src/com/android/cts/appsearch/helper/AppSearchTestService.java b/tests/appsearch/helper-app/src/com/android/cts/appsearch/helper/AppSearchTestService.java
index 38a52c4..4bc004d 100644
--- a/tests/appsearch/helper-app/src/com/android/cts/appsearch/helper/AppSearchTestService.java
+++ b/tests/appsearch/helper-app/src/com/android/cts/appsearch/helper/AppSearchTestService.java
@@ -21,9 +21,11 @@
import android.app.Service;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.AppSearchSessionShim;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByDocumentIdRequest;
+import android.app.appsearch.GetSchemaResponse;
import android.app.appsearch.GlobalSearchSessionShim;
import android.app.appsearch.PutDocumentsRequest;
import android.app.appsearch.SearchResultsShim;
@@ -119,6 +121,24 @@
}
}
+ public List<String> globalGetSchema(String packageName, String databaseName) {
+ try {
+ GetSchemaResponse response =
+ mGlobalSearchSessionShim.getSchema(packageName, databaseName).get();
+ if (response == null || response.getSchemas().isEmpty()) {
+ return null;
+ }
+ List<String> schemas = new ArrayList(response.getSchemas().size());
+ for (AppSearchSchema schema : response.getSchemas()) {
+ schemas.add(schema.toString());
+ }
+ return schemas;
+ } catch (Exception e) {
+ Log.e(TAG, "Error retrieving global schema.", e);
+ return null;
+ }
+ }
+
@Override
public boolean indexGloballySearchableDocument(
String databaseName, String namespace, String id, List<Bundle> permissionBundles) {
diff --git a/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
index 4ff0bcc..d558ae0 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
@@ -29,6 +29,7 @@
import android.app.appsearch.AppSearchSessionShim;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByDocumentIdRequest;
+import android.app.appsearch.GetSchemaResponse;
import android.app.appsearch.GlobalSearchSessionShim;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.PutDocumentsRequest;
@@ -684,6 +685,147 @@
}
@Test
+ public void testGlobalGetSchema_packageAccess_defaultAccess() throws Exception {
+ // 1. Create a schema in the test with default (no) access.
+ mDb.setSchema(
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .build())
+ .get();
+
+ // 2. Neither PKG_A nor PKG_B should be able to retrieve the schema.
+ List<String> schemaStrings = getSchemaAsPackage(PKG_A);
+ assertThat(schemaStrings).isNull();
+
+ schemaStrings = getSchemaAsPackage(PKG_B);
+ assertThat(schemaStrings).isNull();
+ }
+
+ @Test
+ public void testGlobalGetSchema_packageAccess_singleAccess() throws Exception {
+ // 1. Create a schema in the test with access granted to PKG_A, but not PKG_B.
+ mDb.setSchema(
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .setSchemaTypeVisibilityForPackage(
+ AppSearchEmail.SCHEMA_TYPE,
+ /*visible=*/ true,
+ new PackageIdentifier(PKG_A, PKG_A_CERT_SHA256))
+ .build())
+ .get();
+
+ // 2. Only PKG_A should be able to retrieve the schema.
+ List<String> schemaStrings = getSchemaAsPackage(PKG_A);
+ assertThat(schemaStrings).containsExactly(AppSearchEmail.SCHEMA.toString());
+
+ schemaStrings = getSchemaAsPackage(PKG_B);
+ assertThat(schemaStrings).isNull();
+ }
+
+ @Test
+ public void testGlobalGetSchema_packageAccess_multiAccess() throws Exception {
+ // 1. Create a schema in the test with access granted to PKG_A and PKG_B.
+ mDb.setSchema(
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .setSchemaTypeVisibilityForPackage(
+ AppSearchEmail.SCHEMA_TYPE,
+ /*visible=*/ true,
+ new PackageIdentifier(PKG_A, PKG_A_CERT_SHA256))
+ .setSchemaTypeVisibilityForPackage(
+ AppSearchEmail.SCHEMA_TYPE,
+ /*visible=*/ true,
+ new PackageIdentifier(PKG_B, PKG_B_CERT_SHA256))
+ .build())
+ .get();
+
+ // 2. Both packages should be able to retrieve the schema.
+ List<String> schemaStrings = getSchemaAsPackage(PKG_A);
+ assertThat(schemaStrings).containsExactly(AppSearchEmail.SCHEMA.toString());
+
+ schemaStrings = getSchemaAsPackage(PKG_B);
+ assertThat(schemaStrings).containsExactly(AppSearchEmail.SCHEMA.toString());
+ }
+
+ @Test
+ public void testGlobalGetSchema_packageAccess_revokeAccess() throws Exception {
+ // 1. Create a schema in the test with access granted to PKG_A.
+ mDb.setSchema(
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .setSchemaTypeVisibilityForPackage(
+ AppSearchEmail.SCHEMA_TYPE,
+ /*visible=*/ true,
+ new PackageIdentifier(PKG_A, PKG_A_CERT_SHA256))
+ .build())
+ .get();
+
+ // 2. Now revoke that access.
+ mDb.setSchema(
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .setSchemaTypeVisibilityForPackage(
+ AppSearchEmail.SCHEMA_TYPE,
+ /*visible=*/ false,
+ new PackageIdentifier(PKG_A, PKG_A_CERT_SHA256))
+ .build())
+ .get();
+
+ // 3. PKG_A should NOT be able to retrieve the schema.
+ List<String> schemaStrings = getSchemaAsPackage(PKG_A);
+ assertThat(schemaStrings).isNull();
+ }
+
+ @Test
+ public void testGlobalGetSchema_globalAccess_singleAccess() throws Exception {
+ // 1. Index documents for PKG_A and PKG_B. This will set the schema for each with the
+ // corresponding access set.
+ indexGloballySearchableDocument(PKG_A, DB_NAME, NAMESPACE_NAME, "id1");
+ indexNotGloballySearchableDocument(PKG_B, DB_NAME, NAMESPACE_NAME, "id1");
+
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> {
+ mGlobalSearchSession =
+ GlobalSearchSessionShimImpl.createGlobalSearchSessionAsync(
+ mContext).get();
+
+ // 2. The schema for PKG_A should be retrievable, but PKG_B should not be.
+ GetSchemaResponse response =
+ mGlobalSearchSession.getSchema(PKG_A, DB_NAME).get();
+ assertThat(response.getSchemas()).hasSize(1);
+
+ response = mGlobalSearchSession.getSchema(PKG_B, DB_NAME).get();
+ assertThat(response.getSchemas()).isEmpty();
+ },
+ READ_GLOBAL_APP_SEARCH_DATA);
+ }
+
+ @Test
+ public void testGlobalGetSchema_globalAccess_multiAccess() throws Exception {
+ // 1. Index documents for PKG_A and PKG_B. This will set the schema for each with the
+ // corresponding access set.
+ indexGloballySearchableDocument(PKG_A, DB_NAME, NAMESPACE_NAME, "id1");
+ indexGloballySearchableDocument(PKG_B, DB_NAME, NAMESPACE_NAME, "id1");
+
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> {
+ mGlobalSearchSession =
+ GlobalSearchSessionShimImpl.createGlobalSearchSessionAsync(
+ mContext).get();
+
+ // 2. The schema for both PKG_A and PKG_B should be retrievable.
+ GetSchemaResponse response =
+ mGlobalSearchSession.getSchema(PKG_A, DB_NAME).get();
+ assertThat(response.getSchemas()).hasSize(1);
+
+ response = mGlobalSearchSession.getSchema(PKG_B, DB_NAME).get();
+ assertThat(response.getSchemas()).hasSize(1);
+ },
+ READ_GLOBAL_APP_SEARCH_DATA);
+ }
+
+
+ @Test
public void testReportSystemUsage() throws Exception {
// Insert schema
mDb.setSchema(new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
@@ -882,6 +1024,17 @@
assertThat(observer.getDocumentChanges()).isEmpty();
}
+ private List<String> getSchemaAsPackage(String pkg) throws Exception {
+ GlobalSearchSessionPlatformCtsTest.TestServiceConnection serviceConnection =
+ bindToHelperService(pkg);
+ try {
+ ICommandReceiver commandReceiver = serviceConnection.getCommandReceiver();
+ return commandReceiver.globalGetSchema(mContext.getPackageName(), DB_NAME);
+ } finally {
+ serviceConnection.unbind();
+ }
+ }
+
private void assertPackageCannotAccess(String pkg) throws Exception {
GlobalSearchSessionPlatformCtsTest.TestServiceConnection serviceConnection =
bindToHelperService(pkg);
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
index 209385c..1707e39 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CameraPolicyTest.java
@@ -17,6 +17,7 @@
package android.devicepolicy.cts;
import static android.Manifest.permission.CAMERA;
+import static android.content.pm.PackageManager.FEATURE_CAMERA;
import static com.google.common.truth.Truth.assertThat;
@@ -35,6 +36,7 @@
import com.android.bedstead.harrier.DeviceState;
import com.android.bedstead.harrier.annotations.EnsureHasPermission;
import com.android.bedstead.harrier.annotations.Postsubmit;
+import com.android.bedstead.harrier.annotations.RequireFeature;
import com.android.bedstead.harrier.annotations.enterprise.CanSetPolicyTest;
import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
import com.android.bedstead.harrier.annotations.enterprise.PolicyAppliesTest;
@@ -52,6 +54,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+@RequireFeature(FEATURE_CAMERA)
@RunWith(BedsteadJUnit4.class)
public final class CameraPolicyTest {
@ClassRule
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
index bfb00d1..f1694b8 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
@@ -28,8 +28,6 @@
import static com.android.queryable.queries.ActivityQuery.activity;
import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
-import static com.google.common.truth.Truth.assertThat;
-
import android.app.admin.DevicePolicyManager;
import android.app.admin.ManagedProfileProvisioningParams;
import android.app.admin.ProvisioningException;
@@ -50,6 +48,7 @@
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.users.UserReference;
+import com.android.bedstead.nene.utils.Poll;
import com.android.bedstead.remotedpc.RemoteDpc;
import com.android.bedstead.testapp.TestApp;
import com.android.bedstead.testapp.TestAppInstance;
@@ -116,8 +115,11 @@
profile = sDevicePolicyManager.createAndProvisionManagedProfile(
MANAGED_PROFILE_PROVISIONING_PARAMS);
- assertThat(TestApis.packages().installedForUser(UserReference.of(profile)))
- .contains(Package.of(roleHolderApp.packageName()));
+ UserReference userReference = UserReference.of(profile);
+ Poll.forValue(() -> TestApis.packages().installedForUser(userReference))
+ .toMeet(packages -> packages.contains(Package.of(roleHolderApp.packageName())))
+ .errorOnFail("Role holder package not installed on the managed profile.")
+ .await();
} finally {
if (profile != null) {
TestApis.users().find(profile).remove();
@@ -149,8 +151,11 @@
/* adminExtras= */ null,
/* flags= */ 0);
- assertThat(TestApis.packages().installedForUser(UserReference.of(managedUser)))
- .contains(Package.of(roleHolderApp.packageName()));
+ UserReference userReference = UserReference.of(managedUser);
+ Poll.forValue(() -> TestApis.packages().installedForUser(userReference))
+ .toMeet(packages -> packages.contains(Package.of(roleHolderApp.packageName())))
+ .errorOnFail("Role holder package not installed on the managed user.")
+ .await();
} finally {
if (managedUser != null) {
TestApis.users().find(managedUser).remove();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NearbyAppStreamingPolicyTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/NearbyAppStreamingPolicyTest.java
index 591c078..87b0efc4 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NearbyAppStreamingPolicyTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NearbyAppStreamingPolicyTest.java
@@ -25,7 +25,6 @@
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.Postsubmit;
import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
import com.android.bedstead.harrier.annotations.enterprise.PolicyAppliesTest;
import com.android.bedstead.harrier.policies.NearbyAppStreamingPolicy;
@@ -50,14 +49,12 @@
}
@PolicyAppliesTest(policy = NearbyAppStreamingPolicy.class)
- @Postsubmit(reason = "new test")
public void getNearbyAppStreamingPolicy_defaultToSameManagedAccountOnly() {
assertThat(mDevicePolicyManager.getNearbyAppStreamingPolicy())
.isEqualTo(DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY);
}
@PolicyAppliesTest(policy = NearbyAppStreamingPolicy.class)
- @Postsubmit(reason = "new test")
public void setNearbyAppStreamingPolicy_policyApplied_works() {
int originalPolicy = mDevicePolicyManager.getNearbyAppStreamingPolicy();
@@ -73,7 +70,6 @@
}
@CannotSetPolicyTest(policy = NearbyAppStreamingPolicy.class)
- @Postsubmit(reason = "new test")
public void setNearbyAppStreamingPolicy_policyIsNotAllowedToBeSet_throwsException() {
assertThrows(SecurityException.class, () ->
mDevicePolicyManager.setNearbyAppStreamingPolicy(
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NearbyNotificationStreamingPolicyTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/NearbyNotificationStreamingPolicyTest.java
index f50d985..c5d9ca1 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NearbyNotificationStreamingPolicyTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NearbyNotificationStreamingPolicyTest.java
@@ -25,7 +25,6 @@
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.Postsubmit;
import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
import com.android.bedstead.harrier.annotations.enterprise.PolicyAppliesTest;
import com.android.bedstead.harrier.policies.NearbyNotificationStreamingPolicy;
@@ -50,14 +49,12 @@
}
@PolicyAppliesTest(policy = NearbyNotificationStreamingPolicy.class)
- @Postsubmit(reason = "new test")
public void getNearbyNotificationStreamingPolicy_defaultToSameManagedAccountOnly() {
assertThat(mDevicePolicyManager.getNearbyNotificationStreamingPolicy())
.isEqualTo(DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY);
}
@PolicyAppliesTest(policy = NearbyNotificationStreamingPolicy.class)
- @Postsubmit(reason = "new test")
public void setNearbyNotificationStreamingPolicy_policyApplied_works() {
int originalPolicy = mDevicePolicyManager.getNearbyNotificationStreamingPolicy();
@@ -73,7 +70,6 @@
}
@CannotSetPolicyTest(policy = NearbyNotificationStreamingPolicy.class)
- @Postsubmit(reason = "new test")
public void setNearbyAppStreamingPolicy_policyIsNotAllowedToBeSet_throwsException() {
assertThrows(SecurityException.class, () ->
mDevicePolicyManager.setNearbyNotificationStreamingPolicy(
diff --git a/tests/framework/base/locale/OWNERS b/tests/framework/base/locale/OWNERS
index 694a4ac..0e13191 100644
--- a/tests/framework/base/locale/OWNERS
+++ b/tests/framework/base/locale/OWNERS
@@ -1,3 +1,4 @@
# Bug template url: https://b.corp.google.com/issues/new?component=1082762&template=1601534
pratyushmore@google.com
-goldmanj@google.com
\ No newline at end of file
+goldmanj@google.com
+ankitavyas@google.com
diff --git a/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java b/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
index 23143fc..9fdf472 100644
--- a/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
+++ b/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
@@ -26,6 +26,7 @@
import static android.localemanager.cts.util.LocaleConstants.INSTALLER_APP_CREATION_INFO_PROVIDER_ACTION;
import static android.localemanager.cts.util.LocaleConstants.INSTALLER_APP_MAIN_ACTIVITY;
import static android.localemanager.cts.util.LocaleConstants.INSTALLER_PACKAGE;
+import static android.localemanager.cts.util.LocaleConstants.NON_EXISTENT_PACKAGE;
import static android.localemanager.cts.util.LocaleConstants.TEST_APP_BROADCAST_INFO_PROVIDER_ACTION;
import static android.localemanager.cts.util.LocaleConstants.TEST_APP_BROADCAST_RECEIVER;
import static android.localemanager.cts.util.LocaleConstants.TEST_APP_CONFIG_CHANGED_INFO_PROVIDER_ACTION;
@@ -472,6 +473,36 @@
assertEquals(LocaleList.getEmptyLocaleList(), sLocaleManager.getApplicationLocales());
}
+ @Test(expected = NullPointerException.class)
+ public void testGetApplicationLocales_nullPackageName_throwsNPE() {
+ sLocaleManager.getApplicationLocales(/* appPackageName= */ null);
+ fail("Expected NullPointerException due to null package name argument.");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetApplicationLocales_invalidPackageName_throwsIllegalArgumentException() {
+ sLocaleManager.getApplicationLocales(NON_EXISTENT_PACKAGE);
+ fail("Expected IllegalArgumentException due to invalid package.");
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testSetApplicationLocales_nullPackageName_throwsNPE() {
+ sLocaleManager.setApplicationLocales(/* appPackageName= */ null, DEFAULT_APP_LOCALES);
+ fail("Expected NullPointerException due to null package name argument.");
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testSetApplicationLocales_nullLocales_throwsNPE() {
+ sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, /* locales= */ null);
+ fail("Expected NullPointerException due to null locales argument.");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetApplicationLocales_invalidPackageName_throwsIllegalArgumentException() {
+ sLocaleManager.setApplicationLocales(NON_EXISTENT_PACKAGE, DEFAULT_APP_LOCALES);
+ fail("Expected IllegalArgumentException due to invalid package.");
+ }
+
/**
* Verifies that the locales are correctly set for calling(instrumentation) app
* by fetching locales of the app with a binder call.
diff --git a/tests/framework/base/locale/util/src/android/localemanager/cts/util/LocaleConstants.java b/tests/framework/base/locale/util/src/android/localemanager/cts/util/LocaleConstants.java
index fd94bba..dcbe09d 100644
--- a/tests/framework/base/locale/util/src/android/localemanager/cts/util/LocaleConstants.java
+++ b/tests/framework/base/locale/util/src/android/localemanager/cts/util/LocaleConstants.java
@@ -38,6 +38,8 @@
public static final String INSTALLER_PACKAGE = "android.localemanager.cts.installer";
+ public static final String NON_EXISTENT_PACKAGE = "android.localemanager.nonexistentapp";
+
public static final ComponentName TEST_APP_MAIN_ACTIVITY = new ComponentName(TEST_APP_PACKAGE,
TEST_APP_PACKAGE + ".MainActivity");
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
index 3e49a2d..0377c7a 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
@@ -20,8 +20,8 @@
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.UNEVEN_CONTAINERS_DEFAULT_SPLIT_RATIO;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.assertValidSplit;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertNotVisible;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForFillsTask;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForVisible;
import static org.junit.Assert.assertTrue;
@@ -91,7 +91,7 @@
mReportedDisplayMetrics.setSize(new Size((int) (originalDisplaySize.getWidth() * 0.9),
originalDisplaySize.getHeight()));
waitForFillsTask(secondaryActivity);
- assertTrue(waitForVisible(primaryActivity, false));
+ waitAndAssertNotVisible(primaryActivity);
// Return the display to its original size and verify that the activities are split
secondaryActivity.resetBoundsChangeCounter();
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
index 2adc1b7..ff16142 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
@@ -24,7 +24,7 @@
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.EMBEDDED_ACTIVITY_ID;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityCrossUidInSplit;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityCrossUidInSplit_expectFail;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -122,7 +122,7 @@
// Start an activity that will attempt to embed TestActivityKnownEmbeddingCerts
startActivityNewTask(SIGNED_EMBEDDING_ACTIVITY);
- assertTrue(waitForResumed(EMBEDDED_ACTIVITY_ID));
+ waitAndAssertResumed(EMBEDDED_ACTIVITY_ID);
TestActivityWithId embeddedActivity = getResumedActivityById(EMBEDDED_ACTIVITY_ID);
assertNotNull(embeddedActivity);
assertTrue(mActivityEmbeddingComponent.isActivityEmbedded(embeddedActivity));
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
index 69ac897..c1fa777 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
@@ -22,8 +22,8 @@
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifyNotSplit;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.verifyFillsTask;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForFinishing;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertFinishing;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed;
import static androidx.window.extensions.embedding.SplitRule.FINISH_ADJACENT;
import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
@@ -129,7 +129,7 @@
.preventSplitActivities().setFinishPrimaryWithSecondary(FINISH_NEVER).start();
// Verify the paired finish behavior
activityPair.second.finish();
- assertTrue(waitForResumed(activityPair.first));
+ waitAndAssertResumed(activityPair.first);
}
/**
@@ -145,7 +145,7 @@
.preventSplitActivities().setFinishPrimaryWithSecondary(FINISH_ADJACENT).start();
// Verify the paired finish behavior
activityPair.second.finish();
- assertTrue(waitForResumed(activityPair.first));
+ waitAndAssertResumed(activityPair.first);
}
/**
@@ -161,7 +161,7 @@
.preventSplitActivities().setFinishPrimaryWithSecondary(FINISH_ALWAYS).start();
// Verify the paired finish behavior
activityPair.second.finish();
- assertTrue(waitForFinishing(activityPair.first));
+ waitAndAssertFinishing(activityPair.first);
}
/**
@@ -194,7 +194,7 @@
.setFinishPrimaryWithSecondary(FINISH_ADJACENT).start();
// Verify the paired finish behavior
activityPair.second.finish();
- assertTrue(waitForFinishing(activityPair.first));
+ waitAndAssertFinishing(activityPair.first);
}
/**
@@ -209,7 +209,7 @@
.setFinishPrimaryWithSecondary(FINISH_ALWAYS).start();
// Verify the paired finish behavior
activityPair.second.finish();
- assertTrue(waitForFinishing(activityPair.first));
+ waitAndAssertFinishing(activityPair.first);
}
/**
@@ -224,7 +224,7 @@
.preventSplitActivities().setFinishSecondaryWithPrimary(FINISH_NEVER).start();
// Verify the paired finish behavior
activityPair.first.finish();
- assertTrue(waitForResumed(activityPair.second));
+ waitAndAssertResumed(activityPair.second);
}
/**
@@ -240,7 +240,7 @@
.preventSplitActivities().setFinishSecondaryWithPrimary(FINISH_ADJACENT).start();
// Verify the paired finish behavior
activityPair.first.finish();
- assertTrue(waitForResumed(activityPair.second));
+ waitAndAssertResumed(activityPair.second);
}
/**
@@ -256,7 +256,7 @@
.preventSplitActivities().setFinishSecondaryWithPrimary(FINISH_ALWAYS).start();
// Verify the paired finish behavior
activityPair.first.finish();
- assertTrue(waitForFinishing(activityPair.second));
+ waitAndAssertFinishing(activityPair.second);
}
/**
@@ -289,7 +289,7 @@
.setFinishSecondaryWithPrimary(FINISH_ADJACENT).start();
// Verify the paired finish behavior
activityPair.first.finish();
- assertTrue(waitForFinishing(activityPair.second));
+ waitAndAssertFinishing(activityPair.second);
}
/**
@@ -304,7 +304,7 @@
.setFinishSecondaryWithPrimary(FINISH_ALWAYS).start();
// Verify the paired finish behavior
activityPair.first.finish();
- assertTrue(waitForFinishing(activityPair.second));
+ waitAndAssertFinishing(activityPair.second);
}
/**
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
index 5c2d696..38c34f1 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
@@ -22,7 +22,7 @@
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.getPrimaryStackTopActivity;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.getSecondaryStackTopActivity;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -255,14 +255,14 @@
alwaysExpandedActivityId);
// Verify that the always expanded activity is resumed and fills its parent
- waitForResumed(alwaysExpandedActivityId);
+ waitAndAssertResumed(alwaysExpandedActivityId);
Activity alwaysExpandedActivity = getResumedActivityById(alwaysExpandedActivityId);
assertEquals(getMaximumActivityBounds(alwaysExpandedActivity),
getActivityBounds(alwaysExpandedActivity));
// Finish the always expanded activity and verify that the split is resumed
alwaysExpandedActivity.finish();
- waitForResumed(Arrays.asList(primaryActivity, secondaryActivity));
+ waitAndAssertResumed(Arrays.asList(primaryActivity, secondaryActivity));
assertValidSplit(primaryActivity, secondaryActivity, splitPairRule);
}
@@ -296,14 +296,14 @@
alwaysExpandedActivityId);
// Verify that the always expanded activity is resumed and fills its parent
- waitForResumed(alwaysExpandedActivityId);
+ waitAndAssertResumed(alwaysExpandedActivityId);
Activity alwaysExpandedActivity = getResumedActivityById(alwaysExpandedActivityId);
assertEquals(getMaximumActivityBounds(alwaysExpandedActivity),
getActivityBounds(alwaysExpandedActivity));
// Finish the always expanded activity and verify that the split is resumed
alwaysExpandedActivity.finish();
- waitForResumed(Arrays.asList(primaryActivity, secondaryActivity));
+ waitAndAssertResumed(Arrays.asList(primaryActivity, secondaryActivity));
assertValidSplit(primaryActivity, secondaryActivity, splitPairRule);
}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
index 693d854..bc00111 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
@@ -19,12 +19,12 @@
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.DEFAULT_SPLIT_RATIO;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.assertValidSplit;
import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.verifyFillsTask;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForFinishing;
-import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitForResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertFinishing;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertNotResumed;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed;
import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
@@ -83,7 +83,7 @@
// Finishing the primary activity and verify that the placeholder activity is also finishing
primaryActivity.finish();
- assertTrue(waitForFinishing(placeholderActivity));
+ waitAndAssertFinishing(placeholderActivity);
}
/**
@@ -104,7 +104,7 @@
// the primary activity fills the task.
Activity primaryActivity = startActivityNewTask(TestActivityWithId.class,
PRIMARY_ACTIVITY_ID);
- assertFalse(waitForResumed(PLACEHOLDER_ACTIVITY_ID));
+ waitAndAssertNotResumed(PLACEHOLDER_ACTIVITY_ID);
verifyFillsTask(primaryActivity);
}
@@ -129,7 +129,7 @@
// Finish the placeholder activity and verify that the primary activity is also finishing
placeholderActivity.finish();
- assertTrue(waitForFinishing(primaryActivity));
+ waitAndAssertFinishing(primaryActivity);
}
/**
@@ -193,7 +193,7 @@
// Verify that the placeholder activity was finished and that the primary activity now
// fills the task.
- assertTrue(waitForFinishing(placeholderActivity));
+ waitAndAssertFinishing(placeholderActivity);
assertTrue(primaryActivity.waitForBoundsChange());
verifyFillsTask(primaryActivity);
}
@@ -222,7 +222,7 @@
Activity primaryActivity = startActivityNewTask(TestActivityWithId.class,
PRIMARY_ACTIVITY_ID);
verifyFillsTask(primaryActivity);
- assertFalse(waitForResumed(PLACEHOLDER_ACTIVITY_ID));
+ waitAndAssertNotResumed(PLACEHOLDER_ACTIVITY_ID);
// Increase display width by 10% so that the primary and placeholder activities are stacked
final Size currentSize = mReportedDisplayMetrics.getSize();
@@ -230,7 +230,7 @@
currentSize.getHeight()));
// Verify that the placeholder activity is launched into a split with the primary activity
- assertTrue(waitForResumed(PLACEHOLDER_ACTIVITY_ID));
+ waitAndAssertResumed(PLACEHOLDER_ACTIVITY_ID);
Activity placeholderActivity = getResumedActivityById(PLACEHOLDER_ACTIVITY_ID);
assertValidSplit(primaryActivity, placeholderActivity, splitPlaceholderRule);
}
@@ -268,7 +268,7 @@
// Verify that the placeholder was not finished and fills the task
assertTrue(placeholderActivity.waitForBoundsChange());
verifyFillsTask(placeholderActivity);
- assertTrue(waitForResumed(Arrays.asList(placeholderActivity)));
+ waitAndAssertResumed(Arrays.asList(placeholderActivity));
}
/**
@@ -350,10 +350,10 @@
// Launch the primary activity
startActivityNewTask(TestActivityWithId.class, primaryActivityId);
// Get primary activity
- assertTrue(waitForResumed(primaryActivityId));
+ waitAndAssertResumed(primaryActivityId);
Activity primaryActivity = getResumedActivityById(primaryActivityId);
// Get placeholder activity
- assertTrue(waitForResumed(placeholderActivityId));
+ waitAndAssertResumed(placeholderActivityId);
Activity placeholderActivity = getResumedActivityById(placeholderActivityId);
// Verify they are correctly split
assertValidSplit(primaryActivity, placeholderActivity, splitPlaceholderRule);
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
index ff60f60..0e415aa 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
@@ -91,7 +91,7 @@
startActivityFromActivity(activityLaunchingFrom, TestActivityWithId.class,
secondActivityId);
// Verify both activities are in the correct lifecycle state
- waitForResumed(secondActivityId);
+ waitAndAssertResumed(secondActivityId);
assertFalse(isActivityResumed(activityLaunchingFrom));
TestActivity secondActivity = getResumedActivityById(secondActivityId);
// Verify the second activity is not split with the first
@@ -101,16 +101,17 @@
public static Activity startActivityAndVerifySplit(@NonNull Activity activityLaunchingFrom,
@NonNull Activity expectedPrimaryActivity, @NonNull Class secondActivityClass,
- @NonNull SplitPairRule splitPairRule, @NonNull String secondActivityId,
+ @NonNull SplitPairRule splitPairRule, @NonNull String secondaryActivityId,
int expectedCallbackCount,
@NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer) {
// Set the expected callback count
splitInfoConsumer.setCount(expectedCallbackCount);
// Start second activity
- startActivityFromActivity(activityLaunchingFrom, secondActivityClass, secondActivityId);
+ startActivityFromActivity(activityLaunchingFrom, secondActivityClass, secondaryActivityId);
- // Get updated split info
+ // A split info callback should occur after the new activity is launched because the split
+ // states have changed.
List<SplitInfo> activeSplitStates = null;
try {
activeSplitStates = splitInfoConsumer.waitAndGet();
@@ -118,15 +119,17 @@
fail("startActivityAndVerifySplit() InterruptedException");
}
- // Get second activity from split info
- Activity secondActivity = getSecondActivity(activeSplitStates, expectedPrimaryActivity,
- secondActivityId);
- assertNotNull(secondActivity);
+ // Wait for secondary activity to be resumed and verify that the newly sent split info
+ // contains the secondary activity.
+ waitAndAssertResumed(secondaryActivityId);
+ final Activity secondaryActivity = getResumedActivityById(secondaryActivityId);
+ assertTrue(splitInfoTopSplitIsCorrect(activeSplitStates, expectedPrimaryActivity,
+ secondaryActivity));
- assertValidSplit(expectedPrimaryActivity, secondActivity, splitPairRule);
+ assertValidSplit(expectedPrimaryActivity, secondaryActivity, splitPairRule);
// Return second activity for easy access in calling method
- return secondActivity;
+ return secondaryActivity;
}
public static Activity startActivityAndVerifySplit(@NonNull Activity primaryActivity,
@@ -234,7 +237,7 @@
*/
public static void assertValidSplit(@NonNull Activity primaryActivity,
@Nullable Activity secondaryActivity, SplitRule splitRule) {
- waitForResumed(secondaryActivity != null
+ waitAndAssertResumed(secondaryActivity != null
? Arrays.asList(primaryActivity, secondaryActivity)
: Collections.singletonList(primaryActivity));
@@ -273,7 +276,7 @@
.equals(getMaximumActivityBounds(activity)));
}
- public static boolean waitForResumed(
+ private static boolean waitForResumed(
@NonNull List<Activity> activityList) {
final long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < WAIT_FOR_LIFECYCLE_TIMEOUT_MS) {
@@ -291,7 +294,7 @@
return false;
}
- public static boolean waitForResumed(@NonNull String activityId) {
+ private static boolean waitForResumed(@NonNull String activityId) {
final long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < WAIT_FOR_LIFECYCLE_TIMEOUT_MS) {
if (getResumedActivityById(activityId) != null) {
@@ -301,7 +304,30 @@
return false;
}
- public static boolean waitForVisible(@NonNull Activity activity, boolean visible) {
+ private static boolean waitForResumed(@NonNull Activity activity) {
+ return waitForResumed(Arrays.asList(activity));
+ }
+
+ public static void waitAndAssertResumed(@NonNull String activityId) {
+ assertTrue("Activity with id=" + activityId + " should be resumed",
+ waitForResumed(activityId));
+ }
+
+ public static void waitAndAssertResumed(@NonNull Activity activity) {
+ assertTrue(activity + " should be resumed", waitForResumed(activity));
+ }
+
+ public static void waitAndAssertResumed(@NonNull List<Activity> activityList) {
+ assertTrue("All activities in this list should be resumed:" + activityList,
+ waitForResumed(activityList));
+ }
+
+ public static void waitAndAssertNotResumed(@NonNull String activityId) {
+ assertFalse("Activity with id=" + activityId + " should not be resumed",
+ waitForResumed(activityId));
+ }
+
+ private static boolean waitForVisible(@NonNull Activity activity, boolean visible) {
final long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < WAIT_FOR_LIFECYCLE_TIMEOUT_MS) {
if (WindowManagerJetpackTestBase.isActivityVisible(activity) == visible) {
@@ -311,11 +337,12 @@
return false;
}
- public static boolean waitForResumed(@NonNull Activity activity) {
- return waitForResumed(Arrays.asList(activity));
+ public static void waitAndAssertNotVisible(@NonNull Activity activity) {
+ assertTrue(activity + " should not be visible",
+ waitForVisible(activity, false /* visible */));
}
- public static boolean waitForFinishing(@NonNull Activity activity) {
+ private static boolean waitForFinishing(@NonNull Activity activity) {
final long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < WAIT_FOR_LIFECYCLE_TIMEOUT_MS) {
if (activity.isFinishing()) {
@@ -325,6 +352,10 @@
return activity.isFinishing();
}
+ public static void waitAndAssertFinishing(@NonNull Activity activity) {
+ assertTrue(activity + " should be finishing", waitForFinishing(activity));
+ }
+
@Nullable
public static Activity getPrimaryStackTopActivity(SplitInfo splitInfo) {
List<Activity> primaryActivityStack = splitInfo.getPrimaryActivityStack().getActivities();
@@ -378,4 +409,12 @@
outSecondaryActivityBounds.set(leftContainerBounds);
}
}
+
+ private static boolean splitInfoTopSplitIsCorrect(@NonNull List<SplitInfo> splitInfoList,
+ @NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) {
+ assertFalse("Split info callback should not be empty", splitInfoList.isEmpty());
+ final SplitInfo topSplit = splitInfoList.get(splitInfoList.size() - 1);
+ return primaryActivity.equals(getPrimaryStackTopActivity(topSplit))
+ && secondaryActivity.equals(getSecondaryStackTopActivity(topSplit));
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfacePackageFlickerTest.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfacePackageFlickerTest.java
index cb91494..b654c73 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SurfacePackageFlickerTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfacePackageFlickerTest.java
@@ -50,6 +50,8 @@
import org.junit.Test;
import org.junit.rules.TestName;
+import java.util.concurrent.CountDownLatch;
+
public class SurfacePackageFlickerTest {
private static final int DEFAULT_LAYOUT_WIDTH = 100;
private static final int DEFAULT_LAYOUT_HEIGHT = 100;
@@ -86,6 +88,8 @@
private SurfaceView mSurfaceView;
private SurfaceControlViewHost mSurfaceControlViewHost;
private FrameLayout mParent;
+ private final CountDownLatch mFirstDrawLatch = new CountDownLatch(1);
+
private final Runnable mRecreateSurfaceViewCallback = new Runnable() {
public void run() {
@@ -125,9 +129,18 @@
v.getViewTreeObserver().registerFrameCommitCallback(() -> {
parent.post(mRecreateSurfaceViewCallback);
+ mFirstDrawLatch.countDown();
});
}
+ public void waitForReady() {
+ try {
+ mFirstDrawLatch.await();
+ } catch (Exception e) {
+ // Oh well
+ }
+ }
+
@Override
public void end() {
mSurfaceControlViewHost.release();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
index 322355f..2e29a74 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
@@ -73,12 +73,11 @@
import java.util.ArrayList;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeoutException;
/**
* Ensure moving windows and tapping is done synchronously.
@@ -728,9 +727,10 @@
eventHover.setSource(InputDevice.SOURCE_MOUSE);
try {
mInstrumentation.sendPointerSync(eventHover);
- fail("Not allowed to inject event to the window from another process.");
- } catch (SecurityException e) {
- // Should not be allowed to inject event to the window from another process.
+ fail("Not allowed to inject to windows owned by another uid from Instrumentation.");
+ } catch (RuntimeException e) {
+ // Should not be allowed to inject event to a window owned by another uid from the
+ // Instrumentation class.
}
}
diff --git a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
index 3f8e8b1..81fdb5a 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
@@ -17,6 +17,7 @@
package android.location.cts.fine;
import static android.Manifest.permission.LOCATION_BYPASS;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.AppOpsManager.OPSTR_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OPSTR_MONITOR_LOCATION;
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
@@ -59,6 +60,7 @@
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
+import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -125,6 +127,7 @@
"invalid_location_attribution_tag";
private static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist";
+ private static final String ADAS_SETTINGS_ALLOWLIST = "adas_settings_allowlist";
private Random mRandom;
private Context mContext;
@@ -234,6 +237,110 @@
}
@Test
+ public void testGetLastKnownLocation_AdasLocationSettings_ReturnsLocation() throws Exception {
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE));
+
+ try (LocationListenerCapture capture = new LocationListenerCapture(mContext);
+ DeviceConfigStateHelper locationDeviceConfigStateHelper =
+ new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_LOCATION)) {
+
+ locationDeviceConfigStateHelper.set(ADAS_SETTINGS_ALLOWLIST,
+ mContext.getPackageName());
+
+ mManager.addTestProvider(
+ GPS_PROVIDER,
+ false,
+ true,
+ false,
+ false,
+ true,
+ true,
+ true,
+ Criteria.POWER_HIGH,
+ Criteria.ACCURACY_FINE);
+
+ Location loc = createLocation(GPS_PROVIDER, mRandom);
+ mManager.setTestProviderLocation(GPS_PROVIDER, loc);
+
+ mManager.setTestProviderEnabled(GPS_PROVIDER, true);
+
+ getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(LOCATION_BYPASS, WRITE_SECURE_SETTINGS);
+
+ try {
+ // Returns loc when ADAS toggle is on.
+ mManager.setLocationEnabledForUser(false, mContext.getUser());
+ mManager.setAdasGnssLocationEnabled(true);
+ assertThat(
+ mManager.getLastKnownLocation(
+ GPS_PROVIDER,
+ new LastLocationRequest.Builder()
+ .setAdasGnssBypass(true)
+ .build()))
+ .isEqualTo(loc);
+
+ } finally {
+ mManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
+ getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+ }
+
+ @Test
+ public void testGetLastKnownLocation_AdasLocationSettings_ReturnsNull() throws Exception {
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE));
+
+ try (LocationListenerCapture capture = new LocationListenerCapture(mContext);
+ DeviceConfigStateHelper locationDeviceConfigStateHelper =
+ new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_LOCATION)) {
+
+ locationDeviceConfigStateHelper.set(ADAS_SETTINGS_ALLOWLIST,
+ mContext.getPackageName());
+
+ mManager.addTestProvider(
+ GPS_PROVIDER,
+ false,
+ true,
+ false,
+ false,
+ true,
+ true,
+ true,
+ Criteria.POWER_HIGH,
+ Criteria.ACCURACY_FINE);
+
+ Location loc = createLocation(GPS_PROVIDER, mRandom);
+ mManager.setTestProviderLocation(GPS_PROVIDER, loc);
+
+ mManager.setTestProviderEnabled(GPS_PROVIDER, true);
+
+ getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(LOCATION_BYPASS, WRITE_SECURE_SETTINGS);
+
+ try {
+ // Returns null when ADAS toggle is off
+ mManager.setAdasGnssLocationEnabled(false);
+ mManager.setLocationEnabledForUser(false, mContext.getUser());
+
+ assertThat(
+ mManager.getLastKnownLocation(
+ GPS_PROVIDER,
+ new LastLocationRequest.Builder()
+ .setAdasGnssBypass(true)
+ .build()))
+ .isNull();
+
+ } finally {
+ mManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
+ mManager.setAdasGnssLocationEnabled(true);
+ getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+ }
+
+ @Test
public void testGetLastKnownLocation_RemoveProvider() {
Location loc1 = createLocation(TEST_PROVIDER, mRandom);
@@ -843,6 +950,56 @@
}
@Test
+ public void testRequestLocationUpdates_AdasGnssBypass() throws Exception {
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE));
+
+ try (LocationListenerCapture capture = new LocationListenerCapture(mContext);
+ DeviceConfigStateHelper locationDeviceConfigStateHelper =
+ new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_LOCATION)) {
+
+ locationDeviceConfigStateHelper.set(ADAS_SETTINGS_ALLOWLIST,
+ mContext.getPackageName());
+
+ mManager.addTestProvider(
+ GPS_PROVIDER,
+ false,
+ true,
+ false,
+ false,
+ true,
+ true,
+ true,
+ Criteria.POWER_HIGH,
+ Criteria.ACCURACY_FINE);
+
+ getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(LOCATION_BYPASS);
+ try {
+ mManager.requestLocationUpdates(
+ GPS_PROVIDER,
+ new LocationRequest.Builder(0)
+ .setAdasGnssBypass(true)
+ .build(),
+ Executors.newSingleThreadExecutor(),
+ capture);
+ } finally {
+ getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+ }
+
+ // turn off provider
+ mManager.setTestProviderEnabled(GPS_PROVIDER, false);
+
+ // test that all restrictions are bypassed
+ Location loc = createLocation(GPS_PROVIDER, mRandom);
+ mManager.setTestProviderLocation(GPS_PROVIDER, loc);
+ assertThat(capture.getNextLocation(FAILURE_TIMEOUT_MS)).isEqualTo(loc);
+ loc = createLocation(GPS_PROVIDER, mRandom);
+ mManager.setTestProviderLocation(GPS_PROVIDER, loc);
+ assertThat(capture.getNextLocation(FAILURE_TIMEOUT_MS)).isEqualTo(loc);
+ }
+ }
+
+ @Test
public void testMonitoring() throws Exception {
AppOpsManager appOps = Objects.requireNonNull(
mContext.getSystemService(AppOpsManager.class));
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/GnssSingleSatCorrectionTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/GnssSingleSatCorrectionTest.java
index 4ba143b..24691b4 100644
--- a/tests/location/location_privileged/src/android/location/cts/privileged/GnssSingleSatCorrectionTest.java
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/GnssSingleSatCorrectionTest.java
@@ -61,6 +61,23 @@
}
@Test
+ public void testWriteToParcelWithoutSomeOptionalFields() {
+ GnssSingleSatCorrection object = new GnssSingleSatCorrection.Builder()
+ .setConstellationType(GnssStatus.CONSTELLATION_GALILEO)
+ .setSatelliteId(12)
+ .setCarrierFrequencyHz(1575420000f)
+ .setProbabilityLineOfSight(0.1f)
+ .build();
+ Parcel parcel = Parcel.obtain();
+ object.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ GnssSingleSatCorrection fromParcel =
+ GnssSingleSatCorrection.CREATOR.createFromParcel(parcel);
+ assertEquals(object, fromParcel);
+ parcel.recycle();
+ }
+
+ @Test
public void testClear() {
GnssSingleSatCorrection.Builder builder = createTestSingleSatCorrectionBuilder();
builder.clearProbabilityLineOfSight();
diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
index 83a7da7..cfe7835 100644
--- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
@@ -29,6 +29,7 @@
import org.junit.Assert;
import org.junit.Assume;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -92,6 +93,7 @@
* it should be capable of displaying the same
*/
@Test
+ @Ignore("TODO(b/228237404) Enable once display capabilities can be queried at codec2 level")
public void testHDRDisplayCapabilities() {
Assume.assumeTrue("Test needs Android 13", IS_AT_LEAST_T);
Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/"));
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
index 10ec1e6..9f0bc44 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
@@ -96,7 +96,19 @@
reportPerfClass + " for requirement " + this.id + " performance class should at " +
"least be: " + expectedPerfClass);
for (RequiredMeasurement<?> rm: this.mRequiredMeasurements.values()) {
- Log.w(Requirement.TAG, rm.toString());
+ Map<Integer, RequirementConstants.Result> perfClasses = rm.getPerformanceClass();
+ int maxMetPerformanceClass = 0;
+ for (int pc: perfClasses.keySet()) {
+ if (perfClasses.get(pc) == RequirementConstants.Result.MET) {
+ maxMetPerformanceClass = Math.max(maxMetPerformanceClass, pc);
+ }
+ }
+
+ if (maxMetPerformanceClass < expectedPerfClass) {
+ Log.w(Requirement.TAG, rm.toString());
+ } else {
+ Log.i(Requirement.TAG, rm.toString());
+ }
}
return false;
} else {
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
index e48ba00..bb4e6e3 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
@@ -596,7 +596,10 @@
@Test
fun ensurePhoneCallOpsRestricted() {
- assumeTrue(mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))
+ val pm = mContext.packageManager
+ assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) ||
+ pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) ||
+ pm.hasSystemFeature(PackageManager.FEATURE_TELECOM))
val micReturn = mAppOps.noteOp(OPSTR_PHONE_CALL_MICROPHONE, Process.myUid(), mOpPackageName,
null, null)
assertEquals(MODE_IGNORED, micReturn)
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
index f3918c7..dde5621 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
@@ -26,8 +26,6 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -42,7 +40,6 @@
private static final String TAG = BluetoothA2dpSinkTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_A2DP_SINK = "profile_supported_a2dp_sink";
private boolean mHasBluetooth;
private BluetoothAdapter mAdapter;
@@ -57,17 +54,11 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH);
+ mHasBluetooth = TestUtils.hasBluetooth();
if (!mHasBluetooth) return;
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth.services");
- int a2dpSinkSupportId = bluetoothResources.getIdentifier(
- PROFILE_SUPPORTED_A2DP_SINK, "bool", "com.android.bluetooth.services");
- assertTrue("resource profile_supported_a2dp not found", a2dpSinkSupportId != 0);
- mIsA2dpSinkSupported = bluetoothResources.getBoolean(a2dpSinkSupportId);
+ mIsA2dpSinkSupported = TestUtils.isProfileEnabled(BluetoothProfile.A2DP_SINK);
if (!mIsA2dpSinkSupported) return;
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -89,15 +80,18 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (!(mHasBluetooth && mIsA2dpSinkSupported)) return;
-
+ if (!(mHasBluetooth && mIsA2dpSinkSupported)) {
+ return;
+ }
if (mAdapter != null && mBluetoothA2dpSink != null) {
mAdapter.closeProfileProxy(BluetoothProfile.A2DP_SINK, mBluetoothA2dpSink);
mBluetoothA2dpSink = null;
mIsProfileReady = false;
}
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mUiAutomation.dropShellPermissionIdentity();
mAdapter = null;
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
index 191c4a2..912bf70 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
@@ -21,12 +21,9 @@
import android.app.UiAutomation;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -41,7 +38,6 @@
private static final String TAG = BluetoothA2dpTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_A2DP = "profile_supported_a2dp";
private boolean mHasBluetooth;
private BluetoothAdapter mAdapter;
@@ -56,10 +52,13 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH);
+ mHasBluetooth = TestUtils.hasBluetooth();
if (!mHasBluetooth) return;
+
+ mIsA2dpSupported = TestUtils.isProfileEnabled(BluetoothProfile.A2DP_SINK);
+ if (!mIsA2dpSupported) return;
+
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
@@ -72,14 +71,6 @@
mIsProfileReady = false;
mBluetoothA2dp = null;
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth.services");
- int a2dpSupportId = bluetoothResources.getIdentifier(
- PROFILE_SUPPORTED_A2DP, "bool", "com.android.bluetooth.services");
- assertTrue("resource profile_supported_a2dp not found", a2dpSupportId != 0);
- mIsA2dpSupported = bluetoothResources.getBoolean(a2dpSupportId);
- if (!mIsA2dpSupported) return;
-
mAdapter.getProfileProxy(getContext(), new BluetoothA2dpServiceListener(),
BluetoothProfile.A2DP);
}
@@ -87,16 +78,19 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (mHasBluetooth) {
- if (mAdapter != null && mBluetoothA2dp != null) {
- mAdapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
- mBluetoothA2dp = null;
- mIsProfileReady = false;
- }
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- mUiAutomation.dropShellPermissionIdentity();
+ if (!(mHasBluetooth && mIsA2dpSupported)) {
+ return;
}
+ if (mAdapter != null && mBluetoothA2dp != null) {
+ mAdapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
+ mBluetoothA2dp = null;
+ mIsProfileReady = false;
+ }
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ mUiAutomation.dropShellPermissionIdentity();
}
public void test_getConnectedDevices() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
index 54766fe..9e9459c 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
@@ -35,12 +35,15 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.Build;
import android.os.SystemProperties;
import android.test.AndroidTestCase;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.ApiLevelUtil;
+
import java.io.IOException;
import java.util.List;
import java.util.Set;
@@ -576,8 +579,19 @@
BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO));
assertEquals("HAP_CLIENT",
BluetoothProfile.getProfileName(BluetoothProfile.HAP_CLIENT));
- assertEquals("UNKNOWN_PROFILE",
- BluetoothProfile.getProfileName(BluetoothProfile.HAP_CLIENT + 1));
+
+ if (!ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+ return;
+ }
+
+ assertEquals("VOLUME_CONTROL",
+ BluetoothProfile.getProfileName(BluetoothProfile.VOLUME_CONTROL));
+ assertEquals("CSIP_SET_COORDINATOR",
+ BluetoothProfile.getProfileName(BluetoothProfile.CSIP_SET_COORDINATOR));
+ assertEquals("LE_AUDIO_BROADCAST",
+ BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO_BROADCAST));
+ assertEquals("LE_AUDIO_BROADCAST_ASSISTANT",
+ BluetoothProfile.getProfileName(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT));
}
private static void sleep(long t) {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java
new file mode 100644
index 0000000..fa96ddf
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2022 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.bluetooth.cts;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import static org.junit.Assert.assertThrows;
+
+import android.app.UiAutomation;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.ParcelUuid;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BluetoothCsipSetCoordinatorTest extends AndroidTestCase {
+ private static final String TAG = BluetoothCsipSetCoordinatorTest.class.getSimpleName();
+
+ private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
+
+ private boolean mHasBluetooth;
+ private BluetoothAdapter mAdapter;
+
+ private BluetoothCsipSetCoordinator mBluetoothCsipSetCoordinator;
+ private boolean mIsCsipSetCoordinatorSupported;
+ private boolean mIsProfileReady;
+ private Condition mConditionProfileIsConnected;
+ private ReentrantLock mProfileConnectedlock;
+ private boolean mGroupLockCallbackCalled;
+ private TestCallback mTestCallback;
+ private Executor mTestExecutor;
+ private BluetoothDevice mTestDevice;
+ private boolean mIsLocked;
+ private int mTestOperationStatus;
+ private int mTestGroupId;
+
+ class TestCallback implements BluetoothCsipSetCoordinator.ClientLockCallback {
+ @Override
+ public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) {
+ mGroupLockCallbackCalled = true;
+ assertTrue(groupId == mTestGroupId);
+ assertTrue(opStatus == mTestOperationStatus);
+ assertTrue(isLocked == mIsLocked);
+ }
+ };
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+ mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_BLUETOOTH);
+
+ if (!mHasBluetooth) return;
+
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+ BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
+ mAdapter = manager.getAdapter();
+ assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
+
+ mProfileConnectedlock = new ReentrantLock();
+ mConditionProfileIsConnected = mProfileConnectedlock.newCondition();
+ mIsProfileReady = false;
+ mBluetoothCsipSetCoordinator = null;
+
+ boolean isLeAudioSupportedInConfig =
+ TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO);
+ boolean isCsipConfigEnabled =
+ TestUtils.getProfileConfigValueOrDie(BluetoothProfile.CSIP_SET_COORDINATOR);
+ if (isLeAudioSupportedInConfig) {
+ /* If Le Audio is supported then CSIP shall be supported */
+ assertTrue("Config must be true when profile is supported", isCsipConfigEnabled);
+ }
+
+ if (isCsipConfigEnabled) {
+ mIsCsipSetCoordinatorSupported = mAdapter.getProfileProxy(getContext(),
+ new BluetoothCsipServiceListener(),
+ BluetoothProfile.CSIP_SET_COORDINATOR);
+ assertTrue("Service shall be supported ", mIsCsipSetCoordinatorSupported);
+
+ mTestCallback = new TestCallback();
+ mTestExecutor = mContext.getMainExecutor();
+ }
+ }
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mHasBluetooth) {
+ if (mBluetoothCsipSetCoordinator != null) {
+ mBluetoothCsipSetCoordinator.close();
+ mBluetoothCsipSetCoordinator = null;
+ mIsProfileReady = false;
+ mTestDevice = null;
+ mIsLocked = false;
+ mTestCallback = null;
+ mTestExecutor = null;
+ }
+ if (mAdapter != null ) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ mAdapter = null;
+ }
+ TestUtils.dropPermissionAsShellUid();
+ }
+ }
+
+ public void testGetConnectedDevices() {
+ if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothCsipSetCoordinator);
+
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+ // Verify returns empty list if bluetooth is not enabled
+ List<BluetoothDevice> connectedDevices = mBluetoothCsipSetCoordinator.getConnectedDevices();
+ assertTrue(connectedDevices.isEmpty());
+ }
+
+ public void testGetDevicesMatchingConnectionStates() {
+ if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothCsipSetCoordinator);
+
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+ // Verify returns empty list if bluetooth is not enabled
+ List<BluetoothDevice> connectedDevices =
+ mBluetoothCsipSetCoordinator.getDevicesMatchingConnectionStates(null);
+ assertTrue(connectedDevices.isEmpty());
+ }
+
+ public void testGetGroupUuidMapByDevice() {
+ if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothCsipSetCoordinator);
+
+ mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+
+ TestUtils.dropPermissionAsShellUid();
+ // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
+ assertThrows(SecurityException.class, () ->
+ mBluetoothCsipSetCoordinator.getGroupUuidMapByDevice(mTestDevice));
+
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+ Map<Integer, ParcelUuid> result = mBluetoothCsipSetCoordinator
+ .getGroupUuidMapByDevice(mTestDevice);
+ assertTrue(result.isEmpty());
+ }
+
+ public void testLockUnlockGroup() {
+ if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothCsipSetCoordinator);
+
+ mTestGroupId = 1;
+ // Verify parameter
+ assertThrows(NullPointerException.class, () ->
+ mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId, null, mTestCallback));
+ assertThrows(NullPointerException.class, () ->
+ mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId, mTestExecutor, null));
+
+ TestUtils.dropPermissionAsShellUid();
+ // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
+ assertThrows(SecurityException.class,
+ () -> mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId, mTestExecutor,
+ mTestCallback));
+
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+ // Lock group
+ try {
+ UUID uuid = mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId,
+ mTestExecutor, mTestCallback);
+ mBluetoothCsipSetCoordinator.unlockGroup(uuid);
+ } catch (Exception e) {
+ fail("Exception caught from register(): " + e.toString());
+ }
+ }
+
+ public void testTestLockCallback() {
+ if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothCsipSetCoordinator);
+
+ /* Note. This is just for api coverage until proper testing tools are set up */
+ mTestGroupId = 1;
+ mTestOperationStatus = 1;
+ mIsLocked = true;
+
+ mTestCallback.onGroupLockSet(mTestGroupId, mTestOperationStatus, mIsLocked);
+ assertTrue(mGroupLockCallbackCalled);
+ }
+
+ public void testGetAllGroupIds() {
+ if (!(mHasBluetooth && mIsCsipSetCoordinatorSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothCsipSetCoordinator);
+
+ TestUtils.dropPermissionAsShellUid();
+ assertThrows(SecurityException.class, () ->
+ mBluetoothCsipSetCoordinator.getAllGroupIds(BluetoothUuid.CAP));
+
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ List<Integer> result = mBluetoothCsipSetCoordinator.getAllGroupIds(null);
+ assertTrue(result.isEmpty());
+ }
+
+ private boolean waitForProfileConnect() {
+ mProfileConnectedlock.lock();
+ try {
+ // Wait for the Adapter to be disabled
+ while (!mIsProfileReady) {
+ if (!mConditionProfileIsConnected.await(
+ PROXY_CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ // Timeout
+ Log.e(TAG, "Timeout while waiting for Profile Connect");
+ break;
+ } // else spurious wakeups
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "waitForProfileConnect: interrrupted");
+ } finally {
+ mProfileConnectedlock.unlock();
+ }
+ return mIsProfileReady;
+ }
+
+ private final class BluetoothCsipServiceListener implements
+ BluetoothProfile.ServiceListener {
+
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ mProfileConnectedlock.lock();
+ mBluetoothCsipSetCoordinator = (BluetoothCsipSetCoordinator) proxy;
+ mIsProfileReady = true;
+ try {
+ mConditionProfileIsConnected.signal();
+ } finally {
+ mProfileConnectedlock.unlock();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ }
+ }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
index 6c7b933..c83f014 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
@@ -54,7 +54,6 @@
private static final String TAG = BluetoothHapClientTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_HAP_CLIENT = "profile_supported_hap_client";
private Context mContext;
private boolean mHasBluetooth;
@@ -76,37 +75,40 @@
if (!mHasBluetooth) {
return;
}
+
+ mIsHapClientSupported = TestUtils.isProfileEnabled(BluetoothProfile.HAP_CLIENT);
+ if (!mIsHapClientSupported) {
+ return;
+ }
+
TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT);
mAdapter = TestUtils.getBluetoothAdapterOrDie();
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
-
mProfileConnectedlock = new ReentrantLock();
mConditionProfileIsConnected = mProfileConnectedlock.newCondition();
mIsProfileReady = false;
mBluetoothHapClient = null;
- mIsHapClientSupported = TestUtils.getProfileConfigValueOrDie(BluetoothProfile.HAP_CLIENT);
- if (!mIsHapClientSupported) {
- return;
- }
-
mAdapter.getProfileProxy(mContext, new BluetoothHapClientServiceListener(),
BluetoothProfile.HAP_CLIENT);
}
@After
public void tearDown() throws Exception {
- if (mHasBluetooth) {
- if (mAdapter != null && mBluetoothHapClient != null) {
- mBluetoothHapClient.close();
- mBluetoothHapClient = null;
- mIsProfileReady = false;
- }
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- TestUtils.dropPermissionAsShellUid();
+ if (!(mHasBluetooth && mIsHapClientSupported)) {
+ return;
}
+ if (mAdapter != null && mBluetoothHapClient != null) {
+ mBluetoothHapClient.close();
+ mBluetoothHapClient = null;
+ mIsProfileReady = false;
+ }
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ TestUtils.dropPermissionAsShellUid();
}
@Test
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
index afafa39..df8ecf1 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
@@ -61,20 +61,27 @@
if (!mHasBluetooth) {
return;
}
+
+ mIsHapSupported = TestUtils.isProfileEnabled(BluetoothProfile.HAP_CLIENT);
+ if (!mIsHapSupported) {
+ return;
+ }
+
TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT);
mAdapter = TestUtils.getBluetoothAdapterOrDie();
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
-
- mIsHapSupported = TestUtils.getProfileConfigValueOrDie(BluetoothProfile.HAP_CLIENT);
}
@After
public void tearDown() {
- if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- TestUtils.dropPermissionAsShellUid();
+ if (!(mHasBluetooth && mIsHapSupported)) {
+ return;
}
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ TestUtils.dropPermissionAsShellUid();
}
@Test
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
index cf8f1a2..6925d9e 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
@@ -19,13 +19,12 @@
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import android.app.UiAutomation;
-import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -40,7 +39,6 @@
private static final String TAG = BluetoothHeadsetTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_HEADSET = "profile_supported_hs_hfp";
private boolean mHasBluetooth;
private BluetoothAdapter mAdapter;
@@ -57,8 +55,11 @@
super.setUp();
mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH);
-
if (!mHasBluetooth) return;
+
+ mIsHeadsetSupported = TestUtils.isProfileEnabled(BluetoothProfile.HEADSET);
+ if (!mIsHeadsetSupported) return;
+
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
@@ -71,14 +72,6 @@
mIsProfileReady = false;
mBluetoothHeadset = null;
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth.services");
- int headsetSupportId = bluetoothResources.getIdentifier(
- PROFILE_SUPPORTED_HEADSET, "bool", "com.android.bluetooth.services");
- assertTrue("resource profile_supported_hs_hfp not found", headsetSupportId != 0);
- mIsHeadsetSupported = bluetoothResources.getBoolean(headsetSupportId);
- if (!mIsHeadsetSupported) return;
-
mAdapter.getProfileProxy(getContext(), new BluetoothHeadsetServiceListener(),
BluetoothProfile.HEADSET);
}
@@ -86,16 +79,19 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (mHasBluetooth) {
- if (mAdapter != null && mBluetoothHeadset != null) {
- mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
- mBluetoothHeadset = null;
- mIsProfileReady = false;
- }
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- mUiAutomation.dropShellPermissionIdentity();
+ if (!(mHasBluetooth && mIsHeadsetSupported)) {
+ return;
}
+ if (mAdapter != null && mBluetoothHeadset != null) {
+ mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
+ mBluetoothHeadset = null;
+ mIsProfileReady = false;
+ }
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ mUiAutomation.dropShellPermissionIdentity();
}
public void test_getConnectedDevices() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
index 857e12c..1a258bc 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
@@ -39,7 +39,6 @@
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -56,7 +55,6 @@
private static final String TAG = BluetoothHidDevice.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_HID_DEVICE = "profile_supported_hid_device";
private boolean mHasBluetooth;
private boolean mIsHidSupported;
@@ -76,6 +74,10 @@
mHasBluetooth =
getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
if (!mHasBluetooth) return;
+
+ mIsHidSupported = TestUtils.isProfileEnabled(BluetoothProfile.HID_DEVICE);
+ if (!mIsHidSupported) return;
+
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
@@ -89,13 +91,6 @@
mBluetoothHidDevice = null;
mExecutor = Executors.newSingleThreadExecutor();
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth.services");
- int hidSupportId = bluetoothResources.getIdentifier(PROFILE_SUPPORTED_HID_DEVICE, "bool",
- "com.android.bluetooth.services");
- mIsHidSupported = bluetoothResources.getBoolean(hidSupportId);
- if (!mIsHidSupported) return;
-
mAdapter.getProfileProxy(getContext(), new BluetoothHidServiceListener(),
BluetoothProfile.HID_DEVICE);
}
@@ -103,17 +98,20 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (mHasBluetooth) {
- if (mAdapter != null && mBluetoothHidDevice != null) {
- mAdapter.closeProfileProxy(BluetoothProfile.HID_DEVICE, mBluetoothHidDevice);
- mBluetoothHidDevice = null;
- mIsProfileReady = false;
- }
- mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- mUiAutomation.dropShellPermissionIdentity();
+ if (!(mHasBluetooth && mIsHidSupported)) {
+ return;
}
+ if (mAdapter != null && mBluetoothHidDevice != null) {
+ mAdapter.closeProfileProxy(BluetoothProfile.HID_DEVICE, mBluetoothHidDevice);
+ mBluetoothHidDevice = null;
+ mIsProfileReady = false;
+ }
+ mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ mUiAutomation.dropShellPermissionIdentity();
}
public void test_getDevicesMatchingConnectionStates() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
index 0100442..0446adb 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
@@ -75,8 +75,7 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
@@ -85,8 +84,7 @@
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
if (!mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -95,7 +93,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
index 08deed4..c22e77d5 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
@@ -81,8 +81,7 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
@@ -91,8 +90,7 @@
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
if (!mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -101,7 +99,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
index 0db7a05..2a1f31d 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
@@ -16,25 +16,26 @@
package android.bluetooth.cts;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
import static org.junit.Assert.assertThrows;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudio;
-import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothLeAudioCodecConfig;
+import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.os.Build;
import android.test.AndroidTestCase;
import android.util.Log;
-import androidx.test.InstrumentationRegistry;
-
import com.android.compatibility.common.util.ApiLevelUtil;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -45,8 +46,6 @@
private static final String TAG = BluetoothLeAudioTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_LE_AUDIO = "profile_supported_leaudio";
- private static final int LE_AUDIO_PROFILE_CONSTANT = 22;
private boolean mHasBluetooth;
private BluetoothAdapter mAdapter;
@@ -56,17 +55,72 @@
private boolean mIsProfileReady;
private Condition mConditionProfileIsConnected;
private ReentrantLock mProfileConnectedlock;
+ private Executor mTestExecutor;
+ private TestCallback mTestCallback;
+ private boolean mCodecConfigChangedCalled;
+ private boolean mGroupNodeAddedCalled;
+ private boolean mGroupNodeRemovedCalled;
+ private boolean mGroupStatusChangedCalled;
+ private BluetoothDevice mTestDevice;
+ private int mTestGroupId;
+ private int mTestGroupStatus;
+
+ private static final BluetoothLeAudioCodecConfig LC3_16KHZ_CONFIG =
+ new BluetoothLeAudioCodecConfig.Builder()
+ .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
+ .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000)
+ .build();
+
+ private static final List<BluetoothLeAudioCodecConfig> TEST_CODEC_CAPA_CONFIG =
+ new ArrayList() {{
+ add(LC3_16KHZ_CONFIG);
+ }};
+
+ private static final BluetoothLeAudioCodecStatus TEST_CODEC_STATUS =
+ new BluetoothLeAudioCodecStatus(LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG,
+ TEST_CODEC_CAPA_CONFIG, TEST_CODEC_CAPA_CONFIG,
+ TEST_CODEC_CAPA_CONFIG, TEST_CODEC_CAPA_CONFIG);
+
+ class TestCallback implements BluetoothLeAudio.Callback {
+ @Override
+ public void onCodecConfigChanged(int groupId,
+ BluetoothLeAudioCodecStatus status) {
+ mCodecConfigChangedCalled = true;
+ assertTrue(groupId == mTestGroupId);
+ assertTrue(status == TEST_CODEC_STATUS);
+ }
+ @Override
+ public void onGroupNodeAdded(BluetoothDevice device, int groupId) {
+ mGroupNodeAddedCalled = true;
+ assertTrue(groupId == mTestGroupId);
+ assertTrue(device == mTestDevice);
+ }
+ @Override
+ public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
+ mGroupNodeRemovedCalled = true;
+ assertTrue(groupId == mTestGroupId);
+ assertTrue(device == mTestDevice);
+ }
+ @Override
+ public void onGroupStatusChanged(int groupId, int groupStatus) {
+ mGroupStatusChangedCalled = true;
+ assertTrue(groupId == mTestGroupId);
+ assertTrue(groupStatus == mTestGroupStatus);
+ }
+ };
@Override
public void setUp() throws Exception {
super.setUp();
- if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S)) {
+ if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH);
-
if (!mHasBluetooth) return;
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(android.Manifest.permission.BLUETOOTH_CONNECT);
+
+ mIsLeAudioSupported = TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO);
+ if (!mIsLeAudioSupported) return;
+
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
mAdapter = manager.getAdapter();
@@ -77,33 +131,30 @@
mIsProfileReady = false;
mBluetoothLeAudio = null;
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth.services");
- int leAudioSupportId = bluetoothResources.getIdentifier(
- PROFILE_SUPPORTED_LE_AUDIO, "bool", "com.android.bluetooth.services");
- if (leAudioSupportId == 0) return;
- mIsLeAudioSupported = bluetoothResources.getBoolean(leAudioSupportId);
- if (!mIsLeAudioSupported) return;
-
mAdapter.getProfileProxy(getContext(), new BluetoothLeAudioServiceListener(),
- LE_AUDIO_PROFILE_CONSTANT);
+ BluetoothProfile.LE_AUDIO);
+
+ mTestExecutor = mContext.getMainExecutor();
+ mTestCallback = new TestCallback();
}
}
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (mHasBluetooth) {
- if (mAdapter != null && mBluetoothLeAudio != null) {
- mBluetoothLeAudio.close();
- mBluetoothLeAudio = null;
- mIsProfileReady = false;
- }
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .dropShellPermissionIdentity();
- mAdapter = null;
+ if (!(mHasBluetooth && mIsLeAudioSupported)) {
+ return;
}
+ if (mBluetoothLeAudio != null) {
+ mBluetoothLeAudio.close();
+ mBluetoothLeAudio = null;
+ mIsProfileReady = false;
+ }
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ TestUtils.dropPermissionAsShellUid();
+ mAdapter = null;
}
public void testGetConnectedDevices() {
@@ -139,7 +190,7 @@
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeAudio);
- BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+ mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
// Verify returns false when invalid input is given
assertEquals(BluetoothProfile.STATE_DISCONNECTED,
@@ -149,7 +200,7 @@
// Verify returns false if bluetooth is not enabled
assertEquals(BluetoothProfile.STATE_DISCONNECTED,
- mBluetoothLeAudio.getConnectionState(testDevice));
+ mBluetoothLeAudio.getConnectionState(mTestDevice));
}
public void testGetAudioLocation() {
@@ -158,13 +209,13 @@
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeAudio);
- BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+ mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
// Verify returns false if bluetooth is not enabled
assertEquals(BluetoothLeAudio.AUDIO_LOCATION_INVALID,
- mBluetoothLeAudio.getAudioLocation(testDevice));
+ mBluetoothLeAudio.getAudioLocation(mTestDevice));
}
public void test_setgetConnectionPolicy() {
@@ -178,33 +229,86 @@
mBluetoothLeAudio.getConnectionPolicy(null));
}
- public void testRegisterCallback() {
+ public void testRegisterCallbackNoPermission() {
+ if (!(mHasBluetooth && mIsLeAudioSupported)) return;
+
+ TestUtils.dropPermissionAsShellUid();
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothLeAudio);
+
+ // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
+ assertThrows(SecurityException.class,
+ () -> mBluetoothLeAudio.registerCallback(mTestExecutor, mTestCallback));
+
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+ }
+
+ public void testRegisterUnregisterCallback() {
if (!(mHasBluetooth && mIsLeAudioSupported)) return;
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeAudio);
- Executor executor = mContext.getMainExecutor();
- BluetoothLeAudio.Callback callback =
- new BluetoothLeAudio.Callback() {
- @Override
- public void onCodecConfigChanged(int groupId,
- BluetoothLeAudioCodecStatus status) {}
- @Override
- public void onGroupNodeAdded(BluetoothDevice device, int groupId) {}
- @Override
- public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {}
- @Override
- public void onGroupStatusChanged(int groupId, int groupStatus) {}
- };
-
// Verify parameter
assertThrows(NullPointerException.class, () ->
- mBluetoothLeAudio.registerCallback(null, callback));
+ mBluetoothLeAudio.registerCallback(null, mTestCallback));
assertThrows(NullPointerException.class, () ->
- mBluetoothLeAudio.registerCallback(executor, null));
+ mBluetoothLeAudio.registerCallback(mTestExecutor, null));
assertThrows(NullPointerException.class, () ->
mBluetoothLeAudio.unregisterCallback(null));
+
+ // Test success register unregister
+ try {
+ mBluetoothLeAudio.registerCallback(mTestExecutor, mTestCallback);
+ } catch (Exception e) {
+ fail("Exception caught from register(): " + e.toString());
+ }
+
+ try {
+ mBluetoothLeAudio.unregisterCallback(mTestCallback);
+ } catch (Exception e) {
+ fail("Exception caught from unregister(): " + e.toString());
+ }
+ }
+
+ public void testCallback() {
+ if (!(mHasBluetooth && mIsLeAudioSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothLeAudio);
+
+ mTestGroupId = 1;
+ mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+ mTestGroupStatus = 0;
+
+ mCodecConfigChangedCalled = false;
+ mGroupNodeAddedCalled = false;
+ mGroupStatusChangedCalled = false;
+ mGroupNodeRemovedCalled = false;
+
+ mTestCallback.onCodecConfigChanged(mTestGroupId, TEST_CODEC_STATUS);
+ mTestCallback.onGroupNodeAdded(mTestDevice, mTestGroupId);
+ mTestCallback.onGroupNodeRemoved(mTestDevice, mTestGroupId);
+ mTestCallback.onGroupStatusChanged(mTestGroupId, mTestGroupStatus);
+
+ assertTrue(mCodecConfigChangedCalled);
+ assertTrue(mGroupNodeAddedCalled);
+ assertTrue(mGroupNodeRemovedCalled);
+ assertTrue(mGroupStatusChangedCalled);
+ }
+
+ public void testGetConnectedGroupLeadDevice() {
+ if (!(mHasBluetooth && mIsLeAudioSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothLeAudio);
+
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+ int groupId = 1;
+
+ // Verify returns null for unknown group id
+ assertEquals(null, mBluetoothLeAudio.getConnectedGroupLeadDevice(groupId));
}
private boolean waitForProfileConnect() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
index 667aa52..1d800bd 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
@@ -95,8 +95,7 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
@@ -114,7 +113,9 @@
mBluetoothLeBroadcastAssistant = null;
mIsProfileReady = false;
}
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
index 7390b82..b72a04c 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
@@ -69,8 +69,7 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
@@ -79,8 +78,8 @@
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
if (!mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(
+ BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -89,7 +88,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
index ad96fe0..bd1d9b9 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
@@ -92,8 +92,7 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
@@ -102,8 +101,7 @@
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
if (!mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -112,7 +110,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
index cc28740..edb8b06 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
@@ -87,8 +87,7 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
@@ -97,8 +96,7 @@
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
if (!mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -107,7 +105,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
index 910e55d..4f36364 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
@@ -83,8 +83,7 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
@@ -93,8 +92,7 @@
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
if (!mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -103,7 +101,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
index d73f721..a58ae03 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
@@ -55,7 +55,6 @@
private static final String TAG = BluetoothLeBroadcastTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_LE_BROADCAST = "profile_supported_le_broadcast";
private Context mContext;
private boolean mHasBluetooth;
@@ -90,7 +89,7 @@
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
if (mIsLeBroadcastSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO_BROADCAST);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -111,7 +110,9 @@
mBluetoothLeBroadcast = null;
mIsProfileReady = false;
}
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
index 29d9bd5..1f220cd 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
@@ -27,7 +27,6 @@
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSap;
import android.content.pm.PackageManager;
-import android.sysprop.BluetoothProperties;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
@@ -62,7 +61,7 @@
if (!mHasBluetooth) return;
- mIsSapSupported = BluetoothProperties.isProfileSapServerEnabled().orElse(false);
+ mIsSapSupported = TestUtils.isProfileEnabled(BluetoothProfile.SAP);
if (!mIsSapSupported) return;
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -90,7 +89,9 @@
mIsProfileReady = false;
}
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mUiAutomation.dropShellPermissionIdentity();
mAdapter = null;
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java
new file mode 100644
index 0000000..b6d20ec
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2022 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.bluetooth.cts;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import static org.junit.Assert.assertThrows;
+
+import android.app.UiAutomation;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothVolumeControl;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BluetoothVolumeControlTest extends AndroidTestCase {
+ private static final String TAG = BluetoothVolumeControlTest.class.getSimpleName();
+
+ private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
+
+ private boolean mHasBluetooth;
+ private BluetoothAdapter mAdapter;
+
+ private BluetoothVolumeControl mBluetoothVolumeControl;
+ private boolean mIsVolumeControlSupported;
+ private boolean mIsProfileReady;
+ private Condition mConditionProfileIsConnected;
+ private ReentrantLock mProfileConnectedlock;
+ private boolean mVolumeOffsetChangedCallbackCalled;
+ private TestCallback mTestCallback;
+ private Executor mTestExecutor;
+ private BluetoothDevice mTestDevice;
+ private int mTestVolumeOffset;
+
+ class TestCallback implements BluetoothVolumeControl.Callback {
+ @Override
+ public void onVolumeOffsetChanged(BluetoothDevice device, int volumeOffset) {
+ mVolumeOffsetChangedCallbackCalled = true;
+ assertTrue(device == mTestDevice);
+ assertTrue(volumeOffset == mTestVolumeOffset);
+ }
+ };
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+ mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_BLUETOOTH);
+
+ if (!mHasBluetooth) return;
+
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+
+ BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
+ mAdapter = manager.getAdapter();
+ assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
+
+ mProfileConnectedlock = new ReentrantLock();
+ mConditionProfileIsConnected = mProfileConnectedlock.newCondition();
+ mIsProfileReady = false;
+ mBluetoothVolumeControl = null;
+
+ boolean isLeAudioSupportedInConfig =
+ TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO);
+ boolean isVolumeControlEnabledInConfig =
+ TestUtils.getProfileConfigValueOrDie(BluetoothProfile.VOLUME_CONTROL);
+ if (isLeAudioSupportedInConfig) {
+ /* If Le Audio is supported then Volume Control shall be supported */
+ assertTrue("Config must be true when profile is supported",
+ isVolumeControlEnabledInConfig);
+ }
+
+ if (isVolumeControlEnabledInConfig) {
+ mIsVolumeControlSupported = mAdapter.getProfileProxy(getContext(),
+ new BluetoothVolumeControlServiceListener(),
+ BluetoothProfile.VOLUME_CONTROL);
+ assertTrue("Service shall be supported ", mIsVolumeControlSupported);
+
+ mTestCallback = new TestCallback();
+ mTestExecutor = mContext.getMainExecutor();
+ }
+ }
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mHasBluetooth) {
+ if (mBluetoothVolumeControl != null) {
+ mBluetoothVolumeControl.close();
+ mBluetoothVolumeControl = null;
+ mIsProfileReady = false;
+ mTestDevice = null;
+ mTestVolumeOffset = 0;
+ mTestCallback = null;
+ mTestExecutor = null;
+ }
+ if (mAdapter != null ) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ mAdapter = null;
+ }
+ TestUtils.dropPermissionAsShellUid();
+ }
+ }
+
+ public void testGetConnectedDevices() {
+ if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothVolumeControl);
+
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+ // Verify returns empty list if bluetooth is not enabled
+ List<BluetoothDevice> connectedDevices = mBluetoothVolumeControl.getConnectedDevices();
+ assertTrue(connectedDevices.isEmpty());
+ }
+
+ public void testGetDevicesMatchingConnectionStates() {
+ if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothVolumeControl);
+
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+ // Verify returns empty list if bluetooth is not enabled
+ List<BluetoothDevice> connectedDevices =
+ mBluetoothVolumeControl.getDevicesMatchingConnectionStates(null);
+ assertTrue(connectedDevices.isEmpty());
+ }
+
+ public void testRegisterUnregisterCallback() {
+ if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothVolumeControl);
+
+ // Verify parameter
+ assertThrows(NullPointerException.class, () ->
+ mBluetoothVolumeControl.registerCallback(null, mTestCallback));
+ assertThrows(NullPointerException.class, () ->
+ mBluetoothVolumeControl.registerCallback(mTestExecutor, null));
+ assertThrows(NullPointerException.class, () ->
+ mBluetoothVolumeControl.unregisterCallback(null));
+
+ // Test success register unregister
+ try {
+ mBluetoothVolumeControl.registerCallback(mTestExecutor, mTestCallback);
+ } catch (Exception e) {
+ fail("Exception caught from register(): " + e.toString());
+ }
+
+ try {
+ mBluetoothVolumeControl.unregisterCallback(mTestCallback);
+ } catch (Exception e) {
+ fail("Exception caught from unregister(): " + e.toString());
+ }
+
+ TestUtils.dropPermissionAsShellUid();
+ // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED
+ assertThrows(SecurityException.class,
+ () -> mBluetoothVolumeControl.registerCallback(mTestExecutor, mTestCallback));
+
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+ }
+
+ public void testSetVolumeOffset() {
+ if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothVolumeControl);
+
+ mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+
+ try {
+ mBluetoothVolumeControl.setVolumeOffset(mTestDevice, 0);
+ } catch (Exception e) {
+ fail("Exception caught from connect(): " + e.toString());
+ }
+ }
+
+ public void testIsVolumeOffsetAvailable() {
+ if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothVolumeControl);
+
+ mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+ // Verify returns false if bluetooth is not enabled
+ assertTrue(!mBluetoothVolumeControl.isVolumeOffsetAvailable(mTestDevice));
+ }
+
+ public void testVolumeOffsetCallback() {
+ if (!(mHasBluetooth && mIsVolumeControlSupported)) return;
+
+ assertTrue(waitForProfileConnect());
+ assertNotNull(mBluetoothVolumeControl);
+
+ mTestDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+ mVolumeOffsetChangedCallbackCalled = false;
+
+ /* Note. This is just for api coverage until proper testing tools are set up */
+ mTestVolumeOffset = 1;
+ mTestCallback.onVolumeOffsetChanged(mTestDevice, mTestVolumeOffset);
+ assertTrue(mVolumeOffsetChangedCallbackCalled);
+ }
+
+ private boolean waitForProfileConnect() {
+ mProfileConnectedlock.lock();
+ try {
+ // Wait for the Adapter to be disabled
+ while (!mIsProfileReady) {
+ if (!mConditionProfileIsConnected.await(
+ PROXY_CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ // Timeout
+ Log.e(TAG, "Timeout while waiting for Profile Connect");
+ break;
+ } // else spurious wakeups
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "waitForProfileConnect: interrrupted");
+ } finally {
+ mProfileConnectedlock.unlock();
+ }
+ return mIsProfileReady;
+ }
+
+ private final class BluetoothVolumeControlServiceListener implements
+ BluetoothProfile.ServiceListener {
+
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ mProfileConnectedlock.lock();
+ mBluetoothVolumeControl = (BluetoothVolumeControl) proxy;
+ mIsProfileReady = true;
+ try {
+ mConditionProfileIsConnected.signal();
+ } finally {
+ mProfileConnectedlock.unlock();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ }
+ }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
index 7a227fc..2ed393f 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
@@ -83,6 +83,9 @@
if (!isBleSupported()) return;
mIsBleSupported = true;
+ mIsHearingAidSupported = TestUtils.isProfileEnabled(BluetoothProfile.HEARING_AID);
+ if (!mIsHearingAidSupported) return;
+
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
@@ -95,17 +98,18 @@
mConditionProfileIsConnected = mProfileConnectedlock.newCondition();
mIsProfileReady = false;
mService = null;
- mIsHearingAidSupported = mBluetoothAdapter.getProfileProxy(getContext(),
- new HearingAidsServiceListener(),
- BluetoothProfile.HEARING_AID);
- if (!mIsHearingAidSupported) return;
+ mBluetoothAdapter.getProfileProxy(getContext(), new HearingAidsServiceListener(),
+ BluetoothProfile.HEARING_AID);
}
@Override
public void tearDown() {
- if (!mIsBleSupported) return;
-
- assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
+ if (!(mIsBleSupported && mIsHearingAidSupported)) {
+ return;
+ }
+ if (mBluetoothAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
+ }
mUiAutomation.dropShellPermissionIdentity();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
index d152d84..62d1c6a 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
@@ -51,7 +51,9 @@
if (!TestUtils.isBleSupported(getContext())) {
return;
}
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
index 40d89fd..34bc0a3 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
@@ -16,9 +16,7 @@
package android.bluetooth.cts;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,8 +26,8 @@
import android.bluetooth.le.ScanRecord;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.provider.Settings;
+import android.sysprop.BluetoothProperties;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -50,93 +48,6 @@
static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth.services";
/**
- * Get the Config.xml name tag for a particular Bluetooth profile
- * @param profile profile id from {@link BluetoothProfile}
- * @return config name tag, or null if the tag name is not available
- */
- @Nullable static String profileIdToConfigTag(int profile) {
- switch (profile) {
- case BluetoothProfile.A2DP:
- return "profile_supported_a2dp";
- case BluetoothProfile.A2DP_SINK:
- return "profile_supported_a2dp_sink";
- case BluetoothProfile.HEADSET:
- return "profile_supported_hs_hfp";
- case BluetoothProfile.HEADSET_CLIENT:
- return "profile_supported_hfpclient";
- case BluetoothProfile.HID_HOST:
- return "profile_supported_hid_host";
- case BluetoothProfile.OPP:
- return "profile_supported_opp";
- case BluetoothProfile.PAN:
- return "profile_supported_pan";
- case BluetoothProfile.PBAP:
- return "profile_supported_pbap";
- case BluetoothProfile.GATT:
- return "profile_supported_gatt";
- case BluetoothProfile.MAP:
- return "profile_supported_map";
- // Hidden profile
- // case BluetoothProfile.AVRCP:
- // return "profile_supported_avrcp_target";
- case BluetoothProfile.AVRCP_CONTROLLER:
- return "profile_supported_avrcp_controller";
- case BluetoothProfile.SAP:
- return "profile_supported_sap";
- case BluetoothProfile.PBAP_CLIENT:
- return "profile_supported_pbapclient";
- case BluetoothProfile.MAP_CLIENT:
- return "profile_supported_mapmce";
- case BluetoothProfile.HID_DEVICE:
- return "profile_supported_hid_device";
- case BluetoothProfile.LE_AUDIO:
- return "profile_supported_le_audio";
- case BluetoothProfile.LE_AUDIO_BROADCAST:
- return "profile_supported_le_audio_broadcast";
- case BluetoothProfile.VOLUME_CONTROL:
- return "profile_supported_vc";
- // Hidden profile
- // case BluetoothProfile.MCP_SERVER:
- // return "profile_supported_mcp_server";
- case BluetoothProfile.CSIP_SET_COORDINATOR:
- return "profile_supported_csip_set_coordinator";
- // Hidden profile
- // case BluetoothProfile.LE_CALL_CONTROL:
- // return "profile_supported_le_call_control";
- case BluetoothProfile.HAP_CLIENT:
- return "profile_supported_hap_client";
- case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT:
- return "profile_supported_bass_client";
- default:
- return null;
- }
- }
-
- /**
- * Checks if a particular Bluetooth profile is configured for this device
- * Fail the test if profile config status cannot be obtained
- */
- static boolean getProfileConfigValueOrDie(int profile) {
- String profileConfigValueTag = profileIdToConfigTag(profile);
- assertNotNull(profileConfigValueTag);
- assertNotEquals("profile tag cannot be empty", 0, profileConfigValueTag.length());
- Context context = InstrumentationRegistry.getInstrumentation().getContext();
- Resources bluetoothResources = null;
- try {
- bluetoothResources = context.getPackageManager().getResourcesForApplication(
- BLUETOOTH_PACKAGE_NAME);
- } catch (PackageManager.NameNotFoundException e) {
- fail("Cannot get Bluetooth package resource");
- }
- int resourceId = bluetoothResources.getIdentifier(
- profileConfigValueTag, "bool", BLUETOOTH_PACKAGE_NAME);
- if (resourceId == 0) {
- return false;
- }
- return bluetoothResources.getBoolean(resourceId);
- }
-
- /**
* Checks whether this device has Bluetooth feature
* @return true if this device has Bluetooth feature
*/
@@ -147,10 +58,74 @@
}
/**
+ * Get the current enabled status of a given profile
+ */
+ static boolean isProfileEnabled(int profile) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false);
+ case BluetoothProfile.A2DP_SINK:
+ return BluetoothProperties.isProfileA2dpSinkEnabled().orElse(false);
+ // Hidden profile
+ // case BluetoothProfile.AVRCP:
+ // return BluetoothProperties.isProfileAvrcpTargetEnabled().orElse(false);
+ case BluetoothProfile.AVRCP_CONTROLLER:
+ return BluetoothProperties.isProfileAvrcpControllerEnabled().orElse(false);
+ case BluetoothProfile.CSIP_SET_COORDINATOR:
+ return BluetoothProperties.isProfileCsipSetCoordinatorEnabled().orElse(false);
+ case BluetoothProfile.GATT:
+ return BluetoothProperties.isProfileGattEnabled().orElse(false);
+ case BluetoothProfile.HAP_CLIENT:
+ return BluetoothProperties.isProfileHapClientEnabled().orElse(false);
+ case BluetoothProfile.HEADSET:
+ return BluetoothProperties.isProfileHfpAgEnabled().orElse(false);
+ case BluetoothProfile.HEADSET_CLIENT:
+ return BluetoothProperties.isProfileHfpHfEnabled().orElse(false);
+ case BluetoothProfile.HEARING_AID:
+ return BluetoothProperties.isProfileAshaCentralEnabled().orElse(false);
+ case BluetoothProfile.HID_DEVICE:
+ return BluetoothProperties.isProfileHidDeviceEnabled().orElse(false);
+ case BluetoothProfile.HID_HOST:
+ return BluetoothProperties.isProfileHidHostEnabled().orElse(false);
+ case BluetoothProfile.LE_AUDIO:
+ return BluetoothProperties.isProfileBapUnicastServerEnabled().orElse(false);
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ return BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false);
+ case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT:
+ return BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false);
+ // Hidden profile
+ // case BluetoothProfile.LE_CALL_CONTROL:
+ // return BluetoothProperties.isProfileTbsServerEnabled().orElse(false);
+ case BluetoothProfile.MAP:
+ return BluetoothProperties.isProfileMapServerEnabled().orElse(false);
+ case BluetoothProfile.MAP_CLIENT:
+ return BluetoothProperties.isProfileMapClientEnabled().orElse(false);
+ // Hidden profile
+ // case BluetoothProfile.MCP_SERVER:
+ // return BluetoothProperties.isProfileMcpServerEnabled().orElse(false);
+ case BluetoothProfile.OPP:
+ return BluetoothProperties.isProfileOppEnabled().orElse(false);
+ case BluetoothProfile.PAN:
+ return BluetoothProperties.isProfilePanNapEnabled().orElse(false)
+ || BluetoothProperties.isProfilePanPanuEnabled().orElse(false);
+ case BluetoothProfile.PBAP:
+ return BluetoothProperties.isProfilePbapServerEnabled().orElse(false);
+ case BluetoothProfile.PBAP_CLIENT:
+ return BluetoothProperties.isProfilePbapClientEnabled().orElse(false);
+ case BluetoothProfile.SAP:
+ return BluetoothProperties.isProfileSapServerEnabled().orElse(false);
+ case BluetoothProfile.VOLUME_CONTROL:
+ return BluetoothProperties.isProfileVcServerEnabled().orElse(false);
+ default:
+ return false;
+ }
+ }
+
+ /**
* Adopt shell UID's permission via {@link android.app.UiAutomation}
* @param permission permission to adopt
*/
- static void adoptPermissionAsShellUid(@NonNull String permission) {
+ static void adoptPermissionAsShellUid(@Nullable String... permission) {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(permission);
}
diff --git a/tests/tests/car_builtin/src/android/car/cts/builtin/os/BuildHelperTest.java b/tests/tests/car_builtin/src/android/car/cts/builtin/os/BuildHelperTest.java
index aab86c7..218486b 100644
--- a/tests/tests/car_builtin/src/android/car/cts/builtin/os/BuildHelperTest.java
+++ b/tests/tests/car_builtin/src/android/car/cts/builtin/os/BuildHelperTest.java
@@ -42,7 +42,6 @@
assertTrue(BuildHelper.isUserBuild());
assertFalse(BuildHelper.isUserDebugBuild());
assertFalse(BuildHelper.isEngBuild());
- assertFalse(BuildHelper.isDebuggableBuild());
break;
case BUILD_TYPE_USER_DEBUG:
assertFalse(BuildHelper.isUserBuild());
diff --git a/tests/tests/car_builtin/src/android/car/cts/builtin/provider/SettingsHelperTest.java b/tests/tests/car_builtin/src/android/car/cts/builtin/provider/SettingsHelperTest.java
new file mode 100644
index 0000000..e6c21f2
--- /dev/null
+++ b/tests/tests/car_builtin/src/android/car/cts/builtin/provider/SettingsHelperTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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.car.cts.builtin.provider;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.car.builtin.provider.SettingsHelper;
+
+import org.junit.Test;
+
+public final class SettingsHelperTest {
+
+ @Test
+ public void testConstants() {
+ assertWithMessage("SYSTEM_LOCALES").that(SettingsHelper.SYSTEM_LOCALES)
+ .isEqualTo("system_locales");
+ }
+}
diff --git a/tests/tests/car_builtin/src/android/car/cts/builtin/util/AssistUtilsHelperTest.java b/tests/tests/car_builtin/src/android/car/cts/builtin/util/AssistUtilsHelperTest.java
index ecd867b..eb5b0e9 100644
--- a/tests/tests/car_builtin/src/android/car/cts/builtin/util/AssistUtilsHelperTest.java
+++ b/tests/tests/car_builtin/src/android/car/cts/builtin/util/AssistUtilsHelperTest.java
@@ -16,7 +16,7 @@
package android.car.cts.builtin.util;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.app.Instrumentation;
import android.car.builtin.util.AssistUtilsHelper;
@@ -26,10 +26,13 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -42,23 +45,27 @@
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final Context mContext = mInstrumentation.getContext();
+ @Before
+ public void setUp() throws Exception {
+ mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
+ PERMISSION_ACCESS_VOICE_INTERACTION_SERVICE);
+ }
+
+ @After
+ public void cleanUp() {
+ mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
+ }
+
@Test
public void testOnShownCallback() throws Exception {
- try {
- // setup
- mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
- PERMISSION_ACCESS_VOICE_INTERACTION_SERVICE);
+ SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
+ AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
+ callbackHelperImpl.waitForCallback();
- // execution
- SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
- AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
- callbackHelperImpl.waitForCallbackLatch();
+ assertWithMessage("Voice session shown")
+ .that(callbackHelperImpl.isSessionOnShown()).isTrue();
- // assertion
- assertTrue(callbackHelperImpl.isSessionOnShown());
- } finally {
- mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
- }
+ hideSessionAndWait();
}
@Test
@@ -67,7 +74,98 @@
// call onFailed API
}
- private static class SessionShowCallbackHelperImpl implements
+ @Test
+ public void isSessionRunning_whenSessionIsShown_succeeds() throws Exception {
+ SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
+ AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
+ callbackHelperImpl.waitForCallback();
+
+ assertWithMessage("Voice interaction session running")
+ .that(AssistUtilsHelper.isSessionRunning(mContext)).isTrue();
+
+ hideSessionAndWait();
+ }
+
+ @Test
+ public void registerVoiceInteractionSessionListenerHelper_onShowSession() throws Exception {
+ VoiceInteractionSessionListener listener = new VoiceInteractionSessionListener();
+ AssistUtilsHelper.registerVoiceInteractionSessionListenerHelper(mContext, listener);
+
+ SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
+ AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
+ callbackHelperImpl.waitForCallback();
+
+ listener.waitForSessionChange();
+
+ assertWithMessage("Voice interaction session shown")
+ .that(listener.mIsSessionShown).isTrue();
+
+ hideSessionAndWait();
+ }
+
+ @Test
+ public void registerVoiceInteractionSessionListenerHelper_hideCurrentSession()
+ throws Exception {
+ VoiceInteractionSessionListener listener = new VoiceInteractionSessionListener();
+ AssistUtilsHelper.registerVoiceInteractionSessionListenerHelper(mContext, listener);
+
+ SessionShowCallbackHelperImpl callbackHelperImpl = new SessionShowCallbackHelperImpl();
+ AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, callbackHelperImpl);
+ callbackHelperImpl.waitForCallback();
+
+ listener.waitForSessionChange();
+ listener.reset();
+
+ AssistUtilsHelper.hideCurrentSession(mContext);
+ listener.waitForSessionChange();
+
+ assertWithMessage("Voice interaction session shown")
+ .that(listener.mIsSessionShown).isFalse();
+ }
+
+ private void hideSessionAndWait() throws Exception {
+ if (!AssistUtilsHelper.isSessionRunning(mContext)) {
+ return;
+ }
+ VoiceInteractionSessionListener listener = new VoiceInteractionSessionListener();
+ AssistUtilsHelper.registerVoiceInteractionSessionListenerHelper(mContext, listener);
+
+ listener.reset();
+ listener.waitForSessionChange();
+ }
+
+ private static final class VoiceInteractionSessionListener implements
+ AssistUtilsHelper.VoiceInteractionSessionListenerHelper {
+
+ private final Semaphore mChangeWait = new Semaphore(0);
+ private boolean mIsSessionShown;
+
+ @Override
+ public void onVoiceSessionShown() {
+ mIsSessionShown = true;
+ Log.d(TAG, "onVoiceSessionShown is called");
+ mChangeWait.release();
+ }
+
+ @Override
+ public void onVoiceSessionHidden() {
+ mIsSessionShown = false;
+ Log.d(TAG, "onVoiceSessionHidden is called");
+ mChangeWait.release();
+ }
+
+ private void waitForSessionChange() throws Exception {
+ if (!mChangeWait.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ throw new IllegalStateException("Timed out waiting for session change");
+ }
+ }
+
+ private void reset() {
+ mChangeWait.drainPermits();
+ }
+ }
+
+ private static final class SessionShowCallbackHelperImpl implements
AssistUtilsHelper.VoiceInteractionSessionShowCallbackHelper {
private final CountDownLatch mCallbackLatch = new CountDownLatch(1);
@@ -87,7 +185,7 @@
return mIsSessionOnShown;
}
- private void waitForCallbackLatch() throws Exception {
+ private void waitForCallback() throws Exception {
mCallbackLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
}
}
diff --git a/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java b/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
index 2001aa3..a907340 100644
--- a/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
+++ b/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
@@ -48,7 +48,7 @@
*/
public final class GameServiceTestService extends Service {
- private static final long SCREENSHOT_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
+ private static final long SCREENSHOT_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(15);
@Nullable
private ActivityResult mLastActivityResult;
diff --git a/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java b/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
index bf17876..fb3d7ed 100644
--- a/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java
@@ -50,7 +50,7 @@
@RunWith(AndroidJUnit4.class)
public class SystemPaletteTest {
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final String TAG = "SystemPaletteTest";
@Test
@@ -87,7 +87,7 @@
});
final int[] allColors = new int[65];
- new PollingCheck(5_000L, "Invalid tonal palettes for " + color + " " + style) {
+ new PollingCheck(15_000L, "Invalid tonal palettes for " + color + " " + style) {
@Override
protected boolean check() {
@@ -98,8 +98,12 @@
System.arraycopy(getAllNeutral2Colors(context), 0, allColors, 52, 13);
if (DEBUG) {
+ final String setting = Settings.Secure
+ .getString(context.getContentResolver(),
+ Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES);
Log.d(TAG, "Expected:\n" + Arrays.toString(expectedPalette)
- + "\nActual:\n" + Arrays.toString(allColors));
+ + "\nActual:\n" + Arrays.toString(allColors)
+ + "\nSetting:\n" + setting);
}
return Arrays.equals(allColors, expectedPalette);
diff --git a/tests/tests/keystore/Android.bp b/tests/tests/keystore/Android.bp
index a7e67e3..076ce21 100644
--- a/tests/tests/keystore/Android.bp
+++ b/tests/tests/keystore/Android.bp
@@ -62,6 +62,7 @@
"android.test.base",
],
static_libs: [
+ "Nene",
"androidx.test.rules",
"compatibility-device-util-axt",
"core-tests-support",
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index bd9a64a..3e3a3c0 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -64,6 +64,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.keystore.cts.Attestation;
import android.keystore.cts.util.TestUtils;
import android.os.Build;
import android.os.SystemProperties;
@@ -80,6 +81,8 @@
import androidx.test.filters.RequiresDevice;
import androidx.test.runner.AndroidJUnit4;
+import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.permissions.PermissionContext;
import com.android.compatibility.common.util.CddTest;
import com.google.common.collect.ImmutableSet;
@@ -490,6 +493,54 @@
}
}
+ @Test
+ public void testEcAttestation_UniqueIdWorksWithCorrectPermission() throws Exception {
+ String keystoreAlias = "test_key";
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+ .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+ .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setAttestationChallenge(new byte[128])
+ .setUniqueIdIncluded(true)
+ .build();
+
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+
+ try (PermissionContext c = TestApis.permissions().withPermission(
+ "android.permission.REQUEST_UNIQUE_ID_ATTESTATION")) {
+ generateKeyPair(KEY_ALGORITHM_EC, spec);
+ Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
+ Attestation attestation = Attestation.loadFromCertificate((X509Certificate) certificates[0]);
+ byte[] firstUniqueId = attestation.getUniqueId();
+ assertTrue("UniqueId must not be empty", firstUniqueId.length > 0);
+
+ // The unique id rotates (30 days in the default implementation), and it's possible to
+ // get a spurious failure if the test runs exactly when the rotation occurs. Allow a
+ // single retry, just in case.
+ byte[] secondUniqueId = null;
+ for (int i = 0; i < 2; ++i) {
+ keyStore.deleteEntry(keystoreAlias);
+
+ generateKeyPair(KEY_ALGORITHM_EC, spec);
+ certificates = keyStore.getCertificateChain(keystoreAlias);
+ attestation = Attestation.loadFromCertificate((X509Certificate) certificates[0]);
+ secondUniqueId = attestation.getUniqueId();
+
+ if (Arrays.equals(firstUniqueId, secondUniqueId)) {
+ break;
+ } else {
+ firstUniqueId = secondUniqueId;
+ secondUniqueId = null;
+ }
+ }
+ assertTrue("UniqueIds must be consistent",
+ Arrays.equals(firstUniqueId, secondUniqueId));
+
+ } finally {
+ keyStore.deleteEntry(keystoreAlias);
+ }
+ }
+
@RequiresDevice
@Test
public void testRsaAttestation() throws Exception {
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
index ff1944e..7412d16 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
@@ -23,6 +23,7 @@
import android.media.MediaFormat;
import android.media.cts.InputSurface;
import android.media.cts.OutputSurface;
+import android.media.cts.TestArgs;
import android.opengl.GLES20;
import android.util.Log;
@@ -126,15 +127,35 @@
final List<Object[]> argsList = new ArrayList<>();
int argLength = exhaustiveArgsList.get(0).length;
for (Object[] arg : exhaustiveArgsList) {
- String[] encoderNamesForMime = MediaUtils.getEncoderNamesForMime((String)arg[0]);
- String[] decoderNamesForMime = MediaUtils.getDecoderNamesForMime((String)arg[0]);
- Object[] testArgs = new Object[argLength + 2];
- // Add encoder name and decoder name as first two arguments and then
- // copy arguments passed
- testArgs[0] = encoderNamesForMime[0];
- testArgs[1] = decoderNamesForMime[0];
- System.arraycopy(arg, 0, testArgs, 2, argLength);
- argsList.add(testArgs);
+ String mediaType = (String)arg[0];
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+ String[] encoderNames = MediaUtils.getEncoderNamesForMime(mediaType);
+ String[] decoderNames = MediaUtils.getDecoderNamesForMime(mediaType);
+ // First pair of decoder and encoder that supports given mediaType is chosen
+ outerLoop:
+ for (String decoder : decoderNames) {
+ if (TestArgs.CODEC_PREFIX != null && !decoder.startsWith(TestArgs.CODEC_PREFIX)) {
+ continue;
+ }
+ for (String encoder : encoderNames) {
+ if (TestArgs.CODEC_PREFIX != null &&
+ !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
+ continue;
+ }
+ Object[] testArgs = new Object[argLength + 2];
+ // Add encoder name and decoder name as first two arguments and then
+ // copy arguments passed
+ testArgs[0] = encoder;
+ testArgs[1] = decoder;
+ System.arraycopy(arg, 0, testArgs, 2, argLength);
+ argsList.add(testArgs);
+ // Only one combination of encoder and decoder is tested
+ break outerLoop;
+ }
+ }
}
return argsList;
}
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
index 4a023a0..be4c935 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
@@ -28,6 +28,7 @@
import android.media.cts.NdkMediaCodec;
import android.media.cts.OutputSurface;
import android.media.cts.SdkMediaCodec;
+import android.media.cts.TestArgs;
import android.opengl.GLES20;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresDevice;
@@ -128,17 +129,41 @@
int argLength = exhaustiveArgsList.get(0).length;
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (Object[] arg : exhaustiveArgsList) {
- MediaFormat format = MediaFormat.createVideoFormat((String)arg[0], (Integer)arg[1],
+ String mediaType = (String)arg[0];
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+
+ MediaFormat format = MediaFormat.createVideoFormat(mediaType, (Integer)arg[1],
(Integer)arg[2]);
- String eName = mcl.findEncoderForFormat(format);
- String dName = mcl.findDecoderForFormat(format);
-
- Object[] testArgs = new Object[argLength + 2];
- testArgs[0] = eName;
- testArgs[1] = dName;
- System.arraycopy(arg, 0, testArgs, 2, argLength);
- argsList.add(testArgs);
+ String[] encoderNames = MediaUtils.getEncoderNamesForMime(mediaType);
+ String[] decoderNames = MediaUtils.getDecoderNamesForMime(mediaType);
+ // First pair of decoder and encoder that supports given format is chosen
+ outerLoop:
+ for (String decoder : decoderNames) {
+ if (TestArgs.CODEC_PREFIX != null && !decoder.startsWith(TestArgs.CODEC_PREFIX)) {
+ continue;
+ }
+ for (String encoder : encoderNames) {
+ if (TestArgs.CODEC_PREFIX != null &&
+ !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
+ continue;
+ }
+ if (MediaUtils.supports(encoder, format) &&
+ MediaUtils.supports(decoder, format)) {
+ Object[] testArgs = new Object[argLength + 2];
+ testArgs[0] = encoder;
+ testArgs[1] = decoder;
+ System.arraycopy(arg, 0, testArgs, 2, argLength);
+ argsList.add(testArgs);
+ // Test only the first codecs that support given format.
+ // Remove the following break statement to test all codecs on the device.
+ break outerLoop;
+ }
+ }
+ }
}
return argsList;
}
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
index 5ef9e7f..ecdb084 100755
--- a/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
@@ -30,6 +30,7 @@
import android.media.cts.InputSurface;
import android.media.cts.NonMediaMainlineTest;
import android.media.cts.OutputSurface;
+import android.media.cts.TestArgs;
import android.opengl.GLES20;
import android.os.Build;
import android.os.Bundle;
@@ -95,8 +96,6 @@
private static final boolean VERBOSE = false; // lots of logging
private static final boolean DEBUG_SAVE_FILE = false; // save copy of encoded movie
private static final String DEBUG_FILE_NAME_BASE = "/sdcard/test.";
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
- private static String mCodecPrefix;
// Virtual display characteristics. Scaled down from full display size because not all
// devices can encode at the resolution of their own display.
@@ -161,9 +160,14 @@
final List<Object[]> argsList = new ArrayList<>();
int argLength = exhaustiveArgsList.get(0).length;
for (Object[] arg : exhaustiveArgsList) {
- String[] componentNames = MediaUtils.getEncoderNamesForMime((String)arg[0]);
+ String mediaType = (String)arg[0];
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+ String[] componentNames = MediaUtils.getEncoderNamesForMime(mediaType);
for (String name : componentNames) {
- if (mCodecPrefix != null && !name.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null && !name.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
Object[] testArgs = new Object[argLength + 1];
@@ -176,11 +180,6 @@
return argsList;
}
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
-
@Parameterized.Parameters(name = "{index}({0}:{6})")
public static Collection<Object[]> input() {
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
index 1dd7651..61e8e33 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
@@ -21,6 +21,7 @@
import android.media.MediaFormat;
import android.media.cts.MediaCodecWrapper;
import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.TestArgs;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
@@ -96,9 +97,6 @@
// Maximum allowed key frame interval variation from the target value.
private static final int MAX_KEYFRAME_INTERVAL_VARIATION = 3;
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
- private static final String mCodecPrefix;
-
@Parameterized.Parameter(0)
public String mCodecName;
@@ -108,18 +106,18 @@
@Parameterized.Parameter(2)
public int mBitRateMode;
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
-
static private List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) {
final List<Object[]> argsList = new ArrayList<>();
int argLength = exhaustiveArgsList.get(0).length;
for (Object[] arg : exhaustiveArgsList) {
- String[] encodersForMime = MediaUtils.getEncoderNamesForMime((String) arg[0]);
+ String mediaType = (String)arg[0];
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+ String[] encodersForMime = MediaUtils.getEncoderNamesForMime(mediaType);
for (String encoder : encodersForMime) {
- if (mCodecPrefix != null && !encoder.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null && !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
Object[] testArgs = new Object[argLength + 1];
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
index 2cd4632..ab46ff1 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
@@ -20,6 +20,7 @@
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.cts.NonMediaMainlineTest;
+import android.media.cts.TestArgs;
import android.platform.test.annotations.RequiresDevice;
import android.util.Log;
import android.util.Size;
@@ -73,14 +74,24 @@
if (info.isAlias() || info.isEncoder()) {
continue;
}
+ String name = info.getName();
+ if (TestArgs.CODEC_PREFIX != null && !name.startsWith(TestArgs.CODEC_PREFIX)) {
+ continue;
+ }
+
for (String type : info.getSupportedTypes()) {
if (!SUPPORTED_TYPES.contains(type)) {
continue;
}
- testParams.add(new Object[] { info.getName(), type, Integer.valueOf(90) });
- testParams.add(new Object[] { info.getName(), type, Integer.valueOf(180) });
- testParams.add(new Object[] { info.getName(), type, Integer.valueOf(270) });
- testParams.add(new Object[] { info.getName(), type, Integer.valueOf(360) });
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !type.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+
+ testParams.add(new Object[] { name, type, Integer.valueOf(90) });
+ testParams.add(new Object[] { name, type, Integer.valueOf(180) });
+ testParams.add(new Object[] { name, type, Integer.valueOf(270) });
+ testParams.add(new Object[] { name, type, Integer.valueOf(360) });
}
}
return testParams;
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/VideoEncodingStatisticsTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoEncodingStatisticsTest.java
index a6e587a..eb3bcf3 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/VideoEncodingStatisticsTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoEncodingStatisticsTest.java
@@ -23,6 +23,7 @@
import android.media.MediaFormat;
import android.media.cts.MediaCodecWrapper;
import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.TestArgs;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
@@ -69,9 +70,6 @@
// List of bitrates used in quality and basic bitrate tests.
private static final int[] TEST_BITRATES_SET = { 300000, 500000, 700000, 900000 };
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
- private static final String mCodecPrefix;
-
@Parameterized.Parameter(0)
public String mCodecName;
@@ -81,18 +79,18 @@
@Parameterized.Parameter(2)
public int mBitRateMode;
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
-
static private List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) {
final List<Object[]> argsList = new ArrayList<>();
int argLength = exhaustiveArgsList.get(0).length;
for (Object[] arg : exhaustiveArgsList) {
- String[] encodersForMime = MediaUtils.getEncoderNamesForMime((String) arg[0]);
+ String mediaType = (String)arg[0];
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+ String[] encodersForMime = MediaUtils.getEncoderNamesForMime(mediaType);
for (String encoder : encodersForMime) {
- if (mCodecPrefix != null && !encoder.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null && !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
Object[] testArgs = new Object[argLength + 1];
diff --git a/tests/tests/media/common/src/android/media/cts/TestArgs.java b/tests/tests/media/common/src/android/media/cts/TestArgs.java
new file mode 100644
index 0000000..a470d41
--- /dev/null
+++ b/tests/tests/media/common/src/android/media/cts/TestArgs.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 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.cts;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+/**
+ * Contains arguments passed to the tests.
+ */
+public final class TestArgs {
+ private static final String CODEC_PREFIX_KEY = "codec-prefix";
+ public static final String CODEC_PREFIX;
+ private static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix";
+ public static final String MEDIA_TYPE_PREFIX;
+
+ static {
+ android.os.Bundle args = InstrumentationRegistry.getArguments();
+ CODEC_PREFIX = args.getString(CODEC_PREFIX_KEY);
+ MEDIA_TYPE_PREFIX = args.getString(MEDIA_TYPE_PREFIX_KEY);
+ }
+}
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
index e9b1093..32d2762 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
@@ -30,6 +30,7 @@
import android.media.cts.MediaTestBase;
import android.media.cts.OutputSurface;
import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
import android.os.Build;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
@@ -90,20 +91,12 @@
super.tearDown();
}
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
- private static final String mCodecPrefix;
-
@Parameterized.Parameter(0)
public String mCodecName;
@Parameterized.Parameter(1)
public CodecList mCodecs;
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
-
public static Iterable<Codec> H264(CodecFactory factory) {
return factory.createCodecList(
MediaFormat.MIMETYPE_VIDEO_AVC,
@@ -277,7 +270,8 @@
if (arg instanceof CodecList) {
CodecList codecList = (CodecList)arg;
for (Codec codec : codecList) {
- if (mCodecPrefix != null && !codec.name.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null &&
+ !codec.name.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
Object[] testArgs = new Object[2];
@@ -1542,6 +1536,11 @@
public CodecFamily(String mime, final String ... resources) {
try {
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mime.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ return;
+ }
+
/* read all media */
Media[] mediaList = new Media[resources.length];
for (int i = 0; i < resources.length; i++) {
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
index 0f3281c..6a2a0c3 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
@@ -27,6 +27,7 @@
import android.media.MediaFormat;
import android.media.cts.MediaCodecTunneledPlayer;
import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.TestArgs;
import android.os.Environment;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
@@ -69,7 +70,6 @@
private static final int ALLOWED_GREATEST_PIXEL_DIFFERENCE = 90;
private static final int OFFSET = 10;
private static final long PER_TEST_TIMEOUT_MS = 60000;
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
private static final String[] VIDEO_FILES = {
// 144p
"video_decode_accuracy_and_capability-h264_256x108_30fps.mp4",
@@ -150,7 +150,6 @@
private static final String INP_PREFIX = WorkDir.getMediaDirString() +
"assets/decode_accuracy/";
- private static String mCodecPrefix;
private View videoView;
private VideoViewFactory videoViewFactory;
@@ -160,11 +159,6 @@
private String methodName;
private SimplePlayer player;
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
-
public DecodeAccuracyTest(String decoderName, String fileName, String testName) {
this.testName = testName;
this.fileName = fileName;
@@ -199,9 +193,14 @@
MediaFormat mediaFormat =
MediaUtils.getTrackFormatForResource(INP_PREFIX + file, "video");
String mediaType = mediaFormat.getString(MediaFormat.KEY_MIME);
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
String[] componentNames = MediaUtils.getDecoderNamesForMime(mediaType);
for (String componentName : componentNames) {
- if (mCodecPrefix != null && !componentName.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null &&
+ !componentName.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
if (MediaUtils.supports(componentName, mediaFormat)) {
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
index 0c79713..5cdafaf 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
@@ -24,6 +24,7 @@
import android.media.MediaFormat;
import android.media.cts.MediaTestBase;
import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
@@ -77,12 +78,10 @@
private static final String REPORT_LOG_NAME = "CtsMediaDecoderTestCases";
private static final String TAG = "DecoderConformanceTest";
private static final String CONFORMANCE_SUBDIR = "conformance_vectors/";
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
private static final String mInpPrefix = WorkDir.getMediaDirString() + CONFORMANCE_SUBDIR;
private static final Map<String, String> MIMETYPE_TO_TAG = new HashMap<String, String>() {{
put(MediaFormat.MIMETYPE_VIDEO_VP9, "vp9");
}};
- private static String mCodecPrefix;
private final String mDecoderName;
private final String mMediaType;
@@ -93,21 +92,20 @@
private DeviceReportLog mReportLog;
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
-
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() throws Exception {
final String[] mediaTypeList = new String[] {MediaFormat.MIMETYPE_VIDEO_VP9};
final List<Object[]> argsList = new ArrayList<>();
for (String mediaType : mediaTypeList) {
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
String[] componentNames = MediaUtils.getDecoderNamesForMime(mediaType);
List<String> testVectors = readCodecTestVectors(mediaType);
for (String testVector : testVectors) {
for (String name : componentNames) {
- if (mCodecPrefix != null && !name.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null && !name.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
argsList.add(new Object[] {name, mediaType, testVector});
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
index e2f5b25..d31e8b4 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
@@ -32,6 +32,7 @@
import android.media.MediaFormat;
import android.media.cts.CodecUtils;
import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
import android.os.Handler;
import android.os.HandlerThread;
import android.platform.test.annotations.AppModeFull;
@@ -129,8 +130,15 @@
final List<Object[]> argsList = new ArrayList<>();
for (MediaAssets assets : ASSETS) {
String mime = assets.getMime();
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mime.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
String[] decoders = MediaUtils.getDecoderNamesForMime(mime);
for (String decoder: decoders) {
+ if (TestArgs.CODEC_PREFIX != null && !decoder.startsWith(TestArgs.CODEC_PREFIX)) {
+ continue;
+ }
for (MediaAsset asset : assets.getAssets()) {
String id = asset.getWidth() + "x" + asset.getHeight();
id += "_" + asset.getBitDepth() + "bit";
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
index 61f6cdd..35d377e 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
@@ -28,6 +28,7 @@
import android.media.cts.MediaHeavyPresubmitTest;
import android.media.cts.MediaTestBase;
import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
import android.media.cts.TestUtils;
import android.os.Bundle;
import android.platform.test.annotations.AppModeFull;
@@ -85,9 +86,6 @@
private static final int MAX_SIZE_SAMPLES_IN_MEMORY_BYTES = 12 << 20; // 12MB
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
- private static final String mCodecPrefix;
-
private final String mDecoderName;
private final String mMediaType;
private final String[] mResources;
@@ -106,9 +104,14 @@
final List<Object[]> argsList = new ArrayList<>();
int argLength = exhaustiveArgsList.get(0).length;
for (Object[] arg : exhaustiveArgsList) {
- String[] decoders = MediaUtils.getDecoderNamesForMime((String) arg[0]);
+ String mediaType = (String)arg[0];
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+ String[] decoders = MediaUtils.getDecoderNamesForMime(mediaType);
for (String decoder : decoders) {
- if (mCodecPrefix != null && !decoder.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null && !decoder.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
Object[] testArgs = new Object[argLength + 1];
@@ -121,11 +124,6 @@
return argsList;
}
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
-
@Parameterized.Parameters(name = "{index}({0}:{3})")
public static Collection<Object[]> input() {
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
index c95ed69..226bf85 100644
--- a/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
@@ -21,6 +21,7 @@
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.RequiresDevice;
import android.util.Log;
@@ -84,9 +85,6 @@
private static boolean sSaveResults = false;
static final Map<String, String> mDefaultEncoders = new HashMap<>();
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
- private static String mCodecPrefix;
-
private final String mEncoderName;
private final String mMime;
private final int[] mProfiles;
@@ -111,9 +109,14 @@
final List<Object[]> argsList = new ArrayList<>();
int argLength = exhaustiveArgsList.get(0).length;
for (Object[] arg : exhaustiveArgsList) {
- String[] componentNames = MediaUtils.getEncoderNamesForMime((String)arg[0]);
+ String mediaType = (String)arg[0];
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+ String[] componentNames = MediaUtils.getEncoderNamesForMime(mediaType);
for (String name : componentNames) {
- if (mCodecPrefix != null && !name.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null && !name.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
Object[] testArgs = new Object[argLength + 1];
@@ -125,11 +128,6 @@
return argsList;
}
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
-
@Parameterized.Parameters(name = "{index}({0}_{1})")
public static Collection<Object[]> input() {
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
index b92f2ca..7b55e80 100644
--- a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
@@ -42,6 +42,7 @@
import android.media.cts.NonMediaMainlineTest;
import android.media.cts.OutputSurface;
import android.media.cts.Preconditions;
+import android.media.cts.TestArgs;
import android.net.Uri;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
@@ -100,9 +101,6 @@
private static final String SOURCE_URL =
mInpPrefix + "video_480x360_mp4_h264_871kbps_30fps.mp4";
- private static final String CODEC_PREFIX_KEY = "codec-prefix";
- private static final String mCodecPrefix;
-
private final Encoder mEncHandle;
private final boolean DEBUG = false;
@@ -119,10 +117,6 @@
super.tearDown();
}
- static {
- android.os.Bundle args = InstrumentationRegistry.getArguments();
- mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
- }
class VideoStorage {
private LinkedList<Pair<ByteBuffer, BufferInfo>> mStream;
@@ -1271,9 +1265,14 @@
final List<Object[]> argsList = new ArrayList<>();
int argLength = exhaustiveArgsList.get(0).length;
for (Object[] arg : exhaustiveArgsList) {
- String[] encodersForMime = MediaUtils.getEncoderNamesForMime((String) arg[0]);
+ String mediaType = (String)arg[0];
+ if (TestArgs.MEDIA_TYPE_PREFIX != null &&
+ !mediaType.startsWith(TestArgs.MEDIA_TYPE_PREFIX)) {
+ continue;
+ }
+ String[] encodersForMime = MediaUtils.getEncoderNamesForMime(mediaType);
for (String encoder : encodersForMime) {
- if (mCodecPrefix != null && !encoder.startsWith(mCodecPrefix)) {
+ if (TestArgs.CODEC_PREFIX != null && !encoder.startsWith(TestArgs.CODEC_PREFIX)) {
continue;
}
Object[] testArgs = new Object[argLength + 1];
diff --git a/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java b/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java
index 6061f4a..3345f56 100644
--- a/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java
+++ b/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java
@@ -179,7 +179,7 @@
}
@Test
- @SystemUserOnly(reason = "Profiles are only supported on system user.")
+ @RequireFeature(FEATURE_MANAGED_USERS)
@EnsureHasPermission({CREATE_USERS, QUERY_USERS})
public void testManagedProfile() throws Exception {
UserHandle userHandle = null;
@@ -187,7 +187,9 @@
try {
userHandle = mUserManager.createProfile(
"Managed profile", UserManager.USER_TYPE_PROFILE_MANAGED, new HashSet<>());
- assertThat(userHandle).isNotNull();
+
+ // Not all devices and user types support managed profiles; skip if this one doesn't.
+ assumeTrue("Couldn't create a managed profile", userHandle != null);
final UserManager umOfProfile = sContext
.createPackageContextAsUser("android", 0, userHandle)
@@ -208,21 +210,24 @@
@Test
@EnsureHasPermission({QUERY_USERS})
public void testSystemUser() throws Exception {
- final UserManager umOfSys = sContext
- .createPackageContextAsUser("android", 0, UserHandle.SYSTEM)
- .getSystemService(UserManager.class);
+ final UserManager umOfSys = sContext
+ .createPackageContextAsUser("android", 0, UserHandle.SYSTEM)
+ .getSystemService(UserManager.class);
- assertThat(umOfSys.isSystemUser()).isTrue();
+ // TODO(b/222584163): Remove the if{} clause after v33 Sdk bump.
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
+ assertThat(umOfSys.isSystemUser()).isTrue();
+ }
- // We cannot demand what type of user SYSTEM is, but we can say some things it isn't.
- assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE)).isFalse();
- assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_PROFILE_MANAGED)).isFalse();
- assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_FULL_GUEST)).isFalse();
+ // We cannot demand what type of user SYSTEM is, but we can say some things it isn't.
+ assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE)).isFalse();
+ assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_PROFILE_MANAGED)).isFalse();
+ assertThat(umOfSys.isUserOfType(UserManager.USER_TYPE_FULL_GUEST)).isFalse();
- assertThat(umOfSys.isProfile()).isFalse();
- assertThat(umOfSys.isManagedProfile()).isFalse();
- assertThat(umOfSys.isManagedProfile(UserHandle.USER_SYSTEM)).isFalse();
- assertThat(umOfSys.isCloneProfile()).isFalse();
+ assertThat(umOfSys.isProfile()).isFalse();
+ assertThat(umOfSys.isManagedProfile()).isFalse();
+ assertThat(umOfSys.isManagedProfile(UserHandle.USER_SYSTEM)).isFalse();
+ assertThat(umOfSys.isCloneProfile()).isFalse();
}
@Test
diff --git a/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
index 175a07c..3c99b34 100644
--- a/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
@@ -17,7 +17,6 @@
package android.permission.cts;
import static org.junit.Assert.assertThrows;
-import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;
@@ -83,22 +82,6 @@
}
/**
- * Verify that calling {@link EthernetManager#updateConfiguration(
- *String, EthernetNetworkUpdateRequest, Executor, BiConsumer)} requires automotive feature.
- * <p>Tests Feature:
- * {@link PackageManager#FEATURE_AUTOMOTIVE}.
- */
- @Test
- public void testUpdateConfigurationWithCapabilitiesRequiresAutomotiveFeature() {
- assumeFalse(mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE));
- assertThrows("Should not be able to update NetworkCapabilities without automotive feature",
- UnsupportedOperationException.class,
- () -> mEthernetManager.updateConfiguration(TEST_IFACE,
- buildUpdateRequest(), null, null));
- }
-
- /**
* Verify that calling {@link EthernetManager#enableInterface}
* requires permissions.
* <p>Tests Permission:
diff --git a/tests/tests/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java b/tests/tests/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java
index 1d678ed..700ca09 100644
--- a/tests/tests/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java
@@ -280,12 +280,12 @@
private void enableTestMode() {
runShellCommandOrThrow("dumpsys activity service"
- + " com.android.bluetooth/.btservice.AdapterService set-test-mode enabled");
+ + " com.android.bluetooth.btservice.AdapterService set-test-mode enabled");
}
private void disableTestMode() {
runShellCommandOrThrow("dumpsys activity service"
- + " com.android.bluetooth/.btservice.AdapterService set-test-mode disabled");
+ + " com.android.bluetooth.btservice.AdapterService set-test-mode disabled");
}
}
diff --git a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
index bd0c5dc..a117799 100755
--- a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
@@ -44,8 +44,6 @@
import com.android.compatibility.common.util.PropertyUtil;
import com.android.compatibility.common.util.SystemUtil;
-import com.google.common.collect.ImmutableSet;
-
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,27 +70,6 @@
@AppModeFull(reason = "This test test platform properties, not capabilities of an apps")
@RunWith(AndroidJUnit4.class)
public class PrivappPermissionsTest {
-
- // TODO(b/191740932): Migrate APK-in-APEX priv-app allowlists inside their respective modules,
- // so that {@code adb shell cmd package get-privapp-permissions <packageName>} lists them.
- private static final Set<String> APK_IN_APEX_ALLOWLIST_MIGRATION_BURNDOWN_LIST =
- ImmutableSet.of(
- // Updatable APEX packages
- "com.google.android.networkstack.tethering",
- "com.google.android.ext.services",
- "com.google.android.providers.media.module",
- "com.google.android.permissioncontroller",
- "com.google.android.cellbroadcastreceiver",
- "com.google.android.cellbroadcastservice",
- // AOSP APEX packages
- "com.android.networkstack.tethering",
- "com.android.ext.services",
- "com.android.providers.media.module",
- "com.android.permissioncontroller",
- "com.android.cellbroadcastreceiver.module",
- "com.android.cellbroadcastservice"
- );
-
private static final String TAG = "PrivappPermissionsTest";
private static final String PLATFORM_PACKAGE_NAME = "android";
@@ -130,12 +107,6 @@
continue;
}
- // Exempt apk-in-apexes that have not had their priv-app permission allowlist .xml
- // migrated inside their respective APEXes.
- if (APK_IN_APEX_ALLOWLIST_MIGRATION_BURNDOWN_LIST.contains(packageName)) {
- continue;
- }
-
PackageInfo factoryPkg = pm
.getPackageInfo(packageName, MATCH_FACTORY_ONLY | GET_PERMISSIONS
| MATCH_UNINSTALLED_PACKAGES);
diff --git a/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
index 5310f49..4a63df5 100644
--- a/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
@@ -289,7 +289,9 @@
launchApp()
clickPermissionRequestDenyButton()
waitForIdle()
- assertNotificationReviewRequiredState(shouldBeSet = false)
+ SystemUtil.eventually {
+ assertNotificationReviewRequiredState(shouldBeSet = false)
+ }
}
@Test
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
index 6e0f72c..f5d7fc1 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
@@ -87,15 +87,15 @@
fun testDenyGrantDenyCalendarDuringReview() {
startAppActivityAndAssertResultCode(Activity.RESULT_OK) {
// Deny
- click(By.text("Calendar"))
+ clickPermissionControllerUi(By.text("Calendar"))
// Confirm deny
click(By.res("android:id/button1"))
// Grant
- click(By.text("Calendar"))
+ clickPermissionControllerUi(By.text("Calendar"))
// Deny
- click(By.text("Calendar"))
+ clickPermissionControllerUi(By.text("Calendar"))
clickPermissionReviewContinue()
}
diff --git a/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt
index d39fb9c..0b88e01 100644
--- a/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt
@@ -30,6 +30,7 @@
import androidx.test.filters.SdkSuppress
import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import java.util.regex.Pattern
import org.junit.After
import org.junit.Assume
import org.junit.Before
@@ -43,6 +44,7 @@
companion object {
const val LOCATION = -1
const val WARNING_BANNER_ENABLED = "warning_banner_enabled"
+ const val DELAY_MILLIS = 3000L
}
val sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!!
@@ -129,7 +131,8 @@
android.os.Process.myUserHandle())
if (enable) {
try {
- waitFindObjectOrNull(By.text("CLOSE"))?.click()
+ val closePattern = Pattern.compile("close", Pattern.CASE_INSENSITIVE)
+ waitFindObjectOrNull(By.text(closePattern), DELAY_MILLIS)?.click()
} catch (e: Exception) {
// Do nothing, warning didn't show up so test can proceed
}
diff --git a/tests/tests/settings/src/android/settings/cts/TetherProvisioningCarrierDialogActivityTest.java b/tests/tests/settings/src/android/settings/cts/TetherProvisioningCarrierDialogActivityTest.java
index 51664ea..c47ba7a 100644
--- a/tests/tests/settings/src/android/settings/cts/TetherProvisioningCarrierDialogActivityTest.java
+++ b/tests/tests/settings/src/android/settings/cts/TetherProvisioningCarrierDialogActivityTest.java
@@ -17,6 +17,7 @@
package android.settings.cts;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -28,6 +29,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,6 +39,13 @@
*/
@RunWith(AndroidJUnit4.class)
public class TetherProvisioningCarrierDialogActivityTest {
+ @Before
+ public void setUp() throws Exception {
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+ assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ }
+
@Test
public void testTetheringProvisioningCarrierUiExisted() throws RemoteException {
final Intent intent = new Intent(Settings.ACTION_TETHER_UNSUPPORTED_CARRIER_UI);
diff --git a/tests/tests/taskfpscallback/Android.bp b/tests/tests/taskfpscallback/Android.bp
new file mode 100644
index 0000000..8a89132
--- /dev/null
+++ b/tests/tests/taskfpscallback/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 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: "CtsTaskFpsCallbackTestCases",
+ defaults: ["cts_defaults"],
+
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "compatibility-device-util-axt",
+ "ctsdeviceutillegacy-axt",
+ "ctstestrunner-axt",
+ "ctstestserver",
+ "junit",
+ "truth-prebuilt",
+ ],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "test_current",
+}
diff --git a/tests/tests/taskfpscallback/AndroidManifest.xml b/tests/tests/taskfpscallback/AndroidManifest.xml
new file mode 100644
index 0000000..f7bd875
--- /dev/null
+++ b/tests/tests/taskfpscallback/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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.taskfpscallback.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".TaskFpsCallbackCtsActivity"
+ android:label="TaskFpsCallbackCtsActivity"
+ android:visibleToInstantApps="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.taskfpscallback.cts"
+ android:label="CTS tests of android TaskFpsCallback service">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+</manifest>
diff --git a/tests/tests/taskfpscallback/AndroidTest.xml b/tests/tests/taskfpscallback/AndroidTest.xml
new file mode 100644
index 0000000..5a41eb4
--- /dev/null
+++ b/tests/tests/taskfpscallback/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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 TaskFpsCallback test cases">
+ <option name="test-suite-tag" value="cts" />
+
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <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" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsTaskFpsCallbackTestCases.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.taskfpscallback.cts" />
+ </test>
+</configuration>
diff --git a/tests/tests/taskfpscallback/OWNERS b/tests/tests/taskfpscallback/OWNERS
new file mode 100644
index 0000000..d0186b0
--- /dev/null
+++ b/tests/tests/taskfpscallback/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 878256
+lpy@google.com
+xwxw@google.com
diff --git a/tests/tests/taskfpscallback/TEST_MAPPING b/tests/tests/taskfpscallback/TEST_MAPPING
new file mode 100644
index 0000000..4db2309
--- /dev/null
+++ b/tests/tests/taskfpscallback/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsTaskFpsCallbackTestCases"
+ }
+ ]
+}
+
diff --git a/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsActivity.java b/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsActivity.java
new file mode 100644
index 0000000..284de4e
--- /dev/null
+++ b/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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.taskfpscallback.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class TaskFpsCallbackCtsActivity extends Activity {
+
+ private static final String TAG = "TaskFpsCallbackCtsActivity";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsTest.java b/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsTest.java
new file mode 100644
index 0000000..44cf60d
--- /dev/null
+++ b/tests/tests/taskfpscallback/src/android/taskfpscallback/cts/TaskFpsCallbackCtsTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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.taskfpscallback.cts;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertThrows;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.view.WindowManager;
+import android.window.TaskFpsCallback;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TaskFpsCallbackCtsTest {
+ private static final String TAG = "TaskFpsCallbackCtsTest";
+
+ private TaskFpsCallbackCtsActivity mActivity;
+ private Context mContext;
+ private WindowManager mWindowManager;
+
+ @Rule
+ public ActivityScenarioRule<TaskFpsCallbackCtsActivity> mActivityRule =
+ new ActivityScenarioRule<>(TaskFpsCallbackCtsActivity.class);
+
+ @Before
+ public void setUp() {
+ mActivityRule.getScenario().onActivity(activity -> {
+ mActivity = activity;
+ });
+
+ final Instrumentation instrumentation = getInstrumentation();
+ mContext = instrumentation.getContext();
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+ }
+
+ @Test
+ public void testRegister() throws Exception {
+ final TaskFpsCallback callback = new TaskFpsCallback() {
+ @Override
+ public void onFpsReported(float fps) {
+ // Ignore
+ }
+ };
+
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mWindowManager,
+ (windowManager) -> windowManager.registerTaskFpsCallback(
+ mActivity.getTaskId(), Runnable::run, callback),
+ "android.permission.ACCESS_FPS_COUNTER");
+
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mWindowManager,
+ (windowManager) -> windowManager.unregisterTaskFpsCallback(callback),
+ "android.permission.ACCESS_FPS_COUNTER");
+ }
+
+ @Test
+ public void testRegisterWithoutPermission() {
+ final TaskFpsCallback callback = new TaskFpsCallback() {
+ @Override
+ public void onFpsReported(float fps) {
+ // Ignore
+ }
+ };
+ assertThrows(SecurityException.class, () -> mWindowManager.registerTaskFpsCallback(
+ mActivity.getTaskId(), Runnable::run, callback));
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
index 57af2d1..f107d4b 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
@@ -577,19 +577,25 @@
return;
}
- mContext.getPackageManager().setApplicationEnabledSetting(CARMODE_APP1_PACKAGE,
- COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
- mInCallCallbacks.resetLatch();
+ try {
+ mContext.getPackageManager().setApplicationEnabledSetting(CARMODE_APP1_PACKAGE,
+ COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
+ mInCallCallbacks.resetLatch();
- // Place a call and verify it went to the default dialer
- placeAndVerifyCall();
- verifyConnectionForOutgoingCall();
+ // Place a call and verify it went to the default dialer
+ placeAndVerifyCall();
+ verifyConnectionForOutgoingCall();
- // Now, request automotive projection; shouldn't unbind from default dialer.
- requestAndVerifyAutomotiveProjection(mCarModeIncallServiceControlOne, true);
- assertFalse(mInCallCallbacks.waitForUnbind());
+ // Now, request automotive projection; shouldn't unbind from default dialer.
+ requestAndVerifyAutomotiveProjection(mCarModeIncallServiceControlOne, true);
+ assertFalse(mInCallCallbacks.waitForUnbind());
- releaseAutomotiveProjection(mCarModeIncallServiceControlOne);
+ releaseAutomotiveProjection(mCarModeIncallServiceControlOne);
+ mInCallCallbacks.getService().disconnectAllCalls();
+ } finally {
+ mContext.getPackageManager().setApplicationEnabledSetting(CARMODE_APP1_PACKAGE,
+ COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/InCallServiceFlagChecker.java b/tests/tests/telecom/src/android/telecom/cts/InCallServiceFlagChecker.java
new file mode 100644
index 0000000..33e611c
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/InCallServiceFlagChecker.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 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.telecom.cts;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * The CTS test class InCallServiceFlagChecker is meant to check flags passed into InCallController.
+ * Flags passed in InCallController can cause unwanted behavior.
+ */
+public class InCallServiceFlagChecker extends BaseTelecomTestWithMockServices {
+
+ public static final String LOG_TAG = InCallServiceFlagChecker.class.getName();
+ public static final String TARGET_SERVICE = ".MockInCallService:system";
+ public static final String DUMPSYS_COMMAND = "dumpsys activity services android.telecom.cts";
+ public static final String FLAGS_TEXT_MATCHER = "flags=0x";
+ public static final int FLAGS_OFFSET = FLAGS_TEXT_MATCHER.length();
+ public static final char SPACE_CHAR = ' ';
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ NewOutgoingCallBroadcastReceiver.reset();
+ if (mShouldTestTelecom) {
+ setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ TestUtils.clearSystemDialerOverride(getInstrumentation());
+ TestUtils.removeTestEmergencyNumber(getInstrumentation(), TEST_EMERGENCY_NUMBER);
+ }
+
+ /**
+ * CTS test to ensure InCallService bindings DO NOT have BIND_ABOVE_CLIENT flag set on binding.
+ */
+ public void testIsBindAboveClientFlagSet() throws Exception {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ // Trigger InCallService so flags can be examined. Otherwise, the service will not show
+ // up when the dumpsys command is executed.
+ placeAndVerifyCall();
+ verifyConnectionForOutgoingCall();
+
+ // Dump the testing package, android.telecom.cts, service information. Doing so will expose
+ // if the BIND_ABOVE_CLIENT flag is set or not on our target InCallService.
+ String dumpOutput = TestUtils.executeShellCommand(getInstrumentation(), DUMPSYS_COMMAND);
+ assertNotNull(dumpOutput);
+ assertTrue(dumpOutput.length() > 0);
+ Log.i(LOG_TAG, dumpOutput);
+
+ // Take off chunk of unwanted pretext.
+ String targetSection = dumpOutput.substring(dumpOutput.indexOf(TARGET_SERVICE));
+ assertNotNull(targetSection);
+ assertTrue(targetSection.length() > 0);
+ Log.i(LOG_TAG, targetSection);
+
+ // extract the TARGET_SERVICE flags
+ int flagsValue = extractFlagsValue(targetSection);
+
+ // assert the Context.BIND_ABOVE_CLIENT flag is not set!
+ assertTrue((flagsValue & Context.BIND_ABOVE_CLIENT) != Context.BIND_ABOVE_CLIENT);
+ }
+
+
+ /**
+ * Find the first flag section of the given string which contains ConnectionRecordObject's
+ *
+ * @param s ConnectionRecordObject in string format.
+ * @return flags decimal value in the ConnectionRecordObject.
+ */
+ public int extractFlagsValue(String s) {
+ // builder to pull flag digits from dumped text
+ StringBuilder sb = new StringBuilder();
+
+ // find the flags=0x text to begin pulling flag digits
+ int i = s.indexOf(FLAGS_TEXT_MATCHER) + FLAGS_OFFSET;
+ int n = s.length();
+
+ // If (i == -1) then this means the flag information was never found.
+ assertTrue(i != -1); // assert we found flag info
+
+ // stop when either there is no more text or a space is encountered
+ while (i < n && s.charAt(i) != SPACE_CHAR) {
+ sb.append(s.charAt(i));
+ i++;
+ }
+
+ // convert string builder into string
+ String extractedFlagAsString = sb.toString();
+ assertNotNull(extractedFlagAsString);
+ assertTrue(extractedFlagAsString.length() > 0);
+ Log.i(LOG_TAG, extractedFlagAsString);
+
+ // convert the string into and integer and return value
+ return Integer.parseInt(extractedFlagAsString);
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
index d63d90d..bd060b9 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
@@ -162,11 +162,13 @@
assertRemaining(sms.getMessageBody().length(), result[2], SmsMessage.MAX_USER_DATA_SEPTETS);
assertEquals(SmsMessage.ENCODING_7BIT, result[3]);
- // Test createFromPdu to test ENCODING_KSC5601
+ // Test createFromPdu to test ENCODING_KSC5601 (0x84 as DCS in pdu)
+ // The only way of retrieving the Data Coding Scheme is via SmsMessage#calculateLength,
+ // but it never returns ENCODING_KSC5601, so we're testing for ENCODING_16BIT instead.
pdu = "07916164260220F0040B914151245584F600846060605130308A04D4F29C0E";
sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
- result = SmsMessage.calculateLength(sms.getMessageBody(), true);
- assertEquals(SmsMessage.ENCODING_KSC5601, result[3]);
+ result = SmsMessage.calculateLength(sms.getMessageBody(), false);
+ assertEquals(SmsMessage.ENCODING_16BIT, result[3]);
}
private void assertRemaining(int messageLength, int remaining, int maxChars) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/NinePatchTests.kt b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/NinePatchTests.kt
index a190501..910f9d6 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/NinePatchTests.kt
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/NinePatchTests.kt
@@ -23,7 +23,7 @@
import android.graphics.Rect
import android.graphics.NinePatch
import android.uirendering.cts.R
-import android.uirendering.cts.bitmapcomparers.ExactComparer
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer
import android.uirendering.cts.testinfrastructure.ActivityTestBase
import android.uirendering.cts.testinfrastructure.CanvasClient
import androidx.test.runner.AndroidJUnit4
@@ -58,7 +58,7 @@
for (paint in arrayOf(null, Paint(), Paint().apply { isFilterBitmap = false })) {
addCanvasClientWithoutUsingPicture(NinePatchCanvasClient(np, paint), hw)
}
- runWithComparer(ExactComparer())
+ runWithComparer(MSSIMComparer(0.99))
}
np.bitmap.recycle()
diff --git a/tests/tests/view/Android.bp b/tests/tests/view/Android.bp
index f49b64c..a2d863b 100644
--- a/tests/tests/view/Android.bp
+++ b/tests/tests/view/Android.bp
@@ -41,6 +41,7 @@
"ctstestrunner-axt",
"cts-input-lib",
"cts-hardware-lib",
+ "junit-params",
"mockito-target-minus-junit4",
"platform-test-annotations",
"ub-uiautomator",
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 1f6befb..514c6fa 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -148,6 +148,17 @@
</intent-filter>
</activity>
+ <activity android:name="android.view.cts.SDRTestActivity"
+ android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
+ android:screenOrientation="locked"
+ android:label="SDRTestActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="android.view.cts.TextureViewCameraActivity"
android:screenOrientation="locked"
android:exported="true">
diff --git a/tests/tests/view/res/layout-watch/gesture_exclusion_basic.xml b/tests/tests/view/res/layout-watch/gesture_exclusion_basic.xml
new file mode 100644
index 0000000..2b15fc5
--- /dev/null
+++ b/tests/tests/view/res/layout-watch/gesture_exclusion_basic.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/abslistview_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <View
+ android:id="@+id/animating_view"
+ android:layout_width="5px"
+ android:layout_height="5px"
+ android:layout_gravity="center|left"
+ android:background="#ff00ff00" />
+</FrameLayout>
diff --git a/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java b/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
index 962d75a..ba711c4 100644
--- a/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
+++ b/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
@@ -93,7 +93,9 @@
@After
public void teardown() {
- mOrientationSession.close();
+ if (mOrientationSession != null) {
+ mOrientationSession.close();
+ }
}
@Test
diff --git a/tests/tests/view/src/android/view/cts/SDRTestActivity.java b/tests/tests/view/src/android/view/cts/SDRTestActivity.java
new file mode 100644
index 0000000..c862a31
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/SDRTestActivity.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2022 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.view.cts;
+
+import android.app.Activity;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+
+public class SDRTestActivity extends Activity
+ implements SurfaceHolder.Callback, SurfaceTextureListener {
+ private SurfaceView mSurfaceView;
+ private TextureView mTextureView;
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {}
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {}
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mTextureView = new TextureView(this);
+ mSurfaceView = new SurfaceView(this);
+ mTextureView.setSurfaceTextureListener(this);
+
+ FrameLayout content = new FrameLayout(this);
+ content.addView(mSurfaceView,
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ content.addView(mTextureView,
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+
+ mSurfaceView.getHolder().addCallback(this);
+ setContentView(content);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ }
+
+ public TextureView getTextureView() {
+ return mTextureView;
+ }
+
+ public SurfaceView getSurfaceView() {
+ return mSurfaceView;
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {}
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
+}
+
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index d5972a4..3140c2a 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -24,37 +24,46 @@
import static android.opengl.GLES20.glScissor;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.app.Instrumentation;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorSpace;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
+import android.hardware.DataSpace;
import android.media.Image;
import android.media.ImageWriter;
import android.util.Half;
import android.view.PixelCopy;
import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import android.view.cts.util.BitmapDumper;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.SynchronousPixelCopy;
import com.android.compatibility.common.util.WidgetTestUtils;
import org.junit.Assert;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -66,8 +75,11 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
@MediumTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnitParamsRunner.class)
public class TextureViewTest {
static final int EGL_GL_COLORSPACE_SRGB_KHR = 0x3089;
@@ -78,11 +90,23 @@
static final int EGL_GL_COLORSPACE_SCRGB_EXT = 0x3351;
static final int EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT = 0x3350;
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ assertNotNull(mInstrumentation);
+ }
+
@Rule
public ActivityTestRule<TextureViewCtsActivity> mActivityRule =
new ActivityTestRule<>(TextureViewCtsActivity.class, false, false);
@Rule
+ public ActivityTestRule<SDRTestActivity> mSDRActivityRule =
+ new ActivityTestRule<>(SDRTestActivity.class, false, false);
+
+ @Rule
public TestName mTestName = new TestName();
@Test
@@ -235,6 +259,137 @@
screenshot.getPixel(texturePos.right - 10, texturePos.bottom - 10));
}
+ private static Object[] testDataSpaces() {
+ return new Integer[] {
+ DataSpace.DATASPACE_SCRGB_LINEAR,
+ DataSpace.DATASPACE_SRGB,
+ DataSpace.DATASPACE_SCRGB,
+ DataSpace.DATASPACE_DISPLAY_P3,
+ DataSpace.DATASPACE_ADOBE_RGB,
+ DataSpace.DATASPACE_BT2020,
+ DataSpace.DATASPACE_BT709,
+ DataSpace.DATASPACE_DCI_P3,
+ DataSpace.DATASPACE_SRGB_LINEAR
+ };
+ }
+
+ @Test
+ @Parameters(method = "testDataSpaces")
+ public void testSDRFromSurfaceViewAndTextureView(int dataSpace) throws Throwable {
+ final int tiffanyBlue = 0xFF0ABAB5;
+ Color color = Color.valueOf(tiffanyBlue).convert(ColorSpace.getFromDataSpace(dataSpace));
+ long converted = color.pack();
+ assertNotEquals(Color.valueOf(tiffanyBlue).toArgb(), color);
+
+ final SDRTestActivity activity =
+ mSDRActivityRule.launchActivity(/*startIntent*/ null);
+
+ TextureView textureView = activity.getTextureView();
+ int width = textureView.getWidth();
+ int height = textureView.getHeight();
+
+ // through textureView, paint left part
+ SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
+ Surface surface = new Surface(surfaceTexture);
+ assertTrue(surface.isValid());
+
+ ImageWriter writer = new ImageWriter
+ .Builder(surface)
+ .setHardwareBufferFormat(PixelFormat.RGBA_8888)
+ .setDataSpace(dataSpace)
+ .build();
+ Image image = writer.dequeueInputImage();
+ assertEquals(dataSpace, image.getDataSpace());
+ Image.Plane plane = image.getPlanes()[0];
+ Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4, image.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setAntiAlias(false);
+ paint.setColor(converted);
+ canvas.drawRect(width / 2, 0f, width, height, paint);
+ bitmap.copyPixelsToBuffer(plane.getBuffer());
+ writer.queueInputImage(image);
+
+ final Rect textureViewPos = new Rect();
+ WidgetTestUtils.runOnMainAndDrawSync(mSDRActivityRule,
+ activity.findViewById(android.R.id.content), () -> {
+ int[] outLocation = new int[2];
+ textureView.getLocationInSurface(outLocation);
+ textureViewPos.left = outLocation[0] + width / 2;
+ textureViewPos.top = outLocation[1];
+ textureViewPos.right = textureViewPos.left + width / 2;
+ textureViewPos.bottom = textureViewPos.top + height;
+ });
+
+ Bitmap textureViewScreenshot = Bitmap.createBitmap(
+ textureViewPos.width(), textureViewPos.height(), Bitmap.Config.ARGB_8888);
+ int textureViewResult =
+ new SynchronousPixelCopy().request(surface, textureViewPos, textureViewScreenshot);
+ assertEquals("Copy request failed", PixelCopy.SUCCESS, textureViewResult);
+
+ // through surfaceView, paint right part
+ SurfaceView surfaceView = activity.getSurfaceView();
+ surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ ImageWriter writer = new ImageWriter
+ .Builder(holder.getSurface())
+ .setHardwareBufferFormat(PixelFormat.RGBA_8888)
+ .setDataSpace(dataSpace)
+ .build();
+ Image image = writer.dequeueInputImage();
+ assertEquals(dataSpace, image.getDataSpace());
+ Image.Plane plane = image.getPlanes()[0];
+ Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4, image.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setAntiAlias(false);
+ paint.setColor(converted);
+ canvas.drawRect(0f, 0f, width / 2, height, paint);
+ bitmap.copyPixelsToBuffer(plane.getBuffer());
+ writer.queueInputImage(image);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {}
+ });
+
+ // wait here to ensure SF has latched the buffer that has been queued in
+ // this is the easiest way to solve copy failure but sacrifice the performance.
+ Thread.sleep(100);
+ final Rect surfaceViewPos = new Rect();
+ WidgetTestUtils.runOnMainAndDrawSync(mSDRActivityRule, surfaceView, () -> {
+ ((ViewGroup) surfaceView.getParent()).removeView(surfaceView);
+ activity.setContentView(surfaceView);
+ int[] outLocation = new int[2];
+ surfaceView.getLocationInSurface(outLocation);
+ surfaceViewPos.left = outLocation[0];
+ surfaceViewPos.top = outLocation[1];
+ surfaceViewPos.right = surfaceViewPos.left + width / 2;
+ surfaceViewPos.bottom = surfaceViewPos.top + height;
+ });
+
+ Bitmap surfaceViewScreenshot = mInstrumentation
+ .getUiAutomation()
+ .takeScreenshot(activity.getWindow());
+ // resize screenshot to left part
+ surfaceViewScreenshot.setWidth(surfaceViewPos.width());
+ surfaceViewScreenshot.setHeight(surfaceViewPos.height());
+ assertEquals(ColorSpace.get(ColorSpace.Named.SRGB),
+ surfaceViewScreenshot.getColorSpace());
+
+ int surfaceViewResult = new SynchronousPixelCopy()
+ .request(surfaceView, surfaceViewPos, surfaceViewScreenshot);
+ assertEquals("Copy request failed", PixelCopy.SUCCESS, surfaceViewResult);
+
+ assertTrue(textureViewScreenshot.sameAs(surfaceViewScreenshot));
+ }
+
@Test
public void testCropRect() throws Throwable {
final TextureViewCtsActivity activity = mActivityRule.launchActivity(/*startIntent*/ null);
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
index 4bdc106..e5a7ecf 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -261,6 +261,8 @@
(FrameLayout) findViewById(android.R.id.content));
});
+ animationTestCase.waitForReady();
+
mHandler.postDelayed(() -> {
Log.d(TAG, "Starting capture");
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ISurfaceValidatorTestCase.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ISurfaceValidatorTestCase.java
index 0de06d2..71810d9 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ISurfaceValidatorTestCase.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ISurfaceValidatorTestCase.java
@@ -37,4 +37,8 @@
boundsToCheck.offset(topLeft[0], topLeft[1]);
return boundsToCheck;
}
+
+ default void waitForReady() {
+ return;
+ }
}
diff --git a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
index df36111..8e3c098 100644
--- a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
+++ b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java
@@ -59,11 +59,11 @@
import com.google.uwb.support.multichip.ChipInfoParams;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
+import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
@@ -402,15 +402,41 @@
}
private class UwbVendorUciCallback implements UwbManager.UwbVendorUciCallback {
+ private final CountDownLatch mRspCountDownLatch;
+ private final CountDownLatch mNtfCountDownLatch;
+
+ public int gid;
+ public int oid;
+ public byte[] payload;
+
+ UwbVendorUciCallback(
+ @NonNull CountDownLatch rspCountDownLatch,
+ @NonNull CountDownLatch ntfCountDownLatch) {
+ mRspCountDownLatch = rspCountDownLatch;
+ mNtfCountDownLatch = ntfCountDownLatch;
+ }
+
@Override
- public void onVendorUciResponse(int gid, int oid, byte[] payload) { }
+ public void onVendorUciResponse(int gid, int oid, byte[] payload) {
+ this.gid = gid;
+ this.oid = oid;
+ this.payload = payload;
+ mRspCountDownLatch.countDown();
+ }
+
@Override
- public void onVendorUciNotification(int gid, int oid, byte[] payload) { }
+ public void onVendorUciNotification(int gid, int oid, byte[] payload) {
+ this.gid = gid;
+ this.oid = oid;
+ this.payload = payload;
+ mNtfCountDownLatch.countDown();
+ }
}
@Test
public void testRegisterVendorUciCallbackWithoutUwbPrivileged() {
- UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback();
+ UwbManager.UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
try {
mUwbManager.registerUwbVendorUciCallback(
Executors.newSingleThreadExecutor(), cb);
@@ -423,14 +449,19 @@
}
@Test
- @Ignore("Not implemented yet")
public void testUnregisterVendorUciCallbackWithoutUwbPrivileged() {
- UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback();
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ UwbManager.UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
mUwbManager.registerUwbVendorUciCallback(
Executors.newSingleThreadExecutor(), cb);
} catch (SecurityException e) {
/* pass */
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
}
try {
mUwbManager.unregisterUwbVendorUciCallback(cb);
@@ -444,7 +475,8 @@
@Test
public void testInvalidCallbackUnregisterVendorUciCallback() {
- UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback();
+ UwbManager.UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1));
try {
mUwbManager.registerUwbVendorUciCallback(
Executors.newSingleThreadExecutor(), cb);
@@ -876,4 +908,70 @@
uiAutomation.dropShellPermissionIdentity();
}
}
+
+ @Test
+ public void testSendVendorUciMessage() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CountDownLatch rspCountDownLatch = new CountDownLatch(1);
+ CountDownLatch ntfCountDownLatch = new CountDownLatch(1);
+ UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(rspCountDownLatch, ntfCountDownLatch);
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ mUwbManager.registerUwbVendorUciCallback(
+ Executors.newSingleThreadExecutor(), cb);
+
+ // Send random payload with a vendor gid.
+ byte[] payload = new byte[100];
+ new Random().nextBytes(payload);
+ int gid = 9;
+ int oid = 1;
+ mUwbManager.sendVendorUciMessage(gid, oid, payload);
+
+ // Wait for response.
+ assertThat(rspCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(cb.gid).isEqualTo(gid);
+ assertThat(cb.oid).isEqualTo(oid);
+ assertThat(cb.payload).isNotEmpty();
+ } catch (SecurityException e) {
+ /* pass */
+ } finally {
+ mUwbManager.unregisterUwbVendorUciCallback(cb);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ public void testSendVendorUciMessageWithFragmentedPackets() throws Exception {
+ UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ CountDownLatch rspCountDownLatch = new CountDownLatch(1);
+ CountDownLatch ntfCountDownLatch = new CountDownLatch(1);
+ UwbVendorUciCallback cb =
+ new UwbVendorUciCallback(rspCountDownLatch, ntfCountDownLatch);
+ try {
+ // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
+ uiAutomation.adoptShellPermissionIdentity();
+ mUwbManager.registerUwbVendorUciCallback(
+ Executors.newSingleThreadExecutor(), cb);
+
+ // Send random payload > 255 bytes with a vendor gid.
+ byte[] payload = new byte[400];
+ new Random().nextBytes(payload);
+ int gid = 9;
+ int oid = 1;
+ mUwbManager.sendVendorUciMessage(gid, oid, payload);
+
+ // Wait for response.
+ assertThat(rspCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(cb.gid).isEqualTo(gid);
+ assertThat(cb.oid).isEqualTo(oid);
+ assertThat(cb.payload).isNotEmpty();
+ } catch (SecurityException e) {
+ /* pass */
+ } finally {
+ mUwbManager.unregisterUwbVendorUciCallback(cb);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tools/cts-tradefed/res/config/cts-developer-exclude.xml b/tools/cts-tradefed/res/config/cts-developer-exclude.xml
index c7bec16..21f0411 100644
--- a/tools/cts-tradefed/res/config/cts-developer-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-developer-exclude.xml
@@ -16,4 +16,6 @@
-->
<configuration description="Excluded CTS developer tests from main CTS runs">
<!-- Exclude the set of tests from the cts-developer plan here -->
+ <option name="compatibility:exclude-filter" value="CtsServiceKillTestCases" />
+
</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-developer.xml b/tools/cts-tradefed/res/config/cts-developer.xml
index 62c39d8..601227e 100644
--- a/tools/cts-tradefed/res/config/cts-developer.xml
+++ b/tools/cts-tradefed/res/config/cts-developer.xml
@@ -21,5 +21,6 @@
<option name="plan" value="cts-developer" />
<!-- Include the set of tests from the cts-developer-exclude plan here -->
+ <option name="compatibility:include-filter" value="CtsServiceKillTestCases" />
</configuration>