Merge "Snap for 6533464 from 1b19277298b490ffd0ff6a4e34d14f4832cdec5a to sdk-release" into sdk-release
diff --git a/apps/CameraITS/Android.mk b/apps/CameraITS/Android.mk
index 8f7ed7c..b0cabc7 100644
--- a/apps/CameraITS/Android.mk
+++ b/apps/CameraITS/Android.mk
@@ -13,6 +13,8 @@
# limitations under the License.
#
+LOCAL_PATH := $(call my-dir)
+
its-dir-name := CameraITS
its-dir := $(HOST_OUT)/$(its-dir-name)
its-build-stamp := $(its-dir)/build_stamp
@@ -21,11 +23,11 @@
.PHONY: camera-its
-$(its-dir): $(its-build-stamp)
-
-$(its-build-stamp): $(ACP)
- echo $(its_dir)
- mkdir -p $(its-dir)
- $(ACP) -rfp cts/apps/$(its-dir-name)/* $(its-dir)
- rm $(its-dir)/Android.mk
+$(its-build-stamp): PRIVATE_PATH := $(LOCAL_PATH)
+$(its-build-stamp): PRIVATE_OUT := $(its-dir)
+$(its-build-stamp): $(ACP) $(call find-files-in-subdirs,.,*,$(LOCAL_PATH))
+ rm -rf $(PRIVATE_OUT)
+ mkdir -p $(PRIVATE_OUT)
+ $(ACP) -rfp $(PRIVATE_PATH)/* $(PRIVATE_OUT)/
+ rm $(PRIVATE_OUT)/Android.mk
touch $@
diff --git a/apps/CameraITS/tests/scene1/test_multi_camera_match.py b/apps/CameraITS/tests/scene1/test_multi_camera_match.py
index 311a36f..2429837 100644
--- a/apps/CameraITS/tests/scene1/test_multi_camera_match.py
+++ b/apps/CameraITS/tests/scene1/test_multi_camera_match.py
@@ -41,6 +41,7 @@
for i in ids:
physical_props = cam.get_camera_properties_by_id(i)
its.caps.skip_unless(not its.caps.mono_camera(physical_props))
+ its.caps.skip_unless(its.caps.backward_compatible(physical_props))
yuv_sizes[i] = its.objects.get_available_output_sizes(
'yuv', physical_props)
if i == ids[0]: # get_available_output_sizes returns sorted list
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 7885d4f..6802f8d 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -171,7 +171,7 @@
# $(hide) $(ACP) -fp cts/apps/CtsVerifier/assets/scripts/execute_power_tests.py $@
cts : $(verifier-zip)
-$(verifier-zip) : $(HOST_OUT)/CameraITS
+$(verifier-zip) : $(HOST_OUT)/CameraITS/build_stamp
$(verifier-zip) : $(foreach app,$(apps-to-include),$(call apk-location-for,$(app)))
$(verifier-zip) : $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk | $(ACP)
$(hide) mkdir -p $(verifier-dir)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/admin/OWNERS
new file mode 100644
index 0000000..79d60e5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/OWNERS
@@ -0,0 +1,5 @@
+# Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204
+alexkershaw@google.com
+eranm@google.com
+rubinxu@google.com
+sandness@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OWNERS
new file mode 100644
index 0000000..e303d2b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 142675
+set noparent
+suprabh@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/audio/OWNERS
new file mode 100644
index 0000000..eb8de3b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 48436
+elaurent@google.com
+pmclean@google.com
+philburk@google.com
+per-file HifiUltrasound*.java = xlythe@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/OWNERS
new file mode 100644
index 0000000..52531aa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 27441
+zachoverflow@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS
new file mode 100644
index 0000000..8ebd7b8
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 41727
+include platform/frameworks/av:/camera/OWNER
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/OWNERS
new file mode 100644
index 0000000..78b6baa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 31568
+jplemieux@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/dialer/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/dialer/OWNERS
new file mode 100644
index 0000000..a7c1795
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/dialer/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 20868
+tgunn@google.com
+breadley@google.com
+hallliu@google.com
+xiaotonj@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/OWNERS
new file mode 100644
index 0000000..869f601
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 187788
+dimuthu@google.com
+leegao@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/OWNERS
new file mode 100644
index 0000000..79d60e5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/OWNERS
@@ -0,0 +1,5 @@
+# Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204
+alexkershaw@google.com
+eranm@google.com
+rubinxu@google.com
+sandness@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/net/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/net/OWNERS
new file mode 100644
index 0000000..733502a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/net/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 31808
+lorenzo@google.com
+satk@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/OWNERS
new file mode 100644
index 0000000..cbc5f31
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 78930
+juliacr@google.com
+kozynski@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/IncomingCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/IncomingCallTestActivity.java
index d80924c..f17c9ee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/IncomingCallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/IncomingCallTestActivity.java
@@ -24,6 +24,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
@@ -38,6 +39,7 @@
* able to be answered.
*/
public class IncomingCallTestActivity extends PassFailButtons.Activity {
+ private static final String TAG = "TelecomIncomingCall";
private Button mRegisterAndEnablePhoneAccount;
private Button mConfirmPhoneAccountEnabled;
@@ -77,6 +79,7 @@
mConfirmPhoneAccountEnabled.setEnabled(true);
} else {
+ Log.w(TAG, "Step 1 fail - couldn't register phone account");
mStep1Status.setImageResource(R.drawable.fs_error);
}
});
@@ -91,9 +94,11 @@
PhoneAccount account = PhoneAccountUtils.getPhoneAccount(this);
if (account != null && account.isEnabled()) {
getPassButton().setEnabled(true);
+ Log.i(TAG, "Step 1 pass - account is enabled.");
mStep1Status.setImageResource(R.drawable.fs_good);
mConfirmPhoneAccountEnabled.setEnabled(false);
} else {
+ Log.w(TAG, "Step 1 fail - account is not enabled.");
mStep1Status.setImageResource(R.drawable.fs_error);
}
});
@@ -112,9 +117,11 @@
TelecomManager telecomManager =
(TelecomManager) getSystemService(Context.TELECOM_SERVICE);
if (telecomManager == null) {
+ Log.w(TAG, "Step 2 fail - telecom service is null");
mStep2Status.setImageResource(R.drawable.fs_error);
return;
}
+ Log.i(TAG, "Step 2 pass - adding new incoming call");
telecomManager.addNewIncomingCall(PhoneAccountUtils.TEST_PHONE_ACCOUNT_HANDLE, extras);
mStep2Status.setImageResource(R.drawable.fs_good);
});
@@ -126,8 +133,10 @@
}
mConfirmIncomingCallAnswered.setOnClickListener(v -> {
if (confirmIncomingCall()) {
+ Log.i(TAG, "Step 3 pass - new incoming call answered");
mStep3Status.setImageResource(R.drawable.fs_good);
} else {
+ Log.w(TAG, "Step 3 fail - failed to answer new incoming call");
mStep3Status.setImageResource(R.drawable.fs_error);
}
PhoneAccountUtils.unRegisterTestPhoneAccount(this);
@@ -148,13 +157,16 @@
List<CtsConnection> ongoingConnections =
CtsConnectionService.getConnectionService().getConnections();
if (ongoingConnections == null || ongoingConnections.size() != 1) {
+ Log.w(TAG, "Step 3 fail - no ongoing call found");
return false;
}
CtsConnection incomingConnection = ongoingConnections.get(0);
if (!incomingConnection.isIncomingCall()) {
+ Log.w(TAG, "Step 3 fail - ongoing call isn't incoming");
return false;
}
if (incomingConnection.getState() != Connection.STATE_ACTIVE) {
+ Log.w(TAG, "Step 3 fail - ongoing call is not active");
return false;
}
incomingConnection.onDisconnect();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OWNERS
new file mode 100644
index 0000000..0cfd686
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 151185
+tgunn@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OutgoingCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OutgoingCallTestActivity.java
index 5f5c605..9fc0381 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OutgoingCallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/OutgoingCallTestActivity.java
@@ -22,6 +22,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
@@ -36,6 +37,7 @@
* the CtsConnectionService.
*/
public class OutgoingCallTestActivity extends PassFailButtons.Activity {
+ private final static String TAG = "TelecomOutgoingCall";
private Button mRegisterAndEnablePhoneAccount;
private Button mConfirmPhoneAccountEnabled;
@@ -71,9 +73,10 @@
// the default.
Intent intent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
startActivity(intent);
-
+ Log.i(TAG, "Step 1 - phone account registered");
mConfirmPhoneAccountEnabled.setEnabled(true);
} else {
+ Log.w(TAG, "Step 1 fail - phone account registration failed");
mStep1Status.setImageResource(R.drawable.fs_error);
}
});
@@ -90,9 +93,11 @@
if (account != null && account.isEnabled() &&
PhoneAccountUtils.TEST_PHONE_ACCOUNT_HANDLE.equals(defaultOutgoingAccount)) {
getPassButton().setEnabled(true);
+ Log.i(TAG, "Step 1 pass - phone account registration confirmed");
mStep1Status.setImageResource(R.drawable.fs_good);
mConfirmPhoneAccountEnabled.setEnabled(false);
} else {
+ Log.w(TAG, "Step 1 fail - phone account registration failed");
mStep1Status.setImageResource(R.drawable.fs_error);
}
});
@@ -106,6 +111,7 @@
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(TEST_DIAL_NUMBER);
startActivity(intent);
+ Log.i(TAG, "Step 1 pass - dial intent sent");
mStep2Status.setImageResource(R.drawable.fs_good);
});
@@ -115,8 +121,10 @@
}
mConfirmOutgoingCall.setOnClickListener(v -> {
if (confirmOutgoingCall()) {
+ Log.i(TAG, "Step 3 pass - call confirmed");
mStep3Status.setImageResource(R.drawable.fs_good);
} else {
+ Log.w(TAG, "Step 3 fail - failed to confirm call");
mStep3Status.setImageResource(R.drawable.fs_error);
}
PhoneAccountUtils.unRegisterTestPhoneAccount(this);
@@ -137,10 +145,12 @@
List<CtsConnection> ongoingConnections =
CtsConnectionService.getConnectionService().getConnections();
if (ongoingConnections == null || ongoingConnections.size() != 1) {
+ Log.w(TAG, "Step 3 fail - no ongoing connections");
return false;
}
CtsConnection outgoingConnection = ongoingConnections.get(0);
if (outgoingConnection.isIncomingCall()) {
+ Log.w(TAG, "Step 3 fail - call is not outgoing");
return false;
}
outgoingConnection.onDisconnect();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
index a8c2caa..e273616 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/SelfManagedIncomingCallTestActivity.java
@@ -23,6 +23,7 @@
import android.telecom.Connection;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
+import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
@@ -37,6 +38,7 @@
* call or when there is an ongoing self-managed call in another app.
*/
public class SelfManagedIncomingCallTestActivity extends PassFailButtons.Activity {
+ private static final String TAG = "SelfManagedIncomingCall";
private Uri TEST_DIAL_NUMBER_1 = Uri.fromParts("tel", "6505551212", null);
private Uri TEST_DIAL_NUMBER_2 = Uri.fromParts("tel", "4085551212", null);
@@ -51,6 +53,7 @@
@Override
void onShowIncomingCallUi(CtsConnection connection) {
// The system should have displayed the incoming call UI; this is a fail.
+ Log.w(TAG, "Step 3 fail - got unexpected onShowIncomingCallUi");
mStep3Status.setImageResource(R.drawable.fs_error);
getPassButton().setEnabled(false);
};
@@ -58,6 +61,7 @@
@Override
void onAnswer(CtsConnection connection, int videoState) {
// Call was answered, so disconnect it now.
+ Log.i(TAG, "Step 3 - Incoming call answered.");
connection.onDisconnect();
};
@@ -71,10 +75,11 @@
(TelecomManager) getSystemService(Context.TELECOM_SERVICE);
if (telecomManager == null || !telecomManager.isInManagedCall()) {
// Should still be in a managed call; only one would need to be disconnected.
+ Log.w(TAG, "Step 3 fail - not in managed call as expected.");
mStep3Status.setImageResource(R.drawable.fs_error);
return;
}
-
+ Log.i(TAG, "Step 3 pass - call disconnected");
mStep3Status.setImageResource(R.drawable.fs_good);
getPassButton().setEnabled(true);
}
@@ -100,8 +105,10 @@
account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
mRegisterPhoneAccount.setEnabled(false);
mVerifyCall.setEnabled(true);
+ Log.i(TAG, "Step 1 pass - account registered");
mStep1Status.setImageResource(R.drawable.fs_good);
} else {
+ Log.w(TAG, "Step 1 fail - account not registered");
mStep1Status.setImageResource(R.drawable.fs_error);
}
});
@@ -112,10 +119,12 @@
TelecomManager telecomManager =
(TelecomManager) getSystemService(Context.TELECOM_SERVICE);
if (telecomManager == null || !telecomManager.isInManagedCall()) {
+ Log.w(TAG, "Step 2 fail - expected to be in a managed call");
mStep2Status.setImageResource(R.drawable.fs_error);
mPlaceCall.setEnabled(false);
} else {
mStep2Status.setImageResource(R.drawable.fs_good);
+ Log.i(TAG, "Step 2 pass - device in a managed call");
mVerifyCall.setEnabled(false);
mPlaceCall.setEnabled(true);
}
@@ -135,6 +144,7 @@
TelecomManager telecomManager =
(TelecomManager) getSystemService(Context.TELECOM_SERVICE);
if (telecomManager == null) {
+ Log.w(TAG, "Step 2 fail - telecom manager null");
mStep2Status.setImageResource(R.drawable.fs_error);
return new Throwable("Could not get telecom service.");
}
@@ -144,12 +154,14 @@
CtsConnectionService ctsConnectionService =
CtsConnectionService.waitForAndGetConnectionService();
if (ctsConnectionService == null) {
+ Log.w(TAG, "Step 2 fail - ctsConnectionService null");
mStep2Status.setImageResource(R.drawable.fs_error);
return new Throwable("Could not get connection service.");
}
CtsConnection connection = ctsConnectionService.waitForAndGetConnection();
if (connection == null) {
+ Log.w(TAG, "Step 2 fail - could not get connection");
mStep2Status.setImageResource(R.drawable.fs_error);
return new Throwable("Could not get connection.");
}
@@ -167,6 +179,7 @@
int capabilities = connection.getConnectionCapabilities();
capabilities &= ~Connection.CAPABILITY_HOLD;
connection.setConnectionCapabilities(capabilities);
+ Log.w(TAG, "Step 2 - connection added");
return null;
} catch (Throwable t) {
return t;
@@ -179,6 +192,7 @@
mStep2Status.setImageResource(R.drawable.fs_good);
mPlaceCall.setEnabled(false);
} else {
+ Log.i(TAG, "Step 2 pass - connection added");
mStep2Status.setImageResource(R.drawable.fs_error);
}
}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
index c478961..ac30d80 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -100,9 +100,12 @@
store.addResult(IS_ACTIVE_ADMIN, deviceAdminPackages.contains(pkg.packageName));
- final boolean isDefaultAccessibilityComponent = pkg.packageName.equals(
- defaultAccessibilityComponent.getPackageName()
- );
+ boolean isDefaultAccessibilityComponent = false;
+ if (defaultAccessibilityComponent != null) {
+ isDefaultAccessibilityComponent = pkg.packageName.equals(
+ defaultAccessibilityComponent.getPackageName()
+ );
+ }
store.addResult(DEFAULT_ACCESSIBILITY_SERVICE, isDefaultAccessibilityComponent);
String sha256_cert = PackageUtil.computePackageSignatureDigest(pkg.packageName);
diff --git a/hostsidetests/appcompat/OWNERS b/hostsidetests/appcompat/OWNERS
index 4e3ec29..11a1173 100644
--- a/hostsidetests/appcompat/OWNERS
+++ b/hostsidetests/appcompat/OWNERS
@@ -4,6 +4,5 @@
platform-compat-eng+reviews@google.com
andreionea@google.com
-atrost@google.com
mathewi@google.com
-satayev@google.com
\ No newline at end of file
+satayev@google.com
diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
index 699af1a..2f8790f 100644
--- a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
+++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
@@ -43,6 +43,11 @@
installPackage(TEST_APK, true);
}
+ @Override
+ protected void tearDown() throws Exception {
+ uninstallPackage(TEST_PKG, true);
+ }
+
public void testIsChangeEnabled() throws Exception {
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest", "isChangeEnabled_changeEnabled",
/*enabledChanges*/ImmutableSet.of(CTS_SYSTEM_API_CHANGEID),
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
index bd17748..1eb58ef 100644
--- a/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
@@ -35,7 +35,22 @@
public void testAccessSerialPermissionNeeded() throws Exception {
// Build.SERIAL should not provide the device serial for modern apps.
// We don't know the serial but know that it should be the dummy
- // value returned to unauthorized callers, so make sure that value is returned.
+ // value returned to unauthorized callers, so make sure that value
+ assertTrue("Build.SERIAL must not work for modern apps",
+ Build.UNKNOWN.equals(Build.SERIAL));
+
+ // We don't have the read phone state permission, so this should throw
+ try {
+ Build.getSerial();
+ fail("getSerial() must be gated on the READ_PHONE_STATE permission");
+ } catch (SecurityException e) {
+ /* expected */
+ }
+
+ // Now grant ourselves READ_PHONE_STATE
+ grantReadPhoneStatePermission();
+
+ // Build.SERIAL should not provide the device serial for modern apps.
assertTrue("Build.SERIAL must not work for modern apps",
Build.UNKNOWN.equals(Build.SERIAL));
@@ -53,4 +68,10 @@
+ e);
}
}
+
+ private void grantReadPhoneStatePermission() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ InstrumentationRegistry.getContext().getPackageName(),
+ android.Manifest.permission.READ_PHONE_STATE);
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
index 78f2fff..3763797 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -61,6 +61,7 @@
import java.io.OutputStream;
import java.util.HashSet;
import java.util.concurrent.Callable;
+import java.util.concurrent.TimeoutException;
@RunWith(AndroidJUnit4.class)
public class MediaStorageTest {
@@ -75,6 +76,9 @@
private ContentResolver mContentResolver;
private int mUserId;
+ private static int currentAttempt = 0;
+ private static final int MAX_NUMBER_OF_ATTEMPT = 10;
+
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
@@ -441,9 +445,19 @@
runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
}
- static File stageFile(File file) throws IOException {
- file.getParentFile().mkdirs();
- file.createNewFile();
- return file;
+ static File stageFile(File file) throws Exception {
+ // Sometimes file creation fails due to slow permission update, try more times
+ while(currentAttempt < MAX_NUMBER_OF_ATTEMPT) {
+ try {
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ return file;
+ } catch(IOException e) {
+ currentAttempt++;
+ // wait 500ms
+ Thread.sleep(500);
+ }
+ }
+ throw new TimeoutException("File creation failed due to slow permission update");
}
}
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
index d287311..a02ac15 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
@@ -45,6 +45,7 @@
import android.os.storage.StorageManager;
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.test.InstrumentationTestCase;
@@ -100,6 +101,8 @@
device.waitForIdle();
if (!isTV(getContext())) {
+ UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true));
+ uiScrollable.scrollTextIntoView("internal storage");
device.findObject(new UiSelector().textContains("internal storage")).click();
device.waitForIdle();
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
index fc7a232..7f85baa 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
@@ -87,6 +87,12 @@
mUiDevice.pressHome();
}
+ public void testLockTaskIsActive() throws Exception {
+ Log.d(TAG, "testLockTaskIsActive on host-driven test");
+ waitAndCheckLockedActivityIsResumed();
+ checkLockedActivityIsRunning();
+ }
+
/**
* On low-RAM devices, this test can take too long to finish, so the test runner can incorrectly
* assume it's finished. Therefore, only use it once in a given test.
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 2e9ae39..c4efeda 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -956,7 +956,7 @@
}
public void testSetMeteredDataDisabledPackages() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
return;
}
installAppAsUser(METERED_DATA_APP_APK, mUserId);
@@ -1128,6 +1128,9 @@
// Wait for the LockTask starting
waitForBroadcastIdle();
+ // Make sure that the LockTaskUtilityActivityIfWhitelisted was started.
+ executeDeviceTestMethod(".LockTaskHostDrivenTest", "testLockTaskIsActive");
+
// Try to open settings via adb
executeShellCommand("am start -a android.settings.SETTINGS");
diff --git a/hostsidetests/hdmicec/app/Android.bp b/hostsidetests/hdmicec/app/Android.bp
index b0f8f71..6d713d3 100644
--- a/hostsidetests/hdmicec/app/Android.bp
+++ b/hostsidetests/hdmicec/app/Android.bp
@@ -16,5 +16,9 @@
name: "HdmiCecHelperApp",
defaults: ["cts_defaults"],
srcs: ["src/**/*.java"],
- sdk_version: "current",
+ static_libs: [
+ "services.core",
+ "guava",
+ "androidx.test.runner",
+ ],
}
diff --git a/hostsidetests/hdmicec/app/AndroidManifest.xml b/hostsidetests/hdmicec/app/AndroidManifest.xml
index edf28a8..bb19e50 100644
--- a/hostsidetests/hdmicec/app/AndroidManifest.xml
+++ b/hostsidetests/hdmicec/app/AndroidManifest.xml
@@ -32,6 +32,7 @@
<action android:name="android.hdmicec.app.UNMUTE" />
<action android:name="android.hdmicec.app.REPORT_VOLUME" />
<action android:name="android.hdmicec.app.SET_VOLUME" />
+ <action android:name="android.hdmicec.app.GET_SUPPORTED_SAD_FORMATS" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
diff --git a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
index 058442a..f04490f 100644
--- a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
+++ b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiCecAudioManager.java
@@ -22,6 +22,10 @@
import android.os.Bundle;
import android.util.Log;
+import com.android.server.hdmi.SadConfigurationReaderTest;
+
+import org.junit.runner.JUnitCore;
+
/**
* A simple app that can be used to mute, unmute, set volume or get the volume status of a device.
* The actions supported are:
@@ -79,6 +83,10 @@
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
Log.i(TAG, "Set volume to " + volume + " (" + percentVolume + "%)");
break;
+ case "android.hdmicec.app.GET_SUPPORTED_SAD_FORMATS":
+ JUnitCore junit = new JUnitCore();
+ junit.run(SadConfigurationReaderTest.class);
+ break;
default:
Log.w(TAG, "Unknown intent!");
}
diff --git a/hostsidetests/hdmicec/app/src/com/android/server/hdmi/SadConfigurationReaderTest.java b/hostsidetests/hdmicec/app/src/com/android/server/hdmi/SadConfigurationReaderTest.java
new file mode 100644
index 0000000..48665ac
--- /dev/null
+++ b/hostsidetests/hdmicec/app/src/com/android/server/hdmi/SadConfigurationReaderTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.util.Log;
+
+import com.android.server.hdmi.HdmiUtils.CodecSad;
+import com.android.server.hdmi.HdmiUtils.DeviceConfig;
+import com.google.common.hash.HashCode;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+/**
+ * Reads short audio descriptors from a configuration file and outputs to a log
+ * for use by host side tests.
+ */
+public class SadConfigurationReaderTest {
+
+ private static final String TAG = "SadConfigurationReaderTest";
+
+ // Variable should be copy of SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH in
+ // frameworks/base/services/core/java/com/android/server/hdmi/
+ // HdmiCecLocalDeviceAudioSystem.java
+ private final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
+
+ @Test
+ public void parseSadConfigXML() {
+ List<DeviceConfig> deviceConfigs = null;
+ File file = new File(SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH);
+ if (file.exists()) {
+ try {
+ InputStream in = new FileInputStream(file);
+ deviceConfigs = HdmiUtils.ShortAudioDescriptorXmlParser.parse(in);
+ in.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading file: " + file.getAbsolutePath(), e);
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "Unable to parse file: " + file.getAbsolutePath(), e);
+ }
+ } else {
+ Log.e(TAG, "No config file present at " + file.getAbsolutePath());
+ return;
+ }
+ DeviceConfig deviceConfigToUse = null;
+ if (deviceConfigs != null && deviceConfigs.size() > 0) {
+ for (DeviceConfig deviceConfig : deviceConfigs) {
+ if (deviceConfig.name.equals("VX_AUDIO_DEVICE_IN_HDMI_ARC")) {
+ deviceConfigToUse = deviceConfig;
+ break;
+ }
+ }
+ if (deviceConfigToUse == null) {
+ Log.w(TAG, "sadConfig.xml does not have required device info for "
+ + "VX_AUDIO_DEVICE_IN_HDMI_ARC");
+ return;
+ }
+ List<Integer> audioCodecFormats = new ArrayList<>();
+ List<String> shortAudioDescriptors = new ArrayList<>();
+ for (CodecSad codecSad : deviceConfigToUse.supportedCodecs) {
+ audioCodecFormats.add(codecSad.audioCodec);
+ shortAudioDescriptors.add(HashCode.fromBytes(codecSad.sad).toString());
+ }
+ String audioCodecFormatsString = audioCodecFormats.toString();
+ String shortAudioDescriptorsString = shortAudioDescriptors.toString();
+ Log.i(TAG, "Supported Audio Formats");
+ Log.i(TAG, audioCodecFormatsString.substring(1, audioCodecFormatsString.length() - 1));
+ Log.i(TAG, shortAudioDescriptorsString
+ .substring(1, shortAudioDescriptorsString.length() - 1));
+ }
+ }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
index bf5ff8d..bce34e1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,64 +16,152 @@
package android.hdmicec.cts;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
-public enum CecMessage {
- FEATURE_ABORT(0x00),
- TEXT_VIEW_ON(0x0d),
- SET_MENU_LANGUAGE(0x32),
- STANDBY(0x36),
- USER_CONTROL_PRESSED(0x44),
- USER_CONTROL_RELEASED(0x45),
- GIVE_OSD_NAME(0x46),
- SET_OSD_NAME(0x47),
- SYSTEM_AUDIO_MODE_REQUEST(0x70),
- GIVE_AUDIO_STATUS(0x71),
- SET_SYSTEM_AUDIO_MODE(0x72),
- REPORT_AUDIO_STATUS(0x7a),
- GIVE_SYSTEM_AUDIO_MODE_STATUS(0x7d),
- SYSTEM_AUDIO_MODE_STATUS(0x7e),
- ACTIVE_SOURCE(0x82),
- GIVE_PHYSICAL_ADDRESS(0x83),
- REPORT_PHYSICAL_ADDRESS(0x84),
- REQUEST_ACTIVE_SOURCE(0x85),
- SET_STREAM_PATH(0x86),
- DEVICE_VENDOR_ID(0x87),
- VENDOR_COMMAND(0x89),
- GIVE_DEVICE_VENDOR_ID(0x8c),
- GIVE_POWER_STATUS(0x8f),
- REPORT_POWER_STATUS(0x90),
- GET_MENU_LANGUAGE(0x91),
- INACTIVE_SOURCE(0x9d),
- CEC_VERSION(0x9e),
- GET_CEC_VERSION(0x9f),
- INITIATE_ARC(0xc0),
- ARC_INITIATED(0xc1),
- REQUEST_ARC_INITIATION(0xc3),
- REQUEST_ARC_TERMINATION(0xc4),
- TERMINATE_ARC(0xc5),
- ABORT(0xff);
+public class CecMessage {
- private final int messageId;
- private static Map messageMap = new HashMap<>();
+ private static final int HEXADECIMAL_RADIX = 16;
- static {
- for (CecMessage message : CecMessage.values()) {
- messageMap.put(message.messageId, message);
+ /** Gets the hexadecimal ASCII character values of a string. */
+ public static String getHexAsciiString(String string) {
+ String asciiString = "";
+ byte[] ascii = string.trim().getBytes();
+
+ for (byte b : ascii) {
+ asciiString.concat(Integer.toHexString(b));
}
+
+ return asciiString;
}
- public static CecMessage getMessage(int messageId) {
- return (CecMessage) messageMap.get(messageId);
+ public static String formatParams(String rawParams) {
+ StringBuilder params = new StringBuilder("");
+ int position = 0;
+ int endPosition = 2;
+
+ do {
+ params.append(":" + rawParams.substring(position, endPosition));
+ position = endPosition;
+ endPosition += 2;
+ } while (endPosition <= rawParams.length());
+ return params.toString();
}
- @Override
- public String toString() {
- return String.format("%02x", messageId);
+ public static String formatParams(long rawParam) {
+ StringBuilder params = new StringBuilder("");
+
+ do {
+ params.insert(0, ":" + String.format("%02x", rawParam % 256));
+ rawParam >>= 8;
+ } while (rawParam > 0);
+
+ return params.toString();
}
- private CecMessage(int messageId) {
- this.messageId = messageId;
+ /**
+ * Formats the rawParam into CEC message parameters. The parameters will be at least
+ * minimumNibbles long.
+ */
+ public static String formatParams(long rawParam, int minimumNibbles) {
+ StringBuilder params = new StringBuilder("");
+
+ do {
+ params.insert(0, ":" + String.format("%02x", rawParam % 256));
+ rawParam >>= 8;
+ minimumNibbles -= 2;
+ } while (rawParam > 0 || minimumNibbles > 0);
+
+ return params.toString();
+ }
+
+ public static int hexStringToInt(String message) {
+ return Integer.parseInt(message, HEXADECIMAL_RADIX);
+ }
+
+ public static String getAsciiString(String message) {
+ String params = getNibbles(message).substring(4);
+ StringBuilder builder = new StringBuilder();
+
+ for (int i = 2; i <= params.length(); i += 2) {
+ builder.append((char) hexStringToInt(params.substring(i - 2, i)));
+ }
+
+ return builder.toString();
+ }
+
+ public static String getParamsAsString(String message) {
+ return getNibbles(message).substring(4);
+ }
+
+ /** Gets the params from a CEC message. */
+ public static int getParams(String message) {
+ return hexStringToInt(getNibbles(message).substring(4));
+ }
+
+ /** Gets the first 'numNibbles' number of param nibbles from a CEC message. */
+ public static int getParams(String message, int numNibbles) {
+ int paramStart = 4;
+ int end = numNibbles + paramStart;
+ return hexStringToInt(getNibbles(message).substring(paramStart, end));
+ }
+
+ /**
+ * From the params of a CEC message, gets the nibbles from position start to position end.
+ * The start and end are relative to the beginning of the params. For example, in the following
+ * message - 4F:82:10:00:04, getParamsFromMessage(message, 0, 4) will return 0x1000 and
+ * getParamsFromMessage(message, 4, 6) will return 0x04.
+ */
+ public static int getParams(String message, int start, int end) {
+ return hexStringToInt(getNibbles(message).substring(4).substring(start, end));
+ }
+
+ /**
+ * Gets the source logical address from a CEC message.
+ */
+ public static LogicalAddress getSource(String message) {
+ String param = getNibbles(message).substring(0, 1);
+ return LogicalAddress.getLogicalAddress(hexStringToInt(param));
+ }
+
+ /** Gets the destination logical address from a CEC message. */
+ public static LogicalAddress getDestination(String message) {
+ String param = getNibbles(message).substring(1, 2);
+ return LogicalAddress.getLogicalAddress(hexStringToInt(param));
+ }
+
+ /** Gets the operand from a CEC message. */
+ public static CecOperand getOperand(String message) {
+ String param = getNibbles(message).substring(2, 4);
+ return CecOperand.getOperand(hexStringToInt(param));
+ }
+
+ /**
+ * Converts ascii characters to hexadecimal numbers that can be appended to a CEC message as
+ * params. For example, "spa" will be converted to ":73:70:61"
+ */
+ public static String convertStringToHexParams(String rawParams) {
+ StringBuilder params = new StringBuilder("");
+ for (int i = 0; i < rawParams.length(); i++) {
+ params.append(String.format(":%02x", (int) rawParams.charAt(i)));
+ }
+ return params.toString();
+ }
+
+ private static String getNibbles(String message) {
+ final String tag1 = "group1";
+ final String tag2 = "group2";
+ String paramsPattern = "(?:.*[>>|<<].*?)" +
+ "(?<" + tag1 + ">[\\p{XDigit}{2}:]+)" +
+ "(?<" + tag2 + ">\\p{XDigit}{2})" +
+ "(?:.*?)";
+ String nibbles = "";
+
+ Pattern p = Pattern.compile(paramsPattern);
+ Matcher m = p.matcher(message);
+ if (m.matches()) {
+ nibbles = m.group(tag1).replace(":", "") + m.group(tag2);
+ }
+ return nibbles;
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
new file mode 100644
index 0000000..5aa547e
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum CecOperand {
+ FEATURE_ABORT(0x00),
+ TEXT_VIEW_ON(0x0d),
+ SET_MENU_LANGUAGE(0x32),
+ STANDBY(0x36),
+ USER_CONTROL_PRESSED(0x44),
+ USER_CONTROL_RELEASED(0x45),
+ GIVE_OSD_NAME(0x46),
+ SET_OSD_NAME(0x47),
+ SYSTEM_AUDIO_MODE_REQUEST(0x70),
+ GIVE_AUDIO_STATUS(0x71),
+ SET_SYSTEM_AUDIO_MODE(0x72),
+ REPORT_AUDIO_STATUS(0x7a),
+ GIVE_SYSTEM_AUDIO_MODE_STATUS(0x7d),
+ SYSTEM_AUDIO_MODE_STATUS(0x7e),
+ ACTIVE_SOURCE(0x82),
+ GIVE_PHYSICAL_ADDRESS(0x83),
+ REPORT_PHYSICAL_ADDRESS(0x84),
+ REQUEST_ACTIVE_SOURCE(0x85),
+ SET_STREAM_PATH(0x86),
+ DEVICE_VENDOR_ID(0x87),
+ VENDOR_COMMAND(0x89),
+ GIVE_DEVICE_VENDOR_ID(0x8c),
+ GIVE_POWER_STATUS(0x8f),
+ REPORT_POWER_STATUS(0x90),
+ GET_MENU_LANGUAGE(0x91),
+ INACTIVE_SOURCE(0x9d),
+ CEC_VERSION(0x9e),
+ GET_CEC_VERSION(0x9f),
+ REPORT_SHORT_AUDIO_DESCRIPTOR(0xa3),
+ REQUEST_SHORT_AUDIO_DESCRIPTOR(0xa4),
+ INITIATE_ARC(0xc0),
+ ARC_INITIATED(0xc1),
+ REQUEST_ARC_INITIATION(0xc3),
+ REQUEST_ARC_TERMINATION(0xc4),
+ TERMINATE_ARC(0xc5),
+ ABORT(0xff);
+
+ private final int operandCode;
+ private static Map operandMap = new HashMap<>();
+
+ static {
+ for (CecOperand operand : CecOperand.values()) {
+ operandMap.put(operand.operandCode, operand);
+ }
+ }
+
+ public static CecOperand getOperand(int messageId) {
+ return (CecOperand) operandMap.get(messageId);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%02x", operandCode);
+ }
+
+ private CecOperand(int operandCode) {
+ this.operandCode = operandCode;
+ }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
index 570ed15..51da280 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
@@ -16,10 +16,6 @@
package android.hdmicec.cts;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.RunUtil;
@@ -32,10 +28,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.junit.Rule;
import org.junit.rules.ExternalResource;
/** Class that helps communicate with the cec-client */
@@ -44,8 +38,6 @@
private static final String CEC_CONSOLE_READY = "waiting for input";
private static final int MILLISECONDS_TO_READY = 10000;
private static final int DEFAULT_TIMEOUT = 20000;
- private static final String HDMI_CEC_FEATURE = "feature:android.hardware.hdmi.cec";
- private static final int HEXADECIMAL_RADIX = 16;
private static final int BUFFER_SIZE = 1024;
private Process mCecClient;
@@ -53,29 +45,16 @@
private BufferedReader mInputConsole;
private boolean mCecClientInitialised = false;
- private CecDevice targetDevice;
- private BaseHostJUnit4Test testObject;
+ private LogicalAddress targetDevice;
private String clientParams[];
- public HdmiCecClientWrapper(CecDevice targetDevice, BaseHostJUnit4Test testObject,
- String ...clientParams) {
+ public HdmiCecClientWrapper(LogicalAddress targetDevice, String ...clientParams) {
this.targetDevice = targetDevice;
- this.testObject = testObject;
this.clientParams = clientParams;
}
@Override
protected void before() throws Throwable {
- ITestDevice testDevice;
- testDevice = testObject.getDevice();
- assertWithMessage("Device not set").that(testDevice).isNotNull();
-
- assertThat(isHdmiCecFeatureSupported(testDevice)).isTrue();
-
- String deviceTypeCsv = testDevice.executeShellCommand("getprop ro.hdmi.device_type").trim();
- List<String> deviceType = Arrays.asList(deviceTypeCsv.replaceAll("\\s+", "").split(","));
- assertThat(deviceType.contains(targetDevice.getDeviceType())).isTrue();
-
this.init();
};
@@ -84,20 +63,9 @@
this.killCecProcess();
};
- /**
- * Checks if the HDMI CEC feature is running on the device. Call this function before running
- * any HDMI CEC tests.
- * This could throw a DeviceNotAvailableException.
- */
- private static boolean isHdmiCecFeatureSupported(ITestDevice device) throws Exception {
- return device.hasFeature(HDMI_CEC_FEATURE);
- }
-
/** Initialise the client */
private void init() throws Exception {
- boolean gotExpectedOut = false;
List<String> commands = new ArrayList();
- int seconds = 0;
commands.add("cec-client");
/* "-p 2" starts the client as if it is connected to HDMI port 2, taking the physical
@@ -138,15 +106,15 @@
* Sends a CEC message with source marked as broadcast to the device passed in the constructor
* through the output console of the cec-communication channel.
*/
- public void sendCecMessage(CecMessage message) throws Exception {
- sendCecMessage(CecDevice.BROADCAST, targetDevice, message, "");
+ public void sendCecMessage(CecOperand message) throws Exception {
+ sendCecMessage(LogicalAddress.BROADCAST, targetDevice, message, "");
}
/**
* Sends a CEC message from source device to the device passed in the constructor through the
* output console of the cec-communication channel.
*/
- public void sendCecMessage(CecDevice source, CecMessage message) throws Exception {
+ public void sendCecMessage(LogicalAddress source, CecOperand message) throws Exception {
sendCecMessage(source, targetDevice, message, "");
}
@@ -154,8 +122,8 @@
* Sends a CEC message from source device to a destination device through the output console of
* the cec-communication channel.
*/
- public void sendCecMessage(CecDevice source, CecDevice destination,
- CecMessage message) throws Exception {
+ public void sendCecMessage(LogicalAddress source, LogicalAddress destination,
+ CecOperand message) throws Exception {
sendCecMessage(source, destination, message, "");
}
@@ -163,8 +131,8 @@
* Sends a CEC message from source device to a destination device through the output console of
* the cec-communication channel with the appended params.
*/
- public void sendCecMessage(CecDevice source, CecDevice destination,
- CecMessage message, String params) throws Exception {
+ public void sendCecMessage(LogicalAddress source, LogicalAddress destination,
+ CecOperand message, String params) throws Exception {
checkCecClient();
mOutputConsole.write("tx " + source + destination + ":" + message + params);
mOutputConsole.newLine();
@@ -175,13 +143,13 @@
* Sends a <USER_CONTROL_PRESSED> and <USER_CONTROL_RELEASED> from source to destination
* through the output console of the cec-communication channel with the mentioned keycode.
*/
- public void sendUserControlPressAndRelease(CecDevice source, CecDevice destination,
+ public void sendUserControlPressAndRelease(LogicalAddress source, LogicalAddress destination,
int keycode, boolean holdKey) throws Exception {
sendUserControlPress(source, destination, keycode, holdKey);
/* Sleep less than 200ms between press and release */
TimeUnit.MILLISECONDS.sleep(100);
mOutputConsole.write("tx " + source + destination + ":" +
- CecMessage.USER_CONTROL_RELEASED);
+ CecOperand.USER_CONTROL_RELEASED);
mOutputConsole.flush();
}
@@ -190,11 +158,11 @@
* cec-communication channel with the mentioned keycode. If holdKey is true, the method will
* send multiple <UCP> messages to simulate a long press. No <UCR> will be sent.
*/
- public void sendUserControlPress(CecDevice source, CecDevice destination,
+ public void sendUserControlPress(LogicalAddress source, LogicalAddress destination,
int keycode, boolean holdKey) throws Exception {
String key = String.format("%02x", keycode);
String command = "tx " + source + destination + ":" +
- CecMessage.USER_CONTROL_PRESSED + ":" + key;
+ CecOperand.USER_CONTROL_PRESSED + ":" + key;
if (holdKey) {
/* Repeat once between 200ms and 450ms for at least 5 seconds. Since message will be
@@ -218,7 +186,8 @@
* of the cec-communication channel immediately followed by <UCP> [secondKeycode]. No <UCR>
* message is sent.
*/
- public void sendUserControlInterruptedPressAndHold(CecDevice source, CecDevice destination,
+ public void sendUserControlInterruptedPressAndHold(
+ LogicalAddress source, LogicalAddress destination,
int firstKeycode, int secondKeycode, boolean holdKey) throws Exception {
sendUserControlPress(source, destination, firstKeycode, holdKey);
/* Sleep less than 200ms between press and release */
@@ -262,8 +231,8 @@
/** Gets all the messages received from the given source device during a period of duration
* seconds.
*/
- public List<CecMessage> getAllMessages(CecDevice source, int duration) throws Exception {
- List<CecMessage> receivedMessages = new ArrayList<>();
+ public List<CecOperand> getAllMessages(LogicalAddress source, int duration) throws Exception {
+ List<CecOperand> receivedOperands = new ArrayList<>();
long startTime = System.currentTimeMillis();
long endTime = startTime;
Pattern pattern = Pattern.compile("(.*>>)(.*?)" +
@@ -274,15 +243,15 @@
if (mInputConsole.ready()) {
String line = mInputConsole.readLine();
if (pattern.matcher(line).matches()) {
- CecMessage message = getOperandFromMessage(line);
- if (!receivedMessages.contains(message)) {
- receivedMessages.add(message);
+ CecOperand operand = CecMessage.getOperand(line);
+ if (!receivedOperands.contains(operand)) {
+ receivedOperands.add(operand);
}
}
}
endTime = System.currentTimeMillis();
}
- return receivedMessages;
+ return receivedOperands;
}
@@ -291,8 +260,8 @@
* returns the first line that contains that message within default timeout. If the CEC message
* is not found within the timeout, an exception is thrown.
*/
- public String checkExpectedOutput(CecMessage expectedMessage) throws Exception {
- return checkExpectedOutput(CecDevice.BROADCAST, expectedMessage, DEFAULT_TIMEOUT);
+ public String checkExpectedOutput(CecOperand expectedMessage) throws Exception {
+ return checkExpectedOutput(LogicalAddress.BROADCAST, expectedMessage, DEFAULT_TIMEOUT);
}
/**
@@ -300,8 +269,8 @@
* communication channel and returns the first line that contains that message within
* default timeout. If the CEC message is not found within the timeout, an exception is thrown.
*/
- public String checkExpectedOutput(CecDevice toDevice,
- CecMessage expectedMessage) throws Exception {
+ public String checkExpectedOutput(LogicalAddress toDevice,
+ CecOperand expectedMessage) throws Exception {
return checkExpectedOutput(toDevice, expectedMessage, DEFAULT_TIMEOUT);
}
@@ -310,9 +279,9 @@
* returns the first line that contains that message within timeoutMillis. If the CEC message
* is not found within the timeout, an exception is thrown.
*/
- public String checkExpectedOutput(CecMessage expectedMessage,
+ public String checkExpectedOutput(CecOperand expectedMessage,
long timeoutMillis) throws Exception {
- return checkExpectedOutput(CecDevice.BROADCAST, expectedMessage, timeoutMillis);
+ return checkExpectedOutput(LogicalAddress.BROADCAST, expectedMessage, timeoutMillis);
}
/**
@@ -320,7 +289,7 @@
* communication channel and returns the first line that contains that message within
* timeoutMillis. If the CEC message is not found within the timeout, an exception is thrown.
*/
- public String checkExpectedOutput(CecDevice toDevice, CecMessage expectedMessage,
+ public String checkExpectedOutput(LogicalAddress toDevice, CecOperand expectedMessage,
long timeoutMillis) throws Exception {
checkCecClient();
long startTime = System.currentTimeMillis();
@@ -349,8 +318,8 @@
* within the default timeout. If the CEC message is not found within the timeout, function
* returns without error.
*/
- public void checkOutputDoesNotContainMessage(CecDevice toDevice,
- CecMessage incorrectMessage) throws Exception {
+ public void checkOutputDoesNotContainMessage(LogicalAddress toDevice,
+ CecOperand incorrectMessage) throws Exception {
checkOutputDoesNotContainMessage(toDevice, incorrectMessage, DEFAULT_TIMEOUT);
}
@@ -360,7 +329,7 @@
* within timeoutMillis. If the CEC message is not found within the timeout, function returns
* without error.
*/
- public void checkOutputDoesNotContainMessage(CecDevice toDevice, CecMessage incorrectMessage,
+ public void checkOutputDoesNotContainMessage(LogicalAddress toDevice, CecOperand incorrectMessage,
long timeoutMillis) throws Exception {
checkCecClient();
@@ -377,168 +346,13 @@
if (pattern.matcher(line).matches()) {
CLog.v("Found " + incorrectMessage.name() + " in " + line);
throw new Exception("Found " + incorrectMessage.name() + " to " + toDevice +
- " with params " + getParamsFromMessage(line));
+ " with params " + CecMessage.getParamsAsString(line));
}
}
endTime = System.currentTimeMillis();
}
}
- /** Gets the hexadecimal ASCII character values of a string. */
- public String getHexAsciiString(String string) {
- String asciiString = "";
- byte[] ascii = string.trim().getBytes();
-
- for (byte b : ascii) {
- asciiString.concat(Integer.toHexString(b));
- }
-
- return asciiString;
- }
-
- public String formatParams(String rawParams) {
- StringBuilder params = new StringBuilder("");
- int position = 0;
- int endPosition = 2;
-
- do {
- params.append(":" + rawParams.substring(position, endPosition));
- position = endPosition;
- endPosition += 2;
- } while (endPosition <= rawParams.length());
- return params.toString();
- }
-
- public String formatParams(long rawParam) {
- StringBuilder params = new StringBuilder("");
-
- do {
- params.insert(0, ":" + String.format("%02x", rawParam % 256));
- rawParam >>= 8;
- } while (rawParam > 0);
-
- return params.toString();
- }
-
- /** Formats the rawParam into CEC message parameters. The parameters will be at least
- * minimumNibbles long. */
- public String formatParams(long rawParam, int minimumNibbles) {
- StringBuilder params = new StringBuilder("");
-
- do {
- params.insert(0, ":" + String.format("%02x", rawParam % 256));
- rawParam >>= 8;
- minimumNibbles -= 2;
- } while (rawParam > 0 || minimumNibbles > 0);
-
- return params.toString();
- }
-
- /** Formats a CEC message in the hex colon format (sd:op:xx:xx). */
- public String formatMessage(CecDevice source, CecDevice destination, CecMessage message,
- int params) {
- StringBuilder cecMessage = new StringBuilder("" + source + destination + ":" + message);
-
- cecMessage.append(formatParams(params));
-
- return cecMessage.toString();
- }
-
- public static int hexStringToInt(String message) {
- return Integer.parseInt(message, HEXADECIMAL_RADIX);
- }
-
- public String getAsciiStringFromMessage(String message) {
- String params = getNibbles(message).substring(4);
- StringBuilder builder = new StringBuilder();
-
- for (int i = 2; i <= params.length(); i += 2) {
- builder.append((char) hexStringToInt(params.substring(i - 2, i)));
- }
-
- return builder.toString();
- }
-
- /**
- * Gets the params from a CEC message.
- */
- public int getParamsFromMessage(String message) {
- return hexStringToInt(getNibbles(message).substring(4));
- }
-
- /**
- * Gets the first 'numNibbles' number of param nibbles from a CEC message.
- */
- public int getParamsFromMessage(String message, int numNibbles) {
- int paramStart = 4;
- int end = numNibbles + paramStart;
- return hexStringToInt(getNibbles(message).substring(paramStart, end));
- }
-
- /**
- * From the params of a CEC message, gets the nibbles from position start to position end.
- * The start and end are relative to the beginning of the params. For example, in the following
- * message - 4F:82:10:00:04, getParamsFromMessage(message, 0, 4) will return 0x1000 and
- * getParamsFromMessage(message, 4, 6) will return 0x04.
- */
- public int getParamsFromMessage(String message, int start, int end) {
- return hexStringToInt(getNibbles(message).substring(4).substring(start, end));
- }
-
- /**
- * Gets the source logical address from a CEC message.
- */
- public CecDevice getSourceFromMessage(String message) {
- String param = getNibbles(message).substring(0, 1);
- return CecDevice.getDevice(hexStringToInt(param));
- }
-
- /**
- * Converts ascii characters to hexadecimal numbers that can be appended to a CEC message as
- * params. For example, "spa" will be converted to ":73:70:61"
- */
- public static String convertStringToHexParams(String rawParams) {
- StringBuilder params = new StringBuilder("");
- for (int i = 0; i < rawParams.length(); i++) {
- params.append(String.format(":%02x", (int) rawParams.charAt(i)));
- }
- return params.toString();
- }
-
-
- /**
- * Gets the destination logical address from a CEC message.
- */
- public CecDevice getDestinationFromMessage(String message) {
- String param = getNibbles(message).substring(1, 2);
- return CecDevice.getDevice(hexStringToInt(param));
- }
-
- /**
- * Gets the operand from a CEC message.
- */
- public CecMessage getOperandFromMessage(String message) {
- String param = getNibbles(message).substring(2, 4);
- return CecMessage.getMessage(hexStringToInt(param));
- }
-
- private String getNibbles(String message) {
- final String tag1 = "group1";
- final String tag2 = "group2";
- String paramsPattern = "(?:.*[>>|<<].*?)" +
- "(?<" + tag1 + ">[\\p{XDigit}{2}:]+)" +
- "(?<" + tag2 + ">\\p{XDigit}{2})" +
- "(?:.*?)";
- String nibbles = "";
-
- Pattern p = Pattern.compile(paramsPattern);
- Matcher m = p.matcher(message);
- if (m.matches()) {
- nibbles = m.group(tag1).replace(":", "") + m.group(tag2);
- }
- return nibbles;
- }
-
/**
* Kills the cec-client process that was created in init().
*/
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
index 0565eae..8af4a8b 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
@@ -47,4 +47,11 @@
public static final int CEC_DEVICE_TYPE_PLAYBACK_DEVICE = 4;
public static final int CEC_DEVICE_TYPE_AUDIO_SYSTEM = 5;
+ /** Feature Abort Reasons */
+ public static final int ABORT_UNRECOGNIZED_MODE = 0;
+ public static final int ABORT_NOT_IN_CORRECT_MODE = 1;
+ public static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
+ public static final int ABORT_INVALID_OPERAND = 3;
+ public static final int ABORT_REFUSED = 4;
+ public static final int ABORT_UNABLE_TO_DETERMINE = 5;
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecDevice.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/LogicalAddress.java
similarity index 73%
rename from hostsidetests/hdmicec/src/android/hdmicec/cts/CecDevice.java
rename to hostsidetests/hdmicec/src/android/hdmicec/cts/LogicalAddress.java
index 3aeb318..5bcf5d1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecDevice.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/LogicalAddress.java
@@ -19,7 +19,7 @@
import java.util.HashMap;
import java.util.Map;
-public enum CecDevice {
+public enum LogicalAddress {
TV(0x0),
RECORDER_1(0x1),
RECORDER_2(0x2),
@@ -37,17 +37,24 @@
SPECIFIC_USE(0xe),
BROADCAST(0xf);
- private final int playerId;
+ private final int address;
private static Map deviceMap = new HashMap<>();
+ // CEC Device feature list
+ public static final String HDMI_CEC_FEATURE = "feature:android.hardware.hdmi.cec";
+ public static final String LEANBACK_FEATURE = "feature:android.software.leanback";
+
+ // CEC Device property list
+ public static final String HDMI_DEVICE_TYPE_PROPERTY = "ro.hdmi.device_type";
+
@Override
public String toString() {
- return Integer.toHexString(this.playerId);
+ return Integer.toHexString(this.address);
}
static {
- for (CecDevice device : CecDevice.values()) {
- deviceMap.put(device.playerId, device);
+ for (LogicalAddress device : LogicalAddress.values()) {
+ deviceMap.put(device.address, device);
}
}
@@ -75,11 +82,11 @@
}
}
- public static CecDevice getDevice(int playerId) {
- return (CecDevice) deviceMap.get(playerId);
+ public static LogicalAddress getLogicalAddress(int address) {
+ return (LogicalAddress) deviceMap.get(address);
}
- private CecDevice(int playerId) {
- this.playerId = playerId;
+ private LogicalAddress(int address) {
+ this.address = address;
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredFeatureRule.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredFeatureRule.java
new file mode 100644
index 0000000..8fb70ec
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredFeatureRule.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Rule to check if the required device feature is available on device.
+ */
+public class RequiredFeatureRule implements TestRule {
+
+ private final BaseHostJUnit4Test mTest;
+ private final String mFeature;
+
+ public RequiredFeatureRule(BaseHostJUnit4Test test, String feature) {
+ mTest = test;
+ mFeature = feature;
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ ITestDevice testDevice = mTest.getDevice();
+ // Checks if the device is available.
+ assumeTrue("Test device is not available", testDevice != null);
+ // Checks if the requested feature is available on the device.
+ assumeTrue(mFeature + " not present in DUT " + testDevice.getSerialNumber(),
+ testDevice.hasFeature(mFeature));
+ base.evaluate();
+ }
+ };
+ }
+}
+
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredPropertyRule.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredPropertyRule.java
new file mode 100644
index 0000000..f3998adb
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/RequiredPropertyRule.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Rule class that allows for checking device property value against required values.
+ * Static functions in this class can be used to check properties as a list or single value.
+ */
+public class RequiredPropertyRule implements TestRule {
+
+ // Do not allow instantiation.
+ private RequiredPropertyRule(){}
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ base.evaluate();
+ }
+ };
+ }
+
+ private static String getDevicePropertyValue(BaseHostJUnit4Test test, String propertyName)
+ throws Throwable {
+ ITestDevice testDevice = test.getDevice();
+ // Checks if the device is available.
+ assumeTrue("Test device is not available", testDevice != null);
+ return testDevice.executeShellCommand("getprop " + propertyName).trim();
+
+ }
+
+ public static RequiredPropertyRule isEqualTo(final BaseHostJUnit4Test test,
+ final String propertyName, final String propertyValue) {
+ return new RequiredPropertyRule() {
+ @Override
+ public Statement apply(final Statement base, final Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ String deviceProperty = getDevicePropertyValue(test, propertyName);
+ assumeTrue("Required property " + propertyName + " = " + propertyValue
+ + " is not present in " + deviceProperty
+ + " of device " + test.getDevice().getSerialNumber(),
+ deviceProperty.equals(propertyValue));
+ base.evaluate();
+ }
+ };
+ }
+ };
+ }
+
+ public static RequiredPropertyRule asCsvContainsValue(final BaseHostJUnit4Test test,
+ final String propertyName, final String propertyValue) {
+ return new RequiredPropertyRule() {
+ @Override
+ public Statement apply(final Statement base, final Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ List<String> deviceProperties =
+ Arrays.asList(getDevicePropertyValue(test, propertyName)
+ .replaceAll("\\s+", "")
+ .split(","));
+ assumeTrue(
+ "Required property " + propertyName + " = " + propertyValue
+ + " is not present in " + deviceProperties.toString()
+ + " of device " + test.getDevice().getSerialNumber(),
+ deviceProperties.contains(propertyValue));
+ base.evaluate();
+ }
+ };
+ }
+ };
+ }
+}
+
+
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
index eaaf6df..897a35e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
@@ -18,31 +18,46 @@
import static org.junit.Assume.assumeNoException;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
/** HDMI CEC test to test audio return channel control (Section 11.2.17) */
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecAudioReturnChannelControlTest extends BaseHostJUnit4Test {
- private static final CecDevice AUDIO_DEVICE = CecDevice.AUDIO_SYSTEM;
+
+ private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
+
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE);
@Rule
- public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ AUDIO_DEVICE.getDeviceType()))
+ .around(hdmiCecClient);
private void checkArcIsInitiated(){
try {
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.REQUEST_ARC_INITIATION);
- hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.INITIATE_ARC);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.REQUEST_ARC_INITIATION);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
} catch(Exception e) {
assumeNoException(e);
}
@@ -55,13 +70,13 @@
*/
@Test
public void cect_11_2_17_1_InitiateArc() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
- CecMessage.REPORT_PHYSICAL_ADDRESS,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+ CecOperand.REPORT_PHYSICAL_ADDRESS,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
getDevice().executeShellCommand("reboot");
getDevice().waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
- hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.INITIATE_ARC);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
}
/**
@@ -72,13 +87,13 @@
@Test
public void cect_11_2_17_2_TerminateArc() throws Exception {
checkArcIsInitiated();
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
- CecMessage.REPORT_PHYSICAL_ADDRESS,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+ CecOperand.REPORT_PHYSICAL_ADDRESS,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
try {
- hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.TERMINATE_ARC);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TERMINATE_ARC);
} finally {
getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
}
@@ -91,13 +106,13 @@
*/
@Test
public void cect_11_2_17_3_RequestToInitiateArc() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
- CecMessage.REPORT_PHYSICAL_ADDRESS,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+ CecOperand.REPORT_PHYSICAL_ADDRESS,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.REQUEST_ARC_INITIATION);
- hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.INITIATE_ARC);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.REQUEST_ARC_INITIATION);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
}
/**
@@ -108,12 +123,12 @@
@Test
public void cect_11_2_17_4_RequestToTerminateArc() throws Exception {
checkArcIsInitiated();
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
- CecMessage.REPORT_PHYSICAL_ADDRESS,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+ CecOperand.REPORT_PHYSICAL_ADDRESS,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.REQUEST_ARC_TERMINATION);
- hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.TERMINATE_ARC);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.REQUEST_ARC_TERMINATION);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TERMINATE_ARC);
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java
new file mode 100644
index 0000000..f7644f2
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts.audio;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.HdmiCecClientWrapper;
+import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.Scanner;
+import java.util.concurrent.TimeUnit;
+
+/** HDMI CEC test to verify that device ignores invalid messages (Section 12) */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class HdmiCecInvalidMessagesTest extends BaseHostJUnit4Test {
+
+ private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
+ private static final String PROPERTY_LOCALE = "persist.sys.locale";
+
+ /** The package name of the APK. */
+ private static final String PACKAGE = "android.hdmicec.app";
+
+ /** The class name of the main activity in the APK. */
+ private static final String CLASS = "HdmiCecKeyEventCapture";
+
+ /** The command to launch the main activity. */
+ private static final String START_COMMAND = String.format(
+ "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
+
+ /** The command to clear the main activity. */
+ private static final String CLEAR_COMMAND = String.format("pm clear %s", PACKAGE);
+
+ private static final int WAIT_TIME = 10;
+
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE);
+
+ @Rule
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ AUDIO_DEVICE.getDeviceType()))
+ .around(hdmiCecClient);
+
+ private String getSystemLocale() throws Exception {
+ ITestDevice device = getDevice();
+ return device.executeShellCommand("getprop " + PROPERTY_LOCALE).trim();
+ }
+
+ private void setSystemLocale(String locale) throws Exception {
+ ITestDevice device = getDevice();
+ device.executeShellCommand("setprop " + PROPERTY_LOCALE + " " + locale);
+ }
+
+ private boolean isLanguageEditable() throws Exception {
+ String val = getDevice().executeShellCommand("getprop ro.hdmi.set_menu_language");
+ return val.trim().equals("true") ? true : false;
+ }
+
+ private static String extractLanguage(String locale) {
+ return locale.split("[^a-zA-Z]")[0];
+ }
+
+ private void checkArcIsInitiated(){
+ try {
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.REQUEST_ARC_INITIATION);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
+ } catch(Exception e) {
+ assumeNoException(e);
+ }
+ }
+
+ private void logShouldNotContain(String expectedOut) throws Exception {
+ ITestDevice device = getDevice();
+ TimeUnit.SECONDS.sleep(WAIT_TIME);
+ String logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
+ // Search for string.
+ String testString = "";
+ Scanner in = new Scanner(logs);
+ while (in.hasNextLine()) {
+ String line = in.nextLine();
+ if(line.startsWith("I/" + CLASS)) {
+ testString = line.split(":")[1].trim();
+ break;
+ }
+ }
+ device.executeAdbCommand("logcat", "-c");
+ assertThat(testString).doesNotContain(expectedOut);
+ }
+
+ /**
+ * Test 12-1
+ * Tests that the device ignores every broadcast only message that is received as
+ * directly addressed.
+ */
+ @Test
+ public void cect_12_1_BroadcastReceivedAsDirectlyAddressed() throws Exception {
+ /* <Set Menu Language> */
+ assumeTrue(isLanguageEditable());
+ final String locale = getSystemLocale();
+ final String originalLanguage = extractLanguage(locale);
+ final String language = originalLanguage.equals("spa") ? "eng" : "spa";
+ try {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ AUDIO_DEVICE,
+ CecOperand.SET_MENU_LANGUAGE,
+ CecMessage.convertStringToHexParams(language));
+ assertThat(originalLanguage).isEqualTo(extractLanguage(getSystemLocale()));
+ } finally {
+ // If the language was incorrectly changed during the test, restore it.
+ setSystemLocale(locale);
+ }
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <GET_CEC_VERSION> if received as
+ * a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_getCecVersion() throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.GET_CEC_VERSION);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.TV,
+ CecOperand.CEC_VERSION);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <GIVE_PHYSICAL_ADDRESS> if received
+ * as a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_givePhysicalAddress()
+ throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.GIVE_PHYSICAL_ADDRESS);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.BROADCAST,
+ CecOperand.REPORT_PHYSICAL_ADDRESS);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <GIVE_AUDIO_STATUS> if received as
+ * a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveAudioStatus() throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.GIVE_AUDIO_STATUS);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.TV,
+ CecOperand.REPORT_AUDIO_STATUS);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <GIVE_POWER_STATUS> if received as
+ * a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_givePowerStatus() throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.GIVE_POWER_STATUS);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.TV,
+ CecOperand.REPORT_POWER_STATUS);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <GIVE_DEVICE_VENDOR_ID> if received
+ * as a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveDeviceVendorId()
+ throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.GIVE_DEVICE_VENDOR_ID);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.BROADCAST,
+ CecOperand.DEVICE_VENDOR_ID);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <GIVE_OSD_NAME> if received as
+ * a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveOsdName() throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.GIVE_OSD_NAME);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.TV,
+ CecOperand.SET_OSD_NAME);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <GIVE_SYSTEM_AUDIO_MODE_STATUS> if
+ * received as a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_giveSystemAudioModeStatus()
+ throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.TV,
+ CecOperand.SYSTEM_AUDIO_MODE_STATUS);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <REQUEST_SHORT_AUDIO_DESCRIPTOR> if
+ * received as a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_requestShortAudioDescriptor()
+ throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.REQUEST_SHORT_AUDIO_DESCRIPTOR,
+ CecMessage.formatParams("01020304"));
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.TV,
+ CecOperand.REPORT_SHORT_AUDIO_DESCRIPTOR);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <SYSTEM_AUDIO_MODE_REQUEST> if
+ * received as a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_systemAudioModeRequest()
+ throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.BROADCAST,
+ CecOperand.SET_SYSTEM_AUDIO_MODE);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <REQUEST_ARC_INITIATION> if
+ * received as a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_requestArcInitiation()
+ throws Exception {
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.REQUEST_ARC_INITIATION);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.BROADCAST,
+ CecOperand.INITIATE_ARC);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <REQUEST_ARC_TERMINATION> if
+ * received as a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_requestArcTermination()
+ throws Exception {
+ checkArcIsInitiated();
+ hdmiCecClient.sendCecMessage(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ CecOperand.REQUEST_ARC_TERMINATION);
+ hdmiCecClient.checkOutputDoesNotContainMessage(
+ LogicalAddress.BROADCAST,
+ CecOperand.TERMINATE_ARC);
+ }
+
+ /**
+ * Test 12-2
+ * Tests that the device ignores directly addressed message <USER_CONTROL_PRESSED> if received
+ * as a broadcast message
+ */
+ @Test
+ public void cect_12_2_DirectlyAddressedReceivedAsBroadcast_userControlPressed()
+ throws Exception {
+ ITestDevice device = getDevice();
+ // Clear activity
+ device.executeShellCommand(CLEAR_COMMAND);
+ // Clear logcat.
+ device.executeAdbCommand("logcat", "-c");
+ // Start the APK and wait for it to complete.
+ device.executeShellCommand(START_COMMAND);
+ hdmiCecClient.sendUserControlPressAndRelease(
+ LogicalAddress.TV,
+ LogicalAddress.BROADCAST,
+ HdmiCecConstants.CEC_CONTROL_UP,
+ false);
+ logShouldNotContain("Short press KEYCODE_DPAD_UP");
+ }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
index c81b550..d0f052d 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
@@ -18,26 +18,41 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
/** HDMI CEC test to verify logical address after device reboot (Section 10.2.5) */
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecLogicalAddressTest extends BaseHostJUnit4Test {
- private static final CecDevice AUDIO_DEVICE = CecDevice.AUDIO_SYSTEM;
+
+ private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
+
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE);
@Rule
- public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ AUDIO_DEVICE.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 10.2.5-1
@@ -49,7 +64,7 @@
ITestDevice device = getDevice();
device.executeShellCommand("reboot");
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
- assertThat(hdmiCecClient.getSourceFromMessage(message)).isEqualTo(AUDIO_DEVICE);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+ assertThat(CecMessage.getSource(message)).isEqualTo(AUDIO_DEVICE);
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
index 9931e26..e7e0be4 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecRemoteControlPassThroughTest.java
@@ -18,15 +18,18 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -51,9 +54,18 @@
private static final int WAIT_TIME = 10;
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.AUDIO_SYSTEM);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.AUDIO_SYSTEM, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.AUDIO_SYSTEM.getDeviceType()))
+ .around(hdmiCecClient);
private void lookForLog(String expectedOut) throws Exception {
ITestDevice device = getDevice();
@@ -87,22 +99,22 @@
device.executeAdbCommand("logcat", "-c");
// Start the APK and wait for it to complete.
device.executeShellCommand(START_COMMAND);
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_UP, false);
lookForLog("Short press KEYCODE_DPAD_UP");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_DOWN, false);
lookForLog("Short press KEYCODE_DPAD_DOWN");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_LEFT, false);
lookForLog("Short press KEYCODE_DPAD_LEFT");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_RIGHT, false);
lookForLog("Short press KEYCODE_DPAD_RIGHT");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_SELECT, false);
lookForLog("Short press KEYCODE_DPAD_CENTER");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_BACK, false);
lookForLog("Short press KEYCODE_BACK");
}
@@ -121,22 +133,22 @@
device.executeAdbCommand("logcat", "-c");
// Start the APK and wait for it to complete.
device.executeShellCommand(START_COMMAND);
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_UP, true);
lookForLog("Long press KEYCODE_DPAD_UP");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_DOWN, true);
lookForLog("Long press KEYCODE_DPAD_DOWN");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_LEFT, true);
lookForLog("Long press KEYCODE_DPAD_LEFT");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_RIGHT, true);
lookForLog("Long press KEYCODE_DPAD_RIGHT");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_SELECT, true);
lookForLog("Long press KEYCODE_DPAD_CENTER");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_BACK, true);
lookForLog("Long press KEYCODE_BACK");
}
@@ -155,22 +167,22 @@
device.executeAdbCommand("logcat", "-c");
// Start the APK and wait for it to complete.
device.executeShellCommand(START_COMMAND);
- hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_UP, true);
lookForLog("Long press KEYCODE_DPAD_UP");
- hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_DOWN, true);
lookForLog("Long press KEYCODE_DPAD_DOWN");
- hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_LEFT, true);
lookForLog("Long press KEYCODE_DPAD_LEFT");
- hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_RIGHT, true);
lookForLog("Long press KEYCODE_DPAD_RIGHT");
- hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_SELECT, true);
lookForLog("Long press KEYCODE_DPAD_CENTER");
- hdmiCecClient.sendUserControlPress(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
+ hdmiCecClient.sendUserControlPress(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
HdmiCecConstants.CEC_CONTROL_BACK, true);
lookForLog("Long press KEYCODE_BACK");
}
@@ -190,28 +202,28 @@
device.executeAdbCommand("logcat", "-c");
// Start the APK and wait for it to complete.
device.executeShellCommand(START_COMMAND);
- hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
- CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_UP,
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+ LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_UP,
HdmiCecConstants.CEC_CONTROL_BACK, true);
lookForLog("Long press KEYCODE_DPAD_UP");
- hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
- CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_DOWN,
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+ LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_DOWN,
HdmiCecConstants.CEC_CONTROL_UP, true);
lookForLog("Long press KEYCODE_DPAD_DOWN");
- hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
- CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_LEFT,
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+ LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_LEFT,
HdmiCecConstants.CEC_CONTROL_DOWN, true);
lookForLog("Long press KEYCODE_DPAD_LEFT");
- hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
- CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_RIGHT,
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+ LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_RIGHT,
HdmiCecConstants.CEC_CONTROL_LEFT, true);
lookForLog("Long press KEYCODE_DPAD_RIGHT");
- hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
- CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_SELECT,
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+ LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_SELECT,
HdmiCecConstants.CEC_CONTROL_RIGHT, true);
lookForLog("Long press KEYCODE_DPAD_CENTER");
- hdmiCecClient.sendUserControlInterruptedPressAndHold(CecDevice.TV,
- CecDevice.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_BACK,
+ hdmiCecClient.sendUserControlInterruptedPressAndHold(LogicalAddress.TV,
+ LogicalAddress.AUDIO_SYSTEM, HdmiCecConstants.CEC_CONTROL_BACK,
HdmiCecConstants.CEC_CONTROL_SELECT, true);
lookForLog("Long press KEYCODE_BACK");
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
index fe4ccf5..4cb7b97 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
@@ -18,13 +18,18 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assume.assumeTrue;
+import com.google.common.collect.Lists;
import com.google.common.collect.Range;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -32,11 +37,17 @@
import org.junit.After;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/** HDMI CEC test to test system audio mode (Section 11.2.15) */
@RunWith(DeviceJUnit4ClassRunner.class)
@@ -48,6 +59,9 @@
/** The class name of the main activity in the APK. */
private static final String CLASS = "HdmiCecAudioManager";
+ /** The tag of the SadConfigurationReaderTest launched by the APK. */
+ private static final String SAD_READER = "SadConfigurationReaderTest";
+
/** The command to launch the main activity. */
private static final String START_COMMAND = String.format(
"am start -n %s/%s.%s -a ", PACKAGE, PACKAGE, CLASS);
@@ -56,13 +70,27 @@
private static final String CLEAR_COMMAND = String.format("pm clear %s", PACKAGE);
private static final int WAIT_TIME = 10;
- private static final CecDevice AUDIO_DEVICE = CecDevice.AUDIO_SYSTEM;
+ private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
private static final int ON = 0x1;
private static final int OFF = 0x0;
+ private static final int MAX_AUDIO_FORMATS = 4;
+ private static final int MAX_VALID_AUDIO_FORMATS = 2;
+ private static final String SAD_CONFIGURATION_MARKER = "Supported Audio Formats";
+
+ private List<Integer> mSupportedAudioFormats = null;
+
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(AUDIO_DEVICE, "-t", "t");
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(AUDIO_DEVICE, this, "-t", "t");
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ AUDIO_DEVICE.getDeviceType()))
+ .around(hdmiCecClient);
private void lookForLogFromHdmiCecAudioManager(String expectedOut) throws Exception {
ITestDevice device = getDevice();
@@ -82,6 +110,71 @@
assertThat(testString).isEqualTo(expectedOut);
}
+ private void lookForLogFromSadConfigurationReaderTest(String expectedOut) throws Exception {
+ ITestDevice device = getDevice();
+ TimeUnit.SECONDS.sleep(WAIT_TIME);
+ String logs =
+ device.executeAdbCommand("logcat", "-v", "brief", "-d", SAD_READER + ":I", "*:S");
+ // Search for string.
+ String testString = "";
+ Scanner in = new Scanner(logs);
+ while (in.hasNextLine()) {
+ String line = in.nextLine();
+ if (line.startsWith("I/" + SAD_READER)) {
+ testString = line.split(":")[1].trim();
+ if (testString.equals(SAD_CONFIGURATION_MARKER)) {
+ List<String> mFormatsLine =
+ Arrays.asList(in.nextLine().split(":")[1].trim().split(", "));
+ List<String> mCodecSADsLine =
+ Arrays.asList(in.nextLine().split(":")[1].trim().split(", "));
+ mSupportedAudioFormats =
+ Lists.transform(mFormatsLine, fl -> Integer.parseInt(fl));
+ break;
+ }
+ }
+ }
+ device.executeAdbCommand("logcat", "-c");
+ assumeTrue(testString.equals(expectedOut));
+ }
+
+ private String getRequestSadFormatsParams(boolean sendValidFormats) throws Exception {
+ ITestDevice device = getDevice();
+ // Clear activity
+ device.executeShellCommand(CLEAR_COMMAND);
+ // Clear logcat.
+ device.executeAdbCommand("logcat", "-c");
+ // Start the APK and wait for it to complete.
+ device.executeShellCommand(START_COMMAND + "android.hdmicec.app.GET_SUPPORTED_SAD_FORMATS");
+ lookForLogFromSadConfigurationReaderTest(SAD_CONFIGURATION_MARKER);
+
+ // Create a list of all the audio format codes according to CEA-861-D. Remove the supported
+ // audio format codes from it, to get the unsupported audio format codes.
+ List<Integer> mAllCodecFormats =
+ IntStream.range(1, 15).boxed().collect(Collectors.toList());
+ List<Integer> unsupportedAudioFormats = new ArrayList<>();
+ unsupportedAudioFormats.addAll(mAllCodecFormats);
+ unsupportedAudioFormats.removeAll(mSupportedAudioFormats);
+ // Create params message for REQUEST_SHORT_AUDIO_DESCRIPTOR
+ String messageParams = "";
+ int i = 0;
+ int listIndex = 0;
+ if (sendValidFormats) {
+ while (i < Math.min(MAX_VALID_AUDIO_FORMATS, mSupportedAudioFormats.size())) {
+ messageParams +=
+ CecMessage.formatParams(mSupportedAudioFormats.get(listIndex), 2);
+ i++;
+ listIndex++;
+ }
+ listIndex = 0;
+ }
+ while (i < Math.min(MAX_AUDIO_FORMATS, unsupportedAudioFormats.size())) {
+ messageParams += CecMessage.formatParams(unsupportedAudioFormats.get(listIndex), 2);
+ i++;
+ listIndex++;
+ }
+ return messageParams;
+ }
+
private void muteDevice() throws Exception {
ITestDevice device = getDevice();
// Clear activity
@@ -126,85 +219,50 @@
}
public void sendSystemAudioModeTermination() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST);
}
public void sendSystemAudioModeInitiation() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
- HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+ HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
}
private int getDutAudioStatus() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE, CecMessage.GIVE_AUDIO_STATUS);
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.REPORT_AUDIO_STATUS);
- return hdmiCecClient.getParamsFromMessage(message);
- }
-
- private void reportAudioStatus_0_unmuted() throws Exception {
- unmuteDevice();
- setDeviceVolume(0);
- int reportedVolume = getDutAudioStatus();
- /* Allow for a range of volume, since the actual volume set will depend on the device's
- volume resolution. */
- assertThat(reportedVolume).isEqualTo(0);
- }
-
- private void reportAudioStatus_50_unmuted() throws Exception {
- unmuteDevice();
- setDeviceVolume(50);
- int reportedVolume = (getDutAudioStatus() * 100) / 127;
- /* Allow for a range of volume, since the actual volume set will depend on the device's
- volume resolution. */
- assertThat(reportedVolume).isIn(Range.closed(40, 60));
- }
-
- private void reportAudioStatus_100_unmuted() throws Exception {
- unmuteDevice();
- setDeviceVolume(100);
- int reportedVolume = getDutAudioStatus();
- /* Allow for a range of volume, since the actual volume set will depend on the device's
- volume resolution. */
- assertThat(reportedVolume).isEqualTo(100);
- }
-
- private void reportAudioStatusMuted() throws Exception {
- muteDevice();
- int reportedVolume = getDutAudioStatus();
- /* If device is muted, the 8th bit of CEC message parameters is set and the volume will
- be greater than 127. */
- assertWithMessage("Device not muted").that(reportedVolume).isGreaterThan(127);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE, CecOperand.GIVE_AUDIO_STATUS);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.REPORT_AUDIO_STATUS);
+ return CecMessage.getParams(message);
}
private void initateSystemAudioModeFromTuner() throws Exception {
getDevice().reboot();
- hdmiCecClient.sendCecMessage(CecDevice.TUNER_1, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TUNER_1, AUDIO_DEVICE,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
handleSetSystemAudioModeOnToTv();
}
private void handleSetSystemAudioModeOnToTv() throws Exception {
- hdmiCecClient.checkExpectedOutput(CecMessage.REQUEST_ACTIVE_SOURCE);
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST, CecMessage.ACTIVE_SOURCE,
- hdmiCecClient.formatParams("2000"));
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ hdmiCecClient.checkExpectedOutput(CecOperand.REQUEST_ACTIVE_SOURCE);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST, CecOperand.ACTIVE_SOURCE,
+ CecMessage.formatParams("2000"));
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
}
private void initiateSystemAudioModeFromDut() throws Exception {
getDevice().reboot();
- hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
- CecMessage.GIVE_SYSTEM_AUDIO_MODE_STATUS);
- hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.INITIATE_ARC);
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.AUDIO_SYSTEM,
- CecMessage.ARC_INITIATED);
+ hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
+ CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.AUDIO_SYSTEM,
+ CecOperand.ARC_INITIATED);
handleSetSystemAudioModeOnToTv();
}
@@ -220,18 +278,18 @@
*/
@Test
public void cect_11_2_15_1_SystemAudioModeRequestAsFollower() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
/* Repeat test for device 0x3 (TUNER_1) */
- hdmiCecClient.sendCecMessage(CecDevice.TUNER_1, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
- message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TUNER_1, AUDIO_DEVICE,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
+ message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
}
/**
@@ -242,8 +300,8 @@
@Test
public void cect_11_2_15_2_SystemAudioModeWithFeatureInitiation() throws Exception {
initiateSystemAudioModeFromDut();
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
}
/**
@@ -255,10 +313,10 @@
@Test
public void cect_11_2_15_3_SystemAudioModeWithFeatureAbort() throws Exception {
initiateSystemAudioModeFromDut();
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE, CecMessage.FEATURE_ABORT,
- hdmiCecClient.formatParams(CecMessage.SET_SYSTEM_AUDIO_MODE + "04"));
- hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
- CecMessage.SET_SYSTEM_AUDIO_MODE);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE, CecOperand.FEATURE_ABORT,
+ CecMessage.formatParams(CecOperand.SET_SYSTEM_AUDIO_MODE + "04"));
+ hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST,
+ CecOperand.SET_SYSTEM_AUDIO_MODE);
//The DUT will need a reboot here so it'll forget the feature abort from the previous
// message. Else it may not respond correctly with a SET_SYSTEM_AUDIO_MODE message
// in future tests.
@@ -273,13 +331,13 @@
@Test
public void cect_11_2_15_4_SystemAudioModeStatusOn() throws Exception {
sendSystemAudioModeInitiation();
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.GIVE_SYSTEM_AUDIO_MODE_STATUS);
- message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.SYSTEM_AUDIO_MODE_STATUS);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+ message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.SYSTEM_AUDIO_MODE_STATUS);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
}
/**
@@ -290,11 +348,11 @@
@Test
public void cect_11_2_15_5_SetSystemAudioModeOff() throws Exception {
sendSystemAudioModeInitiation();
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
sendSystemAudioModeTermination();
- message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+ message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
}
/**
@@ -307,11 +365,11 @@
try {
getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
sendSystemAudioModeInitiation();
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
- message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+ message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
} finally {
getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
}
@@ -324,13 +382,13 @@
*/
@Test
public void cect_11_2_15_7_SystemAudioModeStatusOff() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.SET_SYSTEM_AUDIO_MODE, hdmiCecClient.formatParams(OFF));
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.GIVE_SYSTEM_AUDIO_MODE_STATUS);
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.SYSTEM_AUDIO_MODE_STATUS);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.SET_SYSTEM_AUDIO_MODE, CecMessage.formatParams(OFF));
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.SYSTEM_AUDIO_MODE_STATUS);
+ assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
}
/**
@@ -341,36 +399,113 @@
@Test
public void cect_11_2_15_8_HandleUcpMute() throws Exception {
unmuteDevice();
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, AUDIO_DEVICE,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS));
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, AUDIO_DEVICE,
HdmiCecConstants.CEC_CONTROL_MUTE, false);
assertWithMessage("Device is not muted").that(isDeviceMuted()).isTrue();
}
/**
* Test 11.2.15-9
- * Tests that the device responds with a <Report Audio Status> message to a
- * <Give Audio Status> message.
+ * Tests that the DUT responds with a <Report Audio Status> message with correct parameters
+ * to a <Give Audio Status> message when volume is set to 0% and not muted.
*/
@Test
- public void cect_11_2_15_9_ReportAudioStatus() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
- HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
- /** Set volume to 0 % and check the <Report Audio Status> */
- reportAudioStatus_0_unmuted();
+ public void cect_11_2_15_9_ReportAudioStatus_0_unmuted() throws Exception {
+ sendSystemAudioModeInitiation();
+ unmuteDevice();
+ setDeviceVolume(0);
+ int reportedVolume = getDutAudioStatus();
+ assertThat(reportedVolume).isEqualTo(0);
+ }
- /** Set volume to 50 % and check the <Report Audio Status> */
- reportAudioStatus_50_unmuted();
+ /**
+ * Test 11.2.15-9
+ * Tests that the DUT responds with a <Report Audio Status> message with correct parameters
+ * to a <Give Audio Status> message when volume is set to 50% and not muted.
+ */
+ @Test
+ public void cect_11_2_15_9_ReportAudioStatus_50_unmuted() throws Exception {
+ sendSystemAudioModeInitiation();
+ unmuteDevice();
+ setDeviceVolume(50);
+ int reportedVolume = getDutAudioStatus();
+ /* Allow for a range of volume, since the actual volume set will depend on the device's
+ volume resolution. */
+ assertThat(reportedVolume).isIn(Range.closed(46, 54));
+ }
- /** Set volume to 100 % and check the <Report Audio Status> */
- reportAudioStatus_100_unmuted();
+ /**
+ * Test 11.2.15-9
+ * Tests that the DUT responds with a <Report Audio Status> message with correct parameters
+ * to a <Give Audio Status> message when volume is set to 100% and not muted.
+ */
+ @Test
+ public void cect_11_2_15_9_ReportAudioStatus_100_unmuted() throws Exception {
+ sendSystemAudioModeInitiation();
+ unmuteDevice();
+ setDeviceVolume(100);
+ int reportedVolume = getDutAudioStatus();
+ assertThat(reportedVolume).isEqualTo(100);
+ }
- /** Mute volume and check the <Report Audio Status> */
- reportAudioStatusMuted();
+ /**
+ * Test 11.2.15-9
+ * Tests that the DUT responds with a <Report Audio Status> message with correct parameters
+ * to a <Give Audio Status> message when volume is muted.
+ */
+ @Test
+ public void cect_11_2_15_9_ReportAudioStatusMuted() throws Exception {
+ sendSystemAudioModeInitiation();
+ muteDevice();
+ int reportedVolume = getDutAudioStatus();
+ /* If device is muted, the 8th bit of CEC message parameters is set and the volume will
+ be greater than 127. */
+ assertWithMessage("Device not muted").that(reportedVolume).isGreaterThan(127);
+ }
+
+ /**
+ * Test 11.2.15-13
+ * Tests that the device responds to a <Request Short Audio Descriptor> message with a
+ * <Report Short Audio Descriptor> message with only those audio descriptors that are supported.
+ */
+ @Test
+ public void cect_11_2_15_13_ValidShortAudioDescriptor() throws Exception {
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.REQUEST_SHORT_AUDIO_DESCRIPTOR, getRequestSadFormatsParams(true));
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.REPORT_SHORT_AUDIO_DESCRIPTOR);
+ /* Each Short Audio Descriptor is 3 bytes long. In the first byte of the params, bits 3-6
+ * will have the audio format. Bit 7 will always 0 for audio format defined in CEA-861-D.
+ * Bits 0-2 represent (Max number of channels - 1). Discard bits 0-2 and check for the
+ * format.
+ * Iterate the params by 3 bytes(6 nibbles) and extract only the first byte(2 nibbles).
+ */
+ for (int i = 0; i < Math.min(mSupportedAudioFormats.size(), MAX_VALID_AUDIO_FORMATS); i++) {
+ int audioFormat =
+ CecMessage.getParams(message, 6 * i, 6 * i + 2) >>> 3;
+ assertWithMessage("Could not find audio format " + audioFormat)
+ .that(mSupportedAudioFormats).contains(audioFormat);
+ }
+ }
+
+ /**
+ * Test 11.2.15-14
+ * Tests that the device responds to a <Request Short Audio Descriptor> message with a
+ * <Feature Abort> [“Invalid Operand”] when a <Request Short Audio Descriptor> message is
+ * received with a single [Audio Format ID][Audio Format Code] pair that is not supported.
+ */
+ @Test
+ public void cect_11_2_15_14_InvalidShortAudioDescriptor() throws Exception {
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.REQUEST_SHORT_AUDIO_DESCRIPTOR, getRequestSadFormatsParams(false));
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.FEATURE_ABORT);
+ assertThat(CecOperand.getOperand(CecMessage.getParams(message, 2)))
+ .isEqualTo(CecOperand.REQUEST_SHORT_AUDIO_DESCRIPTOR);
+ assertThat(CecMessage.getParams(message, 2, 4))
+ .isEqualTo(HdmiCecConstants.ABORT_INVALID_OPERAND);
}
/**
@@ -382,14 +517,14 @@
public void cect_11_2_15_16_UnmuteForSystemAudioRequestOn() throws Exception {
muteDevice();
sendSystemAudioModeTermination();
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
- HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
- message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+ HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
+ message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
assertWithMessage("Device muted").that(isDeviceMuted()).isFalse();
}
@@ -400,15 +535,15 @@
*/
@Test
public void cect_11_2_15_17_MuteForSystemAudioRequestOff() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE,
- CecMessage.SYSTEM_AUDIO_MODE_REQUEST,
- hdmiCecClient.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
- HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
+ CecOperand.SYSTEM_AUDIO_MODE_REQUEST,
+ CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
+ HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
sendSystemAudioModeTermination();
- message = hdmiCecClient.checkExpectedOutput(CecMessage.SET_SYSTEM_AUDIO_MODE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+ message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_SYSTEM_AUDIO_MODE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
assertWithMessage("Device not muted").that(isDeviceMuted()).isTrue();
}
@@ -421,10 +556,10 @@
@Test
public void cect_11_2_15_18_SystemAudioModeWithFeatureAbortWithinTime() throws Exception {
initiateSystemAudioModeFromDut();
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE, CecMessage.FEATURE_ABORT,
- hdmiCecClient.formatParams(CecMessage.SET_SYSTEM_AUDIO_MODE + "04"));
- hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
- CecMessage.SET_SYSTEM_AUDIO_MODE);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE, CecOperand.FEATURE_ABORT,
+ CecMessage.formatParams(CecOperand.SET_SYSTEM_AUDIO_MODE + "04"));
+ hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST,
+ CecOperand.SET_SYSTEM_AUDIO_MODE);
}
/**
@@ -437,9 +572,9 @@
@Test
public void cect_11_2_15_19_SystemAudioModeWithFeatureAbortWithinTime() throws Exception {
initateSystemAudioModeFromTuner();
- hdmiCecClient.sendCecMessage(CecDevice.TV, AUDIO_DEVICE, CecMessage.FEATURE_ABORT,
- hdmiCecClient.formatParams(CecMessage.SET_SYSTEM_AUDIO_MODE + "04"));
- hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
- CecMessage.SET_SYSTEM_AUDIO_MODE);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE, CecOperand.FEATURE_ABORT,
+ CecMessage.formatParams(CecOperand.SET_SYSTEM_AUDIO_MODE + "04"));
+ hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST,
+ CecOperand.SET_SYSTEM_AUDIO_MODE);
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java
index 05b2071..ef10425 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java
@@ -18,26 +18,40 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
/** HDMI CEC tests related to the device reporting the device OSD name (Section 11.2.11) */
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecDeviceOsdNameTest extends BaseHostJUnit4Test {
- private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+ private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.PLAYBACK_1.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 11.2.11-1a
@@ -53,9 +67,9 @@
if (deviceName.length() > nameLength) {
deviceName = deviceName.substring(0, nameLength).trim();
}
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_OSD_NAME);
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.SET_OSD_NAME);
- assertThat(hdmiCecClient.getAsciiStringFromMessage(message)).isEqualTo(deviceName);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_OSD_NAME);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.SET_OSD_NAME);
+ assertThat(CecMessage.getAsciiString(message)).isEqualTo(deviceName);
}
/**
@@ -70,10 +84,10 @@
String originalName = device.executeShellCommand("settings get global device_name").trim();
try {
device.executeShellCommand("settings put global device_name '" + testName + "'");
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_OSD_NAME);
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.SET_OSD_NAME);
- assertThat(hdmiCecClient.getAsciiStringFromMessage(message)).isEqualTo(testName);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_OSD_NAME);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.SET_OSD_NAME);
+ assertThat(CecMessage.getAsciiString(message)).isEqualTo(testName);
} finally {
device.executeShellCommand("settings put global device_name '" + originalName + "'");
}
@@ -85,11 +99,11 @@
*/
@Test
public void cect_11_2_11_2_UnregisteredDeviceGiveOsdNameTest() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.PLAYBACK_1, CecMessage.GIVE_OSD_NAME);
- hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.PLAYBACK_1,
- CecMessage.SET_OSD_NAME);
- hdmiCecClient.sendCecMessage(CecDevice.BROADCAST, CecMessage.GIVE_OSD_NAME);
- hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
- CecMessage.SET_OSD_NAME);
+ hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_1, CecOperand.GIVE_OSD_NAME);
+ hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.PLAYBACK_1,
+ CecOperand.SET_OSD_NAME);
+ hdmiCecClient.sendCecMessage(LogicalAddress.BROADCAST, CecOperand.GIVE_OSD_NAME);
+ hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST,
+ CecOperand.SET_OSD_NAME);
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecLogicalAddressTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecLogicalAddressTest.java
index 89d5304..d48a6cd 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecLogicalAddressTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecLogicalAddressTest.java
@@ -18,27 +18,41 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
/** HDMI CEC test to verify physical address after device reboot (Section 10.2.3) */
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecLogicalAddressTest extends BaseHostJUnit4Test {
- private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+ private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.PLAYBACK_1.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 10.2.3-1
@@ -50,7 +64,7 @@
ITestDevice device = getDevice();
device.executeShellCommand("reboot");
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
- assertThat(hdmiCecClient.getSourceFromMessage(message)).isEqualTo(PLAYBACK_DEVICE);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+ assertThat(CecMessage.getSource(message)).isEqualTo(PLAYBACK_DEVICE);
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecOneTouchPlayTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecOneTouchPlayTest.java
index 1b86b12..56a7df2 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecOneTouchPlayTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecOneTouchPlayTest.java
@@ -18,15 +18,19 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -36,9 +40,32 @@
private static final int PHYSICAL_ADDRESS = 0x1000;
+ /** Intent to launch the remote pairing activity */
+ private static final String ACTION_CONNECT_INPUT_NORMAL =
+ "com.google.android.intent.action.CONNECT_INPUT";
+
+ /** Package name of the Settings app */
+ private static final String SETTINGS_PACKAGE =
+ "com.android.tv.settings";
+
+ /** The command to broadcast an intent. */
+ private static final String START_COMMAND = "am start -a ";
+
+ /** The command to stop an app. */
+ private static final String FORCE_STOP_COMMAND = "am force-stop ";
+
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.PLAYBACK_1.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 11.2.1-1
@@ -48,9 +75,25 @@
@Test
public void cect_11_2_1_1_OneTouchPlay() throws Exception {
ITestDevice device = getDevice();
+ device.reboot();
device.executeShellCommand("input keyevent KEYCODE_HOME");
- hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.TEXT_VIEW_ON);
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.ACTIVE_SOURCE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(PHYSICAL_ADDRESS);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TEXT_VIEW_ON);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.ACTIVE_SOURCE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
+ }
+
+ /**
+ * Tests that the device sends a <TEXT_VIEW_ON> when the pairing activity is started on
+ * device, followed by a <ACTIVE_SOURCE> message.
+ */
+ @Test
+ public void cect_PairingActivity_OneTouchPlay() throws Exception {
+ ITestDevice device = getDevice();
+ device.reboot();
+ device.executeShellCommand(START_COMMAND + ACTION_CONNECT_INPUT_NORMAL);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TEXT_VIEW_ON);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.ACTIVE_SOURCE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
+ device.executeShellCommand(FORCE_STOP_COMMAND + SETTINGS_PACKAGE);
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPhysicalAddressTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPhysicalAddressTest.java
index de28c23..bae8490 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPhysicalAddressTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPhysicalAddressTest.java
@@ -18,16 +18,20 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -35,9 +39,18 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecPhysicalAddressTest extends BaseHostJUnit4Test {
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.PLAYBACK_1.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 10.1.2-1
@@ -49,8 +62,8 @@
ITestDevice device = getDevice();
device.executeShellCommand("reboot");
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
- int physicalAddress = hdmiCecClient.getParamsFromMessage(message,
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+ int physicalAddress = CecMessage.getParams(message,
HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH);
assertThat(HdmiCecConstants.PHYSICAL_ADDRESS).isEqualTo(physicalAddress);
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
index 46ac18a..b1bfb1e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
@@ -18,16 +18,20 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -42,9 +46,18 @@
private static final int WAIT_TIME = 5;
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.PLAYBACK_1.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 11.2.14-1
@@ -56,10 +69,10 @@
ITestDevice device = getDevice();
/* Make sure the device is not booting up/in standby */
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_POWER_STATUS);
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.REPORT_POWER_STATUS);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(ON);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.REPORT_POWER_STATUS);
+ assertThat(CecMessage.getParams(message)).isEqualTo(ON);
}
/**
@@ -73,14 +86,14 @@
try {
/* Make sure the device is not booting up/in standby */
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
- // b/154737276 - Required for Yukawa
+ /* Home Key to prevent device from going to deep suspend state */
device.executeShellCommand("input keyevent KEYCODE_HOME");
device.executeShellCommand("input keyevent KEYCODE_SLEEP");
TimeUnit.SECONDS.sleep(WAIT_TIME);
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_POWER_STATUS);
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.REPORT_POWER_STATUS);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(OFF);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.REPORT_POWER_STATUS);
+ assertThat(CecMessage.getParams(message)).isEqualTo(OFF);
} finally {
/* Wake up the device */
device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
index 1981759..160e80f5 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
@@ -18,15 +18,18 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -57,11 +60,30 @@
private static final int WAIT_TIME = 10;
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.PLAYBACK_1.getDeviceType()))
+ .around(hdmiCecClient);
private void lookForLog(String expectedOut) throws Exception {
+ String testString = getKeyPressLog();
+ assertThat(testString).isEqualTo(expectedOut);
+ }
+
+ private void lookForLog(String expectedOut1, String expectedOut2) throws Exception {
+ String testString = getKeyPressLog();
+ assertThat(testString).isAnyOf(expectedOut1, expectedOut2);
+ }
+
+ private String getKeyPressLog() throws Exception {
ITestDevice device = getDevice();
TimeUnit.SECONDS.sleep(WAIT_TIME);
String logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
@@ -76,7 +98,7 @@
}
}
device.executeAdbCommand("logcat", "-c");
- assertThat(testString).isEqualTo(expectedOut);
+ return testString;
}
/**
@@ -93,22 +115,22 @@
device.executeAdbCommand("logcat", "-c");
// Start the APK and wait for it to complete.
device.executeShellCommand(START_COMMAND);
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_UP, false);
lookForLog("Short press KEYCODE_DPAD_UP");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_DOWN, false);
lookForLog("Short press KEYCODE_DPAD_DOWN");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_LEFT, false);
lookForLog("Short press KEYCODE_DPAD_LEFT");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_RIGHT, false);
lookForLog("Short press KEYCODE_DPAD_RIGHT");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_SELECT, false);
- lookForLog("Short press KEYCODE_DPAD_CENTER");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ lookForLog("Short press KEYCODE_DPAD_CENTER", "Short press KEYCODE_ENTER");
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_BACK, false);
lookForLog("Short press KEYCODE_BACK");
}
@@ -127,22 +149,22 @@
device.executeAdbCommand("logcat", "-c");
// Start the APK and wait for it to complete.
device.executeShellCommand(START_COMMAND);
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_UP, true);
lookForLog("Long press KEYCODE_DPAD_UP");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_DOWN, true);
lookForLog("Long press KEYCODE_DPAD_DOWN");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_LEFT, true);
lookForLog("Long press KEYCODE_DPAD_LEFT");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_RIGHT, true);
lookForLog("Long press KEYCODE_DPAD_RIGHT");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_SELECT, true);
- lookForLog("Long press KEYCODE_DPAD_CENTER");
- hdmiCecClient.sendUserControlPressAndRelease(CecDevice.TV, CecDevice.PLAYBACK_1,
+ lookForLog("Long press KEYCODE_DPAD_CENTER", "Long press KEYCODE_ENTER");
+ hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, LogicalAddress.PLAYBACK_1,
HdmiCecConstants.CEC_CONTROL_BACK, true);
lookForLog("Long press KEYCODE_BACK");
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
index f75925f..8bec365 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
@@ -18,16 +18,20 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -39,9 +43,18 @@
private static final int PHYSICAL_ADDRESS = 0x1000;
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.PLAYBACK_1.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 11.2.2-1
@@ -52,14 +65,14 @@
public void cect_11_2_2_1_SetStreamPathToDut() throws Exception {
final long hdmi2Address = 0x2000;
/* Switch to HDMI2. Setup assumes DUT is connected to HDMI1. */
- hdmiCecClient.sendCecMessage(CecDevice.PLAYBACK_2, CecDevice.BROADCAST,
- CecMessage.ACTIVE_SOURCE, hdmiCecClient.formatParams(hdmi2Address));
+ hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_2, LogicalAddress.BROADCAST,
+ CecOperand.ACTIVE_SOURCE, CecMessage.formatParams(hdmi2Address));
TimeUnit.SECONDS.sleep(3);
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
- CecMessage.SET_STREAM_PATH,
- hdmiCecClient.formatParams(HdmiCecConstants.PHYSICAL_ADDRESS));
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.ACTIVE_SOURCE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(PHYSICAL_ADDRESS);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+ CecOperand.SET_STREAM_PATH,
+ CecMessage.formatParams(HdmiCecConstants.PHYSICAL_ADDRESS));
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.ACTIVE_SOURCE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
}
/**
@@ -71,10 +84,10 @@
public void cect_11_2_2_2_RequestActiveSource() throws Exception {
ITestDevice device = getDevice();
device.executeShellCommand("input keyevent KEYCODE_HOME");
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
- CecMessage.REQUEST_ACTIVE_SOURCE);
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.ACTIVE_SOURCE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(PHYSICAL_ADDRESS);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+ CecOperand.REQUEST_ACTIVE_SOURCE);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.ACTIVE_SOURCE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
}
/**
@@ -88,9 +101,9 @@
try {
device.executeShellCommand("input keyevent KEYCODE_HOME");
device.executeShellCommand("input keyevent KEYCODE_SLEEP");
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.INACTIVE_SOURCE);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(PHYSICAL_ADDRESS);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.INACTIVE_SOURCE);
+ assertThat(CecMessage.getParams(message)).isEqualTo(PHYSICAL_ADDRESS);
} finally {
/* Wake up the device */
device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStartupTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStartupTest.java
index 8305fab..06b62f9 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStartupTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStartupTest.java
@@ -20,10 +20,12 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assume.assumeTrue;
-import android.hdmicec.cts.CecDevice;
-import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -33,6 +35,7 @@
import com.google.common.collect.ImmutableList;
import javax.annotation.Nullable;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -44,18 +47,28 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecStartupTest extends BaseHostJUnit4Test {
- private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
- private static final ImmutableList<CecMessage> necessaryMessages =
- new ImmutableList.Builder<CecMessage>()
- .add(CecMessage.REPORT_PHYSICAL_ADDRESS, CecMessage.CEC_VERSION,
- CecMessage.DEVICE_VENDOR_ID, CecMessage.GIVE_POWER_STATUS).build();
- private static final ImmutableList<CecMessage> permissibleMessages =
- new ImmutableList.Builder<CecMessage>()
- .add(CecMessage.VENDOR_COMMAND, CecMessage.GIVE_DEVICE_VENDOR_ID,
- CecMessage.SET_OSD_NAME, CecMessage.GIVE_OSD_NAME).build();
+ private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+ private static final ImmutableList<CecOperand> necessaryMessages =
+ new ImmutableList.Builder<CecOperand>()
+ .add(CecOperand.REPORT_PHYSICAL_ADDRESS, CecOperand.CEC_VERSION,
+ CecOperand.DEVICE_VENDOR_ID, CecOperand.GIVE_POWER_STATUS).build();
+ private static final ImmutableList<CecOperand> permissibleMessages =
+ new ImmutableList.Builder<CecOperand>()
+ .add(CecOperand.VENDOR_COMMAND, CecOperand.GIVE_DEVICE_VENDOR_ID,
+ CecOperand.SET_OSD_NAME, CecOperand.GIVE_OSD_NAME).build();
+
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
@Rule
- public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ PLAYBACK_DEVICE.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Tests that the device sends all the messages that should be sent on startup. It also ensures
@@ -67,26 +80,27 @@
/* Make sure device is playback only. Not applicable to playback/audio combinations */
String deviceTypeCsv = device.executeShellCommand("getprop ro.hdmi.device_type").trim();
- assumeTrue(deviceTypeCsv.equals(CecDevice.PLAYBACK_1.getDeviceType()));
+ assumeTrue(deviceTypeCsv.equals(LogicalAddress.PLAYBACK_1.getDeviceType()));
device.executeShellCommand("reboot");
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
/* Monitor CEC messages for 20s after reboot */
- final List<CecMessage> messagesReceived = hdmiCecClient.getAllMessages(CecDevice.PLAYBACK_1, 20);
+ final List<CecOperand> messagesReceived =
+ hdmiCecClient.getAllMessages(LogicalAddress.PLAYBACK_1, 20);
/* Predicate to apply on necessaryMessages to ensure that all necessaryMessages are received. */
- final Predicate<CecMessage> notReceived = new Predicate<CecMessage>() {
+ final Predicate<CecOperand> notReceived = new Predicate<CecOperand>() {
@Override
- public boolean apply(@Nullable CecMessage cecMessage) {
- return !messagesReceived.contains(cecMessage);
+ public boolean apply(@Nullable CecOperand cecOperand) {
+ return !messagesReceived.contains(cecOperand);
}
};
/* Predicate to apply on messagesReceived to ensure all messages received are permissible. */
- final Predicate<CecMessage> notAllowed = new Predicate<CecMessage>() {
+ final Predicate<CecOperand> notAllowed = new Predicate<CecOperand>() {
@Override
- public boolean apply(@Nullable CecMessage cecMessage) {
- return !(permissibleMessages.contains(cecMessage) || necessaryMessages.contains(cecMessage));
+ public boolean apply(@Nullable CecOperand cecOperand) {
+ return !(permissibleMessages.contains(cecOperand) || necessaryMessages.contains(cecOperand));
}
};
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
index 0e40519..dfa84b8 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
@@ -18,29 +18,42 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
-import java.util.concurrent.TimeUnit;
-
/** HDMI CEC test to verify system audio control commands (Section 11.2.15) */
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecSystemAudioControlTest extends BaseHostJUnit4Test {
- private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+ private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+
+ public HdmiCecClientWrapper hdmiCecClient =
+ new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1, "-t", "a");
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this, "-t", "a");
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ PLAYBACK_DEVICE.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 11.2.15-10
@@ -49,12 +62,12 @@
@Test
public void cect_11_2_15_10_GiveSystemAudioModeStatus() throws Exception {
ITestDevice device = getDevice();
- // b/154737276 - Required for Yukawa
+ /* Home Key to prevent device from going to deep suspend state */
device.executeShellCommand("input keyevent KEYCODE_HOME");
device.executeShellCommand("input keyevent KEYCODE_SLEEP");
device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
- hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
- CecMessage.GIVE_SYSTEM_AUDIO_MODE_STATUS);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM,
+ CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS);
}
/**
@@ -67,19 +80,19 @@
public void cect_11_2_15_11_VolumeUpDownUserControlPressed() throws Exception {
ITestDevice device = getDevice();
device.executeShellCommand("input keyevent KEYCODE_VOLUME_UP");
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
- CecMessage.USER_CONTROL_PRESSED);
- assertThat(hdmiCecClient.getParamsFromMessage(message))
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM,
+ CecOperand.USER_CONTROL_PRESSED);
+ assertThat(CecMessage.getParams(message))
.isEqualTo(HdmiCecConstants.CEC_CONTROL_VOLUME_UP);
- hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM, CecMessage.USER_CONTROL_RELEASED);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM, CecOperand.USER_CONTROL_RELEASED);
device.executeShellCommand("input keyevent KEYCODE_VOLUME_DOWN");
- message = hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
- CecMessage.USER_CONTROL_PRESSED);
- assertThat(hdmiCecClient.getParamsFromMessage(message))
+ message = hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM,
+ CecOperand.USER_CONTROL_PRESSED);
+ assertThat(CecMessage.getParams(message))
.isEqualTo(HdmiCecConstants.CEC_CONTROL_VOLUME_DOWN);
- hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM, CecMessage.USER_CONTROL_RELEASED);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM, CecOperand.USER_CONTROL_RELEASED);
}
/**
@@ -92,10 +105,9 @@
public void cect_11_2_15_12_MuteUserControlPressed() throws Exception {
ITestDevice device = getDevice();
device.executeShellCommand("input keyevent KEYCODE_MUTE");
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
- CecMessage.USER_CONTROL_PRESSED);
- assertThat(hdmiCecClient.getParamsFromMessage(message))
- .isEqualTo(HdmiCecConstants.CEC_CONTROL_MUTE);
- hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM, CecMessage.USER_CONTROL_RELEASED);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM,
+ CecOperand.USER_CONTROL_PRESSED);
+ assertThat(CecMessage.getParams(message)).isEqualTo(HdmiCecConstants.CEC_CONTROL_MUTE);
+ hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM, CecOperand.USER_CONTROL_RELEASED);
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
index d6fb1dc..5e59a8f 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
@@ -21,16 +21,20 @@
import static org.junit.Assume.assumeTrue;
import android.hdmicec.cts.CecClientMessage;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -43,9 +47,18 @@
private static final String PROPERTY_LOCALE = "persist.sys.locale";
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ LogicalAddress.PLAYBACK_1.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 11.2.6-1
@@ -53,7 +66,7 @@
*/
@Test
public void cect_11_2_6_1_Ack() throws Exception {
- String command = CecClientMessage.POLL + " " + CecDevice.PLAYBACK_1;
+ String command = CecClientMessage.POLL + " " + LogicalAddress.PLAYBACK_1;
String expectedOutput = "POLL sent";
hdmiCecClient.sendConsoleMessage(command);
if (!hdmiCecClient.checkConsoleOutput(expectedOutput)) {
@@ -68,11 +81,11 @@
*/
@Test
public void cect_11_2_6_2_GivePhysicalAddress() throws Exception {
- hdmiCecClient.sendCecMessage(CecMessage.GIVE_PHYSICAL_ADDRESS);
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.REPORT_PHYSICAL_ADDRESS);
+ hdmiCecClient.sendCecMessage(CecOperand.GIVE_PHYSICAL_ADDRESS);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
/* The checkExpectedOutput has already verified the first 4 nibbles of the message. We
* have to verify the last 6 nibbles */
- int receivedParams = hdmiCecClient.getParamsFromMessage(message);
+ int receivedParams = CecMessage.getParams(message);
assertThat(HdmiCecConstants.PHYSICAL_ADDRESS).isEqualTo(receivedParams >> 8);
assertThat(HdmiCecConstants.PLAYBACK_DEVICE_TYPE).isEqualTo(receivedParams & 0xFF);
}
@@ -83,10 +96,10 @@
*/
@Test
public void cect_11_2_6_6_GiveCecVersion() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GET_CEC_VERSION);
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
- CecMessage.CEC_VERSION);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isEqualTo(CEC_VERSION_NUMBER);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GET_CEC_VERSION);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV,
+ CecOperand.CEC_VERSION);
+ assertThat(CecMessage.getParams(message)).isEqualTo(CEC_VERSION_NUMBER);
}
/**
@@ -95,11 +108,11 @@
*/
@Test
public void cect_11_2_6_7_GetMenuLanguage() throws Exception {
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GET_MENU_LANGUAGE);
- String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.FEATURE_ABORT);
- int abortedOpcode = hdmiCecClient.getParamsFromMessage(message,
- CecMessage.GET_MENU_LANGUAGE.toString().length());
- assertThat(CecMessage.getMessage(abortedOpcode)).isEqualTo(CecMessage.GET_MENU_LANGUAGE);
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GET_MENU_LANGUAGE);
+ String message = hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.FEATURE_ABORT);
+ int abortedOpcode = CecMessage.getParams(message,
+ CecOperand.GET_MENU_LANGUAGE.toString().length());
+ assertThat(CecOperand.getOperand(abortedOpcode)).isEqualTo(CecOperand.GET_MENU_LANGUAGE);
}
private String getSystemLocale() throws Exception {
@@ -133,8 +146,8 @@
final String language = originalLanguage.equals("spa") ? "eng" : "spa";
final String newLanguage = originalLanguage.equals("spa") ? "en" : "es";
try {
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
- CecMessage.SET_MENU_LANGUAGE, hdmiCecClient.convertStringToHexParams(language));
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+ CecOperand.SET_MENU_LANGUAGE, CecMessage.convertStringToHexParams(language));
assertThat(extractLanguage(getSystemLocale())).isEqualTo(newLanguage);
} finally {
setSystemLocale(locale);
@@ -152,8 +165,8 @@
final String originalLanguage = extractLanguage(locale);
final String language = "spb";
try {
- hdmiCecClient.sendCecMessage(CecDevice.TV, CecDevice.BROADCAST,
- CecMessage.SET_MENU_LANGUAGE, hdmiCecClient.convertStringToHexParams(language));
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
+ CecOperand.SET_MENU_LANGUAGE, CecMessage.convertStringToHexParams(language));
assertThat(extractLanguage(getSystemLocale())).isEqualTo(originalLanguage);
} finally {
setSystemLocale(locale);
@@ -172,8 +185,8 @@
final String originalLanguage = extractLanguage(locale);
final String language = originalLanguage.equals("spa") ? "eng" : "spa";
try {
- hdmiCecClient.sendCecMessage(CecDevice.RECORDER_1, CecDevice.BROADCAST,
- CecMessage.SET_MENU_LANGUAGE, hdmiCecClient.convertStringToHexParams(language));
+ hdmiCecClient.sendCecMessage(LogicalAddress.RECORDER_1, LogicalAddress.BROADCAST,
+ CecOperand.SET_MENU_LANGUAGE, CecMessage.convertStringToHexParams(language));
assertThat(extractLanguage(getSystemLocale())).isEqualTo(originalLanguage);
} finally {
setSystemLocale(locale);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemStandbyTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemStandbyTest.java
index 983b501..369542e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemStandbyTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemStandbyTest.java
@@ -18,18 +18,19 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
-import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDeviceState;
-import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -38,14 +39,24 @@
/** HDMI CEC test to verify the device handles standby correctly (Section 11.2.3) */
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecSystemStandbyTest extends BaseHostJUnit4Test {
- private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+ private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
private static final String HDMI_CONTROL_DEVICE_AUTO_OFF =
"hdmi_control_auto_device_off_enabled";
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ PLAYBACK_DEVICE.getDeviceType()))
+ .around(hdmiCecClient);
private boolean setHdmiControlDeviceAutoOff(boolean turnOn) throws Exception {
ITestDevice device = getDevice();
@@ -58,13 +69,13 @@
return val.equals("1") ? true : false;
}
- private void checkDeviceAsleepAfterStandbySent(CecDevice source, CecDevice destination)
+ private void checkDeviceAsleepAfterStandbySent(LogicalAddress source, LogicalAddress destination)
throws Exception {
ITestDevice device = getDevice();
try {
device.executeShellCommand("input keyevent KEYCODE_HOME");
TimeUnit.SECONDS.sleep(5);
- hdmiCecClient.sendCecMessage(source, destination, CecMessage.STANDBY);
+ hdmiCecClient.sendCecMessage(source, destination, CecOperand.STANDBY);
TimeUnit.SECONDS.sleep(5);
String wakeState = device.executeShellCommand("dumpsys power | grep mWakefulness=");
assertThat(wakeState.trim()).isEqualTo("mWakefulness=Asleep");
@@ -84,19 +95,19 @@
getDevice().waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
try {
TimeUnit.SECONDS.sleep(5);
- checkDeviceAsleepAfterStandbySent(CecDevice.TV, CecDevice.BROADCAST);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.TV, LogicalAddress.BROADCAST);
/* Wake up the TV */
- hdmiCecClient.sendConsoleMessage("on " + CecDevice.TV);
- checkDeviceAsleepAfterStandbySent(CecDevice.RECORDER_1, CecDevice.BROADCAST);
+ hdmiCecClient.sendConsoleMessage("on " + LogicalAddress.TV);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.RECORDER_1, LogicalAddress.BROADCAST);
/* Wake up the TV */
- hdmiCecClient.sendConsoleMessage("on " + CecDevice.TV);
- checkDeviceAsleepAfterStandbySent(CecDevice.AUDIO_SYSTEM, CecDevice.BROADCAST);
+ hdmiCecClient.sendConsoleMessage("on " + LogicalAddress.TV);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.AUDIO_SYSTEM, LogicalAddress.BROADCAST);
/* Wake up the TV */
- hdmiCecClient.sendConsoleMessage("on " + CecDevice.TV);
- checkDeviceAsleepAfterStandbySent(CecDevice.PLAYBACK_2, CecDevice.BROADCAST);
+ hdmiCecClient.sendConsoleMessage("on " + LogicalAddress.TV);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.PLAYBACK_2, LogicalAddress.BROADCAST);
} finally {
/* Wake up the TV */
- hdmiCecClient.sendConsoleMessage("on " + CecDevice.TV);
+ hdmiCecClient.sendConsoleMessage("on " + LogicalAddress.TV);
}
}
@@ -108,11 +119,11 @@
public void cect_11_2_3_3_HandleAddressedStandby() throws Exception {
getDevice().executeShellCommand("reboot");
getDevice().waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
- checkDeviceAsleepAfterStandbySent(CecDevice.TV, CecDevice.PLAYBACK_1);
- checkDeviceAsleepAfterStandbySent(CecDevice.RECORDER_1, CecDevice.PLAYBACK_1);
- checkDeviceAsleepAfterStandbySent(CecDevice.AUDIO_SYSTEM, CecDevice.PLAYBACK_1);
- checkDeviceAsleepAfterStandbySent(CecDevice.PLAYBACK_2, CecDevice.PLAYBACK_1);
- checkDeviceAsleepAfterStandbySent(CecDevice.BROADCAST, CecDevice.PLAYBACK_1);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.TV, LogicalAddress.PLAYBACK_1);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.RECORDER_1, LogicalAddress.PLAYBACK_1);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.AUDIO_SYSTEM, LogicalAddress.PLAYBACK_1);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.PLAYBACK_2, LogicalAddress.PLAYBACK_1);
+ checkDeviceAsleepAfterStandbySent(LogicalAddress.BROADCAST, LogicalAddress.PLAYBACK_1);
}
/**
@@ -125,7 +136,7 @@
boolean wasOn = setHdmiControlDeviceAutoOff(false);
try {
device.executeShellCommand("input keyevent KEYCODE_SLEEP");
- hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST, CecMessage.STANDBY);
+ hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST, CecOperand.STANDBY);
device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
} finally {
setHdmiControlDeviceAutoOff(wasOn);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecVendorCommandsTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecVendorCommandsTest.java
index 3dc1463..edff394 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecVendorCommandsTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecVendorCommandsTest.java
@@ -18,28 +18,42 @@
import static com.google.common.truth.Truth.assertThat;
-import android.hdmicec.cts.CecDevice;
import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
import android.hdmicec.cts.HdmiCecClientWrapper;
import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RequiredPropertyRule;
+import android.hdmicec.cts.RequiredFeatureRule;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.junit.Test;
/** HDMI CEC test to verify device vendor specific commands (Section 11.2.9) */
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecVendorCommandsTest extends BaseHostJUnit4Test {
- private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+ private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
private static final int INCORRECT_VENDOR_ID = 0x0;
+ public HdmiCecClientWrapper hdmiCecClient = new HdmiCecClientWrapper(LogicalAddress.PLAYBACK_1);
+
@Rule
- public HdmiCecClientWrapper hdmiCecClient =
- new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+ public RuleChain ruleChain =
+ RuleChain
+ .outerRule(new RequiredFeatureRule(this, LogicalAddress.HDMI_CEC_FEATURE))
+ .around(new RequiredFeatureRule(this, LogicalAddress.LEANBACK_FEATURE))
+ .around(RequiredPropertyRule.asCsvContainsValue(
+ this,
+ LogicalAddress.HDMI_DEVICE_TYPE_PROPERTY,
+ PLAYBACK_DEVICE.getDeviceType()))
+ .around(hdmiCecClient);
/**
* Test 11.2.9-1
@@ -48,11 +62,14 @@
*/
@Test
public void cect_11_2_9_1_GiveDeviceVendorId() throws Exception {
- for (CecDevice cecDevice : CecDevice.values()) {
- hdmiCecClient.sendCecMessage(cecDevice, CecMessage.GIVE_DEVICE_VENDOR_ID);
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.DEVICE_VENDOR_ID);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).
- isNotEqualTo(INCORRECT_VENDOR_ID);
+ for (LogicalAddress logicalAddress : LogicalAddress.values()) {
+ // Skip the logical address of this device
+ if (logicalAddress == PLAYBACK_DEVICE) {
+ continue;
+ }
+ hdmiCecClient.sendCecMessage(logicalAddress, CecOperand.GIVE_DEVICE_VENDOR_ID);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.DEVICE_VENDOR_ID);
+ assertThat(CecMessage.getParams(message)).isNotEqualTo(INCORRECT_VENDOR_ID);
}
}
@@ -66,7 +83,7 @@
ITestDevice device = getDevice();
device.executeShellCommand("reboot");
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
- String message = hdmiCecClient.checkExpectedOutput(CecMessage.DEVICE_VENDOR_ID);
- assertThat(hdmiCecClient.getParamsFromMessage(message)).isNotEqualTo(INCORRECT_VENDOR_ID);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.DEVICE_VENDOR_ID);
+ assertThat(CecMessage.getParams(message)).isNotEqualTo(INCORRECT_VENDOR_ID);
}
}
diff --git a/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl b/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
index a820ae5..5aafdf0 100644
--- a/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -25,4 +25,5 @@
String getRestrictBackgroundStatus();
void sendNotification(int notificationId, String notificationType);
void registerNetworkCallback(in INetworkCallback cb);
+ void unregisterNetworkCallback();
}
diff --git a/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl b/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
index 740ec26..2048bab 100644
--- a/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
+++ b/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
@@ -17,9 +17,11 @@
package com.android.cts.net.hostside;
import android.net.Network;
+import android.net.NetworkCapabilities;
interface INetworkCallback {
void onBlockedStatusChanged(in Network network, boolean blocked);
void onAvailable(in Network network);
void onLost(in Network network);
+ void onCapabilitiesChanged(in Network network, in NetworkCapabilities cap);
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index c8fe624..3efc6d0 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -30,6 +30,7 @@
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -188,7 +189,9 @@
do {
attempts++;
count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED);
- if (count >= expectedCount) {
+ assertFalse("Expected count " + expectedCount + " but actual is " + count,
+ count > expectedCount);
+ if (count == expectedCount) {
break;
}
Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after "
@@ -708,6 +711,10 @@
mServiceClient.registerNetworkCallback(cb);
}
+ protected void unregisterNetworkCallback() throws Exception {
+ mServiceClient.unregisterNetworkCallback();
+ }
+
/**
* Registers a {@link NotificationListenerService} implementation that will execute the
* notification actions right after the notification is sent.
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
index 3ee7b99..6546e26 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -100,4 +100,8 @@
public void registerNetworkCallback(INetworkCallback cb) throws RemoteException {
mService.registerNetworkCallback(cb);
}
+
+ public void unregisterNetworkCallback() throws RemoteException {
+ mService.unregisterNetworkCallback();
+ }
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index ed397b9..f3cd8a9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -16,18 +16,23 @@
package com.android.cts.net.hostside;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.net.Network;
+import android.net.NetworkCapabilities;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Objects;
@@ -35,15 +40,18 @@
import java.util.concurrent.TimeUnit;
public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
-
private Network mNetwork;
private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
+ @Rule
+ public final MeterednessConfigurationRule mMeterednessConfiguration
+ = new MeterednessConfigurationRule();
enum CallbackState {
NONE,
AVAILABLE,
LOST,
- BLOCKED_STATUS
+ BLOCKED_STATUS,
+ CAPABILITIES
}
private static class CallbackInfo {
@@ -75,7 +83,7 @@
}
private class TestNetworkCallback extends INetworkCallback.Stub {
- private static final int TEST_CALLBACK_TIMEOUT_MS = 200;
+ private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000;
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
@@ -117,12 +125,18 @@
setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
}
- public void expectLostCallback(Network expectedNetwork) {
- expectCallback(CallbackState.LOST, expectedNetwork, null);
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+ setLastCallback(CallbackState.CAPABILITIES, network, cap);
}
- public void expectAvailableCallback(Network expectedNetwork) {
- expectCallback(CallbackState.AVAILABLE, expectedNetwork, null);
+ public Network expectAvailableCallbackAndGetNetwork() {
+ final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+ if (cb.state != CallbackState.AVAILABLE) {
+ fail("Network is not available. Instead obtained the following callback :"
+ + cb);
+ }
+ return cb.network;
}
public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
@@ -130,15 +144,28 @@
expectBlocked);
}
- void assertNoCallback() {
- CallbackInfo cb = null;
- try {
- cb = mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- // Expected.
- }
- if (cb != null) {
- assertNull("Unexpected callback: " + cb, cb);
+ public void waitBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
+ final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS;
+ do {
+ final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis()));
+ if (cb.state == CallbackState.BLOCKED_STATUS) {
+ assertEquals(expectBlocked, cb.arg);
+ return;
+ }
+ } while (System.currentTimeMillis() <= deadline);
+ fail("Didn't receive onBlockedStatusChanged()");
+ }
+
+ public void expectCapabilitiesCallback(Network expectedNetwork, boolean hasCapability,
+ int capability) {
+ final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+ final NetworkCapabilities cap = (NetworkCapabilities) cb.arg;
+ assertEquals(expectedNetwork, cb.network);
+ assertEquals(CallbackState.CAPABILITIES, cb.state);
+ if (hasCapability != cap.hasCapability(capability)) {
+ fail("NetworkCapabilities callback "
+ + (hasCapability ? "missing expected" : "has unexpected")
+ + " capability. " + cb);
}
}
}
@@ -147,13 +174,28 @@
public void setUp() throws Exception {
super.setUp();
- mNetwork = mCm.getActiveNetwork();
-
registerBroadcastReceiver();
removeRestrictBackgroundWhitelist(mUid);
removeRestrictBackgroundBlacklist(mUid);
assertRestrictBackgroundChangedReceived(0);
+
+ // Initial state
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+
+ // Make wifi a metered network.
+ mMeterednessConfiguration.configureNetworkMeteredness(true);
+
+ // Register callback
+ registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+ // Once the wifi is marked as metered, the wifi will reconnect. Wait for onAvailable()
+ // callback to ensure wifi is connected before the test and store the default network.
+ mNetwork = mTestNetworkCallback.expectAvailableCallbackAndGetNetwork();
+ // Check that the network is metered.
+ mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, false /* hasCapability */,
+ NET_CAPABILITY_NOT_METERED);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
}
@After
@@ -162,102 +204,80 @@
setRestrictBackground(false);
setBatterySaverMode(false);
+ unregisterNetworkCallback();
}
@RequiredProperties({DATA_SAVER_MODE})
@Test
public void testOnBlockedStatusChanged_dataSaver() throws Exception {
- // Initial state
- setBatterySaverMode(false);
- setRestrictBackground(false);
-
- final MeterednessConfigurationRule meterednessConfiguration
- = new MeterednessConfigurationRule();
- meterednessConfiguration.configureNetworkMeteredness(true);
try {
- // Register callback
- registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
- mTestNetworkCallback.expectAvailableCallback(mNetwork);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
// Enable restrict background
setRestrictBackground(true);
assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
// Add to whitelist
addRestrictBackgroundWhitelist(mUid);
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
// Remove from whitelist
removeRestrictBackgroundWhitelist(mUid);
assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
} finally {
- meterednessConfiguration.resetNetworkMeteredness();
+ mMeterednessConfiguration.resetNetworkMeteredness();
}
// Set to non-metered network
- meterednessConfiguration.configureNetworkMeteredness(false);
+ mMeterednessConfiguration.configureNetworkMeteredness(false);
+ mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, true /* hasCapability */,
+ NET_CAPABILITY_NOT_METERED);
try {
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
// Disable restrict background, should not trigger callback
setRestrictBackground(false);
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.assertNoCallback();
} finally {
- meterednessConfiguration.resetNetworkMeteredness();
+ mMeterednessConfiguration.resetNetworkMeteredness();
}
}
@RequiredProperties({BATTERY_SAVER_MODE})
@Test
public void testOnBlockedStatusChanged_powerSaver() throws Exception {
- // Set initial state.
- setBatterySaverMode(false);
- setRestrictBackground(false);
-
- final MeterednessConfigurationRule meterednessConfiguration
- = new MeterednessConfigurationRule();
- meterednessConfiguration.configureNetworkMeteredness(true);
try {
- // Register callback
- registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
- mTestNetworkCallback.expectAvailableCallback(mNetwork);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
// Enable Power Saver
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
// Disable Power Saver
setBatterySaverMode(false);
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
} finally {
- meterednessConfiguration.resetNetworkMeteredness();
+ mMeterednessConfiguration.resetNetworkMeteredness();
}
// Set to non-metered network
- meterednessConfiguration.configureNetworkMeteredness(false);
+ mMeterednessConfiguration.configureNetworkMeteredness(false);
+ mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, true /* hasCapability */,
+ NET_CAPABILITY_NOT_METERED);
try {
- mTestNetworkCallback.assertNoCallback();
-
// Enable Power Saver
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
// Disable Power Saver
setBatterySaverMode(false);
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
} finally {
- meterednessConfiguration.resetNetworkMeteredness();
+ mMeterednessConfiguration.resetNetworkMeteredness();
}
}
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
index ec536af..590e17e 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -127,6 +127,16 @@
unregisterNetworkCallback();
}
}
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+ try {
+ cb.onCapabilitiesChanged(network, cap);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Cannot send onCapabilitiesChanged: " + e);
+ unregisterNetworkCallback();
+ }
+ }
};
mCm.registerNetworkCallback(makeWifiNetworkRequest(), mNetworkCallback);
try {
@@ -135,17 +145,21 @@
unregisterNetworkCallback();
}
}
- };
- private void unregisterNetworkCallback() {
- Log.d(TAG, "unregistering network callback");
- mCm.unregisterNetworkCallback(mNetworkCallback);
- mNetworkCallback = null;
- }
+ @Override
+ public void unregisterNetworkCallback() {
+ Log.d(TAG, "unregistering network callback");
+ if (mNetworkCallback != null) {
+ mCm.unregisterNetworkCallback(mNetworkCallback);
+ mNetworkCallback = null;
+ }
+ }
+ };
private NetworkRequest makeWifiNetworkRequest() {
return new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.bp
new file mode 100644
index 0000000..3f6188a
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 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.
+
+cc_test {
+ name: "CVE-2016-2482",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: ["poc.cpp"],
+ include_dirs: ["frameworks/native/include/media/openmax"],
+ shared_libs: [
+ "libnativehelper",
+ "liblog",
+ "libstagefright",
+ "libbinder",
+ "libutils",
+ "libmedia",
+ "libmedia_omx",
+ "libstagefright_foundation",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk
deleted file mode 100644
index b6cdfb0..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (C) 2018 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-2482
-LOCAL_SRC_FILES := poc.cpp
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
- $(TOP)/frameworks/native/include/media/openmax \
-
-LOCAL_SHARED_LIBRARIES := libnativehelper \
- liblog \
- libstagefright \
- libbinder \
- libutils \
- libmedia \
- libmedia_omx \
- libstagefright_foundation
-
-LOCAL_COMPATIBILITY_SUITE := cts sts vts10
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-LOCAL_ARM_MODE := arm
-LOCAL_CPPFLAGS += -Wall -Werror
-
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.bp
new file mode 100644
index 0000000..2a9b893
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 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.
+
+cc_test {
+ name: "CVE-2016-3747",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: ["poc.cpp"],
+ include_dirs: [
+ "frameworks/av/media/libstagefright",
+ "frameworks/native/include/media/openmax",
+ ],
+ shared_libs: [
+ "libstagefright",
+ "libbinder",
+ "libmedia",
+ "libmedia_omx",
+ "liblog",
+ "libutils",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk
deleted file mode 100644
index c4ff5f1..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (C) 2018 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-3747
-LOCAL_SRC_FILES := poc.cpp
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
- $(TOP)/frameworks/av/media/libstagefright \
- $(TOP)/frameworks/native/include/media/openmax
-
-LOCAL_SHARED_LIBRARIES := \
- libstagefright \
- libbinder \
- libmedia \
- libmedia_omx \
- liblog \
- libutils
-
-LOCAL_COMPATIBILITY_SUITE := cts vts10 sts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.bp
new file mode 100644
index 0000000..2a49719
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+ name: "CVE-2016-5862",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: ["poc.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk
deleted file mode 100644
index 75a087e..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-5862
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_COMPATIBILITY_SUITE := cts sts vts10
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.bp
new file mode 100644
index 0000000..b0f0e6c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+ name: "CVE-2016-5867",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: ["poc.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk
deleted file mode 100644
index 4186ccc..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-5867
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_COMPATIBILITY_SUITE := cts sts vts10
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/shortcuts/hostside/Android.bp b/hostsidetests/shortcuts/hostside/Android.bp
index 646953e..3613975 100644
--- a/hostsidetests/shortcuts/hostside/Android.bp
+++ b/hostsidetests/shortcuts/hostside/Android.bp
@@ -27,4 +27,7 @@
"vts10",
"general-tests",
],
+ required: [
+ "CtsBackupHostTestCases",
+ ],
}
diff --git a/hostsidetests/statsd/AndroidTest.xml b/hostsidetests/statsd/AndroidTest.xml
index 2da1662..4598f33 100644
--- a/hostsidetests/statsd/AndroidTest.xml
+++ b/hostsidetests/statsd/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Config for CTS Statsd host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="statsd" />
+ <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
<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" />
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index c076eb6..32b6f01 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -1073,8 +1073,9 @@
// Start test app.
try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
"action.show_notification")) {
- Thread.sleep(WAIT_TIME_SHORT);
// Trigger a pull and wait for new pull before killing the process.
+ Thread.sleep(WAIT_TIME_LONG);
+ // Trigger new pull.
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_LONG);
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
index bf599d3f..adc24db 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
@@ -78,7 +78,7 @@
final String result = getDevice().installPackage(
buildHelper.getTestFile(DEVICE_SIDE_TEST_APK), false, true);
- Thread.sleep(WAIT_TIME_SHORT);
+ Thread.sleep(WAIT_TIME_LONG);
ConfigMetricsReportList reports = getReportList();
assertTrue(reports.getReportsCount() > 0);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
index bca2d47..09598b6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
@@ -51,6 +51,7 @@
@Override
protected void tearDown() throws Exception {
plugInUsb();
+ super.tearDown();
}
/*
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
index cfa0a37..9f15d3a 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
@@ -391,7 +391,7 @@
List<Atom> statsdData = getGaugeMetricDataList();
assertTrue(statsdData.size() > 0);
- assertTrue(statsdData.get(0).getProcStatsPkgProc().getProcStatsSection().getAvailablePagesList().size() > 0);
+ assertTrue(statsdData.get(0).getProcStatsPkgProc().getProcStatsSection().getPackageStatsList().size() > 0);
List<ProcessStatsPackageProto> processStatsPackageProtoList = getAllProcStatsProto();
diff --git a/hostsidetests/theme/assets/R/140dpi.zip b/hostsidetests/theme/assets/S/140dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/140dpi.zip
rename to hostsidetests/theme/assets/S/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/180dpi.zip b/hostsidetests/theme/assets/S/180dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/180dpi.zip
rename to hostsidetests/theme/assets/S/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/200dpi.zip b/hostsidetests/theme/assets/S/200dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/200dpi.zip
rename to hostsidetests/theme/assets/S/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/220dpi.zip b/hostsidetests/theme/assets/S/220dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/220dpi.zip
rename to hostsidetests/theme/assets/S/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/260dpi.zip b/hostsidetests/theme/assets/S/260dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/260dpi.zip
rename to hostsidetests/theme/assets/S/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/280dpi.zip b/hostsidetests/theme/assets/S/280dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/280dpi.zip
rename to hostsidetests/theme/assets/S/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/300dpi.zip b/hostsidetests/theme/assets/S/300dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/300dpi.zip
rename to hostsidetests/theme/assets/S/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/340dpi.zip b/hostsidetests/theme/assets/S/340dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/340dpi.zip
rename to hostsidetests/theme/assets/S/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/360dpi.zip b/hostsidetests/theme/assets/S/360dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/360dpi.zip
rename to hostsidetests/theme/assets/S/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/400dpi.zip b/hostsidetests/theme/assets/S/400dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/400dpi.zip
rename to hostsidetests/theme/assets/S/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/420dpi.zip b/hostsidetests/theme/assets/S/420dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/420dpi.zip
rename to hostsidetests/theme/assets/S/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/440dpi.zip b/hostsidetests/theme/assets/S/440dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/440dpi.zip
rename to hostsidetests/theme/assets/S/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/560dpi.zip b/hostsidetests/theme/assets/S/560dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/560dpi.zip
rename to hostsidetests/theme/assets/S/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/hdpi.zip b/hostsidetests/theme/assets/S/hdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/hdpi.zip
rename to hostsidetests/theme/assets/S/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/ldpi.zip b/hostsidetests/theme/assets/S/ldpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/ldpi.zip
rename to hostsidetests/theme/assets/S/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/mdpi.zip b/hostsidetests/theme/assets/S/mdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/mdpi.zip
rename to hostsidetests/theme/assets/S/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/tvdpi.zip b/hostsidetests/theme/assets/S/tvdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/tvdpi.zip
rename to hostsidetests/theme/assets/S/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xhdpi.zip b/hostsidetests/theme/assets/S/xhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/xhdpi.zip
rename to hostsidetests/theme/assets/S/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xxhdpi.zip b/hostsidetests/theme/assets/S/xxhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/xxhdpi.zip
rename to hostsidetests/theme/assets/S/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xxxhdpi.zip b/hostsidetests/theme/assets/S/xxxhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/R/xxxhdpi.zip
rename to hostsidetests/theme/assets/S/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/ui/Android.mk b/hostsidetests/ui/Android.mk
index 5450454..61bb6d3 100644
--- a/hostsidetests/ui/Android.mk
+++ b/hostsidetests/ui/Android.mk
@@ -24,13 +24,8 @@
LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-$(COMPATIBILITY_TESTCASES_OUT_cts)/CtsUiHostTestCases.jar : $(COMPATIBILITY_TESTCASES_OUT_cts)/com.replica.replicaisland.apk
-
-LOCAL_CTS_TEST_PACKAGE := android.ui.cts
-
# *Not* tagged as a cts test artifact intentionally: b/109660132
+#$(COMPATIBILITY_TESTCASES_OUT_cts)/CtsUiHostTestCases.jar : $(COMPATIBILITY_TESTCASES_OUT_cts)/com.replica.replicaisland.apk
LOCAL_COMPATIBILITY_SUITE := general-tests
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
index 1db254a..0da168b 100644
--- a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
+++ b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
@@ -40,6 +40,7 @@
/**
* Host side CTS tests verifying userspace reboot functionality.
*/
+@RequiresDevice
@RunWith(DeviceJUnit4ClassRunner.class)
public class UserspaceRebootHostTest extends BaseHostJUnit4Test {
@@ -176,6 +177,8 @@
runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
"testVerifyCeStorageUnlocked");
runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
+ "testVerifyReceivedLockedBootCompletedBroadcast", Duration.ofMinutes(3));
+ runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
"testVerifyReceivedBootCompletedBroadcast", Duration.ofMinutes(6));
} finally {
getDevice().executeShellV2Command("cmd lock_settings clear --old 1543");
@@ -207,6 +210,8 @@
runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
"testVerifyCeStorageUnlocked");
runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
+ "testVerifyReceivedLockedBootCompletedBroadcast", Duration.ofMinutes(3));
+ runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest",
"testVerifyReceivedBootCompletedBroadcast", Duration.ofMinutes(6));
} finally {
getDevice().executeShellV2Command("cmd lock_settings clear --old 1543");
@@ -252,7 +257,7 @@
getDevice().setProperty("init.userspace_reboot.sigterm.timeoutmillis", "10");
rebootUserspaceAndWaitForBootComplete();
assertUserspaceRebootFailed();
- assertLastBootReasonIs("userspace_failed,shutdown_aborted");
+ assertLastBootReasonIs("userspace_failed,shutdown_aborted,sigkill");
} finally {
getDevice().setProperty("init.userspace_reboot.sigkill.timeoutmillis", sigkillTimeout);
getDevice().setProperty("init.userspace_reboot.sigterm.timeoutmillis", sigtermTimeout);
diff --git a/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml b/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml
index b541348..97ffde6 100644
--- a/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml
+++ b/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml
@@ -27,14 +27,18 @@
</intent-filter>
</activity>
<uses-library android:name="android.test.runner" />
- <receiver android:name=".BasicUserspaceRebootTest$BootReceiver">
+ <receiver android:name=".BasicUserspaceRebootTest$BootReceiver"
+ android:exported="true"
+ android:directBootAware="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
+ <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<provider android:name=".BasicUserspaceRebootTest$Provider"
android:authorities="com.android.cts.userspacereboot.basic"
- android:exported="true">
+ android:exported="true"
+ android:directBootAware="true">
</provider>
</application>
diff --git a/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java b/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java
index 694d978..c41f221 100644
--- a/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java
+++ b/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java
@@ -95,7 +95,8 @@
}
/**
- * Receiver of {@link Intent.ACTION_BOOT_COMPLETED} broadcast.
+ * Receiver of {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} and
+ * {@link Intent.ACTION_BOOT_COMPLETED} broadcasts.
*/
public static class BootReceiver extends BroadcastReceiver {
@@ -114,7 +115,8 @@
}
/**
- * Returns whenever {@link Intent.ACTION_BOOT_COMPLETED} broadcast was received.
+ * Returns whenever {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} and
+ * {@link Intent.ACTION_BOOT_COMPLETED} broadcast were received.
*/
public static class Provider extends ContentProvider {
@@ -132,9 +134,14 @@
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Context de = getContext().createDeviceProtectedStorageContext();
- File file = new File(de.getFilesDir(), Intent.ACTION_BOOT_COMPLETED.toLowerCase());
- MatrixCursor cursor = new MatrixCursor(new String[]{"exists"});
- cursor.addRow(new Object[] { file.exists() ? 1 : 0});
+ File locked_boot_completed = new File(
+ de.getFilesDir(), Intent.ACTION_LOCKED_BOOT_COMPLETED.toLowerCase());
+ File boot_completed = new File(
+ de.getFilesDir(), Intent.ACTION_BOOT_COMPLETED.toLowerCase());
+ MatrixCursor cursor = new MatrixCursor(
+ new String[]{ "locked_boot_completed", "boot_completed"});
+ cursor.addRow(new Object[] {
+ locked_boot_completed.exists() ? 1 : 0, boot_completed.exists() ? 1 : 0 });
return cursor;
}
diff --git a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java b/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java
index 28603d0..4a512ce 100644
--- a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java
+++ b/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java
@@ -25,6 +25,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.UserManager;
+import android.util.Log;
import com.android.compatibility.common.util.TestUtils;
@@ -48,6 +49,7 @@
private static final String FILE_NAME = "secret.txt";
private static final String SECRET_MESSAGE = "wow, much secret";
+ private static final Duration LOCKED_BOOT_TIMEOUT = Duration.ofMinutes(3);
private static final Duration BOOT_TIMEOUT = Duration.ofMinutes(6);
private final Context mCeContext = getInstrumentation().getContext();
@@ -81,14 +83,26 @@
}
/**
+ * Tests that {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} broadcast was sent.
+ */
+ @Test
+ public void testVerifyReceivedLockedBootCompletedBroadcast() throws Exception {
+ waitForBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED, LOCKED_BOOT_TIMEOUT);
+ }
+
+ /**
* Tests that {@link Intent.ACTION_BOOT_COMPLETED} broadcast was sent.
*/
@Test
public void testVerifyReceivedBootCompletedBroadcast() throws Exception {
+ waitForBroadcast(Intent.ACTION_BOOT_COMPLETED, BOOT_TIMEOUT);
+ }
+
+ private void waitForBroadcast(String intent, Duration timeout) throws Exception {
TestUtils.waitUntil(
- "Didn't receive broadcast " + Intent.ACTION_BOOT_COMPLETED + " in " + BOOT_TIMEOUT,
- (int) BOOT_TIMEOUT.getSeconds(),
- () -> queryBroadcast(Intent.ACTION_BOOT_COMPLETED));
+ "Didn't receive broadcast " + intent + " in " + timeout,
+ (int) timeout.getSeconds(),
+ () -> queryBroadcast(intent));
}
private boolean queryBroadcast(String intent) {
@@ -98,8 +112,14 @@
if (cursor == null) {
return false;
}
- cursor.moveToFirst();
- int index = cursor.getColumnIndex("exists");
+ if (!cursor.moveToFirst()) {
+ Log.w(TAG, "Broadcast: " + intent + " cursor is empty");
+ return false;
+ }
+ String column = intent.equals(Intent.ACTION_LOCKED_BOOT_COMPLETED)
+ ? "locked_boot_completed"
+ : "boot_completed";
+ int index = cursor.getColumnIndex(column);
return cursor.getInt(index) == 1;
}
}
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 5adfac8..f7873ab 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -76,8 +76,8 @@
PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, PACKAGE_NAME_APP3
};
- private static final int WAIT_TIME = 2000;
- private static final int WAITFOR_MSEC = 5000;
+ private static final int WAIT_TIME = 10000;
+ private static final int WAITFOR_MSEC = 10000;
// A secondary test activity from another APK.
static final String SIMPLE_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp";
static final String SIMPLE_SERVICE = ".SimpleService";
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 3d84d388..cfc93be 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -51,6 +51,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.HashMap;
import org.junit.Test;
@@ -224,6 +225,9 @@
updatePreviewSurface(maxPreviewSize);
createImageReader(maxYuvSize, ImageFormat.YUV_420_888, MAX_IMAGES_TO_PREPARE, imageListener);
+ HashMap<Size, Long> yuvMinFrameDurations =
+ mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
+ Long readerMinFrameDuration = yuvMinFrameDurations.get(maxYuvSize);
List<Surface> outputSurfaces = new ArrayList<Surface>();
outputSurfaces.add(mPreviewSurface);
@@ -327,14 +331,16 @@
cameraId,
frameDurationStats.first / 1e6, preparedFrameDurationStats.second / 1e6),
(preparedFrameDurationStats.second <=
- frameDurationStats.first * (1 + PREPARE_PEAK_RATE_BOUNDS)));
+ Math.max(frameDurationStats.first, readerMinFrameDuration) *
+ (1 + PREPARE_PEAK_RATE_BOUNDS)));
mCollector.expectTrue(
String.format("Camera %s: Preview average frame interval affected by use of new " +
"stream: preview avg frame duration: %f ms, with new stream: %f ms",
cameraId,
frameDurationStats.first / 1e6, preparedFrameDurationStats.first / 1e6),
(preparedFrameDurationStats.first <=
- frameDurationStats.first * (1 + PREPARE_FRAME_RATE_BOUNDS)));
+ Math.max(frameDurationStats.first, readerMinFrameDuration) *
+ (1 + PREPARE_FRAME_RATE_BOUNDS)));
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
index bc84c83..f74aa6d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
@@ -31,6 +31,8 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -45,15 +47,10 @@
import android.server.wm.TestJournalProvider.TestJournalContainer;
import android.server.wm.settings.SettingsSession;
-import androidx.annotation.IntDef;
-import androidx.test.filters.FlakyTest;
-
import com.android.compatibility.common.util.SystemUtil;
import org.junit.Test;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
@@ -66,22 +63,6 @@
private static final float EXPECTED_FONT_SIZE_SP = 10.0f;
- /** Verifies if the count of configuration changes is expected. */
- private static final int TEST_MODE_CONFIGURATION_CHANGE = 1;
- /** Verifies if the count of relaunch is expected. */
- private static final int TEST_MODE_RELAUNCH_OR_CONFIG_CHANGE = 2;
- /** Verifies if sizes match. */
- private static final int TEST_MODE_RESIZE = 3;
-
- /** Test mode that defines which lifecycle callback is verified in a particular test */
- @IntDef(flag = true, value = {
- TEST_MODE_CONFIGURATION_CHANGE,
- TEST_MODE_RELAUNCH_OR_CONFIG_CHANGE,
- TEST_MODE_RESIZE
- })
- @Retention(RetentionPolicy.SOURCE)
- private @interface TestMode {}
-
@Test
public void testRotation90Relaunch() throws Exception{
assumeTrue("Skipping test: no rotation support", supportsRotation());
@@ -118,105 +99,95 @@
testRotation(NO_RELAUNCH_ACTIVITY, 2, 0, 0);
}
- @Test
- @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
- public void testRotation180RelaunchWithCutout() throws Exception {
- assumeTrue("Skipping test: no rotation support", supportsRotation());
- assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
-
- testRotation180WithCutout(TEST_ACTIVITY, TEST_MODE_RELAUNCH_OR_CONFIG_CHANGE);
- }
-
- @Test
- @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
- public void testRotation180NoRelaunchWithCutout() throws Exception {
- assumeTrue("Skipping test: no rotation support", supportsRotation());
- assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
-
- testRotation180WithCutout(NO_RELAUNCH_ACTIVITY, TEST_MODE_CONFIGURATION_CHANGE);
- }
-
/**
* Test activity configuration changes for devices with cutout(s). Landscape and
* reverse-landscape rotations should result in same screen space available for apps.
*/
@Test
- @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
- public void testConfigChangeWhenRotatingWithCutout() throws Exception {
+ public void testRotation180RelaunchWithCutout() throws Exception {
assumeTrue("Skipping test: no rotation support", supportsRotation());
assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
- testRotation180WithCutout(TEST_ACTIVITY, TEST_MODE_RESIZE);
+ testRotation180WithCutout(TEST_ACTIVITY, false /* canHandleConfigChange */);
}
- private void testRotation180WithCutout(ComponentName activityName, @TestMode int testMode)
- throws Exception {
+ @Test
+ public void testRotation180NoRelaunchWithCutout() throws Exception {
+ assumeTrue("Skipping test: no rotation support", supportsRotation());
+ assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
+
+ testRotation180WithCutout(NO_RELAUNCH_ACTIVITY, true /* canHandleConfigChange */);
+ }
+
+ private void testRotation180WithCutout(ComponentName activityName,
+ boolean canHandleConfigChange) throws Exception {
launchActivity(activityName);
mAmWmState.computeState(activityName);
try(final RotationSession rotationSession = new RotationSession()) {
- final StateCount count1 = getStateCountForRotation(activityName, rotationSession,
- ROTATION_0 /* before */, ROTATION_180 /* after */);
- final StateCount count2 = getStateCountForRotation(activityName, rotationSession,
- ROTATION_90 /* before */, ROTATION_270 /* after */);
+ final ActivityLifecycleCounts count1 = getLifecycleCountsForRotation(activityName,
+ rotationSession, ROTATION_0 /* before */, ROTATION_180 /* after */,
+ canHandleConfigChange);
+ final int configChangeCount1 = count1
+ .getCount(ActivityCallback.ON_CONFIGURATION_CHANGED);
+ final int relaunchCount1 = count1.getCount(ActivityCallback.ON_CREATE);
- final int configChange = count1.mConfigChangeCount + count2.mConfigChangeCount;
- final int relaunch = count1.mRelaunchCount + count2.mRelaunchCount;
- // There should at least one 180 rotation without resize.
- final boolean sameSize = !count1.mResize || !count2.mResize;
+ final ActivityLifecycleCounts count2 = getLifecycleCountsForRotation(activityName,
+ rotationSession, ROTATION_90 /* before */, ROTATION_270 /* after */,
+ canHandleConfigChange);
+ final int configChangeCount2 = count2
+ .getCount(ActivityCallback.ON_CONFIGURATION_CHANGED);
+ final int relaunchCount2 = count2.getCount(ActivityCallback.ON_CREATE);
- switch(testMode) {
- case TEST_MODE_CONFIGURATION_CHANGE: {
- assertTrue("There must be at most one 180 degree rotation that results in the"
- + " same configuration on device with cutout", configChange <= 1);
- assertEquals("There must be no relaunch during test", 0, relaunch);
- break;
- }
- case TEST_MODE_RELAUNCH_OR_CONFIG_CHANGE: {
- // If the size change does not cross the threshold, the activity will receive
- // onConfigurationChanged instead of relaunching.
- assertTrue("There must be at most one 180 degree rotation that results in"
- + " relaunch or a configuration change on device with cutout",
- relaunch + configChange <= 1);
- break;
- }
- case TEST_MODE_RESIZE: {
- assertTrue("A device with cutout should have the same available screen space"
- + " in landscape and reverse-landscape", sameSize);
- break;
- }
- default: {
- fail("unrecognized test mode: " + testMode);
- }
+ final int configChange = configChangeCount1 + configChangeCount2;
+ final int relaunch = relaunchCount1 + relaunchCount2;
+ if (canHandleConfigChange) {
+ assertWithMessage("There must be at most one 180 degree rotation that results "
+ + "in the same configuration.").that(configChange).isLessThan(2);
+ assertEquals("There must be no relaunch during test", 0, relaunch);
+ return;
}
+
+ // If the size change does not cross the threshold, the activity will receive
+ // onConfigurationChanged instead of relaunching.
+ assertWithMessage("There must be at most one 180 degree rotation that results in "
+ + "relaunch or a configuration change.").that(relaunch + configChange)
+ .isLessThan(2);
+
+ final boolean resize1 = configChangeCount1 + relaunchCount1 > 0;
+ final boolean resize2 = configChangeCount2 + relaunchCount2 > 0;
+ // There should at least one 180 rotation without resize.
+ final boolean sameSize = !resize1 || !resize2;
+
+ assertTrue("A device with cutout should have the same available screen space"
+ + " in landscape and reverse-landscape", sameSize);
}
}
- private StateCount getStateCountForRotation(ComponentName activityName, RotationSession session,
- int before, int after) throws Exception {
+ private ActivityLifecycleCounts getLifecycleCountsForRotation(ComponentName activityName,
+ RotationSession session, int before, int after, boolean canHandleConfigChange) {
+ final int currentRotation = mAmWmState.getWmState().getRotation();
+ // The test verifies the events from "before" rotation to "after" rotation. So when
+ // preparing "before" rotation, the changes should be consumed to avoid being mixed into
+ // the result to verify.
+ final boolean is90DegreeDelta = Math.abs(currentRotation - before) % 2 != 0;
+ if (is90DegreeDelta) {
+ separateTestJournal();
+ }
session.set(before);
+ if (is90DegreeDelta) {
+ // Consume the changes of "before" rotation to make sure the activity is in a stable
+ // state to apply "after" rotation.
+ final ActivityCallback expectedCallback = canHandleConfigChange
+ ? ActivityCallback.ON_CONFIGURATION_CHANGED
+ : ActivityCallback.ON_CREATE;
+ mAmWmState.waitFor("activity rotated with 90 degree delta",
+ () -> new ActivityLifecycleCounts(activityName).getCount(expectedCallback) > 0);
+ }
separateTestJournal();
session.set(after);
mAmWmState.computeState(activityName);
- final ActivityLifecycleCounts counter = new ActivityLifecycleCounts(activityName);
-
- int configChangeCount = counter.getCount(ActivityCallback.ON_CONFIGURATION_CHANGED);
- int relaunchCount = counter.getCount(ActivityCallback.ON_CREATE);
- boolean resize = getLastReportedSizesForActivity(activityName) != null;
-
- return new StateCount(configChangeCount, relaunchCount, resize);
- }
-
- private final static class StateCount {
- final int mConfigChangeCount;
- final int mRelaunchCount;
- final boolean mResize;
-
- StateCount(int configChangeCount, int relaunchCount, boolean resize) {
- mConfigChangeCount = configChangeCount;
- mRelaunchCount = relaunchCount;
- mResize = resize;
- }
+ return new ActivityLifecycleCounts(activityName);
}
@Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index dbbc682..f07519a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -960,6 +960,7 @@
@Test
public void testSetRequestedOrientationWhilePinned() throws Exception {
+ assumeTrue("Skipping test: no rotation support", supportsRotation());
// Launch the PiP activity fixed as portrait, and enter picture-in-picture
launchActivity(PIP_ACTIVITY,
EXTRA_PIP_ORIENTATION, String.valueOf(ORIENTATION_PORTRAIT),
diff --git a/tests/media/DynamicConfig.xml b/tests/media/DynamicConfig.xml
index 3414847..4d6d9d9 100644
--- a/tests/media/DynamicConfig.xml
+++ b/tests/media/DynamicConfig.xml
@@ -1,6 +1,6 @@
<dynamicConfig>
<entry key="media_files_url">
- <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases.zip</value>
+ <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.1.zip</value>
</entry>
</dynamicConfig>
diff --git a/tests/media/jni/NativeCodecDecoderTest.cpp b/tests/media/jni/NativeCodecDecoderTest.cpp
index d5eb85b..8dace62 100644
--- a/tests/media/jni/NativeCodecDecoderTest.cpp
+++ b/tests/media/jni/NativeCodecDecoderTest.cpp
@@ -341,8 +341,6 @@
CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
- CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
- isPass);
CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
CHECK_ERR(
loopCounter == 0 && mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
@@ -462,8 +460,6 @@
CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
- CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
- isPass);
CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass);
if (!isPass) continue;
@@ -483,8 +479,6 @@
CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
- CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
- isPass);
CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass);
if (validateFormat) {
if (mIsCodecInAsyncMode ? !mAsyncHandle.hasOutputFormatChanged()
@@ -611,8 +605,6 @@
CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
- CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log,
- "input cnt != output cnt", isPass);
CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
CHECK_ERR(loopCounter == 0 && mIsAudio &&
(!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
diff --git a/tests/media/jni/NativeCodecTestBase.h b/tests/media/jni/NativeCodecTestBase.h
index 65f4111e..b730fb0 100644
--- a/tests/media/jni/NativeCodecTestBase.h
+++ b/tests/media/jni/NativeCodecTestBase.h
@@ -92,7 +92,12 @@
uint32_t adler32(const uint8_t* input, int offset, int len);
public:
- void saveInPTS(int64_t pts) { inpPtsArray.push_back(pts); }
+ void saveInPTS(int64_t pts) {
+ // Add only Unique timeStamp, discarding any duplicate frame / non-display frame
+ if(0 == std::count(inpPtsArray.begin(), inpPtsArray.end(), pts)) {
+ inpPtsArray.push_back(pts);
+ }
+ }
void saveOutPTS(int64_t pts) { outPtsArray.push_back(pts); }
bool isPtsStrictlyIncreasing(int64_t lastPts);
bool isOutPtsListIdenticalToInpPtsList(bool requireSorting);
diff --git a/tests/media/jni/NativeMediaCommon.cpp b/tests/media/jni/NativeMediaCommon.cpp
index 0debeac..23d9804 100644
--- a/tests/media/jni/NativeMediaCommon.cpp
+++ b/tests/media/jni/NativeMediaCommon.cpp
@@ -48,11 +48,6 @@
bool isCSDIdentical(AMediaFormat* refFormat, AMediaFormat* testFormat) {
const char* mime;
AMediaFormat_getString(refFormat, AMEDIAFORMAT_KEY_MIME, &mime);
- /* TODO(b/154177490) */
- if ((strcmp(mime, AMEDIA_MIMETYPE_VIDEO_VP9) == 0) ||
- (strcmp(mime, AMEDIA_MIMETYPE_VIDEO_AV1) == 0)) {
- return true;
- }
for (int i = 0;; i++) {
std::pair<void*, size_t> refCsd;
std::pair<void*, size_t> testCsd;
diff --git a/tests/media/jni/NativeMuxerTest.cpp b/tests/media/jni/NativeMuxerTest.cpp
index 9e759bc..56f2ba8 100644
--- a/tests/media/jni/NativeMuxerTest.cpp
+++ b/tests/media/jni/NativeMuxerTest.cpp
@@ -66,14 +66,14 @@
bool combineMedias(AMediaMuxer* muxer, MuxerNativeTestHelper* that, const int* repeater);
- bool equals(MuxerNativeTestHelper* that);
+ bool isSubsetOf(MuxerNativeTestHelper* that);
void offsetTimeStamp(int trackID, long tsOffset, int sampleOffset);
private:
void splitMediaToMuxerParameters();
- static const int STTS_TOLERANCE = 100;
+ static const int STTS_TOLERANCE_US = 100;
const char* mSrcPath;
const char* mMime;
int mTrackCount;
@@ -263,7 +263,7 @@
// returns true if 'this' stream is a subset of 'o'. That is all tracks in current media
// stream are present in ref media stream
-bool MuxerNativeTestHelper::equals(MuxerNativeTestHelper* that) {
+bool MuxerNativeTestHelper::isSubsetOf(MuxerNativeTestHelper* that) {
if (this == that) return true;
if (that == nullptr) return false;
@@ -279,31 +279,28 @@
if (thisMime != nullptr && thatMime != nullptr && !strcmp(thisMime, thatMime)) {
if (!isCSDIdentical(thisFormat, thatFormat)) continue;
if (mBufferInfo[i].size() == that->mBufferInfo[j].size()) {
- int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
- for (int k = 0; k < mBufferInfo[i].size(); k++) {
+ int tolerance =
+ !strncmp(thisMime, "video/", strlen("video/")) ? STTS_TOLERANCE_US : 0;
+ tolerance += 1; // rounding error
+ int k = 0;
+ for (; k < mBufferInfo[i].size(); k++) {
AMediaCodecBufferInfo* thisInfo = mBufferInfo[i][k];
AMediaCodecBufferInfo* thatInfo = that->mBufferInfo[j][k];
if (thisInfo->flags != thatInfo->flags) {
- flagsDiff++;
+ break;
}
if (thisInfo->size != thatInfo->size) {
- sizeDiff++;
+ break;
} else if (memcmp(mBuffer + thisInfo->offset,
that->mBuffer + thatInfo->offset, thisInfo->size)) {
- buffDiff++;
+ break;
}
if (abs(thisInfo->presentationTimeUs - thatInfo->presentationTimeUs) >
- STTS_TOLERANCE) {
- tsDiff++;
+ tolerance) {
+ break;
}
}
- if (flagsDiff == 0 && sizeDiff == 0 && tsDiff == 0 && buffDiff == 0)
- break;
- else {
- ALOGV("For mime %s, Total Samples %d, flagsDiff %d, sizeDiff %d, tsDiff "
- "%d, buffDiff %d", thisMime, (int)mBufferInfo[i].size(), flagsDiff,
- sizeDiff, tsDiff, buffDiff);
- }
+ if (k == mBufferInfo[i].size()) break;
}
}
}
@@ -524,7 +521,7 @@
fclose(rfp);
if (muxStatus) {
auto* refInfo = new MuxerNativeTestHelper(crefPath);
- if (!mediaInfoA->equals(refInfo) || !mediaInfoB->equals(refInfo)) {
+ if (!mediaInfoA->isSubsetOf(refInfo) || !mediaInfoB->isSubsetOf(refInfo)) {
isPass = false;
ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing src A and src B "
"failed", csrcPathA, csrcPathB, jformat);
@@ -540,7 +537,7 @@
fclose(ofp);
if (status) {
auto* dstInfo = new MuxerNativeTestHelper(cdstPath);
- if (!dstInfo->equals(refInfo)) {
+ if (!dstInfo->isSubsetOf(refInfo)) {
isPass = false;
ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing "
"src A: %d, src B: %d failed", csrcPathA, csrcPathB,
@@ -614,7 +611,7 @@
AMediaMuxer_delete(muxer);
fclose(ofp);
auto* outInfo = new MuxerNativeTestHelper(cdstPath);
- isPass = mediaInfo->equals(outInfo);
+ isPass = mediaInfo->isSubsetOf(outInfo);
if (!isPass) {
ALOGE("Validation failed after adding timestamp offset to track %d", trackID);
}
@@ -668,7 +665,7 @@
fclose(ofp);
if (muxStatus) {
auto* outInfo = new MuxerNativeTestHelper(cdstPath, cmime);
- result = mediaInfo->equals(outInfo);
+ result = mediaInfo->isSubsetOf(outInfo);
delete outInfo;
}
if ((muxStatus && !result) ||
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java
new file mode 100644
index 0000000..e57b3e7
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediav2.cts;
+
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(Parameterized.class)
+public class CodecDecoderExtTest extends CodecDecoderTestBase {
+ private static final String LOG_TAG = CodecDecoderExtTest.class.getSimpleName();
+
+ private final String mRefFile;
+
+ public CodecDecoderExtTest(String mime, String testFile, String refFile) {
+ super(mime, testFile);
+ mRefFile = refFile;
+ }
+
+ @Parameterized.Parameters(name = "{index}({0})")
+ public static Collection<Object[]> input() {
+ return Arrays.asList(new Object[][]{
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ //show and no-show frames are sent as separate inputs
+ "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm",
+ //show and no-show frames are sent as one input
+ "bbb_340x280_768kbps_30fps_vp9.webm"},
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ //show and no-show frames are sent as separate inputs
+ "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm",
+ //show and no-show frames are sent as one input
+ "bbb_520x390_1mbps_30fps_vp9.webm"},
+ });
+ }
+
+ /**
+ * Test decodes and compares decoded output of two files.
+ */
+ @LargeTest
+ @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testDecodeAndValidate() throws IOException, InterruptedException {
+ ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+ if (listOfDecoders.isEmpty()) {
+ fail("no suitable codecs found for mime: " + mMime);
+ }
+ final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+ for (String decoder : listOfDecoders) {
+ decodeToMemory(mTestFile, decoder, 0, mode, Integer.MAX_VALUE);
+ OutputManager test = mOutputBuff;
+ String log = String.format("codec: %s, test file: %s, ref file: %s:: ", decoder,
+ mTestFile, mRefFile);
+ assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+ assertTrue(log + "no input sent", 0 != mInputCount);
+ assertTrue(log + "output received", 0 != mOutputCount);
+ if (mIsAudio) {
+ assertTrue("reference output pts is not strictly increasing",
+ test.isPtsStrictlyIncreasing(mPrevOutputPts));
+ } else {
+ assertTrue("input pts list and output pts list are not identical",
+ test.isOutPtsListIdenticalToInpPtsList(false));
+ }
+ decodeToMemory(mRefFile, decoder, 0, mode, Integer.MAX_VALUE);
+ OutputManager ref = mOutputBuff;
+ assertTrue(log + "decoder outputs are not identical", ref.equals(test));
+ }
+ }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
index 5d1bc41..58d83c9 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
@@ -21,7 +21,6 @@
import android.media.MediaFormat;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Pair;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.ViewGroup;
@@ -36,7 +35,6 @@
import org.junit.runners.Parameterized;
import java.io.IOException;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -44,31 +42,19 @@
import java.util.List;
import java.util.Set;
-import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(Parameterized.class)
-public class CodecDecoderSurfaceTest extends CodecTestBase {
+public class CodecDecoderSurfaceTest extends CodecDecoderTestBase {
private static final String LOG_TAG = CodecDecoderSurfaceTest.class.getSimpleName();
- private final String mMime;
- private final String mTestFile;
private final String mReconfigFile;
-
- private final ArrayList<ByteBuffer> mCsdBuffers;
- private int mCurrCsdIdx;
-
- private MediaExtractor mExtractor;
private SurfaceView mSurfaceView;
public CodecDecoderSurfaceTest(String mime, String testFile, String reconfigFile) {
- mMime = mime;
- mTestFile = testFile;
+ super(mime, testFile);
mReconfigFile = reconfigFile;
- mCsdBuffers = new ArrayList<>();
- mAsyncHandle = new CodecAsyncHandler();
- mIsAudio = mMime.startsWith("audio/");
}
private void setScreenParams(int width, int height, boolean noStretch) {
@@ -92,85 +78,6 @@
mActivityRule.getActivity().runOnUiThread(() -> mSurfaceView.setLayoutParams(lp));
}
- private MediaFormat setUpSource(String srcFile) throws IOException {
- mExtractor = new MediaExtractor();
- mExtractor.setDataSource(mInpPrefix + srcFile);
- for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
- MediaFormat format = mExtractor.getTrackFormat(trackID);
- if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
- mExtractor.selectTrack(trackID);
- if (!mIsAudio) {
- // COLOR_FormatYUV420Flexible by default should be supported by all components
- // This call shouldn't effect configure() call for any codec
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
- }
- return format;
- }
- }
- fail("No track with mime: " + mMime + " found in file: " + srcFile);
- return null;
- }
-
- private void enqueueCodecConfig(int bufferIndex) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
- inputBuffer.put((ByteBuffer) csdBuffer.rewind());
- mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
- MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
- }
- }
-
- private void queueCodecConfig() throws InterruptedException {
- if (mIsCodecInAsyncMode) {
- for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
- mCurrCsdIdx++) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
- if (element != null) {
- enqueueCodecConfig(element.first);
- }
- }
- } else {
- for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
- enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
- }
- }
- }
-
- void enqueueInput(int bufferIndex) {
- if (mExtractor.getSampleSize() < 0) {
- enqueueEOS(bufferIndex);
- } else {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- mExtractor.readSampleData(inputBuffer, 0);
- int size = (int) mExtractor.getSampleSize();
- long pts = mExtractor.getSampleTime();
- int extractorFlags = mExtractor.getSampleFlags();
- int codecFlags = 0;
- if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
- codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
- }
- if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
- codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
- }
- if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
- codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- mSawInputEOS = true;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
- " flags: " + codecFlags);
- }
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
- if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
- MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
- mOutputBuff.saveInPTS(pts);
- mInputCount++;
- }
- }
- }
-
void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
mSawOutputEOS = true;
@@ -261,6 +168,9 @@
"bbb_520x390_1mbps_30fps_vp8.webm"},
{MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm",
"bbb_520x390_1mbps_30fps_vp9.webm"},
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm",
+ "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm"},
{MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4",
"bbb_520x390_1mbps_30fps_av1.mp4"},
});
@@ -309,8 +219,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / " +
- mInputCount, mInputCount == mOutputCount);
assertTrue(log + "decoder output is flaky", ref.equals(test));
}
mCodec.release();
@@ -389,8 +297,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / "
- + mInputCount, mInputCount == mOutputCount);
assertTrue(log + "decoder output is flaky", ref.equals(test));
/* test flush in eos state */
@@ -407,8 +313,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / "
- + mInputCount, mInputCount == mOutputCount);
assertTrue(log + "decoder output is flaky", ref.equals(test));
}
mCodec.release();
@@ -481,10 +385,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
/* test reconfigure codec at eos state */
@@ -501,10 +401,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
mExtractor.release();
@@ -526,10 +422,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", configRef.equals(test));
mExtractor.release();
}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index ca05d0e..1daf63f 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -16,14 +16,11 @@
package android.mediav2.cts;
-import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
-import android.os.PersistableBundle;
import android.util.Log;
-import android.util.Pair;
import android.view.Surface;
import androidx.test.filters.LargeTest;
@@ -48,7 +45,6 @@
import java.util.List;
import java.util.Set;
-import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -62,30 +58,19 @@
* of these tests are not to cover CDD requirements but to test components and their plugins
*/
@RunWith(Parameterized.class)
-public class CodecDecoderTest extends CodecTestBase {
+public class CodecDecoderTest extends CodecDecoderTestBase {
private static final String LOG_TAG = CodecDecoderTest.class.getSimpleName();
- private final String mMime;
- private final String mTestFile;
private final String mRefFile;
private final String mReconfigFile;
private final float mRmsError;
- private ArrayList<ByteBuffer> mCsdBuffers;
- private int mCurrCsdIdx;
-
- private MediaExtractor mExtractor;
-
public CodecDecoderTest(String mime, String testFile, String refFile, String reconfigFile,
float rmsError) {
- mMime = mime;
- mTestFile = testFile;
+ super(mime, testFile);
mRefFile = refFile;
mReconfigFile = reconfigFile;
mRmsError = rmsError;
- mAsyncHandle = new CodecAsyncHandler();
- mCsdBuffers = new ArrayList<>();
- mIsAudio = mMime.startsWith("audio/");
}
private short[] setUpAudioReference() throws IOException {
@@ -106,164 +91,6 @@
return refData;
}
- private MediaFormat setUpSource(String srcFile) throws IOException {
- mExtractor = new MediaExtractor();
- mExtractor.setDataSource(mInpPrefix + srcFile);
- for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
- MediaFormat format = mExtractor.getTrackFormat(trackID);
- if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
- mExtractor.selectTrack(trackID);
- if (!mIsAudio) {
- // COLOR_FormatYUV420Flexible by default should be supported by all components
- // This call shouldn't effect configure() call for any codec
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
- }
- return format;
- }
- }
- fail("No track with mime: " + mMime + " found in file: " + srcFile);
- return null;
- }
-
- private boolean hasCSD(MediaFormat format) {
- return format.containsKey("csd-0");
- }
-
- private void enqueueCodecConfig(int bufferIndex) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
- inputBuffer.put((ByteBuffer) csdBuffer.rewind());
- mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
- MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
- }
- }
-
- void enqueueInput(int bufferIndex) {
- if (mExtractor.getSampleSize() < 0) {
- enqueueEOS(bufferIndex);
- } else {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- mExtractor.readSampleData(inputBuffer, 0);
- int size = (int) mExtractor.getSampleSize();
- long pts = mExtractor.getSampleTime();
- int extractorFlags = mExtractor.getSampleFlags();
- int codecFlags = 0;
- if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
- codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
- }
- if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
- codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
- }
- if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
- codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- mSawInputEOS = true;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
- " flags: " + codecFlags);
- }
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
- if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
- MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
- mOutputBuff.saveInPTS(pts);
- mInputCount++;
- }
- }
- }
-
- private void enqueueInput(int bufferIndex, ByteBuffer buffer, MediaCodec.BufferInfo info) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- inputBuffer.put((ByteBuffer) buffer.rewind());
- int flags = 0;
- if ((info.flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
- flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
- }
- if ((info.flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
- flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " flags: " + info.flags + " size: " +
- info.size + " timestamp: " + info.presentationTimeUs);
- }
- mCodec.queueInputBuffer(bufferIndex, info.offset, info.size, info.presentationTimeUs,
- flags);
- if (info.size > 0 && ((flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) &&
- ((flags & MediaCodec.BUFFER_FLAG_PARTIAL_FRAME) == 0)) {
- mOutputBuff.saveInPTS(info.presentationTimeUs);
- mInputCount++;
- }
- }
-
- void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
- if (info.size > 0 && mSaveToMem) {
- if (mIsAudio) {
- ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
- mOutputBuff.saveToMemory(buf, info);
- } else {
- // tests both getOutputImage and getOutputBuffer. Can do time division
- // multiplexing but lets allow it for now
- Image img = mCodec.getOutputImage(bufferIndex);
- assertTrue(img != null);
- mOutputBuff.checksum(img);
-
- ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
- mOutputBuff.checksum(buf, info.size);
- }
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mSawOutputEOS = true;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
- info.size + " timestamp: " + info.presentationTimeUs);
- }
- if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
- mOutputBuff.saveOutPTS(info.presentationTimeUs);
- mOutputCount++;
- }
- mCodec.releaseOutputBuffer(bufferIndex, false);
- }
-
- private void doWork(ByteBuffer buffer, ArrayList<MediaCodec.BufferInfo> list)
- throws InterruptedException {
- int frameCount = 0;
- if (mIsCodecInAsyncMode) {
- // output processing after queuing EOS is done in waitForAllOutputs()
- while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < list.size()) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
- if (element != null) {
- int bufferID = element.first;
- MediaCodec.BufferInfo info = element.second;
- if (info != null) {
- dequeueOutput(bufferID, info);
- } else {
- enqueueInput(bufferID, buffer, list.get(frameCount));
- frameCount++;
- }
- }
- }
- } else {
- MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
- // output processing after queuing EOS is done in waitForAllOutputs()
- while (!mSawInputEOS && frameCount < list.size()) {
- int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
- if (outputBufferId >= 0) {
- dequeueOutput(outputBufferId, outInfo);
- } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- mOutFormat = mCodec.getOutputFormat();
- mSignalledOutFormatChanged = true;
- }
- int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
- if (inputBufferId != -1) {
- enqueueInput(inputBufferId, buffer, list.get(frameCount));
- frameCount++;
- }
- }
- }
- }
-
private ArrayList<MediaCodec.BufferInfo> createSubFrames(ByteBuffer buffer, int sfCount) {
int size = (int) mExtractor.getSampleSize();
if (size < 0) return null;
@@ -290,48 +117,6 @@
return list;
}
- private void queueCodecConfig() throws InterruptedException {
- if (mIsCodecInAsyncMode) {
- for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
- mCurrCsdIdx++) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
- if (element != null) {
- enqueueCodecConfig(element.first);
- }
- }
- } else {
- for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
- enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
- }
- }
- }
-
- private void decodeToMemory(String file, String decoder, long pts, int mode, int frameLimit)
- throws IOException, InterruptedException {
- mSaveToMem = true;
- mOutputBuff = new OutputManager();
- mCodec = MediaCodec.createByCodecName(decoder);
- MediaFormat format = setUpSource(file);
- configureCodec(format, false, true, false);
- mCodec.start();
- mExtractor.seekTo(pts, mode);
- doWork(frameLimit);
- queueEOS();
- waitForAllOutputs();
- mCodec.stop();
- mCodec.release();
- mExtractor.release();
- mSaveToMem = false;
- }
-
- @Override
- PersistableBundle validateMetrics(String decoder, MediaFormat format) {
- PersistableBundle metrics = super.validateMetrics(decoder, format);
- assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
- assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 0);
- return metrics;
- }
-
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() {
Set<String> list = new HashSet<>();
@@ -417,6 +202,9 @@
"bbb_520x390_1mbps_30fps_vp8.webm", -1.0f},
{MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm", null,
"bbb_520x390_1mbps_30fps_vp9.webm", -1.0f},
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm", null,
+ "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm", -1.0f},
{MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4", null,
"bbb_520x390_1mbps_30fps_av1.mp4", -1.0f},
});
@@ -481,10 +269,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
if (loopCounter != 0) {
assertTrue(log + "decoder output is flaky", ref.equals(test));
} else {
@@ -620,10 +404,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
/* test flush in eos state */
@@ -640,10 +420,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
if (validateFormat) {
assertTrue(log + "not received format change",
@@ -767,10 +543,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
if (validateFormat) {
assertTrue(log + "not received format change",
@@ -796,10 +568,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
if (validateFormat) {
assertTrue(log + "not received format change",
@@ -836,10 +604,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", configRef.equals(test));
if (validateFormat) {
assertTrue(log + "not received format change",
@@ -980,11 +744,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(
- log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
if (loopCounter != 0) {
assertTrue(log + "decoder output is flaky", ref.equals(test));
} else {
@@ -1079,10 +838,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is not consistent with ref", ref.equals(test));
}
mCodec.release();
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
index d6caa62..180e063 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
@@ -338,15 +338,21 @@
}
private void queueEOS() throws InterruptedException {
- if (!mSawDecInputEOS) {
- if (mIsCodecInAsyncMode) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getInput();
+ if (mIsCodecInAsyncMode) {
+ while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
if (element != null) {
- enqueueDecoderEOS(element.first);
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueDecoderOutput(bufferID, info);
+ } else {
+ enqueueDecoderEOS(element.first);
+ }
}
- } else {
- enqueueDecoderEOS(mDecoder.dequeueInputBuffer(-1));
}
+ } else if (!mSawDecInputEOS) {
+ enqueueDecoderEOS(mDecoder.dequeueInputBuffer(-1));
}
if (mIsCodecInAsyncMode) {
while (!hasSeenError() && !mSawDecOutputEOS) {
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
index 6a4c7d9..79d8f20 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
@@ -60,53 +60,27 @@
* of these tests are not to cover CDD requirements but to test components and their plugins
*/
@RunWith(Parameterized.class)
-public class CodecEncoderTest extends CodecTestBase {
+public class CodecEncoderTest extends CodecEncoderTestBase {
private static final String LOG_TAG = CodecEncoderTest.class.getSimpleName();
- // files are in WorkDir.getMediaDirString();
- private static final String mInputAudioFile = "bbb_2ch_44kHz_s16le.raw";
- private static final String mInputVideoFile = "bbb_cif_yuv420p_30fps.yuv";
- private final int INP_FRM_WIDTH = 352;
- private final int INP_FRM_HEIGHT = 288;
-
- private final String mMime;
private final int[] mBitrates;
private final int[] mEncParamList1;
private final int[] mEncParamList2;
- private final String mInputFile;
private ArrayList<MediaFormat> mFormats;
- private byte[] mInputData;
- private int mNumBytesSubmitted;
- private long mInputOffsetPts;
private int mNumSyncFramesReceived;
private ArrayList<Integer> mSyncFramesPos;
- private int mWidth, mHeight;
- private int mChannels;
- private int mSampleRate;
- private int mFrameRate;
- private int mMaxBFrames;
-
public CodecEncoderTest(String mime, int[] bitrates, int[] encoderInfo1, int[] encoderInfo2) {
- mMime = mime;
- mFrameRate = 30;
- if (mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) mFrameRate = 12;
- else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_H263)) mFrameRate = 12;
- mMaxBFrames = 0;
+ super(mime);
mBitrates = bitrates;
mEncParamList1 = encoderInfo1;
mEncParamList2 = encoderInfo2;
mFormats = new ArrayList<>();
mSyncFramesPos = new ArrayList<>();
- mAsyncHandle = new CodecAsyncHandler();
- mIsAudio = mMime.startsWith("audio/");
- mInputFile = mIsAudio ? mInputAudioFile : mInputVideoFile;
}
@Override
void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
super.resetContext(isAsync, signalEOSWithLastFrame);
- mNumBytesSubmitted = 0;
- mInputOffsetPts = 0;
mNumSyncFramesReceived = 0;
mSyncFramesPos.clear();
}
@@ -114,184 +88,16 @@
@Override
void flushCodec() {
super.flushCodec();
- if (mIsAudio) {
- mInputOffsetPts =
- (mNumBytesSubmitted + 1024) * 1000000L / (2 * mChannels * mSampleRate);
- } else {
- mInputOffsetPts = (mInputCount + 5) * 1000000L / mFrameRate;
- }
- mPrevOutputPts = mInputOffsetPts - 1;
- mNumBytesSubmitted = 0;
mNumSyncFramesReceived = 0;
mSyncFramesPos.clear();
}
- private void setUpSource(String srcFile) throws IOException {
- String inpPath = mInpPrefix + srcFile;
- try (FileInputStream fInp = new FileInputStream(inpPath)) {
- int size = (int) new File(inpPath).length();
- mInputData = new byte[size];
- fInp.read(mInputData, 0, size);
- }
- }
-
- void fillImage(Image image) {
- Assert.assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
- int imageWidth = image.getWidth();
- int imageHeight = image.getHeight();
- Image.Plane[] planes = image.getPlanes();
- int offset = mNumBytesSubmitted;
- for (int i = 0; i < planes.length; ++i) {
- ByteBuffer buf = planes[i].getBuffer();
- int width = imageWidth;
- int height = imageHeight;
- int tileWidth = INP_FRM_WIDTH;
- int tileHeight = INP_FRM_HEIGHT;
- int rowStride = planes[i].getRowStride();
- int pixelStride = planes[i].getPixelStride();
- if (i != 0) {
- width = imageWidth / 2;
- height = imageHeight / 2;
- tileWidth = INP_FRM_WIDTH / 2;
- tileHeight = INP_FRM_HEIGHT / 2;
- }
- if (pixelStride == 1) {
- if (width == rowStride && width == tileWidth && height == tileHeight) {
- buf.put(mInputData, offset, width * height);
- } else {
- for (int z = 0; z < height; z += tileHeight) {
- int rowsToCopy = Math.min(height - z, tileHeight);
- for (int y = 0; y < rowsToCopy; y++) {
- for (int x = 0; x < width; x += tileWidth) {
- int colsToCopy = Math.min(width - x, tileWidth);
- buf.position((z + y) * rowStride + x);
- buf.put(mInputData, offset + y * tileWidth, colsToCopy);
- }
- }
- }
- }
- } else {
- // do it pixel-by-pixel
- for (int z = 0; z < height; z += tileHeight) {
- int rowsToCopy = Math.min(height - z, tileHeight);
- for (int y = 0; y < rowsToCopy; y++) {
- int lineOffset = (z + y) * rowStride;
- for (int x = 0; x < width; x += tileWidth) {
- int colsToCopy = Math.min(width - x, tileWidth);
- for (int w = 0; w < colsToCopy; w++) {
- buf.position(lineOffset + (x + w) * pixelStride);
- buf.put(mInputData[offset + y * tileWidth + w]);
- }
- }
- }
- }
- }
- offset += tileWidth * tileHeight;
- }
- }
-
- void fillByteBuffer(ByteBuffer inputBuffer) {
- int offset = 0, frmOffset = mNumBytesSubmitted;
- for (int plane = 0; plane < 3; plane++) {
- int width = mWidth;
- int height = mHeight;
- int tileWidth = INP_FRM_WIDTH;
- int tileHeight = INP_FRM_HEIGHT;
- if (plane != 0) {
- width = mWidth / 2;
- height = mHeight / 2;
- tileWidth = INP_FRM_WIDTH / 2;
- tileHeight = INP_FRM_HEIGHT / 2;
- }
- for (int k = 0; k < height; k += tileHeight) {
- int rowsToCopy = Math.min(height - k, tileHeight);
- for (int j = 0; j < rowsToCopy; j++) {
- for (int i = 0; i < width; i += tileWidth) {
- int colsToCopy = Math.min(width - i, tileWidth);
- inputBuffer.position(offset + (k + j) * width + i);
- inputBuffer.put(mInputData, frmOffset + j * tileWidth, colsToCopy);
- }
- }
- }
- offset += width * height;
- frmOffset += tileWidth * tileHeight;
- }
- }
-
- void enqueueInput(int bufferIndex) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- if (mNumBytesSubmitted >= mInputData.length) {
- enqueueEOS(bufferIndex);
- } else {
- int size;
- int flags = 0;
- long pts = mInputOffsetPts;
- if (mIsAudio) {
- pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate);
- size = Math.min(inputBuffer.capacity(), mInputData.length - mNumBytesSubmitted);
- inputBuffer.put(mInputData, mNumBytesSubmitted, size);
- if (mNumBytesSubmitted + size >= mInputData.length && mSignalEOSWithLastFrame) {
- flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- mSawInputEOS = true;
- }
- mNumBytesSubmitted += size;
- } else {
- pts += mInputCount * 1000000L / mFrameRate;
- size = mWidth * mHeight * 3 / 2;
- int frmSize = INP_FRM_WIDTH * INP_FRM_HEIGHT * 3 / 2;
- if (mNumBytesSubmitted + frmSize > mInputData.length) {
- fail("received partial frame to encode");
- } else {
- Image img = mCodec.getInputImage(bufferIndex);
- if (img != null) {
- fillImage(img);
- } else {
- if (mWidth == INP_FRM_WIDTH && mHeight == INP_FRM_HEIGHT) {
- inputBuffer.put(mInputData, mNumBytesSubmitted, size);
- } else {
- fillByteBuffer(inputBuffer);
- }
- }
- }
- if (mNumBytesSubmitted + frmSize >= mInputData.length && mSignalEOSWithLastFrame) {
- flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- mSawInputEOS = true;
- }
- mNumBytesSubmitted += frmSize;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
- " flags: " + flags);
- }
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
- mOutputBuff.saveInPTS(pts);
- mInputCount++;
- }
- }
-
void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
- info.size + " timestamp: " + info.presentationTimeUs);
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mSawOutputEOS = true;
- }
- if (info.size > 0) {
- if (mSaveToMem) {
- ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
- mOutputBuff.saveToMemory(buf, info);
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
- mOutputBuff.saveOutPTS(info.presentationTimeUs);
- mOutputCount++;
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
+ if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
mNumSyncFramesReceived += 1;
mSyncFramesPos.add(mOutputCount);
}
- }
- mCodec.releaseOutputBuffer(bufferIndex, false);
+ super.dequeueOutput(bufferIndex, info);
}
private void encodeToMemory(String file, String encoder, int frameLimit, MediaFormat format)
@@ -340,14 +146,6 @@
return colorFormat;
}
- @Override
- PersistableBundle validateMetrics(String codec, MediaFormat format) {
- PersistableBundle metrics = super.validateMetrics(codec, format);
- assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
- assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 1);
- return metrics;
- }
-
private void forceSyncFrame() {
final Bundle syncFrame = new Bundle();
syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index 3e09a8d..1086981 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -22,6 +22,7 @@
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
+import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import android.os.PersistableBundle;
@@ -32,6 +33,10 @@
import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.Assert;
+
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -47,6 +52,7 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.CRC32;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -212,7 +218,10 @@
}
void saveInPTS(long pts) {
- inpPtsList.add(pts);
+ // Add only Unique timeStamp, discarding any duplicate frame / non-display frame
+ if (!inpPtsList.contains(pts)) {
+ inpPtsList.add(pts);
+ }
}
void saveOutPTS(long pts) {
@@ -680,15 +689,21 @@
}
void queueEOS() throws InterruptedException {
- if (!mSawInputEOS) {
- if (mIsCodecInAsyncMode) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
+ if (mIsCodecInAsyncMode) {
+ while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
if (element != null) {
- enqueueEOS(element.first);
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueOutput(bufferID, info);
+ } else {
+ enqueueEOS(element.first);
+ }
}
- } else {
- enqueueEOS(mCodec.dequeueInputBuffer(-1));
}
+ } else if (!mSawInputEOS) {
+ enqueueEOS(mCodec.dequeueInputBuffer(-1));
}
}
@@ -810,3 +825,454 @@
return metrics;
}
}
+
+class CodecDecoderTestBase extends CodecTestBase {
+ private static final String LOG_TAG = CodecDecoderTestBase.class.getSimpleName();
+
+ String mMime;
+ String mTestFile;
+
+ ArrayList<ByteBuffer> mCsdBuffers;
+ private int mCurrCsdIdx;
+
+ MediaExtractor mExtractor;
+
+ CodecDecoderTestBase(String mime, String testFile) {
+ mMime = mime;
+ mTestFile = testFile;
+ mAsyncHandle = new CodecAsyncHandler();
+ mCsdBuffers = new ArrayList<>();
+ mIsAudio = mMime.startsWith("audio/");
+ }
+
+ MediaFormat setUpSource(String srcFile) throws IOException {
+ mExtractor = new MediaExtractor();
+ mExtractor.setDataSource(mInpPrefix + srcFile);
+ for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
+ MediaFormat format = mExtractor.getTrackFormat(trackID);
+ if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
+ mExtractor.selectTrack(trackID);
+ if (!mIsAudio) {
+ // COLOR_FormatYUV420Flexible by default should be supported by all components
+ // This call shouldn't effect configure() call for any codec
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
+ }
+ return format;
+ }
+ }
+ fail("No track with mime: " + mMime + " found in file: " + srcFile);
+ return null;
+ }
+
+ boolean hasCSD(MediaFormat format) {
+ return format.containsKey("csd-0");
+ }
+
+ void enqueueCodecConfig(int bufferIndex) {
+ ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+ ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
+ inputBuffer.put((ByteBuffer) csdBuffer.rewind());
+ mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
+ MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
+ }
+ }
+
+ void enqueueInput(int bufferIndex) {
+ if (mExtractor.getSampleSize() < 0) {
+ enqueueEOS(bufferIndex);
+ } else {
+ ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+ mExtractor.readSampleData(inputBuffer, 0);
+ int size = (int) mExtractor.getSampleSize();
+ long pts = mExtractor.getSampleTime();
+ int extractorFlags = mExtractor.getSampleFlags();
+ int codecFlags = 0;
+ if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+ }
+ if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+ }
+ if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ mSawInputEOS = true;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+ " flags: " + codecFlags);
+ }
+ mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+ if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
+ MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
+ mOutputBuff.saveInPTS(pts);
+ mInputCount++;
+ }
+ }
+ }
+
+ void enqueueInput(int bufferIndex, ByteBuffer buffer, MediaCodec.BufferInfo info) {
+ ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+ inputBuffer.put((ByteBuffer) buffer.rewind());
+ int flags = 0;
+ if ((info.flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+ flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+ }
+ if ((info.flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+ flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "input: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+ info.size + " timestamp: " + info.presentationTimeUs);
+ }
+ mCodec.queueInputBuffer(bufferIndex, info.offset, info.size, info.presentationTimeUs,
+ flags);
+ if (info.size > 0 && ((flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) &&
+ ((flags & MediaCodec.BUFFER_FLAG_PARTIAL_FRAME) == 0)) {
+ mOutputBuff.saveInPTS(info.presentationTimeUs);
+ mInputCount++;
+ }
+ }
+
+ void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+ if (info.size > 0 && mSaveToMem) {
+ if (mIsAudio) {
+ ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+ mOutputBuff.saveToMemory(buf, info);
+ } else {
+ // tests both getOutputImage and getOutputBuffer. Can do time division
+ // multiplexing but lets allow it for now
+ Image img = mCodec.getOutputImage(bufferIndex);
+ assertTrue(img != null);
+ mOutputBuff.checksum(img);
+
+ ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+ mOutputBuff.checksum(buf, info.size);
+ }
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawOutputEOS = true;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+ info.size + " timestamp: " + info.presentationTimeUs);
+ }
+ if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mOutputBuff.saveOutPTS(info.presentationTimeUs);
+ mOutputCount++;
+ }
+ mCodec.releaseOutputBuffer(bufferIndex, false);
+ }
+
+ void doWork(ByteBuffer buffer, ArrayList<MediaCodec.BufferInfo> list)
+ throws InterruptedException {
+ int frameCount = 0;
+ if (mIsCodecInAsyncMode) {
+ // output processing after queuing EOS is done in waitForAllOutputs()
+ while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < list.size()) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+ if (element != null) {
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueOutput(bufferID, info);
+ } else {
+ enqueueInput(bufferID, buffer, list.get(frameCount));
+ frameCount++;
+ }
+ }
+ }
+ } else {
+ MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+ // output processing after queuing EOS is done in waitForAllOutputs()
+ while (!mSawInputEOS && frameCount < list.size()) {
+ int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+ if (outputBufferId >= 0) {
+ dequeueOutput(outputBufferId, outInfo);
+ } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ mOutFormat = mCodec.getOutputFormat();
+ mSignalledOutFormatChanged = true;
+ }
+ int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+ if (inputBufferId != -1) {
+ enqueueInput(inputBufferId, buffer, list.get(frameCount));
+ frameCount++;
+ }
+ }
+ }
+ }
+
+ void queueCodecConfig() throws InterruptedException {
+ if (mIsCodecInAsyncMode) {
+ for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
+ mCurrCsdIdx++) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
+ if (element != null) {
+ enqueueCodecConfig(element.first);
+ }
+ }
+ } else {
+ for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
+ enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
+ }
+ }
+ }
+
+ void decodeToMemory(String file, String decoder, long pts, int mode, int frameLimit)
+ throws IOException, InterruptedException {
+ mSaveToMem = true;
+ mOutputBuff = new OutputManager();
+ mCodec = MediaCodec.createByCodecName(decoder);
+ MediaFormat format = setUpSource(file);
+ configureCodec(format, false, true, false);
+ mCodec.start();
+ mExtractor.seekTo(pts, mode);
+ doWork(frameLimit);
+ queueEOS();
+ waitForAllOutputs();
+ mCodec.stop();
+ mCodec.release();
+ mExtractor.release();
+ mSaveToMem = false;
+ }
+
+ @Override
+ PersistableBundle validateMetrics(String decoder, MediaFormat format) {
+ PersistableBundle metrics = super.validateMetrics(decoder, format);
+ assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
+ assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 0);
+ return metrics;
+ }
+}
+
+class CodecEncoderTestBase extends CodecTestBase {
+ private static final String LOG_TAG = CodecEncoderTestBase.class.getSimpleName();
+
+ // files are in WorkDir.getMediaDirString();
+ private static final String mInputAudioFile = "bbb_2ch_44kHz_s16le.raw";
+ private static final String mInputVideoFile = "bbb_cif_yuv420p_30fps.yuv";
+ private final int INP_FRM_WIDTH = 352;
+ private final int INP_FRM_HEIGHT = 288;
+
+ final String mMime;
+ final String mInputFile;
+ byte[] mInputData;
+ int mNumBytesSubmitted;
+ long mInputOffsetPts;
+
+ int mWidth, mHeight;
+ int mFrameRate;
+ int mMaxBFrames;
+ int mChannels;
+ int mSampleRate;
+
+ CodecEncoderTestBase(String mime) {
+ mMime = mime;
+ mWidth = INP_FRM_WIDTH;
+ mHeight = INP_FRM_HEIGHT;
+ mChannels = 1;
+ mSampleRate = 8000;
+ mFrameRate = 30;
+ mMaxBFrames = 0;
+ if (mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) mFrameRate = 12;
+ else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_H263)) mFrameRate = 12;
+ mAsyncHandle = new CodecAsyncHandler();
+ mIsAudio = mMime.startsWith("audio/");
+ mInputFile = mIsAudio ? mInputAudioFile : mInputVideoFile;
+ }
+
+ @Override
+ void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
+ super.resetContext(isAsync, signalEOSWithLastFrame);
+ mNumBytesSubmitted = 0;
+ mInputOffsetPts = 0;
+ }
+
+ @Override
+ void flushCodec() {
+ super.flushCodec();
+ if (mIsAudio) {
+ mInputOffsetPts =
+ (mNumBytesSubmitted + 1024) * 1000000L / (2 * mChannels * mSampleRate);
+ } else {
+ mInputOffsetPts = (mInputCount + 5) * 1000000L / mFrameRate;
+ }
+ mPrevOutputPts = mInputOffsetPts - 1;
+ mNumBytesSubmitted = 0;
+ }
+
+ void setUpSource(String srcFile) throws IOException {
+ String inpPath = mInpPrefix + srcFile;
+ try (FileInputStream fInp = new FileInputStream(inpPath)) {
+ int size = (int) new File(inpPath).length();
+ mInputData = new byte[size];
+ fInp.read(mInputData, 0, size);
+ }
+ }
+
+ void fillImage(Image image) {
+ Assert.assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
+ int imageWidth = image.getWidth();
+ int imageHeight = image.getHeight();
+ Image.Plane[] planes = image.getPlanes();
+ int offset = mNumBytesSubmitted;
+ for (int i = 0; i < planes.length; ++i) {
+ ByteBuffer buf = planes[i].getBuffer();
+ int width = imageWidth;
+ int height = imageHeight;
+ int tileWidth = INP_FRM_WIDTH;
+ int tileHeight = INP_FRM_HEIGHT;
+ int rowStride = planes[i].getRowStride();
+ int pixelStride = planes[i].getPixelStride();
+ if (i != 0) {
+ width = imageWidth / 2;
+ height = imageHeight / 2;
+ tileWidth = INP_FRM_WIDTH / 2;
+ tileHeight = INP_FRM_HEIGHT / 2;
+ }
+ if (pixelStride == 1) {
+ if (width == rowStride && width == tileWidth && height == tileHeight) {
+ buf.put(mInputData, offset, width * height);
+ } else {
+ for (int z = 0; z < height; z += tileHeight) {
+ int rowsToCopy = Math.min(height - z, tileHeight);
+ for (int y = 0; y < rowsToCopy; y++) {
+ for (int x = 0; x < width; x += tileWidth) {
+ int colsToCopy = Math.min(width - x, tileWidth);
+ buf.position((z + y) * rowStride + x);
+ buf.put(mInputData, offset + y * tileWidth, colsToCopy);
+ }
+ }
+ }
+ }
+ } else {
+ // do it pixel-by-pixel
+ for (int z = 0; z < height; z += tileHeight) {
+ int rowsToCopy = Math.min(height - z, tileHeight);
+ for (int y = 0; y < rowsToCopy; y++) {
+ int lineOffset = (z + y) * rowStride;
+ for (int x = 0; x < width; x += tileWidth) {
+ int colsToCopy = Math.min(width - x, tileWidth);
+ for (int w = 0; w < colsToCopy; w++) {
+ buf.position(lineOffset + (x + w) * pixelStride);
+ buf.put(mInputData[offset + y * tileWidth + w]);
+ }
+ }
+ }
+ }
+ }
+ offset += tileWidth * tileHeight;
+ }
+ }
+
+ void fillByteBuffer(ByteBuffer inputBuffer) {
+ int offset = 0, frmOffset = mNumBytesSubmitted;
+ for (int plane = 0; plane < 3; plane++) {
+ int width = mWidth;
+ int height = mHeight;
+ int tileWidth = INP_FRM_WIDTH;
+ int tileHeight = INP_FRM_HEIGHT;
+ if (plane != 0) {
+ width = mWidth / 2;
+ height = mHeight / 2;
+ tileWidth = INP_FRM_WIDTH / 2;
+ tileHeight = INP_FRM_HEIGHT / 2;
+ }
+ for (int k = 0; k < height; k += tileHeight) {
+ int rowsToCopy = Math.min(height - k, tileHeight);
+ for (int j = 0; j < rowsToCopy; j++) {
+ for (int i = 0; i < width; i += tileWidth) {
+ int colsToCopy = Math.min(width - i, tileWidth);
+ inputBuffer.position(offset + (k + j) * width + i);
+ inputBuffer.put(mInputData, frmOffset + j * tileWidth, colsToCopy);
+ }
+ }
+ }
+ offset += width * height;
+ frmOffset += tileWidth * tileHeight;
+ }
+ }
+
+ void enqueueInput(int bufferIndex) {
+ ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+ if (mNumBytesSubmitted >= mInputData.length) {
+ enqueueEOS(bufferIndex);
+ } else {
+ int size;
+ int flags = 0;
+ long pts = mInputOffsetPts;
+ if (mIsAudio) {
+ pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate);
+ size = Math.min(inputBuffer.capacity(), mInputData.length - mNumBytesSubmitted);
+ inputBuffer.put(mInputData, mNumBytesSubmitted, size);
+ if (mNumBytesSubmitted + size >= mInputData.length && mSignalEOSWithLastFrame) {
+ flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ mSawInputEOS = true;
+ }
+ mNumBytesSubmitted += size;
+ } else {
+ pts += mInputCount * 1000000L / mFrameRate;
+ size = mWidth * mHeight * 3 / 2;
+ int frmSize = INP_FRM_WIDTH * INP_FRM_HEIGHT * 3 / 2;
+ if (mNumBytesSubmitted + frmSize > mInputData.length) {
+ fail("received partial frame to encode");
+ } else {
+ Image img = mCodec.getInputImage(bufferIndex);
+ if (img != null) {
+ fillImage(img);
+ } else {
+ if (mWidth == INP_FRM_WIDTH && mHeight == INP_FRM_HEIGHT) {
+ inputBuffer.put(mInputData, mNumBytesSubmitted, size);
+ } else {
+ fillByteBuffer(inputBuffer);
+ }
+ }
+ }
+ if (mNumBytesSubmitted + frmSize >= mInputData.length && mSignalEOSWithLastFrame) {
+ flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ mSawInputEOS = true;
+ }
+ mNumBytesSubmitted += frmSize;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+ " flags: " + flags);
+ }
+ mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
+ mOutputBuff.saveInPTS(pts);
+ mInputCount++;
+ }
+ }
+
+ void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+ info.size + " timestamp: " + info.presentationTimeUs);
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawOutputEOS = true;
+ }
+ if (info.size > 0) {
+ if (mSaveToMem) {
+ ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+ mOutputBuff.saveToMemory(buf, info);
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mOutputBuff.saveOutPTS(info.presentationTimeUs);
+ mOutputCount++;
+ }
+ }
+ mCodec.releaseOutputBuffer(bufferIndex, false);
+ }
+
+ @Override
+ PersistableBundle validateMetrics(String codec, MediaFormat format) {
+ PersistableBundle metrics = super.validateMetrics(codec, format);
+ assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
+ assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 1);
+ return metrics;
+ }
+}
+
+
diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
index 9438dcb..bbfa5aa 100644
--- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
@@ -18,9 +18,7 @@
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.media.MediaFormat;
-import android.os.Build;
import android.util.Log;
import android.util.Pair;
@@ -39,46 +37,36 @@
import static android.media.MediaCodecInfo.CodecProfileLevel.*;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
/**
* Validate profile and level configuration for listed encoder components
*/
@RunWith(Parameterized.class)
-public class EncoderProfileLevelTest extends CodecTestBase {
+public class EncoderProfileLevelTest extends CodecEncoderTestBase {
private static final String LOG_TAG = EncoderProfileLevelTest.class.getSimpleName();
private static final HashMap<String, int[]> mProfileMap = new HashMap<>();
private static final HashMap<String, Pair<int[], Integer>> mProfileLevelCdd = new HashMap<>();
- private final String mMime;
private MediaFormat mConfigFormat;
- private int mNumBytesSubmitted;
-
- private int mHeight;
- private int mWidth;
- private int mChannels;
- private int mRate;
public EncoderProfileLevelTest(String mime, int bitrate, int encoderInfo1, int encoderInfo2,
int frameRate) {
- mMime = mime;
- mAsyncHandle = new CodecAsyncHandler();
+ super(mime);
mConfigFormat = new MediaFormat();
- mIsAudio = mMime.startsWith("audio/");
mConfigFormat.setString(MediaFormat.KEY_MIME, mMime);
mConfigFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
if (mIsAudio) {
- mRate = encoderInfo1;
+ mSampleRate = encoderInfo1;
mChannels = encoderInfo2;
- mConfigFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, mRate);
+ mConfigFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate);
mConfigFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, mChannels);
} else {
mWidth = encoderInfo1;
mHeight = encoderInfo2;
- mRate = frameRate;
+ mFrameRate = frameRate;
mConfigFormat.setInteger(MediaFormat.KEY_WIDTH, mWidth);
mConfigFormat.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
- mConfigFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mRate);
+ mConfigFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
mConfigFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
mConfigFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
@@ -204,41 +192,7 @@
{MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 176, 144, 20},
{MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 480, 360, 20},
});
-
- ArrayList<String> mimes = new ArrayList<>();
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
- for (MediaCodecInfo codecInfo : codecInfos) {
- if (!codecInfo.isEncoder()) continue;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
- String[] types = codecInfo.getSupportedTypes();
- for (String type : types) {
- if (!mimes.contains(type)) mimes.add(type);
- }
- }
- for (String mime : cddRequiredMimeList) {
- if (!mimes.contains(mime)) {
- fail("media codec encoder list doesn't contain " + mime +
- " as required by cdd");
- }
- }
- final List<Object[]> argsList = new ArrayList<>();
- for (String mime : mimes) {
- boolean miss = true;
- for (int i = 0; i < exhaustiveArgsList.size(); i++) {
- if (mime.equals(exhaustiveArgsList.get(i)[0])) {
- argsList.add(exhaustiveArgsList.get(i));
- miss = false;
- }
- }
- if (miss) {
- if (cddRequiredMimeList.contains(mime)) {
- fail("no test vectors available for " + mime);
- }
- Log.w(LOG_TAG, "no test vectors available for " + mime);
- }
- }
- return argsList;
+ return prepareParamList(cddRequiredMimeList, exhaustiveArgsList, true);
}
static {
@@ -653,45 +607,6 @@
}
@Override
- void enqueueInput(int bufferIndex) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- int size;
- int flags = 0;
- long pts;
- if (mIsAudio) {
- pts = mNumBytesSubmitted * 1000000L / (2 * mChannels * mRate);
- size = inputBuffer.capacity();
- byte[] data = new byte[size];
- inputBuffer.put(data);
- mNumBytesSubmitted += size;
- } else {
- pts = mInputCount * 1000000L / mRate;
- size = mWidth * mHeight * 3 / 2;
- byte[] data = new byte[size];
- inputBuffer.put(data);
- mNumBytesSubmitted += size;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
- " flags: " + flags);
- }
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
- mInputCount++;
- }
-
- @Override
- void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
- info.size + " timestamp: " + info.presentationTimeUs);
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mSawOutputEOS = true;
- }
- mCodec.releaseOutputBuffer(bufferIndex, false);
- }
-
- @Override
boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) {
if (!super.isFormatSimilar(inpFormat, outFormat)) {
Log.e(LOG_TAG, "Basic channel-rate/resolution comparisons failed");
@@ -778,6 +693,7 @@
boolean[] boolStates = {true, false};
MediaFormat format = mConfigFormat;
mOutputBuff = new OutputManager();
+ setUpSource(mInputFile);
int supportedCddCount = listOfEncoders.size() * (cddSupportedMime ? profileCdd.length : 1);
for (String encoder : listOfEncoders) {
mCodec = MediaCodec.createByCodecName(encoder);
diff --git a/tests/media/src/android/mediav2/cts/ExtractorTest.java b/tests/media/src/android/mediav2/cts/ExtractorTest.java
index a5c0eb9..a0aaef1 100644
--- a/tests/media/src/android/mediav2/cts/ExtractorTest.java
+++ b/tests/media/src/android/mediav2/cts/ExtractorTest.java
@@ -197,23 +197,19 @@
static boolean isCSDIdentical(MediaFormat refFormat, MediaFormat testFormat) {
String mime = refFormat.getString(MediaFormat.KEY_MIME);
- /* TODO(b/154177490) */
- if (mime.equals(MediaFormat.MIMETYPE_VIDEO_VP9) ||
- mime.equals(MediaFormat.MIMETYPE_VIDEO_AV1)) {
- return true;
- }
for (int i = 0; ; i++) {
String csdKey = "csd-" + i;
boolean refHasCSD = refFormat.containsKey(csdKey);
boolean testHasCSD = testFormat.containsKey(csdKey);
if (refHasCSD != testHasCSD) {
if (ENABLE_LOGS) {
- Log.w(LOG_TAG, "error, ref fmt has CSD: " + refHasCSD + "test fmt has CSD: " +
+ Log.w(LOG_TAG, "error, ref fmt has CSD: " + refHasCSD + " test fmt has CSD: " +
testHasCSD);
}
return false;
}
if (refHasCSD) {
+ Log.v(LOG_TAG, mime + " has " + csdKey);
ByteBuffer r = refFormat.getByteBuffer(csdKey);
ByteBuffer t = testFormat.getByteBuffer(csdKey);
if (!r.equals(t)) {
@@ -327,7 +323,7 @@
}
if (!testBuffer.equals(refBuffer)) {
if (ENABLE_LOGS) {
- Log.d(LOG_TAG, "Mime: " + refMime + "sample data is not identical");
+ Log.d(LOG_TAG, "Mime: " + refMime + " sample data is not identical");
}
areTracksIdentical = false;
break;
@@ -626,20 +622,16 @@
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() {
- /* TODO: add ts files for MPEG2, AVC and AAC. These parameters are used by seek tests
- as well. Would it make sense to add ts files to this list */
- /* TODO: add .flac, .midi, .wav, .aac-adts to the list */
+ /* TODO(b/157108639) - add missing test files */
return Arrays.asList(new Object[][]{
{MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{
"bbb_cif_768kbps_30fps_mpeg2_stereo_48kHz_192kbps_mp3.mp4",
"bbb_cif_768kbps_30fps_mpeg2.mkv",}},
{MediaFormat.MIMETYPE_VIDEO_H263, new String[]{
"bbb_cif_768kbps_30fps_h263.mp4",
- "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp",
- "bbb_cif_768kbps_30fps_h263_stereo_48kHz_192kbps_flac.mkv",}},
+ "bbb_cif_768kbps_30fps_h263_mono_8kHz_12kbps_amrnb.3gp",}},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, new String[]{
"bbb_cif_768kbps_30fps_mpeg4_stereo_48kHz_192kbps_flac.mp4",
- "bbb_cif_768kbps_30fps_mpeg4.mkv",
"bbb_cif_768kbps_30fps_mpeg4_mono_16kHz_20kbps_amrwb.3gp",}},
{MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{
"bbb_cif_768kbps_30fps_avc_stereo_48kHz_192kbps_vorbis.mp4",
@@ -872,10 +864,6 @@
public void testExtract() throws IOException {
assumeTrue(shouldRunTest(mMime));
assertTrue(mSrcFiles.length > 1);
- assumeTrue("TODO(b/146925481)", mMime == MediaFormat.MIMETYPE_VIDEO_VP8 ||
- mMime == MediaFormat.MIMETYPE_VIDEO_VP9 ||
- mMime == MediaFormat.MIMETYPE_VIDEO_AV1 ||
- mMime == MediaFormat.MIMETYPE_AUDIO_FLAC);
MediaExtractor refExtractor = new MediaExtractor();
refExtractor.setDataSource(mInpPrefix + mSrcFiles[0]);
boolean isOk = true;
@@ -964,8 +952,6 @@
@Test
public void testSeekToZero() throws IOException {
assumeTrue(shouldRunTest(mMime));
- assumeTrue("TODO(b/146925481)", mMime != MediaFormat.MIMETYPE_AUDIO_MPEG &&
- mMime != MediaFormat.MIMETYPE_AUDIO_AAC);
boolean isOk = true;
for (String srcFile : mSrcFiles) {
MediaExtractor extractor = new MediaExtractor();
@@ -1047,10 +1033,6 @@
public void testExtractNative() {
assumeTrue(shouldRunTest(mMime));
assertTrue(mSrcFiles.length > 1);
- assumeTrue("TODO(b/146925481)", mMime == MediaFormat.MIMETYPE_VIDEO_VP8 ||
- mMime == MediaFormat.MIMETYPE_VIDEO_VP9 ||
- mMime == MediaFormat.MIMETYPE_VIDEO_AV1 ||
- mMime == MediaFormat.MIMETYPE_AUDIO_FLAC);
boolean isOk = true;
for (int i = 1; i < mSrcFiles.length; i++) {
if (!nativeTestExtract(mInpPrefix + mSrcFiles[0], mInpPrefix + mSrcFiles[i],
@@ -1103,8 +1085,6 @@
@Test
public void testSeekToZeroNative() {
assumeTrue(shouldRunTest(mMime));
- assumeTrue("TODO(b/146925481)", mMime != MediaFormat.MIMETYPE_AUDIO_MPEG &&
- mMime != MediaFormat.MIMETYPE_AUDIO_AAC);
boolean isOk = true;
for (String srcFile : mSrcFiles) {
if (!nativeTestSeekToZero(mInpPrefix + srcFile, mMime)) {
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index d954f01..7311dc9 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -57,7 +57,9 @@
class MuxerTestHelper {
private static final String LOG_TAG = MuxerTestHelper.class.getSimpleName();
private static final boolean ENABLE_LOGS = false;
- static final int STTS_TOLERANCE = 100;
+ // Stts values within 0.1ms(100us) difference are fudged to save too
+ // many stts entries in MPEG4Writer.
+ static final int STTS_TOLERANCE_US = 100;
private String mSrcPath;
private String mMime;
private int mTrackCount;
@@ -253,10 +255,9 @@
}
}
- @Override
// returns true if 'this' stream is a subset of 'o'. That is all tracks in current media
// stream are present in ref media stream
- public boolean equals(Object o) {
+ boolean isSubsetOf(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MuxerTestHelper that = (MuxerTestHelper) o;
@@ -273,43 +274,39 @@
if (thisMime != null && thisMime.equals(thatMime)) {
if (!ExtractorTest.isCSDIdentical(thisFormat, thatFormat)) continue;
if (mBufferInfo.get(i).size() == that.mBufferInfo.get(j).size()) {
- int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
- for (int k = 0; k < mBufferInfo.get(i).size(); k++) {
+ long tolerance = thisMime.startsWith("video/") ? STTS_TOLERANCE_US : 0;
+ // TODO(b/157008437) - muxed file pts is +1us of target pts
+ tolerance += 1; // rounding error
+ int k = 0;
+ for (; k < mBufferInfo.get(i).size(); k++) {
MediaCodec.BufferInfo thisInfo = mBufferInfo.get(i).get(k);
MediaCodec.BufferInfo thatInfo = that.mBufferInfo.get(j).get(k);
if (thisInfo.flags != thatInfo.flags) {
- flagsDiff++;
+ break;
}
if (thisInfo.size != thatInfo.size) {
- sizeDiff++;
+ break;
} else {
mBuff.position(thisInfo.offset);
mBuff.get(refBuffer, 0, thisInfo.size);
that.mBuff.position(thatInfo.offset);
that.mBuff.get(testBuffer, 0, thatInfo.size);
- for (int count = 0; count < thisInfo.size; count++) {
+ int count = 0;
+ for (; count < thisInfo.size; count++) {
if (refBuffer[count] != testBuffer[count]) {
- buffDiff++;
break;
}
}
+ if (count != thisInfo.size) break;
}
if (Math.abs(
thisInfo.presentationTimeUs - thatInfo.presentationTimeUs) >
- STTS_TOLERANCE) {
- tsDiff++;
+ tolerance) {
+ break;
}
}
- if (flagsDiff != 0 || sizeDiff != 0 || tsDiff != 0 || buffDiff != 0) {
- if (ENABLE_LOGS) {
- Log.d(LOG_TAG, "For track: " + thisMime +
- " Total Samples: " + mBufferInfo.get(i).size() +
- " flagsDiff: " + flagsDiff +
- " sizeDiff: " + sizeDiff +
- " tsDiff: " + tsDiff +
- " buffDiff: " + buffDiff);
- }
- } else break;
+ // all samples are identical. successful match found. move to next track
+ if (k == mBufferInfo.get(i).size()) break;
} else {
if (ENABLE_LOGS) {
Log.d(LOG_TAG, "Mime matched but sample count different." +
@@ -742,7 +739,7 @@
try {
mediaInfoA.combineMedias(muxer, mediaInfoB, new int[]{1, 1});
refInfo = new MuxerTestHelper(mRefPath);
- if (!mediaInfoA.equals(refInfo) || !mediaInfoB.equals(refInfo)) {
+ if (!mediaInfoA.isSubsetOf(refInfo) || !mediaInfoB.isSubsetOf(refInfo)) {
fail(msg + "error ! muxing src A and src B failed");
}
} catch (Exception e) {
@@ -761,7 +758,7 @@
try {
mediaInfoA.combineMedias(muxer, mediaInfoB, numTrack);
MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
- if (!outInfo.equals(refInfo)) {
+ if (!outInfo.isSubsetOf(refInfo)) {
fail(msg + " error ! muxing src A: " + numTrack[0] + " src B: " +
numTrack[1] + "failed");
}
@@ -848,15 +845,11 @@
public void testOffsetPresentationTime() throws IOException {
final int OFFSET_TS = 111000;
Assume.assumeTrue(shouldRunTest(mOutFormat));
- Assume.assumeTrue("TODO(b/148978457)",
- mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
- Assume.assumeTrue("TODO(b/148978457)",
- mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
Assume.assumeTrue("TODO(b/146423022)",
mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
Assume.assumeTrue("TODO(b/146421018)",
mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG);
- assertTrue(OFFSET_TS > MuxerTestHelper.STTS_TOLERANCE);
+ assertTrue(OFFSET_TS > MuxerTestHelper.STTS_TOLERANCE_US);
MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath);
for (int trackID = 0; trackID < mediaInfo.getTrackCount(); trackID++) {
for (int i = 0; i < mOffsetIndices.length; i++) {
@@ -866,7 +859,7 @@
mediaInfo.muxMedia(muxer);
muxer.release();
MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
- if (!outInfo.equals(mediaInfo)) {
+ if (!outInfo.isSubsetOf(mediaInfo)) {
String msg = String.format(
"testOffsetPresentationTime: inp: %s, fmt: %d, trackID %d", mSrcFile,
mOutFormat, trackID);
@@ -881,10 +874,6 @@
@Test
public void testOffsetPresentationTimeNative() {
Assume.assumeTrue(shouldRunTest(mOutFormat));
- Assume.assumeTrue("TODO(b/148978457)",
- mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
- Assume.assumeTrue("TODO(b/148978457)",
- mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
Assume.assumeTrue("TODO(b/146423022)",
mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
Assume.assumeTrue("TODO(b/146421018)",
@@ -996,8 +985,6 @@
public void testSimpleMux() throws IOException {
Assume.assumeTrue("TODO(b/146421018)",
!mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
- Assume.assumeTrue("TODO(b/146923287)",
- !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath, mMime);
assertEquals("error! unexpected track count", 1, mediaInfo.getTrackCount());
for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; format++) {
@@ -1010,7 +997,7 @@
try {
mediaInfo.muxMedia(muxer);
MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
- if (!mediaInfo.equals(outInfo)) {
+ if (!mediaInfo.isSubsetOf(outInfo)) {
fail(msg + "error! output != clone(input)");
}
} catch (Exception e) {
@@ -1027,8 +1014,6 @@
public void testSimpleMuxNative() {
Assume.assumeTrue("TODO(b/146421018)",
!mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
- Assume.assumeTrue("TODO(b/146923287)",
- !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
assertTrue(nativeTestSimpleMux(mInpPath, mOutPath, mMime, selector));
}
}
diff --git a/tests/signature/api-check/OWNERS b/tests/signature/api-check/OWNERS
index 9cedf986..c8305d1 100644
--- a/tests/signature/api-check/OWNERS
+++ b/tests/signature/api-check/OWNERS
@@ -1,8 +1,7 @@
# Bug component: 24949
platform-compat-eng+reviews@google.com
andreionea@google.com
-atrost@google.com
mathewi@google.com
ngeoffray@google.com
paulduffin@google.com
-satayev@google.com
\ No newline at end of file
+satayev@google.com
diff --git a/tests/signature/api-check/hidden-api-blacklist-test-api/OWNERS b/tests/signature/api-check/hidden-api-blacklist-test-api/OWNERS
index f9ba708..66ea541 100644
--- a/tests/signature/api-check/hidden-api-blacklist-test-api/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-test-api/OWNERS
@@ -4,7 +4,6 @@
platform-compat-eng+reviews@google.com
andreionea@google.com
-atrost@google.com
mathewi@google.com
ngeoffray@google.com
satayev@google.com
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
index 6044820..9a08bcf 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -437,7 +437,12 @@
@AppModeFull
public void testAppSummary() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
- if (!shouldTestThisNetworkType(i, MINUTE/2)) {
+ // Use tolerance value that large enough to make sure stats of at
+ // least one bucket is included. However, this is possible that
+ // the test will see data of different app but with the same UID
+ // that created before testing.
+ // TODO: Consider query stats before testing and use the difference to verify.
+ if (!shouldTestThisNetworkType(i, MINUTE * 120)) {
continue;
}
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/.hash b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/.hash
index 425104b..44d6213 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/.hash
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/.hash
@@ -1 +1 @@
-d755ae773aaabd1c48d22b823e29501ee387aff1
+8e163a1b4a6f366aa0c00b6da7fc13a970ee55d8
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/test_package/ITest.aidl
index 887cbe0..75e7a41 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/1/test_package/ITest.aidl
@@ -60,6 +60,7 @@
void renameFoo(inout test_package.Foo foo, String name);
void renameBar(inout test_package.Foo foo, String name);
int getF(in test_package.Foo foo);
+ String RepeatStringNullableLater(String repeated);
const int kZero = 0;
const int kOne = 1;
const int kOnes = -1;
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/.hash b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/.hash
index fce7d03..42915a7 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/.hash
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/.hash
@@ -1 +1 @@
-3589e207cd708912b7551fb40b7fb263ef3e81b4
+dc04ee811daf7720da5a3bcf35847f7e0cea4521
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/test_package/ITest.aidl
index c489e56..bcda3c7 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/2/test_package/ITest.aidl
@@ -60,6 +60,7 @@
void renameFoo(inout test_package.Foo foo, String name);
void renameBar(inout test_package.Foo foo, String name);
int getF(in test_package.Foo foo);
+ @nullable String RepeatStringNullableLater(@nullable String repeated);
int NewMethodThatReturns10();
const int kZero = 0;
const int kOne = 1;
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
index f890351..effb098 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
@@ -77,6 +77,7 @@
void renameFoo(inout test_package.Foo foo, String name);
void renameBar(inout test_package.Foo foo, String name);
int getF(in test_package.Foo foo);
+ @nullable String RepeatStringNullableLater(@nullable String repeated);
int NewMethodThatReturns10();
const int kZero = 0;
const int kOne = 1;
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h b/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
index 0e332cd..89ea368 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
@@ -409,6 +409,22 @@
*_aidl_return = in_value;
return ::ndk::ScopedAStatus(AStatus_newOk());
}
+
+#ifdef USING_VERSION_1
+ ::ndk::ScopedAStatus RepeatStringNullableLater(const std::string& in_value,
+ std::string* _aidl_return) override {
+ *_aidl_return = in_value;
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+#else
+ ::ndk::ScopedAStatus RepeatStringNullableLater(
+ const std::optional<std::string>& in_value,
+ std::optional<std::string>* _aidl_return) override {
+ *_aidl_return = in_value;
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+#endif
+
#ifndef USING_VERSION_1
// All methods added from now on should be within this macro
::ndk::ScopedAStatus NewMethodThatReturns10(int32_t* _aidl_return) override {
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
index 2df3c72..158f1de 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
@@ -792,6 +792,36 @@
}
}
+TEST_P(NdkBinderTest_Aidl, RepeatStringNullableLater) {
+ std::optional<std::string> res;
+
+ std::string name;
+ EXPECT_OK(iface->GetName(&name));
+
+ // Java considers every type to be nullable, but this is okay, since it will
+ // pass back NullPointerException to the client if it does not handle a null
+ // type, similar to how a C++ server would refuse to unparcel a null
+ // non-nullable type. Of course, this is not ideal, but the problem runs very
+ // deep.
+ const bool supports_nullable = !GetParam().shouldBeOld || name == "Java";
+ if (supports_nullable) {
+ EXPECT_OK(iface->RepeatStringNullableLater(std::nullopt, &res));
+ EXPECT_EQ(std::nullopt, res);
+ } else {
+ ndk::ScopedAStatus status = iface->RepeatStringNullableLater(std::nullopt, &res);
+ ASSERT_EQ(STATUS_UNEXPECTED_NULL, AStatus_getStatus(status.get()));
+ }
+
+ EXPECT_OK(iface->RepeatStringNullableLater("", &res));
+ EXPECT_EQ("", res);
+
+ EXPECT_OK(iface->RepeatStringNullableLater("a", &res));
+ EXPECT_EQ("a", res);
+
+ EXPECT_OK(iface->RepeatStringNullableLater("say what?", &res));
+ EXPECT_EQ("say what?", res);
+}
+
TEST_P(NdkBinderTest_Aidl, GetInterfaceVersion) {
int32_t res;
EXPECT_OK(iface->getInterfaceVersion(&res));
@@ -808,7 +838,7 @@
EXPECT_OK(iface->getInterfaceHash(&res));
if (GetParam().shouldBeOld) {
// aidl_api/libbinder_ndk_test_interface/1/.hash
- EXPECT_EQ("d755ae773aaabd1c48d22b823e29501ee387aff1", res);
+ EXPECT_EQ("8e163a1b4a6f366aa0c00b6da7fc13a970ee55d8", res);
} else {
EXPECT_EQ("notfrozen", res);
}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
index 8166e87..c8908ab 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
@@ -118,6 +118,9 @@
void renameBar(inout Foo foo, String name);
int getF(in Foo foo);
+ // Method which is not nullable in version 1, but is nullable in version 2
+ @nullable String RepeatStringNullableLater(@nullable String repeated);
+
// Methods that do not exist in version 1
int NewMethodThatReturns10();
}
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
index 814e1e4..b8b9364 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
@@ -658,4 +658,19 @@
mInterface.renameBar(foo, "MYBAR");
assertEquals("MYBAR", foo.d.a);
}
+
+ @Test
+ public void testRepeatStringNullableLater() throws RemoteException {
+ // see notes in native NdkBinderTest_Aidl RepeatStringNullableLater
+ boolean handlesNull = !mShouldBeOld || mExpectedName == "JAVA";
+ try {
+ assertEquals(null, mInterface.RepeatStringNullableLater(null));
+ assertTrue("should reach here if null is handled", handlesNull);
+ } catch (NullPointerException e) {
+ assertFalse("should reach here if null isn't handled", handlesNull);
+ }
+ assertEquals("", mInterface.RepeatStringNullableLater(""));
+ assertEquals("a", mInterface.RepeatStringNullableLater("a"));
+ assertEquals("foo", mInterface.RepeatStringNullableLater("foo"));
+ }
}
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java b/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
index 7b60483..b0ab502 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
@@ -371,11 +371,6 @@
}
@Override
- public int NewMethodThatReturns10() {
- return 10;
- }
-
- @Override
public Foo repeatFoo(Foo inFoo) {
return inFoo;
}
@@ -398,4 +393,13 @@
return foo.f;
}
+ @Override
+ public String RepeatStringNullableLater(String in_value) {
+ return in_value;
+ }
+
+ @Override
+ public int NewMethodThatReturns10() {
+ return 10;
+ }
}
diff --git a/tests/tests/bluetooth/Android.bp b/tests/tests/bluetooth/Android.bp
index 13e9742..13ee58e 100644
--- a/tests/tests/bluetooth/Android.bp
+++ b/tests/tests/bluetooth/Android.bp
@@ -15,7 +15,10 @@
android_test {
name: "CtsBluetoothTestCases",
defaults: ["cts_defaults"],
- static_libs: ["ctstestrunner-axt"],
+ static_libs: [
+ "ctstestrunner-axt",
+ "bluetooth-test-util-lib",
+ ],
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
diff --git a/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp b/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp
new file mode 100644
index 0000000..0257a41
--- /dev/null
+++ b/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+ name: "bluetooth-test-util-lib",
+
+ static_libs: [
+ "junit",
+ ],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "current",
+}
diff --git a/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java b/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
new file mode 100644
index 0000000..9954f054
--- /dev/null
+++ b/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.cts;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.le.ScanRecord;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Utility for controlling the Bluetooth adapter from CTS test.
+ */
+public class BTAdapterUtils {
+ private static final String TAG = "BTAdapterUtils";
+
+ // ADAPTER_ENABLE_TIMEOUT_MS = AdapterState.BLE_START_TIMEOUT_DELAY +
+ // AdapterState.BREDR_START_TIMEOUT_DELAY
+ private static final int ADAPTER_ENABLE_TIMEOUT_MS = 8000;
+ // ADAPTER_DISABLE_TIMEOUT_MS = AdapterState.BLE_STOP_TIMEOUT_DELAY +
+ // AdapterState.BREDR_STOP_TIMEOUT_DELAY
+ private static final int ADAPTER_DISABLE_TIMEOUT_MS = 5000;
+
+ private static BroadcastReceiver mAdapterIntentReceiver;
+
+ private static Condition mConditionAdapterIsEnabled;
+ private static ReentrantLock mAdapterStateEnablinglock;
+
+ private static Condition mConditionAdapterIsDisabled;
+ private static ReentrantLock mAdapterStateDisablinglock;
+ private static boolean mAdapterVarsInitialized;
+
+ private static class AdapterIntentReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+ int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ Log.d(TAG, "Previous state: " + previousState + " New state: " + newState);
+
+ if (newState == BluetoothAdapter.STATE_ON) {
+ mAdapterStateEnablinglock.lock();
+ try {
+ Log.d(TAG, "Signaling to mConditionAdapterIsEnabled");
+ mConditionAdapterIsEnabled.signal();
+ } finally {
+ mAdapterStateEnablinglock.unlock();
+ }
+ } else if (newState == BluetoothAdapter.STATE_OFF) {
+ mAdapterStateDisablinglock.lock();
+ try {
+ Log.d(TAG, "Signaling to mConditionAdapterIsDisabled");
+ mConditionAdapterIsDisabled.signal();
+ } finally {
+ mAdapterStateDisablinglock.unlock();
+ }
+ }
+ }
+ }
+ }
+
+ /** Enables the Bluetooth Adapter. Return true if it is already enabled or is enabled. */
+ public static boolean enableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
+ if (!mAdapterVarsInitialized) {
+ initAdapterStateVariables(context);
+ }
+
+ if (bluetoothAdapter.isEnabled()) return true;
+
+ Log.d(TAG, "Enabling bluetooth adapter");
+ bluetoothAdapter.enable();
+ mAdapterStateEnablinglock.lock();
+ try {
+ // Wait for the Adapter to be enabled
+ while (!bluetoothAdapter.isEnabled()) {
+ if (!mConditionAdapterIsEnabled.await(
+ ADAPTER_ENABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ // Timeout
+ Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter enable");
+ break;
+ } // else spurious wakeups
+ }
+ } catch(InterruptedException e) {
+ Log.e(TAG, "enableAdapter: interrrupted");
+ } finally {
+ mAdapterStateEnablinglock.unlock();
+ }
+ return bluetoothAdapter.isEnabled();
+ }
+
+ /** Disable the Bluetooth Adapter. Return true if it is already disabled or is disabled. */
+ public static boolean disableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
+ if (!mAdapterVarsInitialized) {
+ initAdapterStateVariables(context);
+ }
+
+ if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
+
+ Log.d(TAG, "Disabling bluetooth adapter");
+ bluetoothAdapter.disable();
+ mAdapterStateDisablinglock.lock();
+ try {
+ // Wait for the Adapter to be disabled
+ while (bluetoothAdapter.getState() != BluetoothAdapter.STATE_OFF) {
+ if (!mConditionAdapterIsDisabled.await(
+ ADAPTER_DISABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ // Timeout
+ Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter disable");
+ break;
+ } // else spurious wakeups
+ }
+ } catch(InterruptedException e) {
+ Log.e(TAG, "enableAdapter: interrrupted");
+ } finally {
+ mAdapterStateDisablinglock.unlock();
+ }
+ return bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF;
+ }
+
+ // Initialize variables required for TestUtils#enableAdapter and TestUtils#disableAdapter
+ private static void initAdapterStateVariables(Context context) {
+ Log.d(TAG, "Initializing adapter state variables");
+ mAdapterIntentReceiver = new AdapterIntentReceiver();
+ IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+ context.registerReceiver(mAdapterIntentReceiver, filter);
+
+ mAdapterStateEnablinglock = new ReentrantLock();
+ mConditionAdapterIsEnabled = mAdapterStateEnablinglock.newCondition();
+ mAdapterStateDisablinglock = new ReentrantLock();
+ mConditionAdapterIsDisabled = mAdapterStateDisablinglock.newCondition();
+
+ mAdapterVarsInitialized = true;
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
index 8616a6d..2eab364 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
@@ -19,30 +19,47 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.test.AndroidTestCase;
+import android.util.Log;
import java.io.IOException;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
/**
* Very basic test, just of the static methods of {@link
* BluetoothAdapter}.
*/
public class BasicAdapterTest extends AndroidTestCase {
- private static final int DISABLE_TIMEOUT = 8000; // ms timeout for BT disable
- private static final int ENABLE_TIMEOUT = 10000; // ms timeout for BT enable
- private static final int POLL_TIME = 400; // ms to poll BT state
- private static final int CHECK_WAIT_TIME = 1000; // ms to wait before enable/disable
+ private static final String TAG = "BasicAdapterTest";
+ private static final int SET_NAME_TIMEOUT = 5000; // ms timeout for setting adapter name
private boolean mHasBluetooth;
+ private ReentrantLock mAdapterNameChangedlock;
+ private Condition mConditionAdapterNameChanged;
+ private boolean mIsAdapterNameChanged;
public void setUp() throws Exception {
super.setUp();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiver(mAdapterNameChangeReceiver, filter);
+
mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH);
+ mAdapterNameChangedlock = new ReentrantLock();
+ mConditionAdapterNameChanged = mAdapterNameChangedlock.newCondition();
+ mIsAdapterNameChanged = false;
}
public void test_getDefaultAdapter() {
@@ -124,8 +141,8 @@
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
for (int i=0; i<5; i++) {
- disable(adapter);
- enable(adapter);
+ assertTrue(BTAdapterUtils.disableAdapter(adapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
}
}
@@ -135,21 +152,34 @@
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- enable(adapter);
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
assertTrue(BluetoothAdapter.checkBluetoothAddress(adapter.getAddress()));
}
- public void test_getName() {
+ public void test_setName_getName() {
if (!mHasBluetooth) {
// Skip the test if bluetooth is not present.
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- enable(adapter);
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
String name = adapter.getName();
assertNotNull(name);
+
+ // Check renaming the adapter
+ String genericName = "Generic Device 1";
+ assertTrue(adapter.setName(genericName));
+ assertTrue(waitForAdapterNameChange());
+ mIsAdapterNameChanged = false;
+ assertEquals(genericName, adapter.getName());
+
+ // Check setting adapter back to original name
+ assertTrue(adapter.setName(name));
+ assertTrue(waitForAdapterNameChange());
+ mIsAdapterNameChanged = false;
+ assertEquals(name, adapter.getName());
}
public void test_getBondedDevices() {
@@ -158,7 +188,7 @@
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- enable(adapter);
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
Set<BluetoothDevice> devices = adapter.getBondedDevices();
assertNotNull(devices);
@@ -174,7 +204,7 @@
}
// getRemoteDevice() should work even with Bluetooth disabled
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- disable(adapter);
+ assertTrue(BTAdapterUtils.disableAdapter(adapter, mContext));
// test bad addresses
try {
@@ -210,7 +240,7 @@
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- enable(adapter);
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
BluetoothServerSocket socket = adapter.listenUsingRfcommWithServiceRecord(
"test", UUID.randomUUID());
@@ -218,75 +248,45 @@
socket.close();
}
- /** Helper to turn BT off.
- * This method will either fail on an assert, or return with BT turned off.
- * Behavior of getState() and isEnabled() are validated along the way.
- */
- private void disable(BluetoothAdapter adapter) {
- sleep(CHECK_WAIT_TIME);
- if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
- assertFalse(adapter.isEnabled());
- return;
- }
-
- assertEquals(BluetoothAdapter.STATE_ON, adapter.getState());
- assertTrue(adapter.isEnabled());
- adapter.disable();
- boolean turnOff = false;
- for (int i=0; i<DISABLE_TIMEOUT/POLL_TIME; i++) {
- sleep(POLL_TIME);
- int state = adapter.getState();
- switch (state) {
- case BluetoothAdapter.STATE_OFF:
- assertFalse(adapter.isEnabled());
- return;
- default:
- if (state != BluetoothAdapter.STATE_ON || turnOff) {
- assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
- turnOff = true;
- }
- break;
- }
- }
- fail("disable() timeout");
- }
-
- /** Helper to turn BT on.
- * This method will either fail on an assert, or return with BT turned on.
- * Behavior of getState() and isEnabled() are validated along the way.
- */
- private void enable(BluetoothAdapter adapter) {
- sleep(CHECK_WAIT_TIME);
- if (adapter.getState() == BluetoothAdapter.STATE_ON) {
- assertTrue(adapter.isEnabled());
- return;
- }
-
- assertEquals(BluetoothAdapter.STATE_OFF, adapter.getState());
- assertFalse(adapter.isEnabled());
- adapter.enable();
- boolean turnOn = false;
- for (int i=0; i<ENABLE_TIMEOUT/POLL_TIME; i++) {
- sleep(POLL_TIME);
- int state = adapter.getState();
- switch (state) {
- case BluetoothAdapter.STATE_ON:
- assertTrue(adapter.isEnabled());
- return;
- default:
- if (state != BluetoothAdapter.STATE_OFF || turnOn) {
- assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
- turnOn = true;
- }
- break;
- }
- }
- fail("enable() timeout");
- }
-
private static void sleep(long t) {
try {
Thread.sleep(t);
} catch (InterruptedException e) {}
}
+
+ private boolean waitForAdapterNameChange() {
+ mAdapterNameChangedlock.lock();
+ try {
+ // Wait for the Adapter name to be changed
+ while (!mIsAdapterNameChanged) {
+ if (!mConditionAdapterNameChanged.await(
+ SET_NAME_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ Log.e(TAG, "Timeout while waiting for adapter name change");
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "waitForAdapterNameChange: interrrupted");
+ } finally {
+ mAdapterNameChangedlock.unlock();
+ }
+ return mIsAdapterNameChanged;
+ }
+
+ private final BroadcastReceiver mAdapterNameChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)) {
+ mAdapterNameChangedlock.lock();
+ mIsAdapterNameChanged = true;
+ try {
+ mConditionAdapterNameChanged.signal();
+ } catch (IllegalMonitorStateException ex) {
+ } finally {
+ mAdapterNameChangedlock.unlock();
+ }
+ }
+ }
+ };
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
index b3bc692..103816e 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
@@ -64,7 +64,6 @@
private static final int SCAN_DURATION_MILLIS = 10000;
private static final int BATCH_SCAN_REPORT_DELAY_MILLIS = 20000;
private static final int SCAN_STOP_TIMEOUT = 2000;
- private static final int ADAPTER_ENABLE_TIMEOUT = 3000;
private CountDownLatch mFlushBatchScanLatch;
private BluetoothAdapter mBluetoothAdapter;
@@ -81,10 +80,7 @@
Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();
if (!mBluetoothAdapter.isEnabled()) {
- // Note it's not reliable to listen for Adapter.ACTION_STATE_CHANGED broadcast and check
- // bluetooth state.
- mBluetoothAdapter.enable();
- TestUtils.sleep(ADAPTER_ENABLE_TIMEOUT);
+ assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext));
}
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mLocationOn = TestUtils.isLocationOn(getContext());
@@ -105,8 +101,7 @@
if (!mLocationOn) {
TestUtils.disableLocation(getContext());
}
- mBluetoothAdapter.disable();
- TestUtils.sleep(ADAPTER_ENABLE_TIMEOUT);
+ assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
}
/**
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
index e94c3e7..4e1419a 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
@@ -62,15 +62,6 @@
private BluetoothAdapter mBluetoothAdapter;
private BroadcastReceiver mIntentReceiver;
- private BroadcastReceiver mAdapterIntentReceiver;
-
- private Condition mConditionAdapterIsEnabled;
- private ReentrantLock mAdapterStateEnablinglock;
-
- private Condition mConditionAdapterIsDisabled;
- private ReentrantLock mAdapterStateDisablinglock;
- private boolean mAdapterOffSignalReceived;
-
private Condition mConditionProfileIsConnected;
private ReentrantLock mProfileConnectedlock;
private boolean mIsProfileReady;
@@ -89,22 +80,13 @@
Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();
- mAdapterStateEnablinglock = new ReentrantLock();
- mConditionAdapterIsEnabled = mAdapterStateEnablinglock.newCondition();
- mAdapterStateDisablinglock = new ReentrantLock();
- mConditionAdapterIsDisabled = mAdapterStateDisablinglock.newCondition();
- mProfileConnectedlock = new ReentrantLock();
- mConditionProfileIsConnected = mProfileConnectedlock.newCondition();
-
- mAdapterIntentReceiver = new AdapterIntentReceiver();
- IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
- mContext.registerReceiver(mAdapterIntentReceiver, filter);
-
- if (!enableAdapter()) {
+ if (!BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext)) {
Log.e(TAG, "Unable to enable Bluetooth Adapter!");
assertTrue(mBluetoothAdapter.isEnabled());
}
+ mProfileConnectedlock = new ReentrantLock();
+ mConditionProfileIsConnected = mProfileConnectedlock.newCondition();
mIsProfileReady = false;
mService = null;
mIsHearingAidSupported = mBluetoothAdapter.getProfileProxy(getContext(),
@@ -117,12 +99,10 @@
public void tearDown() {
if (!mIsBleSupported) return;
- if (!disableAdapter()) {
+ if (!BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext)) {
Log.e(TAG, "Unable to disable Bluetooth Adapter!");
assertTrue(mBluetoothAdapter.isEnabled());
}
-
- mContext.unregisterReceiver(mAdapterIntentReceiver);
}
/**
@@ -244,10 +224,10 @@
mContext.registerReceiver(mIntentReceiver, filter);
Log.d(TAG, "test_getConnectionStateChangedIntent: disable adapter and wait");
- assertTrue(disableAdapter());
+ assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
Log.d(TAG, "test_getConnectionStateChangedIntent: enable adapter and wait");
- assertTrue(enableAdapter());
+ assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext));
int sanityCount = WAIT_FOR_INTENT_TIMEOUT_MS;
while ((numDevices != mIntentCallbackDeviceList.size()) && (sanityCount > 0)) {
@@ -267,86 +247,6 @@
}
}
- private class AdapterIntentReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
- int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
- int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
- Log.d(TAG, "Previous state: " + previousState + " New state: " + newState);
-
- if (newState == BluetoothAdapter.STATE_ON) {
- mAdapterStateEnablinglock.lock();
- try {
- mConditionAdapterIsEnabled.signal();
- } finally {
- mAdapterStateEnablinglock.unlock();
- }
- } else if (newState == BluetoothAdapter.STATE_OFF) {
- mAdapterStateDisablinglock.lock();
- mAdapterOffSignalReceived = true;
- try {
- mConditionAdapterIsDisabled.signal();
- } finally {
- mAdapterStateDisablinglock.unlock();
- }
- }
- }
- }
- }
-
- // Enables the Bluetooth Adapter. Return true if it is already enabled or is enabled.
- private boolean enableAdapter() {
- if (mBluetoothAdapter.isEnabled()) return true;
-
- mBluetoothAdapter.enable();
- mAdapterStateEnablinglock.lock();
- try {
- // Wait for the Adapter to be enabled
- while (!mBluetoothAdapter.isEnabled()) {
- if (!mConditionAdapterIsEnabled.await(
- ADAPTER_ENABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- // Timeout
- Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter enable");
- break;
- } // else spurious wakeups
- }
- } catch(InterruptedException e) {
- Log.e(TAG, "enableAdapter: interrrupted");
- } finally {
- mAdapterStateEnablinglock.unlock();
- }
- if (!mBluetoothAdapter.isEnabled()) {
- return false;
- }
- return true;
- }
-
- // Disable the Bluetooth Adapter. Return true if it is already disabled or is disabled.
- private boolean disableAdapter() {
- // Note: !mBluetoothAdapter.isEnabled() is not an accurate indication that the
- // BluetoothAdapter is OFF.
- mBluetoothAdapter.disable();
- mAdapterOffSignalReceived = false;
- mAdapterStateDisablinglock.lock();
- try {
- // Wait for the Adapter to be disabled
- while (!mAdapterOffSignalReceived) {
- if (!mConditionAdapterIsDisabled.await(
- ADAPTER_DISABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- // Timeout
- Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter disable");
- break;
- } // else spurious wakeups
- }
- } catch(InterruptedException e) {
- Log.e(TAG, "enableAdapter: interrrupted");
- } finally {
- mAdapterStateDisablinglock.unlock();
- }
- return mAdapterOffSignalReceived;
- }
-
private boolean waitForProfileConnect() {
mProfileConnectedlock.lock();
try {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
index 433a39f..b9a93a1 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
@@ -26,7 +26,6 @@
public class LeL2capSocketTest extends AndroidTestCase {
private static final int NUM_ITERATIONS_FOR_REPEATED_TEST = 100;
- private static final int ADAPTER_TOGGLE_TIMEOUT_MS = 4000;
private BluetoothAdapter mAdapter = null;
@@ -40,12 +39,8 @@
assertNotNull("BluetoothAdapter.getDefaultAdapter() returned null. "
+ "Does this device have a Bluetooth adapter?", mAdapter);
if (!mAdapter.isEnabled()) {
- // Note: It's not reliable to listen for Adapter.ACTION_STATE_CHANGED broadcast and
- // check bluetooth state per comments from BluetoothLeScanTest.java
- mAdapter.enable();
- TestUtils.sleep(ADAPTER_TOGGLE_TIMEOUT_MS);
+ assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
}
- assertTrue("Bluetooth failed to be enabled", mAdapter.isEnabled());
}
@Override
@@ -53,9 +48,7 @@
if (!TestUtils.isBleSupported(getContext())) {
return;
}
- mAdapter.disable();
- TestUtils.sleep(ADAPTER_TOGGLE_TIMEOUT_MS);
- assertFalse("Bluetooth failed to be disabled", mAdapter.isEnabled());
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
mAdapter = null;
super.tearDown();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
index 342266f..7c4c454 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,12 @@
package android.bluetooth.cts;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.ScanRecord;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.provider.Settings;
import android.util.Log;
@@ -27,12 +31,14 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
/**
* Utility class for Bluetooth CTS test.
*/
class TestUtils {
-
/**
* Utility method to call hidden ScanRecord.parseFromBytes method.
*/
diff --git a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
index e5ff289..0085a7c 100644
--- a/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/ProvisioningTest.java
@@ -875,5 +875,41 @@
store.deleteCredentialByName("test");
}
+ @Test
+ public void testProvisionAcpIdNotInValidRange() throws IdentityCredentialException {
+ assumeTrue("IC HAL is not implemented", Util.isHalImplemented());
+
+ Context appContext = InstrumentationRegistry.getTargetContext();
+ IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+
+ WritableIdentityCredential wc =
+ store.createCredential("testAcpNotInValidRange", "org.iso.18013-5.2019.mdl");
+
+ Collection<X509Certificate> certificateChain =
+ wc.getCredentialKeyCertificateChain("SomeChallenge".getBytes());
+
+ // Profile 32 (no authentication) - invalid profile id
+ AccessControlProfile noAuthProfile =
+ new AccessControlProfile.Builder(new AccessControlProfileId(32))
+ .setUserAuthenticationRequired(false)
+ .build();
+
+ Collection<AccessControlProfileId> idsNoAuth = new ArrayList<AccessControlProfileId>();
+ idsNoAuth.add(new AccessControlProfileId(32));
+ String mdlNs = "org.iso.18013-5.2019";
+ PersonalizationData personalizationData =
+ new PersonalizationData.Builder()
+ .addAccessControlProfile(noAuthProfile)
+ .putEntry("com.example.ns", "Name", idsNoAuth, Util.cborEncodeString("Alan"))
+ .build();
+
+ // personalize() should fail because of the invalid profile id
+ try {
+ byte[] proofOfProvisioningSignature = wc.personalize(personalizationData);
+ assertTrue(false);
+ } catch (Exception e) {
+ // This is the expected path...
+ }
+ }
}
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index c04a8b4..6cd2441 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -82,6 +82,7 @@
private final static String[] PUBLIC_ART_LIBRARIES = {
"libicui18n.so",
"libicuuc.so",
+ "libnativehelper.so"
};
// The grey-list.
@@ -93,7 +94,6 @@
"libexpat.so",
"libgui.so",
"libmedia.so",
- "libnativehelper.so",
"libskia.so",
"libssl.so",
"libstagefright.so",
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 46592ca..4373fdc 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -68,6 +68,8 @@
import com.google.common.collect.ImmutableSet;
+import androidx.test.filters.RequiresDevice;
+
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
@@ -163,21 +165,26 @@
int[] purposes = {
KM_PURPOSE_SIGN, KM_PURPOSE_VERIFY, KM_PURPOSE_SIGN | KM_PURPOSE_VERIFY
};
+ boolean[] devicePropertiesAttestationValues = {true, false};
+ boolean[] includeValidityDatesValues = {true, false};
for (int curveIndex = 0; curveIndex < curves.length; ++curveIndex) {
for (int challengeIndex = 0; challengeIndex < challenges.length; ++challengeIndex) {
for (int purposeIndex = 0; purposeIndex < purposes.length; ++purposeIndex) {
- try {
- testEcAttestation(challenges[challengeIndex],
- true /* includeValidityDates */,
- curves[curveIndex], keySizes[curveIndex], purposes[purposeIndex]);
- testEcAttestation(challenges[challengeIndex],
- false /* includeValidityDates */,
- curves[curveIndex], keySizes[curveIndex], purposes[purposeIndex]);
- } catch (Throwable e) {
- throw new Exception(
- "Failed on curve " + curveIndex + " and challege " + challengeIndex,
- e);
+ for (boolean includeValidityDates : includeValidityDatesValues) {
+ for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+ try {
+ testEcAttestation(challenges[challengeIndex], includeValidityDates,
+ curves[curveIndex], keySizes[curveIndex],
+ purposes[purposeIndex], devicePropertiesAttestation);
+ } catch (Throwable e) {
+ throw new Exception("Failed on curve " + curveIndex +
+ " challenge " + challengeIndex + " purpose " +
+ purposeIndex + " includeValidityDates " +
+ includeValidityDates + " and devicePropertiesAttestation " +
+ devicePropertiesAttestation, e);
+ }
+ }
}
}
}
@@ -185,43 +192,50 @@
}
public void testEcAttestation_TooLargeChallenge() throws Exception {
- try {
- testEcAttestation(new byte[129], true /* includeValidityDates */, "secp256r1", 256,
- KM_PURPOSE_SIGN);
- fail("Attestation challenges larger than 128 bytes should be rejected");
- } catch (ProviderException e) {
- KeyStoreException cause = (KeyStoreException) e.getCause();
- assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
+ boolean[] devicePropertiesAttestationValues = {true, false};
+ for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+ try {
+ testEcAttestation(new byte[129], true /* includeValidityDates */, "secp256r1", 256,
+ KM_PURPOSE_SIGN, devicePropertiesAttestation);
+ fail("Attestation challenges larger than 128 bytes should be rejected");
+ } catch (ProviderException e) {
+ KeyStoreException cause = (KeyStoreException) e.getCause();
+ assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
+ }
}
}
public void testEcAttestation_NoChallenge() throws Exception {
- String keystoreAlias = "test_key";
- Date now = new Date();
- Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
- Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
- KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
- .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
- .setAttestationChallenge(null)
- .setKeyValidityStart(now)
- .setKeyValidityForOriginationEnd(originationEnd)
- .setKeyValidityForConsumptionEnd(consumptionEnd)
- .build();
+ boolean[] devicePropertiesAttestationValues = {true, false};
+ for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+ String keystoreAlias = "test_key";
+ Date now = new Date();
+ Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
+ Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+ .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+ .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setAttestationChallenge(null)
+ .setKeyValidityStart(now)
+ .setKeyValidityForOriginationEnd(originationEnd)
+ .setKeyValidityForConsumptionEnd(consumptionEnd)
+ .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
+ .build();
- generateKeyPair(KEY_ALGORITHM_EC, spec);
+ generateKeyPair(KEY_ALGORITHM_EC, spec);
- KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
- keyStore.load(null);
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
- try {
- Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- assertEquals(1, certificates.length);
+ try {
+ Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
+ assertEquals(1, certificates.length);
- X509Certificate attestationCert = (X509Certificate) certificates[0];
- assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
- } finally {
- keyStore.deleteEntry(keystoreAlias);
+ X509Certificate attestationCert = (X509Certificate) certificates[0];
+ assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
+ } finally {
+ keyStore.deleteEntry(keystoreAlias);
+ }
}
}
@@ -325,14 +339,19 @@
SIGNATURE_PADDING_RSA_PSS,
},
};
+ boolean[] devicePropertiesAttestationValues = {true, false};
- for (int keySize : keySizes) {
- for (byte[] challenge : challenges) {
- for (int purpose : purposes) {
- if (isEncryptionPurpose(purpose)) {
- testRsaAttestations(keySize, challenge, purpose, encryptionPaddingModes);
- } else {
- testRsaAttestations(keySize, challenge, purpose, signaturePaddingModes);
+ for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+ for (int keySize : keySizes) {
+ for (byte[] challenge : challenges) {
+ for (int purpose : purposes) {
+ if (isEncryptionPurpose(purpose)) {
+ testRsaAttestations(keySize, challenge, purpose, encryptionPaddingModes,
+ devicePropertiesAttestation);
+ } else {
+ testRsaAttestations(keySize, challenge, purpose, signaturePaddingModes,
+ devicePropertiesAttestation);
+ }
}
}
}
@@ -340,46 +359,56 @@
}
public void testRsaAttestation_TooLargeChallenge() throws Exception {
- try {
- testRsaAttestation(new byte[129], true /* includeValidityDates */, 512, PURPOSE_SIGN,
- null /* paddingModes; may be empty because we'll never test them */);
- fail("Attestation challenges larger than 128 bytes should be rejected");
- } catch (ProviderException e) {
- KeyStoreException cause = (KeyStoreException) e.getCause();
- assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
+ boolean[] devicePropertiesAttestationValues = {true, false};
+ for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+ try {
+ testRsaAttestation(new byte[129], true /* includeValidityDates */, 512,
+ PURPOSE_SIGN,
+ null /* paddingModes; may be empty because we'll never test them */,
+ devicePropertiesAttestation);
+ fail("Attestation challenges larger than 128 bytes should be rejected");
+ } catch(ProviderException e){
+ KeyStoreException cause = (KeyStoreException) e.getCause();
+ assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode());
+ }
}
}
public void testRsaAttestation_NoChallenge() throws Exception {
- String keystoreAlias = "test_key";
- Date now = new Date();
- Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
- Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
- KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
- .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
- .setAttestationChallenge(null)
- .setKeyValidityStart(now)
- .setKeyValidityForOriginationEnd(originationEnd)
- .setKeyValidityForConsumptionEnd(consumptionEnd)
- .build();
+ boolean[] devicePropertiesAttestationValues = {true, false};
+ for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+ String keystoreAlias = "test_key";
+ Date now = new Date();
+ Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
+ Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+ .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
+ .setAttestationChallenge(null)
+ .setKeyValidityStart(now)
+ .setKeyValidityForOriginationEnd(originationEnd)
+ .setKeyValidityForConsumptionEnd(consumptionEnd)
+ .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
+ .build();
- generateKeyPair(KEY_ALGORITHM_RSA, spec);
+ generateKeyPair(KEY_ALGORITHM_RSA, spec);
- KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
- keyStore.load(null);
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
- try {
- Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- assertEquals(1, certificates.length);
+ try {
+ Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
+ assertEquals(1, certificates.length);
- X509Certificate attestationCert = (X509Certificate) certificates[0];
- assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
- } finally {
- keyStore.deleteEntry(keystoreAlias);
+ X509Certificate attestationCert = (X509Certificate) certificates[0];
+ assertNull(attestationCert.getExtensionValue(Attestation.KEY_DESCRIPTION_OID));
+ } finally {
+ keyStore.deleteEntry(keystoreAlias);
+ }
}
}
@RestrictedBuildTest
+ @RequiresDevice // Emulators have no place to store the needed key
public void testRsaAttestation_DeviceLocked() throws Exception {
String keystoreAlias = "test_key";
Date now = new Date();
@@ -417,52 +446,62 @@
}
public void testAesAttestation() throws Exception {
- String keystoreAlias = "test_key";
- KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_ENCRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .setAttestationChallenge(new byte[0])
- .build();
- generateKey(spec, KeyProperties.KEY_ALGORITHM_AES);
+ boolean[] devicePropertiesAttestationValues = {true, false};
+ for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+ String keystoreAlias = "test_key";
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias,
+ PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setAttestationChallenge(new byte[0])
+ .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
+ .build();
+ generateKey(spec, KeyProperties.KEY_ALGORITHM_AES);
- KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
- keyStore.load(null);
- try {
- assertNull(keyStore.getCertificateChain(keystoreAlias));
- } finally {
- keyStore.deleteEntry(keystoreAlias);
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ try {
+ assertNull(keyStore.getCertificateChain(keystoreAlias));
+ } finally {
+ keyStore.deleteEntry(keystoreAlias);
+ }
}
}
public void testHmacAttestation() throws Exception {
- String keystoreAlias = "test_key";
- KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
- .build();
+ boolean[] devicePropertiesAttestationValues = {true, false};
+ for (boolean devicePropertiesAttestation : devicePropertiesAttestationValues) {
+ String keystoreAlias = "test_key";
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+ .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation)
+ .build();
- generateKey(spec, KeyProperties.KEY_ALGORITHM_HMAC_SHA256);
+ generateKey(spec, KeyProperties.KEY_ALGORITHM_HMAC_SHA256);
- KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
- keyStore.load(null);
- try {
- assertNull(keyStore.getCertificateChain(keystoreAlias));
- } finally {
- keyStore.deleteEntry(keystoreAlias);
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ try {
+ assertNull(keyStore.getCertificateChain(keystoreAlias));
+ } finally {
+ keyStore.deleteEntry(keystoreAlias);
+ }
}
}
private void testRsaAttestations(int keySize, byte[] challenge, int purpose,
- String[][] paddingModes) throws Exception {
+ String[][] paddingModes, boolean devicePropertiesAttestation) throws Exception {
for (String[] paddings : paddingModes) {
try {
testRsaAttestation(challenge, true /* includeValidityDates */, keySize, purpose,
- paddings);
+ paddings, devicePropertiesAttestation);
testRsaAttestation(challenge, false /* includeValidityDates */, keySize, purpose,
- paddings);
+ paddings, devicePropertiesAttestation);
} catch (Throwable e) {
throw new Exception("Failed on key size " + keySize + " challenge [" +
new String(challenge) + "], purposes " +
- buildPurposeSet(purpose) + " and paddings " +
- ImmutableSet.copyOf(paddings),
+ buildPurposeSet(purpose) + " paddings " +
+ ImmutableSet.copyOf(paddings) + " and devicePropertiesAttestation "
+ + devicePropertiesAttestation,
e);
}
}
@@ -476,9 +515,14 @@
@SuppressWarnings("deprecation")
private void testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize,
- int purposes, String[] paddingModes) throws Exception {
- String keystoreAlias = "test_key";
+ int purposes, String[] paddingModes, boolean devicePropertiesAttestation)
+ throws Exception {
+ Log.i(TAG, "RSA key attestation with: challenge " + Arrays.toString(challenge) +
+ " / includeValidityDates " + includeValidityDates + " / keySize " + keySize +
+ " / purposes " + purposes + " / paddingModes " + Arrays.toString(paddingModes) +
+ " / devicePropertiesAttestation " + devicePropertiesAttestation);
+ String keystoreAlias = "test_key";
Date startTime = new Date();
Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
@@ -486,7 +530,8 @@
new KeyGenParameterSpec.Builder(keystoreAlias, purposes)
.setKeySize(keySize)
.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
- .setAttestationChallenge(challenge);
+ .setAttestationChallenge(challenge)
+ .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation);
if (includeValidityDates) {
builder.setKeyValidityStart(startTime)
@@ -517,7 +562,7 @@
checkRsaKeyDetails(attestation, keySize, purposes, ImmutableSet.copyOf(paddingModes));
checkKeyUsage(attestationCert, purposes);
checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
- attestation);
+ devicePropertiesAttestation, attestation);
} finally {
keyStore.deleteEntry(keystoreAlias);
}
@@ -538,9 +583,13 @@
@SuppressWarnings("deprecation")
private void testEcAttestation(byte[] challenge, boolean includeValidityDates, String ecCurve,
- int keySize, int purposes) throws Exception {
- String keystoreAlias = "test_key";
+ int keySize, int purposes, boolean devicePropertiesAttestation) throws Exception {
+ Log.i(TAG, "EC key attestation with: challenge " + Arrays.toString(challenge) +
+ " / includeValidityDates " + includeValidityDates + " / ecCurve " + ecCurve +
+ " / keySize " + keySize + " / purposes " + purposes +
+ " / devicePropertiesAttestation " + devicePropertiesAttestation);
+ String keystoreAlias = "test_key";
Date startTime = new Date();
Date originationEnd = new Date(startTime.getTime() + ORIGINATION_TIME_OFFSET);
Date consumptionEnd = new Date(startTime.getTime() + CONSUMPTION_TIME_OFFSET);
@@ -548,7 +597,8 @@
purposes)
.setAlgorithmParameterSpec(new ECGenParameterSpec(ecCurve))
.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512)
- .setAttestationChallenge(challenge);
+ .setAttestationChallenge(challenge)
+ .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation);
if (includeValidityDates) {
builder.setKeyValidityStart(startTime)
@@ -571,7 +621,7 @@
checkEcKeyDetails(attestation, ecCurve, keySize);
checkKeyUsage(attestationCert, purposes);
checkKeyIndependentAttestationInfo(challenge, purposes, startTime, includeValidityDates,
- attestation);
+ devicePropertiesAttestation, attestation);
} finally {
keyStore.deleteEntry(keystoreAlias);
}
@@ -596,9 +646,50 @@
}
}
+ private void checkAttestationDeviceProperties(boolean devicePropertiesAttestation,
+ Attestation attestation) {
+ final AuthorizationList keyDetailsList;
+ final AuthorizationList nonKeyDetailsList;
+ if (attestation.getKeymasterSecurityLevel() == KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT) {
+ keyDetailsList = attestation.getTeeEnforced();
+ nonKeyDetailsList = attestation.getSoftwareEnforced();
+ } else {
+ keyDetailsList = attestation.getSoftwareEnforced();
+ nonKeyDetailsList = attestation.getTeeEnforced();
+ }
+
+ if (devicePropertiesAttestation) {
+ assertEquals(Build.BRAND, keyDetailsList.getBrand());
+ assertEquals(Build.DEVICE, keyDetailsList.getDevice());
+ assertEquals(Build.PRODUCT, keyDetailsList.getProduct());
+ assertEquals(Build.MANUFACTURER, keyDetailsList.getManufacturer());
+ assertEquals(Build.MODEL, keyDetailsList.getModel());
+ } else {
+ assertNull(keyDetailsList.getBrand());
+ assertNull(keyDetailsList.getDevice());
+ assertNull(keyDetailsList.getProduct());
+ assertNull(keyDetailsList.getManufacturer());
+ assertNull(keyDetailsList.getModel());
+ }
+ assertNull(nonKeyDetailsList.getBrand());
+ assertNull(nonKeyDetailsList.getDevice());
+ assertNull(nonKeyDetailsList.getProduct());
+ assertNull(nonKeyDetailsList.getManufacturer());
+ assertNull(nonKeyDetailsList.getModel());
+ }
+
+ private void checkAttestationNoUniqueIds(Attestation attestation) {
+ assertNull(attestation.getTeeEnforced().getImei());
+ assertNull(attestation.getTeeEnforced().getMeid());
+ assertNull(attestation.getTeeEnforced().getSerialNumber());
+ assertNull(attestation.getSoftwareEnforced().getImei());
+ assertNull(attestation.getSoftwareEnforced().getMeid());
+ assertNull(attestation.getSoftwareEnforced().getSerialNumber());
+ }
+
private void checkKeyIndependentAttestationInfo(byte[] challenge, int purposes, Date startTime,
- boolean includesValidityDates, Attestation attestation)
- throws NoSuchAlgorithmException, NameNotFoundException {
+ boolean includesValidityDates, boolean devicePropertiesAttestation,
+ Attestation attestation) throws NoSuchAlgorithmException, NameNotFoundException {
checkUnexpectedOids(attestation);
checkAttestationSecurityLevelDependentParams(attestation);
assertNotNull(attestation.getAttestationChallenge());
@@ -612,6 +703,8 @@
checkFlags(attestation);
checkOrigin(attestation);
checkAttestationApplicationId(attestation);
+ checkAttestationDeviceProperties(devicePropertiesAttestation, attestation);
+ checkAttestationNoUniqueIds(attestation);
}
private void checkUnexpectedOids(Attestation attestation) {
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
index 9a6a7de..707730d 100644
--- a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -211,7 +211,6 @@
public void setUp() throws Exception {
Log.i(TAG, "++ setUp() mContext:" + mContext);
if (!hasMidiSupport()) {
- Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
return; // Not supported so don't test it.
}
mMidiManager = (MidiManager)mContext.getSystemService(Context.MIDI_SERVICE);
@@ -250,6 +249,10 @@
public void test_B_SendData() throws Exception {
Log.i(TAG, "++++ test_B_SendData() this:" + System.identityHashCode(this));
+ if (!hasMidiSupport()) {
+ return; // Nothing to test
+ }
+
Assert.assertEquals("Didn't start with 0 sends", 0, getNumSends(mTestContext));
Assert.assertEquals("Didn't start with 0 bytes sent", 0, getNumBytesSent(mTestContext));
diff --git a/tests/tests/net/Android.bp b/tests/tests/net/Android.bp
index 76bb27e..93a6d91 100644
--- a/tests/tests/net/Android.bp
+++ b/tests/tests/net/Android.bp
@@ -47,6 +47,7 @@
"mockwebserver",
"junit",
"junit-params",
+ "libnanohttpd",
"truth-prebuilt",
],
@@ -76,6 +77,7 @@
android_test {
name: "CtsNetTestCasesLatestSdk",
defaults: ["CtsNetTestCasesDefaults"],
+ jni_uses_sdk_apis: true,
min_sdk_version: "29",
target_sdk_version: "29",
test_suites: [
diff --git a/tests/tests/net/AndroidManifest.xml b/tests/tests/net/AndroidManifest.xml
index baf914f..a7e2bd7 100644
--- a/tests/tests/net/AndroidManifest.xml
+++ b/tests/tests/net/AndroidManifest.xml
@@ -26,6 +26,7 @@
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/net/ipsec/Android.bp b/tests/tests/net/ipsec/Android.bp
index f1f120b..16bdb05 100644
--- a/tests/tests/net/ipsec/Android.bp
+++ b/tests/tests/net/ipsec/Android.bp
@@ -33,6 +33,7 @@
"androidx.test.ext.junit",
"compatibility-device-util-axt",
"ctstestrunner-axt",
+ "net-tests-utils",
],
platform_apis: true,
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java
index 6fc7cb3..c767b78 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTest.java
@@ -46,6 +46,7 @@
import com.android.internal.net.ipsec.ike.testutils.CertUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,7 +64,7 @@
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
-public final class IkeSessionParamsTest extends IkeSessionParamsTestBase {
+public final class IkeSessionParamsTest extends IkeSessionTestBase {
private static final int HARD_LIFETIME_SECONDS = (int) TimeUnit.HOURS.toSeconds(20L);
private static final int SOFT_LIFETIME_SECONDS = (int) TimeUnit.HOURS.toSeconds(10L);
private static final int DPD_DELAY_SECONDS = (int) TimeUnit.MINUTES.toSeconds(10L);
@@ -105,6 +106,9 @@
@Before
public void setUp() throws Exception {
+ // This address is never used except for setting up the test network
+ setUpTestNetwork(IPV4_ADDRESS_LOCAL);
+
mServerCaCert = CertUtils.createCertFromPemFile("server-a-self-signed-ca.pem");
mClientEndCert = CertUtils.createCertFromPemFile("client-a-end-cert.pem");
mClientIntermediateCaCertOne =
@@ -114,6 +118,11 @@
mClientPrivateKey = CertUtils.createRsaPrivateKeyFromKeyFile("client-a-private-key.key");
}
+ @After
+ public void tearDown() throws Exception {
+ tearDownTestNetwork();
+ }
+
private static EapSessionConfig.Builder createEapOnlySafeMethodsBuilder() {
return new EapSessionConfig.Builder()
.setEapIdentity(EAP_IDENTITY)
@@ -131,7 +140,7 @@
*/
private IkeSessionParams.Builder createIkeParamsBuilderMinimum() {
return new IkeSessionParams.Builder(sContext)
- .setNetwork(sTunNetwork)
+ .setNetwork(mTunNetwork)
.setServerHostname(IPV4_ADDRESS_REMOTE.getHostAddress())
.addSaProposal(SA_PROPOSAL)
.setLocalIdentification(LOCAL_ID)
@@ -145,7 +154,7 @@
* @see #createIkeParamsBuilderMinimum
*/
private void verifyIkeParamsMinimum(IkeSessionParams sessionParams) {
- assertEquals(sTunNetwork, sessionParams.getNetwork());
+ assertEquals(mTunNetwork, sessionParams.getNetwork());
assertEquals(IPV4_ADDRESS_REMOTE.getHostAddress(), sessionParams.getServerHostname());
assertEquals(Arrays.asList(SA_PROPOSAL), sessionParams.getSaProposals());
assertEquals(LOCAL_ID, sessionParams.getLocalIdentification());
@@ -268,7 +277,7 @@
*/
private IkeSessionParams.Builder createIkeParamsBuilderMinimumWithoutAuth() {
return new IkeSessionParams.Builder(sContext)
- .setNetwork(sTunNetwork)
+ .setNetwork(mTunNetwork)
.setServerHostname(IPV4_ADDRESS_REMOTE.getHostAddress())
.addSaProposal(SA_PROPOSAL)
.setLocalIdentification(LOCAL_ID)
@@ -282,7 +291,7 @@
* @see #createIkeParamsBuilderMinimumWithoutAuth
*/
private void verifyIkeParamsMinimumWithoutAuth(IkeSessionParams sessionParams) {
- assertEquals(sTunNetwork, sessionParams.getNetwork());
+ assertEquals(mTunNetwork, sessionParams.getNetwork());
assertEquals(IPV4_ADDRESS_REMOTE.getHostAddress(), sessionParams.getServerHostname());
assertEquals(Arrays.asList(SA_PROPOSAL), sessionParams.getSaProposals());
assertEquals(LOCAL_ID, sessionParams.getLocalIdentification());
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java
deleted file mode 100644
index c3e3ba3..0000000
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ipsec.ike.cts;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkAddress;
-import android.net.Network;
-import android.net.TestNetworkInterface;
-import android.net.TestNetworkManager;
-import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.platform.test.annotations.AppModeFull;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
-abstract class IkeSessionParamsTestBase extends IkeTestBase {
- // Static state to reduce setup/teardown
- static ConnectivityManager sCM;
- static TestNetworkManager sTNM;
- static ParcelFileDescriptor sTunFd;
- static TestNetworkCallback sTunNetworkCallback;
- static Network sTunNetwork;
-
- static Context sContext = InstrumentationRegistry.getContext();
- static IBinder sBinder = new Binder();
-
- // This method is guaranteed to run in subclasses and will run before subclasses' @BeforeClass
- // methods.
- @BeforeClass
- public static void setUpTestNetworkBeforeClass() throws Exception {
- InstrumentationRegistry.getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity();
- sCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- sTNM = (TestNetworkManager) sContext.getSystemService(Context.TEST_NETWORK_SERVICE);
-
- TestNetworkInterface testIface =
- sTNM.createTunInterface(
- new LinkAddress[] {new LinkAddress(IPV4_ADDRESS_LOCAL, IP4_PREFIX_LEN)});
-
- sTunFd = testIface.getFileDescriptor();
- sTunNetworkCallback =
- TestNetworkUtils.setupAndGetTestNetwork(
- sCM, sTNM, testIface.getInterfaceName(), sBinder);
- sTunNetwork = sTunNetworkCallback.getNetworkBlocking();
- }
-
- // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
- // methods.
- @AfterClass
- public static void tearDownTestNetworkAfterClass() throws Exception {
- sCM.unregisterNetworkCallback(sTunNetworkCallback);
-
- sTNM.teardownTestNetwork(sTunNetwork);
- sTunFd.close();
-
- InstrumentationRegistry.getInstrumentation()
- .getUiAutomation()
- .dropShellPermissionIdentity();
- }
-}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
new file mode 100644
index 0000000..661457f
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipsec.ike.cts;
+
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.LinkAddress;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "MANAGE_IPSEC_TUNNELS permission can't be granted to instant apps")
+public class IkeSessionPskTest extends IkeSessionTestBase {
+ // Test vectors for success workflow
+ private static final String SUCCESS_IKE_INIT_RESP =
+ "46B8ECA1E0D72A18B45427679F9245D421202220000000000000015022000030"
+ + "0000002C010100040300000C0100000C800E0080030000080300000203000008"
+ + "0200000200000008040000022800008800020000A7AA3435D088EC1A2B7C2A47"
+ + "1FA1B85F1066C9B2006E7C353FB5B5FDBC2A88347ED2C6F5B7A265D03AE34039"
+ + "6AAC0145CFCC93F8BDB219DDFF22A603B8856A5DC59B6FAB7F17C5660CF38670"
+ + "8794FC72F273ADEB7A4F316519794AED6F8AB61F95DFB360FAF18C6C8CABE471"
+ + "6E18FE215348C2E582171A57FC41146B16C4AFE429000024A634B61C0E5C90C6"
+ + "8D8818B0955B125A9B1DF47BBD18775710792E651083105C2900001C00004004"
+ + "406FA3C5685A16B9B72C7F2EEE9993462C619ABE2900001C00004005AF905A87"
+ + "0A32222AA284A7070585601208A282F0290000080000402E290000100000402F"
+ + "00020003000400050000000800004014";
+ private static final String SUCCESS_IKE_AUTH_RESP =
+ "46B8ECA1E0D72A18B45427679F9245D42E20232000000001000000EC240000D0"
+ + "0D06D37198F3F0962DE8170D66F1A9008267F98CDD956D984BDCED2FC7FAF84A"
+ + "A6664EF25049B46B93C9ED420488E0C172AA6635BF4011C49792EF2B88FE7190"
+ + "E8859FEEF51724FD20C46E7B9A9C3DC4708EF7005707A18AB747C903ABCEAC5C"
+ + "6ECF5A5FC13633DCE3844A920ED10EF202F115DBFBB5D6D2D7AB1F34EB08DE7C"
+ + "A54DCE0A3A582753345CA2D05A0EFDB9DC61E81B2483B7D13EEE0A815D37252C"
+ + "23D2F29E9C30658227D2BB0C9E1A481EAA80BC6BE9006BEDC13E925A755A0290"
+ + "AEC4164D29997F52ED7DCC2E";
+ private static final String SUCCESS_CREATE_CHILD_RESP =
+ "46B8ECA1E0D72A18B45427679F9245D42E20242000000002000000CC210000B0"
+ + "484565D4AF6546274674A8DE339E9C9584EE2326AB9260F41C4D0B6C5B02D1D"
+ + "2E8394E3CDE3094895F2ACCABCDCA8E82960E5196E9622BD13745FC8D6A2BED"
+ + "E561FF5D9975421BC463C959A3CBA3478256B6D278159D99B512DDF56AC1658"
+ + "63C65A986F395FE8B1476124B91F83FD7865304EB95B22CA4DD9601DA7A2533"
+ + "ABF4B36EB1B8CD09522F6A600032316C74E562E6756D9D49D945854E2ABDC4C"
+ + "3AF36305353D60D40B58BE44ABF82";
+ private static final String SUCCESS_DELETE_CHILD_RESP =
+ "46B8ECA1E0D72A18B45427679F9245D42E202520000000030000004C2A000030"
+ + "0C5CEB882DBCA65CE32F4C53909335F1365C91C555316C5E9D9FB553F7AA916"
+ + "EF3A1D93460B7FABAF0B4B854";
+ private static final String SUCCESS_DELETE_IKE_RESP =
+ "46B8ECA1E0D72A18B45427679F9245D42E202520000000040000004C00000030"
+ + "9352D71100777B00ABCC6BD7DBEA697827FFAAA48DF9A54D1D68161939F5DC8"
+ + "6743A7CEB2BE34AC00095A5B8";
+
+ private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
+ IkeSessionParams ikeParams =
+ new IkeSessionParams.Builder(sContext)
+ .setNetwork(mTunNetwork)
+ .setServerHostname(remoteAddress.getHostAddress())
+ .addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
+ .addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
+ .setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
+ .setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
+ .setAuthPsk(IKE_PSK)
+ .build();
+ return new IkeSession(
+ sContext,
+ ikeParams,
+ buildTunnelModeChildSessionParams(),
+ mUserCbExecutor,
+ mIkeSessionCallback,
+ mFirstChildSessionCallback);
+ }
+
+ @Test
+ public void testIkeSessionSetupAndChildSessionSetupWithTunnelMode() throws Exception {
+ if (!hasTunnelsFeature()) return;
+
+ // Open IKE Session
+ IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
+
+ // IKE INIT and IKE AUTH takes two exchanges. Message ID starts from 2
+ int expectedMsgId = 2;
+
+ verifyIkeSessionSetupBlocking();
+ verifyChildSessionSetupBlocking(
+ mFirstChildSessionCallback,
+ Arrays.asList(TUNNEL_MODE_INBOUND_TS),
+ Arrays.asList(TUNNEL_MODE_OUTBOUND_TS),
+ Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR));
+
+ IpSecTransformCallRecord firstTransformRecordA =
+ mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+ IpSecTransformCallRecord firstTransformRecordB =
+ mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
+ verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
+
+ // Open additional Child Session
+ TestChildSessionCallback additionalChildCb = new TestChildSessionCallback();
+ ikeSession.openChildSession(buildTunnelModeChildSessionParams(), additionalChildCb);
+ mTunUtils.awaitReqAndInjectResp(
+ IKE_DETERMINISTIC_INITIATOR_SPI,
+ expectedMsgId++,
+ true /* expectedUseEncap */,
+ SUCCESS_CREATE_CHILD_RESP);
+
+ // Verify opening additional Child Session
+ verifyChildSessionSetupBlocking(
+ additionalChildCb,
+ Arrays.asList(TUNNEL_MODE_INBOUND_TS),
+ Arrays.asList(TUNNEL_MODE_OUTBOUND_TS),
+ new ArrayList<LinkAddress>());
+ IpSecTransformCallRecord additionalTransformRecordA =
+ additionalChildCb.awaitNextCreatedIpSecTransform();
+ IpSecTransformCallRecord additionalTransformRecordB =
+ additionalChildCb.awaitNextCreatedIpSecTransform();
+ verifyCreateIpSecTransformPair(additionalTransformRecordA, additionalTransformRecordB);
+
+ // Close additional Child Session
+ ikeSession.closeChildSession(additionalChildCb);
+ mTunUtils.awaitReqAndInjectResp(
+ IKE_DETERMINISTIC_INITIATOR_SPI,
+ expectedMsgId++,
+ true /* expectedUseEncap */,
+ SUCCESS_DELETE_CHILD_RESP);
+
+ verifyDeleteIpSecTransformPair(
+ additionalChildCb, additionalTransformRecordA, additionalTransformRecordB);
+ additionalChildCb.awaitOnClosed();
+
+ // Close IKE Session
+ ikeSession.close();
+ performCloseIkeBlocking(expectedMsgId++, SUCCESS_DELETE_IKE_RESP);
+ verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
+ }
+
+ @Test
+ public void testIkeSessionKillWithTunnelMode() throws Exception {
+ if (!hasTunnelsFeature()) return;
+
+ // Open IKE Session
+ IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
+
+ ikeSession.kill();
+ mFirstChildSessionCallback.awaitOnClosed();
+ mIkeSessionCallback.awaitOnClosed();
+ }
+
+ @Test
+ public void testIkeInitFail() throws Exception {
+ final String ikeInitFailRespHex =
+ "46B8ECA1E0D72A180000000000000000292022200000000000000024000000080000000E";
+
+ // Open IKE Session
+ IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ int expectedMsgId = 0;
+ mTunUtils.awaitReqAndInjectResp(
+ IKE_DETERMINISTIC_INITIATOR_SPI,
+ expectedMsgId++,
+ false /* expectedUseEncap */,
+ ikeInitFailRespHex);
+
+ mFirstChildSessionCallback.awaitOnClosed();
+
+ IkeException exception = mIkeSessionCallback.awaitOnClosedException();
+ assertNotNull(exception);
+ assertTrue(exception instanceof IkeProtocolException);
+ IkeProtocolException protocolException = (IkeProtocolException) exception;
+ assertEquals(ERROR_TYPE_NO_PROPOSAL_CHOSEN, protocolException.getErrorType());
+ assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
+ }
+
+ // TODO(b/155821007): Verify rekey process and handling IKE_AUTH failure
+
+ // TODO(b/155821007): Test creating transport mode Child SA
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
new file mode 100644
index 0000000..ade9813
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.net.ipsec.ike.cts;
+
+import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
+import android.net.IpSecManager;
+import android.net.IpSecTransform;
+import android.net.LinkAddress;
+import android.net.Network;
+import android.net.TestNetworkInterface;
+import android.net.TestNetworkManager;
+import android.net.annotations.PolicyDirection;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.testutils.ArrayTrackRecord;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Package private base class for testing IkeSessionParams and IKE exchanges.
+ *
+ * <p>Subclasses MUST explicitly call #setUpTestNetwork and #tearDownTestNetwork to be able to use
+ * the test network
+ *
+ * <p>All IKE Sessions running in test mode will generate SPIs deterministically. That is to say
+ * each IKE Session will always generate the same IKE INIT SPI and test vectors are generated based
+ * on this deterministic IKE SPI. Each test will use different local and remote addresses to avoid
+ * the case that the next test try to allocate the same SPI before the previous test has released
+ * it, since SPI resources are not released in testing thread. Similarly, each test MUST use
+ * different Network instances to avoid sharing the same IkeSocket and hitting IKE SPI collision.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
+abstract class IkeSessionTestBase extends IkeTestBase {
+ // Package-wide common expected results that will be shared by all IKE/Child SA creation tests
+ static final String EXPECTED_REMOTE_APP_VERSION_EMPTY = "";
+ static final byte[] EXPECTED_PROTOCOL_ERROR_DATA_NONE = new byte[0];
+ static final InetAddress EXPECTED_INTERNAL_ADDR =
+ InetAddresses.parseNumericAddress("198.51.100.10");
+ static final LinkAddress EXPECTED_INTERNAL_LINK_ADDR =
+ new LinkAddress(EXPECTED_INTERNAL_ADDR, IP4_PREFIX_LEN);
+
+ static final IkeTrafficSelector TUNNEL_MODE_INBOUND_TS =
+ new IkeTrafficSelector(
+ MIN_PORT, MAX_PORT, EXPECTED_INTERNAL_ADDR, EXPECTED_INTERNAL_ADDR);
+ static final IkeTrafficSelector TUNNEL_MODE_OUTBOUND_TS = DEFAULT_V4_TS;
+
+ static final long IKE_DETERMINISTIC_INITIATOR_SPI = Long.parseLong("46B8ECA1E0D72A18", 16);
+
+ // Static state to reduce setup/teardown
+ static Context sContext = InstrumentationRegistry.getContext();
+ static ConnectivityManager sCM =
+ (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ static TestNetworkManager sTNM;
+
+ private static final int TIMEOUT_MS = 500;
+
+ // Constants to be used for providing different IP addresses for each tests
+ private static final byte IP_ADDR_LAST_BYTE_MAX = (byte) 100;
+ private static final byte[] INITIAL_AVAILABLE_IP4_ADDR_LOCAL =
+ InetAddresses.parseNumericAddress("192.0.2.1").getAddress();
+ private static final byte[] INITIAL_AVAILABLE_IP4_ADDR_REMOTE =
+ InetAddresses.parseNumericAddress("198.51.100.1").getAddress();
+ private static final byte[] NEXT_AVAILABLE_IP4_ADDR_LOCAL = INITIAL_AVAILABLE_IP4_ADDR_LOCAL;
+ private static final byte[] NEXT_AVAILABLE_IP4_ADDR_REMOTE = INITIAL_AVAILABLE_IP4_ADDR_REMOTE;
+
+ ParcelFileDescriptor mTunFd;
+ TestNetworkCallback mTunNetworkCallback;
+ Network mTunNetwork;
+ IkeTunUtils mTunUtils;
+
+ InetAddress mLocalAddress;
+ InetAddress mRemoteAddress;
+
+ Executor mUserCbExecutor;
+ TestIkeSessionCallback mIkeSessionCallback;
+ TestChildSessionCallback mFirstChildSessionCallback;
+
+ // This method is guaranteed to run in subclasses and will run before subclasses' @BeforeClass
+ // methods.
+ @BeforeClass
+ public static void setUpPermissionBeforeClass() throws Exception {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity();
+ sTNM = sContext.getSystemService(TestNetworkManager.class);
+
+ // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
+ // a standard permission is insufficient. So we shell out the appop, to give us the
+ // right appop permissions.
+ setAppOp(OP_MANAGE_IPSEC_TUNNELS, true);
+ }
+
+ // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
+ // methods.
+ @AfterClass
+ public static void tearDownPermissionAfterClass() throws Exception {
+ setAppOp(OP_MANAGE_IPSEC_TUNNELS, false);
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mLocalAddress = getNextAvailableIpv4AddressLocal();
+ mRemoteAddress = getNextAvailableIpv4AddressRemote();
+ setUpTestNetwork(mLocalAddress);
+
+ mUserCbExecutor = Executors.newSingleThreadExecutor();
+ mIkeSessionCallback = new TestIkeSessionCallback();
+ mFirstChildSessionCallback = new TestChildSessionCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ tearDownTestNetwork();
+ }
+
+ void setUpTestNetwork(InetAddress localAddr) throws Exception {
+ int prefixLen = localAddr instanceof Inet4Address ? IP4_PREFIX_LEN : IP4_PREFIX_LEN;
+
+ TestNetworkInterface testIface =
+ sTNM.createTunInterface(new LinkAddress[] {new LinkAddress(localAddr, prefixLen)});
+
+ mTunFd = testIface.getFileDescriptor();
+ mTunNetworkCallback =
+ TestNetworkUtils.setupAndGetTestNetwork(
+ sCM, sTNM, testIface.getInterfaceName(), new Binder());
+ mTunNetwork = mTunNetworkCallback.getNetworkBlocking();
+ mTunUtils = new IkeTunUtils(mTunFd);
+ }
+
+ void tearDownTestNetwork() throws Exception {
+ sCM.unregisterNetworkCallback(mTunNetworkCallback);
+
+ sTNM.teardownTestNetwork(mTunNetwork);
+ mTunFd.close();
+ }
+
+ private static void setAppOp(int appop, boolean allow) {
+ String opName = AppOpsManager.opToName(appop);
+ for (String pkg : new String[] {"com.android.shell", sContext.getPackageName()}) {
+ String cmd =
+ String.format(
+ "appops set %s %s %s",
+ pkg, // Package name
+ opName, // Appop
+ (allow ? "allow" : "deny")); // Action
+
+ SystemUtil.runShellCommand(cmd);
+ }
+ }
+
+ Inet4Address getNextAvailableIpv4AddressLocal() throws Exception {
+ return (Inet4Address)
+ getNextAvailableAddress(
+ NEXT_AVAILABLE_IP4_ADDR_LOCAL,
+ INITIAL_AVAILABLE_IP4_ADDR_LOCAL,
+ false /* isIp6 */);
+ }
+
+ Inet4Address getNextAvailableIpv4AddressRemote() throws Exception {
+ return (Inet4Address)
+ getNextAvailableAddress(
+ NEXT_AVAILABLE_IP4_ADDR_REMOTE,
+ INITIAL_AVAILABLE_IP4_ADDR_REMOTE,
+ false /* isIp6 */);
+ }
+
+ InetAddress getNextAvailableAddress(
+ byte[] nextAddressBytes, byte[] initialAddressBytes, boolean isIp6) throws Exception {
+ int addressLen = isIp6 ? IP6_ADDRESS_LEN : IP4_ADDRESS_LEN;
+
+ synchronized (nextAddressBytes) {
+ if (nextAddressBytes[addressLen - 1] == IP_ADDR_LAST_BYTE_MAX) {
+ resetNextAvailableAddress(nextAddressBytes, initialAddressBytes);
+ }
+
+ InetAddress address = InetAddress.getByAddress(nextAddressBytes);
+ nextAddressBytes[addressLen - 1]++;
+ return address;
+ }
+ }
+
+ private void resetNextAvailableAddress(byte[] nextAddressBytes, byte[] initialAddressBytes) {
+ synchronized (nextAddressBytes) {
+ System.arraycopy(
+ nextAddressBytes, 0, initialAddressBytes, 0, initialAddressBytes.length);
+ }
+ }
+
+ TunnelModeChildSessionParams buildTunnelModeChildSessionParams() {
+ return new TunnelModeChildSessionParams.Builder()
+ .addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
+ .addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
+ .addInternalAddressRequest(AF_INET)
+ .addInternalAddressRequest(AF_INET6)
+ .build();
+ }
+
+ void performSetupIkeAndFirstChildBlocking(String ikeInitRespHex, String ikeAuthRespHex)
+ throws Exception {
+ mTunUtils.awaitReqAndInjectResp(
+ IKE_DETERMINISTIC_INITIATOR_SPI,
+ 0 /* expectedMsgId */,
+ false /* expectedUseEncap */,
+ ikeInitRespHex);
+
+ mTunUtils.awaitReqAndInjectResp(
+ IKE_DETERMINISTIC_INITIATOR_SPI,
+ 1 /* expectedMsgId */,
+ true /* expectedUseEncap */,
+ ikeAuthRespHex);
+ }
+
+ void performSetupIkeAndFirstChildBlocking(
+ String ikeInitRespHex, int expectedAuthReqPktCnt, String... ikeAuthRespPktHex)
+ throws Exception {
+ // TODO: Implemented in followup CL (aosp/1308675) to support awaiting multiple IKE AUTH
+ // request fragments and injecting multiple IKE AUTH response fragments
+ }
+
+ void performCloseIkeBlocking(int expectedMsgId, String deleteIkeRespHex) throws Exception {
+ mTunUtils.awaitReqAndInjectResp(
+ IKE_DETERMINISTIC_INITIATOR_SPI,
+ expectedMsgId,
+ true /* expectedUseEncap */,
+ deleteIkeRespHex);
+ }
+
+ /** Testing callback that allows caller to block current thread until a method get called */
+ static class TestIkeSessionCallback implements IkeSessionCallback {
+ private CompletableFuture<IkeSessionConfiguration> mFutureIkeConfig =
+ new CompletableFuture<>();
+ private CompletableFuture<Boolean> mFutureOnClosedCall = new CompletableFuture<>();
+ private CompletableFuture<IkeException> mFutureOnClosedException =
+ new CompletableFuture<>();
+
+ private int mOnErrorExceptionsCount = 0;
+ private ArrayTrackRecord<IkeProtocolException> mOnErrorExceptionsTrackRecord =
+ new ArrayTrackRecord<>();
+
+ @Override
+ public void onOpened(@NonNull IkeSessionConfiguration sessionConfiguration) {
+ mFutureIkeConfig.complete(sessionConfiguration);
+ }
+
+ @Override
+ public void onClosed() {
+ mFutureOnClosedCall.complete(true /* unused */);
+ }
+
+ @Override
+ public void onClosedExceptionally(@NonNull IkeException exception) {
+ mFutureOnClosedException.complete(exception);
+ }
+
+ @Override
+ public void onError(@NonNull IkeProtocolException exception) {
+ mOnErrorExceptionsTrackRecord.add(exception);
+ }
+
+ public IkeSessionConfiguration awaitIkeConfig() throws Exception {
+ return mFutureIkeConfig.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ public IkeException awaitOnClosedException() throws Exception {
+ return mFutureOnClosedException.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ public IkeProtocolException awaitNextOnErrorException() {
+ return mOnErrorExceptionsTrackRecord.poll(
+ (long) TIMEOUT_MS,
+ mOnErrorExceptionsCount++,
+ (transform) -> {
+ return true;
+ });
+ }
+
+ public void awaitOnClosed() throws Exception {
+ mFutureOnClosedCall.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /** Testing callback that allows caller to block current thread until a method get called */
+ static class TestChildSessionCallback implements ChildSessionCallback {
+ private CompletableFuture<ChildSessionConfiguration> mFutureChildConfig =
+ new CompletableFuture<>();
+ private CompletableFuture<Boolean> mFutureOnClosedCall = new CompletableFuture<>();
+ private CompletableFuture<IkeException> mFutureOnClosedException =
+ new CompletableFuture<>();
+
+ private int mCreatedIpSecTransformCount = 0;
+ private int mDeletedIpSecTransformCount = 0;
+ private ArrayTrackRecord<IpSecTransformCallRecord> mCreatedIpSecTransformsTrackRecord =
+ new ArrayTrackRecord<>();
+ private ArrayTrackRecord<IpSecTransformCallRecord> mDeletedIpSecTransformsTrackRecord =
+ new ArrayTrackRecord<>();
+
+ @Override
+ public void onOpened(@NonNull ChildSessionConfiguration sessionConfiguration) {
+ mFutureChildConfig.complete(sessionConfiguration);
+ }
+
+ @Override
+ public void onClosed() {
+ mFutureOnClosedCall.complete(true /* unused */);
+ }
+
+ @Override
+ public void onClosedExceptionally(@NonNull IkeException exception) {
+ mFutureOnClosedException.complete(exception);
+ }
+
+ @Override
+ public void onIpSecTransformCreated(@NonNull IpSecTransform ipSecTransform, int direction) {
+ mCreatedIpSecTransformsTrackRecord.add(
+ new IpSecTransformCallRecord(ipSecTransform, direction));
+ }
+
+ @Override
+ public void onIpSecTransformDeleted(@NonNull IpSecTransform ipSecTransform, int direction) {
+ mDeletedIpSecTransformsTrackRecord.add(
+ new IpSecTransformCallRecord(ipSecTransform, direction));
+ }
+
+ public ChildSessionConfiguration awaitChildConfig() throws Exception {
+ return mFutureChildConfig.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ public IkeException awaitOnClosedException() throws Exception {
+ return mFutureOnClosedException.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ public IpSecTransformCallRecord awaitNextCreatedIpSecTransform() {
+ return mCreatedIpSecTransformsTrackRecord.poll(
+ (long) TIMEOUT_MS,
+ mCreatedIpSecTransformCount++,
+ (transform) -> {
+ return true;
+ });
+ }
+
+ public IpSecTransformCallRecord awaitNextDeletedIpSecTransform() {
+ return mDeletedIpSecTransformsTrackRecord.poll(
+ (long) TIMEOUT_MS,
+ mDeletedIpSecTransformCount++,
+ (transform) -> {
+ return true;
+ });
+ }
+
+ public void awaitOnClosed() throws Exception {
+ mFutureOnClosedCall.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * This class represents a created or deleted IpSecTransfrom that is provided by
+ * ChildSessionCallback
+ */
+ static class IpSecTransformCallRecord {
+ public final IpSecTransform ipSecTransform;
+ public final int direction;
+
+ IpSecTransformCallRecord(IpSecTransform ipSecTransform, @PolicyDirection int direction) {
+ this.ipSecTransform = ipSecTransform;
+ this.direction = direction;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ipSecTransform, direction);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof IpSecTransformCallRecord)) return false;
+
+ IpSecTransformCallRecord record = (IpSecTransformCallRecord) o;
+ return ipSecTransform.equals(record.ipSecTransform) && direction == record.direction;
+ }
+ }
+
+ void verifyIkeSessionSetupBlocking() throws Exception {
+ IkeSessionConfiguration ikeConfig = mIkeSessionCallback.awaitIkeConfig();
+ assertNotNull(ikeConfig);
+ assertEquals(EXPECTED_REMOTE_APP_VERSION_EMPTY, ikeConfig.getRemoteApplicationVersion());
+ assertTrue(ikeConfig.getRemoteVendorIds().isEmpty());
+ assertTrue(ikeConfig.getPcscfServers().isEmpty());
+ assertTrue(ikeConfig.isIkeExtensionEnabled(EXTENSION_TYPE_FRAGMENTATION));
+
+ IkeSessionConnectionInfo ikeConnectInfo = ikeConfig.getIkeSessionConnectionInfo();
+ assertNotNull(ikeConnectInfo);
+ assertEquals(mLocalAddress, ikeConnectInfo.getLocalAddress());
+ assertEquals(mRemoteAddress, ikeConnectInfo.getRemoteAddress());
+ assertEquals(mTunNetwork, ikeConnectInfo.getNetwork());
+ }
+
+ void verifyChildSessionSetupBlocking(
+ TestChildSessionCallback childCallback,
+ List<IkeTrafficSelector> expectedInboundTs,
+ List<IkeTrafficSelector> expectedOutboundTs,
+ List<LinkAddress> expectedInternalAddresses)
+ throws Exception {
+ ChildSessionConfiguration childConfig = childCallback.awaitChildConfig();
+ assertNotNull(childConfig);
+ assertEquals(expectedInboundTs, childConfig.getInboundTrafficSelectors());
+ assertEquals(expectedOutboundTs, childConfig.getOutboundTrafficSelectors());
+ assertEquals(expectedInternalAddresses, childConfig.getInternalAddresses());
+ assertTrue(childConfig.getInternalSubnets().isEmpty());
+ assertTrue(childConfig.getInternalDnsServers().isEmpty());
+ assertTrue(childConfig.getInternalDhcpServers().isEmpty());
+ }
+
+ void verifyCloseIkeAndChildBlocking(
+ IpSecTransformCallRecord expectedTransformRecordA,
+ IpSecTransformCallRecord expectedTransformRecordB)
+ throws Exception {
+ verifyDeleteIpSecTransformPair(
+ mFirstChildSessionCallback, expectedTransformRecordA, expectedTransformRecordB);
+ mFirstChildSessionCallback.awaitOnClosed();
+ mIkeSessionCallback.awaitOnClosed();
+ }
+
+ static void verifyCreateIpSecTransformPair(
+ IpSecTransformCallRecord transformRecordA, IpSecTransformCallRecord transformRecordB) {
+ IpSecTransform transformA = transformRecordA.ipSecTransform;
+ IpSecTransform transformB = transformRecordB.ipSecTransform;
+
+ assertNotNull(transformA);
+ assertNotNull(transformB);
+
+ Set<Integer> expectedDirections = new HashSet<>();
+ expectedDirections.add(IpSecManager.DIRECTION_IN);
+ expectedDirections.add(IpSecManager.DIRECTION_OUT);
+
+ Set<Integer> resultDirections = new HashSet<>();
+ resultDirections.add(transformRecordA.direction);
+ resultDirections.add(transformRecordB.direction);
+
+ assertEquals(expectedDirections, resultDirections);
+ }
+
+ static void verifyDeleteIpSecTransformPair(
+ TestChildSessionCallback childCb,
+ IpSecTransformCallRecord expectedTransformRecordA,
+ IpSecTransformCallRecord expectedTransformRecordB) {
+ Set<IpSecTransformCallRecord> expectedTransforms = new HashSet<>();
+ expectedTransforms.add(expectedTransformRecordA);
+ expectedTransforms.add(expectedTransformRecordB);
+
+ Set<IpSecTransformCallRecord> resultTransforms = new HashSet<>();
+ resultTransforms.add(childCb.awaitNextDeletedIpSecTransform());
+ resultTransforms.add(childCb.awaitNextDeletedIpSecTransform());
+
+ assertEquals(expectedTransforms, resultTransforms);
+ }
+
+ /** Package private method to check if device has IPsec tunnels feature */
+ static boolean hasTunnelsFeature() {
+ return sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS);
+ }
+
+ // TODO(b/148689509): Verify IKE Session setup using EAP and digital-signature-based auth
+
+ // TODO(b/148689509): Verify hostname based creation
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
index bc2bec6..f07c710 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTestBase.java
@@ -31,13 +31,15 @@
/** Shared parameters and util methods for testing different components of IKE */
abstract class IkeTestBase {
- private static final int MIN_PORT = 0;
- private static final int MAX_PORT = 65535;
+ static final int MIN_PORT = 0;
+ static final int MAX_PORT = 65535;
private static final int INBOUND_TS_START_PORT = MIN_PORT;
private static final int INBOUND_TS_END_PORT = 65520;
private static final int OUTBOUND_TS_START_PORT = 16;
private static final int OUTBOUND_TS_END_PORT = MAX_PORT;
+ static final int IP4_ADDRESS_LEN = 4;
+ static final int IP6_ADDRESS_LEN = 16;
static final int IP4_PREFIX_LEN = 32;
static final int IP6_PREFIX_LEN = 64;
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
new file mode 100644
index 0000000..2bff63a
--- /dev/null
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeTunUtils.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.ipsec.ike.cts;
+
+import static android.net.ipsec.ike.cts.PacketUtils.BytePayload;
+import static android.net.ipsec.ike.cts.PacketUtils.IP4_HDRLEN;
+import static android.net.ipsec.ike.cts.PacketUtils.IP6_HDRLEN;
+import static android.net.ipsec.ike.cts.PacketUtils.Ip4Header;
+import static android.net.ipsec.ike.cts.PacketUtils.Ip6Header;
+import static android.net.ipsec.ike.cts.PacketUtils.IpHeader;
+import static android.net.ipsec.ike.cts.PacketUtils.Payload;
+import static android.net.ipsec.ike.cts.PacketUtils.UDP_HDRLEN;
+import static android.net.ipsec.ike.cts.PacketUtils.UdpHeader;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import static com.android.internal.util.HexDump.hexStringToByteArray;
+
+import static org.junit.Assert.fail;
+
+import android.os.ParcelFileDescriptor;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+public class IkeTunUtils extends TunUtils {
+ private static final int PORT_LEN = 2;
+
+ private static final int NON_ESP_MARKER_LEN = 4;
+ private static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN];
+
+ private static final int IKE_HEADER_LEN = 28;
+ private static final int IKE_INIT_SPI_OFFSET = 0;
+ private static final int IKE_IS_RESP_BYTE_OFFSET = 19;
+ private static final int IKE_MSG_ID_OFFSET = 20;
+
+ public IkeTunUtils(ParcelFileDescriptor tunFd) {
+ super(tunFd);
+ }
+
+ /**
+ * Await the expected IKE request and inject an IKE response.
+ *
+ * @param ikeRespDataHex IKE response hex without IP/UDP headers or NON ESP MARKER.
+ */
+ public byte[] awaitReqAndInjectResp(
+ long expectedInitIkeSpi,
+ int expectedMsgId,
+ boolean expectedUseEncap,
+ String ikeRespDataHex)
+ throws Exception {
+ byte[] request =
+ awaitIkePacket(
+ (pkt) -> {
+ return isExpectedIkePkt(
+ pkt,
+ expectedInitIkeSpi,
+ expectedMsgId,
+ false /* expectedResp */,
+ expectedUseEncap);
+ });
+
+ // Build response header by flipping address and port
+ InetAddress srcAddr = getAddress(request, false /* shouldGetSource */);
+ InetAddress dstAddr = getAddress(request, true /* shouldGetSource */);
+ int srcPort = getPort(request, false /* shouldGetSource */);
+ int dstPort = getPort(request, true /* shouldGetSource */);
+
+ byte[] response =
+ buildIkePacket(
+ srcAddr,
+ dstAddr,
+ srcPort,
+ dstPort,
+ expectedUseEncap,
+ hexStringToByteArray(ikeRespDataHex));
+ injectPacket(response);
+ return request;
+ }
+
+ // TODO: Implemented in followup CL (aosp/1308675) to support awaiting multiple
+ // request fragments and injecting multiple response fragments
+
+ private byte[] awaitIkePacket(Predicate<byte[]> pktVerifier) throws Exception {
+ long endTime = System.currentTimeMillis() + TIMEOUT;
+ int startIndex = 0;
+ synchronized (mPackets) {
+ while (System.currentTimeMillis() < endTime) {
+ byte[] ikePkt = getFirstMatchingPacket(pktVerifier, startIndex);
+ if (ikePkt != null) {
+ return ikePkt; // We've found the packet we're looking for.
+ }
+
+ startIndex = mPackets.size();
+
+ // Try to prevent waiting too long. If waitTimeout <= 0, we've already hit timeout
+ long waitTimeout = endTime - System.currentTimeMillis();
+ if (waitTimeout > 0) {
+ mPackets.wait(waitTimeout);
+ }
+ }
+
+ fail("No matching packet found");
+ }
+
+ throw new IllegalStateException(
+ "Hit an impossible case where fail() didn't throw an exception");
+ }
+
+ private static boolean isExpectedIkePkt(
+ byte[] pkt,
+ long expectedInitIkeSpi,
+ int expectedMsgId,
+ boolean expectedResp,
+ boolean expectedUseEncap) {
+ int ipProtocolOffset = 0;
+ int ikeOffset = 0;
+ if (isIpv6(pkt)) {
+ // IPv6 UDP expectedUseEncap not supported by kernels; assume non-expectedUseEncap.
+ ipProtocolOffset = IP6_PROTO_OFFSET;
+ ikeOffset = IP6_HDRLEN + UDP_HDRLEN;
+ } else {
+ // Use default IPv4 header length (assuming no options)
+ ipProtocolOffset = IP4_PROTO_OFFSET;
+ ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
+
+ if (expectedUseEncap) {
+ if (hasNonEspMarker(pkt)) {
+ ikeOffset += NON_ESP_MARKER_LEN;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return pkt[ipProtocolOffset] == IPPROTO_UDP
+ && isExpectedSpiAndMsgId(
+ pkt, ikeOffset, expectedInitIkeSpi, expectedMsgId, expectedResp);
+ }
+
+ private static boolean hasNonEspMarker(byte[] pkt) {
+ ByteBuffer buffer = ByteBuffer.wrap(pkt);
+ int ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
+ if (buffer.remaining() < ikeOffset) return false;
+
+ buffer.get(new byte[ikeOffset]); // Skip IP and UDP header
+ byte[] nonEspMarker = new byte[NON_ESP_MARKER_LEN];
+ if (buffer.remaining() < NON_ESP_MARKER_LEN) return false;
+
+ buffer.get(nonEspMarker);
+ return Arrays.equals(NON_ESP_MARKER, nonEspMarker);
+ }
+
+ private static boolean isExpectedSpiAndMsgId(
+ byte[] pkt,
+ int ikeOffset,
+ long expectedInitIkeSpi,
+ int expectedMsgId,
+ boolean expectedResp) {
+ if (pkt.length <= ikeOffset + IKE_HEADER_LEN) return false;
+
+ ByteBuffer buffer = ByteBuffer.wrap(pkt);
+ buffer.get(new byte[ikeOffset]); // Skip IP, UDP header (and NON_ESP_MARKER)
+
+ // Check message ID.
+ buffer.get(new byte[IKE_MSG_ID_OFFSET]);
+ int msgId = buffer.getInt();
+ return expectedMsgId == msgId;
+
+ // TODO: Check SPI and packet direction
+ }
+
+ private static InetAddress getAddress(byte[] pkt, boolean shouldGetSource) throws Exception {
+ int ipLen = isIpv6(pkt) ? IP6_ADDR_LEN : IP4_ADDR_LEN;
+ int srcIpOffset = isIpv6(pkt) ? IP6_ADDR_OFFSET : IP4_ADDR_OFFSET;
+ int ipOffset = shouldGetSource ? srcIpOffset : srcIpOffset + ipLen;
+
+ ByteBuffer buffer = ByteBuffer.wrap(pkt);
+ buffer.get(new byte[ipOffset]);
+ byte[] ipAddrBytes = new byte[ipLen];
+ buffer.get(ipAddrBytes);
+ return InetAddress.getByAddress(ipAddrBytes);
+ }
+
+ private static int getPort(byte[] pkt, boolean shouldGetSource) {
+ ByteBuffer buffer = ByteBuffer.wrap(pkt);
+ int srcPortOffset = isIpv6(pkt) ? IP6_HDRLEN : IP4_HDRLEN;
+ int portOffset = shouldGetSource ? srcPortOffset : srcPortOffset + PORT_LEN;
+
+ buffer.get(new byte[portOffset]);
+ return Short.toUnsignedInt(buffer.getShort());
+ }
+
+ private static byte[] buildIkePacket(
+ InetAddress srcAddr,
+ InetAddress dstAddr,
+ int srcPort,
+ int dstPort,
+ boolean useEncap,
+ byte[] ikePacket)
+ throws Exception {
+ if (useEncap) {
+ ByteBuffer buffer = ByteBuffer.allocate(NON_ESP_MARKER_LEN + ikePacket.length);
+ buffer.put(NON_ESP_MARKER);
+ buffer.put(ikePacket);
+ ikePacket = buffer.array();
+ }
+
+ UdpHeader udpPkt = new UdpHeader(srcPort, dstPort, new BytePayload(ikePacket));
+ IpHeader ipPkt = getIpHeader(udpPkt.getProtocolId(), srcAddr, dstAddr, udpPkt);
+ return ipPkt.getPacketBytes();
+ }
+
+ private static IpHeader getIpHeader(
+ int protocol, InetAddress src, InetAddress dst, Payload payload) {
+ if ((src instanceof Inet6Address) != (dst instanceof Inet6Address)) {
+ throw new IllegalArgumentException("Invalid src/dst address combination");
+ }
+
+ if (src instanceof Inet6Address) {
+ return new Ip6Header(protocol, (Inet6Address) src, (Inet6Address) dst, payload);
+ } else {
+ return new Ip4Header(protocol, (Inet4Address) src, (Inet4Address) dst, payload);
+ }
+ }
+}
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
index 71450ea..cb1d826 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
@@ -47,18 +47,18 @@
private static final String TAG = TunUtils.class.getSimpleName();
private static final int DATA_BUFFER_LEN = 4096;
- private static final int TIMEOUT = 100;
+ static final int TIMEOUT = 100;
- private static final int IP4_PROTO_OFFSET = 9;
- private static final int IP6_PROTO_OFFSET = 6;
+ static final int IP4_PROTO_OFFSET = 9;
+ static final int IP6_PROTO_OFFSET = 6;
- private static final int IP4_ADDR_OFFSET = 12;
- private static final int IP4_ADDR_LEN = 4;
- private static final int IP6_ADDR_OFFSET = 8;
- private static final int IP6_ADDR_LEN = 16;
+ static final int IP4_ADDR_OFFSET = 12;
+ static final int IP4_ADDR_LEN = 4;
+ static final int IP6_ADDR_OFFSET = 8;
+ static final int IP6_ADDR_LEN = 16;
+ final List<byte[]> mPackets = new ArrayList<>();
private final ParcelFileDescriptor mTunFd;
- private final List<byte[]> mPackets = new ArrayList<>();
private final Thread mReaderThread;
public TunUtils(ParcelFileDescriptor tunFd) {
@@ -107,7 +107,7 @@
return Arrays.copyOf(inBytes, bytesRead);
}
- private byte[] getFirstMatchingPacket(Predicate<byte[]> verifier, int startIndex) {
+ byte[] getFirstMatchingPacket(Predicate<byte[]> verifier, int startIndex) {
synchronized (mPackets) {
for (int i = startIndex; i < mPackets.size(); i++) {
byte[] pkt = mPackets.get(i);
@@ -198,7 +198,7 @@
}
}
- private static boolean isIpv6(byte[] pkt) {
+ static boolean isIpv6(byte[] pkt) {
// First nibble shows IP version. 0x60 for IPv6
return (pkt[0] & (byte) 0xF0) == (byte) 0x60;
}
diff --git a/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt b/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt
new file mode 100644
index 0000000..0816aba
--- /dev/null
+++ b/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts
+
+import android.Manifest.permission.CONNECTIVITY_INTERNAL
+import android.Manifest.permission.NETWORK_SETTINGS
+import android.Manifest.permission.READ_DEVICE_CONFIG
+import android.Manifest.permission.WRITE_DEVICE_CONFIG
+import android.content.pm.PackageManager.FEATURE_TELEPHONY
+import android.content.pm.PackageManager.FEATURE_WIFI
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.NetworkCallback
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkRequest
+import android.net.Uri
+import android.net.cts.util.CtsNetUtils
+import android.net.wifi.WifiManager
+import android.os.Build
+import android.os.ConditionVariable
+import android.platform.test.annotations.AppModeFull
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import android.text.TextUtils
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.SystemUtil
+import fi.iki.elonen.NanoHTTPD
+import fi.iki.elonen.NanoHTTPD.Response.IStatus
+import fi.iki.elonen.NanoHTTPD.Response.Status
+import junit.framework.AssertionFailedError
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.runner.RunWith
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import kotlin.test.Test
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+
+private const val TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING = "test_captive_portal_https_url"
+private const val TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING = "test_captive_portal_http_url"
+private const val TEST_URL_EXPIRATION_TIME = "test_url_expiration_time"
+
+private const val TEST_HTTPS_URL_PATH = "https_path"
+private const val TEST_HTTP_URL_PATH = "http_path"
+private const val TEST_PORTAL_URL_PATH = "portal_path"
+
+private const val LOCALHOST_HOSTNAME = "localhost"
+
+// Re-connecting to the AP, obtaining an IP address, revalidating can take a long time
+private const val WIFI_CONNECT_TIMEOUT_MS = 120_000L
+private const val TEST_TIMEOUT_MS = 10_000L
+
+private fun <T> CompletableFuture<T>.assertGet(timeoutMs: Long, message: String): T {
+ try {
+ return get(timeoutMs, TimeUnit.MILLISECONDS)
+ } catch (e: TimeoutException) {
+ throw AssertionFailedError(message)
+ }
+}
+
+@AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
+@RunWith(AndroidJUnit4::class)
+class CaptivePortalTest {
+ private val context: android.content.Context by lazy { getInstrumentation().context }
+ private val wm by lazy { context.getSystemService(WifiManager::class.java) }
+ private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
+ private val pm by lazy { context.packageManager }
+ private val utils by lazy { CtsNetUtils(context) }
+
+ private val server = HttpServer()
+
+ @Before
+ fun setUp() {
+ doAsShell(READ_DEVICE_CONFIG) {
+ // Verify that the test URLs are not normally set on the device, but do not fail if the
+ // test URLs are set to what this test uses (URLs on localhost), in case the test was
+ // interrupted manually and rerun.
+ assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING)
+ assertEmptyOrLocalhostUrl(TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING)
+ }
+ clearTestUrls()
+ server.start()
+ }
+
+ @After
+ fun tearDown() {
+ clearTestUrls()
+ server.stop()
+ }
+
+ private fun assertEmptyOrLocalhostUrl(urlKey: String) {
+ val url = DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, urlKey)
+ assertTrue(TextUtils.isEmpty(url) || LOCALHOST_HOSTNAME == Uri.parse(url).host,
+ "$urlKey must not be set in production scenarios (current value: $url)")
+ }
+
+ private fun clearTestUrls() {
+ setHttpsUrl(null)
+ setHttpUrl(null)
+ setUrlExpiration(null)
+ }
+
+ @Test
+ fun testCaptivePortalIsNotDefaultNetwork() {
+ assumeTrue(pm.hasSystemFeature(FEATURE_TELEPHONY))
+ assumeTrue(pm.hasSystemFeature(FEATURE_WIFI))
+ utils.connectToWifi()
+ utils.connectToCell()
+
+ // Have network validation use a local server that serves a HTTPS error / HTTP redirect
+ server.addResponse(TEST_PORTAL_URL_PATH, Status.OK,
+ content = "Test captive portal content")
+ server.addResponse(TEST_HTTPS_URL_PATH, Status.INTERNAL_ERROR)
+ server.addResponse(TEST_HTTP_URL_PATH, Status.REDIRECT,
+ locationHeader = server.makeUrl(TEST_PORTAL_URL_PATH))
+ setHttpsUrl(server.makeUrl(TEST_HTTPS_URL_PATH))
+ setHttpUrl(server.makeUrl(TEST_HTTP_URL_PATH))
+ // URL expiration needs to be in the next 10 minutes
+ setUrlExpiration(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(9))
+
+ // Expect the portal content to be fetched at some point after detecting the portal.
+ // Some implementations may fetch the URL before startCaptivePortalApp is called.
+ val portalContentRequestCv = server.addExpectRequestCv(TEST_PORTAL_URL_PATH)
+
+ // Wait for a captive portal to be detected on the network
+ val wifiNetworkFuture = CompletableFuture<Network>()
+ val wifiCb = object : NetworkCallback() {
+ override fun onCapabilitiesChanged(
+ network: Network,
+ nc: NetworkCapabilities
+ ) {
+ if (nc.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
+ wifiNetworkFuture.complete(network)
+ }
+ }
+ }
+ cm.requestNetwork(NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(), wifiCb)
+
+ try {
+ reconnectWifi()
+ val network = wifiNetworkFuture.assertGet(WIFI_CONNECT_TIMEOUT_MS,
+ "Captive portal not detected after ${WIFI_CONNECT_TIMEOUT_MS}ms")
+
+ val wifiDefaultMessage = "Wifi should not be the default network when a captive " +
+ "portal was detected and another network (mobile data) can provide internet " +
+ "access."
+ assertNotEquals(network, cm.activeNetwork, wifiDefaultMessage)
+
+ val startPortalAppPermission =
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) CONNECTIVITY_INTERNAL
+ else NETWORK_SETTINGS
+ doAsShell(startPortalAppPermission) { cm.startCaptivePortalApp(network) }
+ assertTrue(portalContentRequestCv.block(TEST_TIMEOUT_MS), "The captive portal login " +
+ "page was still not fetched ${TEST_TIMEOUT_MS}ms after startCaptivePortalApp.")
+
+ assertNotEquals(network, cm.activeNetwork, wifiDefaultMessage)
+ } finally {
+ cm.unregisterNetworkCallback(wifiCb)
+ server.stop()
+ // disconnectFromCell should be called after connectToCell
+ utils.disconnectFromCell()
+ }
+
+ clearTestUrls()
+ reconnectWifi()
+ }
+
+ private fun setHttpsUrl(url: String?) = setConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING, url)
+ private fun setHttpUrl(url: String?) = setConfig(TEST_CAPTIVE_PORTAL_HTTP_URL_SETTING, url)
+ private fun setUrlExpiration(timestamp: Long?) = setConfig(TEST_URL_EXPIRATION_TIME,
+ timestamp?.toString())
+
+ private fun setConfig(configKey: String, value: String?) {
+ doAsShell(WRITE_DEVICE_CONFIG) {
+ DeviceConfig.setProperty(
+ NAMESPACE_CONNECTIVITY, configKey, value, false /* makeDefault */)
+ }
+ }
+
+ private fun doAsShell(vararg permissions: String, action: () -> Unit) {
+ // Wrap the below call to allow for more kotlin-like syntax
+ SystemUtil.runWithShellPermissionIdentity(action, permissions)
+ }
+
+ private fun reconnectWifi() {
+ doAsShell(NETWORK_SETTINGS) {
+ assertTrue(wm.disconnect())
+ assertTrue(wm.reconnect())
+ }
+ }
+
+ /**
+ * A minimal HTTP server running on localhost (loopback), on a random available port.
+ */
+ private class HttpServer : NanoHTTPD("localhost", 0 /* auto-select the port */) {
+ // Map of URL path -> HTTP response code
+ private val responses = HashMap<String, Response>()
+
+ // Map of path -> CV to open as soon as a request to the path is received
+ private val waitForRequestCv = HashMap<String, ConditionVariable>()
+
+ /**
+ * Create a URL string that, when fetched, will hit this server with the given URL [path].
+ */
+ fun makeUrl(path: String): String {
+ return Uri.Builder()
+ .scheme("http")
+ .encodedAuthority("localhost:$listeningPort")
+ .query(path)
+ .build()
+ .toString()
+ }
+
+ fun addResponse(
+ path: String,
+ statusCode: IStatus,
+ locationHeader: String? = null,
+ content: String = ""
+ ) {
+ val response = newFixedLengthResponse(statusCode, "text/plain", content)
+ locationHeader?.let { response.addHeader("Location", it) }
+ responses[path] = response
+ }
+
+ /**
+ * Create a [ConditionVariable] that will open when a request to [path] is received.
+ */
+ fun addExpectRequestCv(path: String): ConditionVariable {
+ return ConditionVariable().apply { waitForRequestCv[path] = this }
+ }
+
+ override fun serve(session: IHTTPSession): Response {
+ waitForRequestCv[session.queryParameterString]?.open()
+ return responses[session.queryParameterString]
+ // Default response is a 404
+ ?: super.serve(session)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index 1ee08ff..d498ed9 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,13 +16,17 @@
package android.net.cts;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.content.pm.PackageManager.FEATURE_ETHERNET;
import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
-import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.content.pm.PackageManager.FEATURE_USB_HOST;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
@@ -36,6 +40,16 @@
import static android.system.OsConstants.AF_UNSPEC;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.annotation.NonNull;
import android.app.Instrumentation;
@@ -45,6 +59,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.ConnectivityManager;
@@ -59,10 +74,12 @@
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
+import android.net.NetworkUtils;
import android.net.SocketKeepalive;
import android.net.cts.util.CtsNetUtils;
import android.net.util.KeepaliveUtils;
import android.net.wifi.WifiManager;
+import android.os.Binder;
import android.os.Build;
import android.os.Looper;
import android.os.MessageQueue;
@@ -71,15 +88,22 @@
import android.os.VintfRuntimeInfo;
import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
-import android.test.AndroidTestCase;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.ArrayUtils;
import libcore.io.Streams;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
@@ -105,7 +129,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class ConnectivityManagerTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class ConnectivityManagerTest {
private static final String TAG = ConnectivityManagerTest.class.getSimpleName();
@@ -117,7 +142,10 @@
private static final int INTERVAL_KEEPALIVE_RETRY_MS = 500;
private static final int MAX_KEEPALIVE_RETRY_COUNT = 3;
private static final int MIN_KEEPALIVE_INTERVAL = 10;
- private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 5000;
+
+ // Changing meteredness on wifi involves reconnecting, which can take several seconds (involves
+ // re-associating, DHCP...)
+ private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 30_000;
private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
// device could have only one interface: data, wifi.
@@ -141,22 +169,19 @@
private PackageManager mPackageManager;
private final HashMap<Integer, NetworkConfig> mNetworks =
new HashMap<Integer, NetworkConfig>();
- boolean mWifiConnectAttempted;
+ boolean mWifiWasDisabled;
private UiAutomation mUiAutomation;
private CtsNetUtils mCtsNetUtils;
- private boolean mShellPermissionIdentityAdopted;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- Looper.prepare();
- mContext = getContext();
+ @Before
+ public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getContext();
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mPackageManager = mContext.getPackageManager();
mCtsNetUtils = new CtsNetUtils(mContext);
- mWifiConnectAttempted = false;
+ mWifiWasDisabled = false;
// Get com.android.internal.R.array.networkAttributes
int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
@@ -173,20 +198,17 @@
} catch (Exception e) {}
}
mUiAutomation = mInstrumentation.getUiAutomation();
- mShellPermissionIdentityAdopted = false;
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
// Return WiFi to its original disabled state after tests that explicitly connect.
- if (mWifiConnectAttempted) {
+ if (mWifiWasDisabled) {
mCtsNetUtils.disconnectFromWifi(null);
}
if (mCtsNetUtils.cellConnectAttempted()) {
mCtsNetUtils.disconnectFromCell();
}
- dropShellPermissionIdentity();
- super.tearDown();
}
/**
@@ -195,13 +217,12 @@
* automatically in tearDown().
*/
private Network ensureWifiConnected() {
- if (mWifiManager.isWifiEnabled()) {
- return mCtsNetUtils.getWifiNetwork();
- }
- mWifiConnectAttempted = true;
+ mWifiWasDisabled = !mWifiManager.isWifiEnabled();
+ // Even if wifi is enabled, the network may not be connected or ready yet
return mCtsNetUtils.connectToWifi();
}
+ @Test
public void testIsNetworkTypeValid() {
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI));
@@ -231,12 +252,14 @@
}
+ @Test
public void testSetNetworkPreference() {
// getNetworkPreference() and setNetworkPreference() are both deprecated so they do
// not preform any action. Verify they are at least still callable.
mCm.setNetworkPreference(mCm.getNetworkPreference());
}
+ @Test
public void testGetActiveNetworkInfo() {
NetworkInfo ni = mCm.getActiveNetworkInfo();
@@ -245,6 +268,7 @@
assertTrue(ni.getState() == State.CONNECTED);
}
+ @Test
public void testGetActiveNetwork() {
Network network = mCm.getActiveNetwork();
assertNotNull("You must have an active network connection to complete CTS", network);
@@ -257,6 +281,7 @@
assertTrue(ni.getState() == State.CONNECTED);
}
+ @Test
public void testGetNetworkInfo() {
for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) {
if (shouldBeSupported(type)) {
@@ -275,6 +300,7 @@
}
}
+ @Test
public void testGetAllNetworkInfo() {
NetworkInfo[] ni = mCm.getAllNetworkInfo();
assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES);
@@ -298,6 +324,7 @@
* and that they are made from different IP addresses.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testOpenConnection() throws Exception {
boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
&& mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
@@ -377,6 +404,7 @@
} catch (UnsupportedOperationException expected) {}
}
+ @Test
public void testStartUsingNetworkFeature() {
final String invalidateFeature = "invalidateFeature";
@@ -406,6 +434,7 @@
(networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported());
}
+ @Test
public void testIsNetworkSupported() {
for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
boolean supported = mCm.isNetworkSupported(type);
@@ -417,12 +446,14 @@
}
}
+ @Test
public void testRequestRouteToHost() {
for (int type = -1 ; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
assertRequestRouteToHostUnsupported(type, HOST_ADDRESS);
}
}
+ @Test
public void testTest() {
mCm.getBackgroundDataSetting();
}
@@ -443,6 +474,7 @@
* that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testRegisterNetworkCallback() {
if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
@@ -484,6 +516,7 @@
* of a {@code NetworkCallback}.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testRegisterNetworkCallback_withPendingIntent() {
if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
@@ -529,6 +562,7 @@
* see if we get a callback for an INTERNET request.
*/
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
+ @Test
public void testRequestNetworkCallback() {
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.requestNetwork(new NetworkRequest.Builder()
@@ -552,6 +586,7 @@
* fail. Use WIFI and switch Wi-Fi off.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testRequestNetworkCallback_onUnavailable() {
final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
if (previousWifiEnabledState) {
@@ -589,6 +624,7 @@
/** Verify restricted networks cannot be requested. */
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
+ @Test
public void testRestrictedNetworks() {
// Verify we can request unrestricted networks:
NetworkRequest request = new NetworkRequest.Builder()
@@ -710,6 +746,7 @@
* for metered and unmetered networks.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testGetMultipathPreference() throws Exception {
final ContentResolver resolver = mContext.getContentResolver();
ensureWifiConnected();
@@ -878,18 +915,6 @@
keepalivesPerTransport, nc);
}
- private void adoptShellPermissionIdentity() {
- mUiAutomation.adoptShellPermissionIdentity();
- mShellPermissionIdentityAdopted = true;
- }
-
- private void dropShellPermissionIdentity() {
- if (mShellPermissionIdentityAdopted) {
- mUiAutomation.dropShellPermissionIdentity();
- mShellPermissionIdentityAdopted = false;
- }
- }
-
private static boolean isTcpKeepaliveSupportedByKernel() {
final String kVersionString = VintfRuntimeInfo.getKernelVersion();
return compareMajorMinorVersion(kVersionString, "4.8") >= 0;
@@ -924,6 +949,7 @@
* Verifies that version string compare logic returns expected result for various cases.
* Note that only major and minor number are compared.
*/
+ @Test
public void testMajorMinorVersionCompare() {
assertEquals(0, compareMajorMinorVersion("4.8.1", "4.8"));
assertEquals(1, compareMajorMinorVersion("4.9", "4.8.1"));
@@ -943,6 +969,7 @@
* keepalives is set to 0.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testKeepaliveWifiUnsupported() throws Exception {
if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testKeepaliveUnsupported cannot execute unless device"
@@ -952,32 +979,36 @@
final Network network = ensureWifiConnected();
if (getSupportedKeepalivesForNet(network) != 0) return;
+ final InetAddress srcAddr = getFirstV4Address(network);
+ assumeTrue("This test requires native IPv4", srcAddr != null);
- adoptShellPermissionIdentity();
-
- assertEquals(0, createConcurrentSocketKeepalives(network, 1, 0));
- assertEquals(0, createConcurrentSocketKeepalives(network, 0, 1));
-
- dropShellPermissionIdentity();
+ runWithShellPermissionIdentity(() -> {
+ assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 1, 0));
+ assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
+ });
}
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testCreateTcpKeepalive() throws Exception {
if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testCreateTcpKeepalive cannot execute unless device supports WiFi");
return;
}
- adoptShellPermissionIdentity();
-
final Network network = ensureWifiConnected();
if (getSupportedKeepalivesForNet(network) == 0) return;
+ final InetAddress srcAddr = getFirstV4Address(network);
+ assumeTrue("This test requires native IPv4", srcAddr != null);
+
// If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
// NAT-T keepalive. If keepalive limits from resource overlay is not zero, TCP keepalive
// needs to be supported except if the kernel doesn't support it.
if (!isTcpKeepaliveSupportedByKernel()) {
// Sanity check to ensure the callback result is expected.
- assertEquals(0, createConcurrentSocketKeepalives(network, 0, 1));
+ runWithShellPermissionIdentity(() -> {
+ assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1));
+ });
Log.i(TAG, "testCreateTcpKeepalive is skipped for kernel "
+ VintfRuntimeInfo.getKernelVersion());
return;
@@ -991,6 +1022,8 @@
// Should able to start keep alive offload when socket is idle.
final Executor executor = mContext.getMainExecutor();
final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
+
+ mUiAutomation.adoptShellPermissionIdentity();
try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
sk.start(MIN_KEEPALIVE_INTERVAL);
callback.expectStarted();
@@ -1012,6 +1045,8 @@
// Stop.
sk.stop();
callback.expectStopped();
+ } finally {
+ mUiAutomation.dropShellPermissionIdentity();
}
// Ensure socket is still connected.
@@ -1040,9 +1075,12 @@
// Should get ERROR_SOCKET_NOT_IDLE because there is still data in the receive queue
// that has not been read.
+ mUiAutomation.adoptShellPermissionIdentity();
try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
sk.start(MIN_KEEPALIVE_INTERVAL);
callback.expectError(SocketKeepalive.ERROR_SOCKET_NOT_IDLE);
+ } finally {
+ mUiAutomation.dropShellPermissionIdentity();
}
}
}
@@ -1087,7 +1125,7 @@
}
private @NonNull ArrayList<SocketKeepalive> createConcurrentNattSocketKeepalives(
- @NonNull Network network, int requestCount,
+ @NonNull Network network, @NonNull InetAddress srcAddr, int requestCount,
@NonNull TestSocketKeepaliveCallback callback) throws Exception {
final Executor executor = mContext.getMainExecutor();
@@ -1095,7 +1133,6 @@
// Initialize a real NaT-T socket.
final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
final UdpEncapsulationSocket nattSocket = mIpSec.openUdpEncapsulationSocket();
- final InetAddress srcAddr = getFirstV4Address(network);
final InetAddress dstAddr = getAddrByName(TEST_HOST, AF_INET);
assertNotNull(srcAddr);
assertNotNull(dstAddr);
@@ -1136,11 +1173,12 @@
* @return the total number of keepalives created.
*/
private int createConcurrentSocketKeepalives(
- @NonNull Network network, int nattCount, int tcpCount) throws Exception {
+ @NonNull Network network, @NonNull InetAddress srcAddr, int nattCount, int tcpCount)
+ throws Exception {
final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
- kalist.addAll(createConcurrentNattSocketKeepalives(network, nattCount, callback));
+ kalist.addAll(createConcurrentNattSocketKeepalives(network, srcAddr, nattCount, callback));
kalist.addAll(createConcurrentTcpSocketKeepalives(network, tcpCount, callback));
final int ret = kalist.size();
@@ -1160,6 +1198,7 @@
* get leaked after iterations.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testSocketKeepaliveLimitWifi() throws Exception {
if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testSocketKeepaliveLimitWifi cannot execute unless device"
@@ -1172,33 +1211,39 @@
if (supported == 0) {
return;
}
+ final InetAddress srcAddr = getFirstV4Address(network);
+ assumeTrue("This test requires native IPv4", srcAddr != null);
- adoptShellPermissionIdentity();
+ runWithShellPermissionIdentity(() -> {
+ // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
+ assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT);
- // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
- assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT);
-
- // Verifies that Nat-T keepalives can be established.
- assertEquals(supported, createConcurrentSocketKeepalives(network, supported + 1, 0));
- // Verifies that keepalives don't get leaked in second round.
- assertEquals(supported, createConcurrentSocketKeepalives(network, supported, 0));
+ // Verifies that Nat-T keepalives can be established.
+ assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
+ supported + 1, 0));
+ // Verifies that keepalives don't get leaked in second round.
+ assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
+ 0));
+ });
// If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support
// NAT-T keepalive. Test below cases only if TCP keepalive is supported by kernel.
- if (isTcpKeepaliveSupportedByKernel()) {
- assertEquals(supported, createConcurrentSocketKeepalives(network, 0, supported + 1));
+ if (!isTcpKeepaliveSupportedByKernel()) return;
+
+ runWithShellPermissionIdentity(() -> {
+ assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
+ supported + 1));
// Verifies that different types can be established at the same time.
- assertEquals(supported, createConcurrentSocketKeepalives(network,
+ assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
supported / 2, supported - supported / 2));
// Verifies that keepalives don't get leaked in second round.
- assertEquals(supported, createConcurrentSocketKeepalives(network, 0, supported));
- assertEquals(supported, createConcurrentSocketKeepalives(network,
+ assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0,
+ supported));
+ assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
supported / 2, supported - supported / 2));
- }
-
- dropShellPermissionIdentity();
+ });
}
/**
@@ -1206,6 +1251,7 @@
* don't get leaked after iterations.
*/
@AppModeFull(reason = "Cannot request network in instant app mode")
+ @Test
public void testSocketKeepaliveLimitTelephony() throws Exception {
if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device"
@@ -1222,18 +1268,19 @@
final Network network = mCtsNetUtils.connectToCell();
final int supported = getSupportedKeepalivesForNet(network);
+ final InetAddress srcAddr = getFirstV4Address(network);
+ assumeTrue("This test requires native IPv4", srcAddr != null);
- adoptShellPermissionIdentity();
-
- // Verifies that the supported keepalive slots meet minimum requirement.
- assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT);
-
- // Verifies that Nat-T keepalives can be established.
- assertEquals(supported, createConcurrentSocketKeepalives(network, supported + 1, 0));
- // Verifies that keepalives don't get leaked in second round.
- assertEquals(supported, createConcurrentSocketKeepalives(network, supported, 0));
-
- dropShellPermissionIdentity();
+ runWithShellPermissionIdentity(() -> {
+ // Verifies that the supported keepalive slots meet minimum requirement.
+ assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT);
+ // Verifies that Nat-T keepalives can be established.
+ assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr,
+ supported + 1, 0));
+ // Verifies that keepalives don't get leaked in second round.
+ assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported,
+ 0));
+ });
}
private int getIntResourceForName(@NonNull String resName) {
@@ -1246,6 +1293,7 @@
* Verifies that the keepalive slots are limited as customized for unprivileged requests.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
public void testSocketKeepaliveUnprivileged() throws Exception {
if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testSocketKeepaliveUnprivileged cannot execute unless device"
@@ -1258,6 +1306,8 @@
if (supported == 0) {
return;
}
+ final InetAddress srcAddr = getFirstV4Address(network);
+ assumeTrue("This test requires native IPv4", srcAddr != null);
// Resource ID might be shifted on devices that compiled with different symbols.
// Thus, resolve ID at runtime is needed.
@@ -1273,11 +1323,46 @@
final int expectedUnprivileged =
Math.min(allowedUnprivilegedPerUid, supported - reservedPrivilegedSlots);
assertEquals(expectedUnprivileged,
- createConcurrentSocketKeepalives(network, supported + 1, 0));
+ createConcurrentSocketKeepalives(network, srcAddr, supported + 1, 0));
}
private static void assertGreaterOrEqual(long greater, long lesser) {
assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
greater >= lesser);
}
+
+ /**
+ * Verifies that apps are not allowed to access restricted networks even if they declare the
+ * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests.
+ * See. b/144679405.
+ */
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
+ public void testRestrictedNetworkPermission() throws Exception {
+ // Ensure that CONNECTIVITY_USE_RESTRICTED_NETWORKS isn't granted to this package.
+ final PackageInfo app = mPackageManager.getPackageInfo(mContext.getPackageName(),
+ GET_PERMISSIONS);
+ final int index = ArrayUtils.indexOf(
+ app.requestedPermissions, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ assertTrue(index >= 0);
+ assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED);
+
+ // Ensure that NetworkUtils.queryUserAccess always returns false since this package should
+ // not have netd system permission to call this function.
+ final Network wifiNetwork = ensureWifiConnected();
+ assertFalse(NetworkUtils.queryUserAccess(Binder.getCallingUid(), wifiNetwork.netId));
+
+ // Ensure that this package cannot bind to any restricted network that's currently
+ // connected.
+ Network[] networks = mCm.getAllNetworks();
+ for (Network network : networks) {
+ NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+ if (nc != null && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+ try {
+ network.bindSocket(new Socket());
+ fail("Bind to restricted network " + network + " unexpectedly succeeded");
+ } catch (IOException expected) {}
+ }
+ }
+ }
}
diff --git a/tests/tests/net/src/android/net/cts/DnsResolverTest.java b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
index 1cc49f9..28753ff 100644
--- a/tests/tests/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
@@ -86,7 +86,6 @@
static final int CANCEL_RETRY_TIMES = 5;
static final int QUERY_TIMES = 10;
static final int NXDOMAIN = 3;
- static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000;
private ContentResolver mCR;
private ConnectivityManager mCM;
@@ -107,32 +106,15 @@
mExecutorInline = (Runnable r) -> r.run();
mCR = getContext().getContentResolver();
mCtsNetUtils = new CtsNetUtils(getContext());
- storePrivateDnsSetting();
+ mCtsNetUtils.storePrivateDnsSetting();
}
@Override
protected void tearDown() throws Exception {
- restorePrivateDnsSetting();
+ mCtsNetUtils.restorePrivateDnsSetting();
super.tearDown();
}
- private void storePrivateDnsSetting() {
- // Store private DNS setting
- mOldMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
- mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
- }
-
- private void restorePrivateDnsSetting() throws InterruptedException {
- // restore private DNS setting
- Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
- if ("hostname".equals(mOldMode)) {
- Settings.Global.putString(
- mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
- mCtsNetUtils.awaitPrivateDnsSetting("restorePrivateDnsSetting timeout",
- mCM.getActiveNetwork(), mOldDnsSpecifier, PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
- }
- }
-
private static String byteArrayToHexString(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; ++i) {
@@ -416,16 +398,13 @@
final String msg = "RawQuery " + TEST_NX_DOMAIN + " with private DNS";
// Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
// b/144521720
- Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
- Settings.Global.putString(mCR,
- Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
+ mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
for (Network network : getTestableNetworks()) {
final Network networkForPrivateDns =
(network != null) ? network : mCM.getActiveNetwork();
assertNotNull("Can't find network to await private DNS on", networkForPrivateDns);
mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
- networkForPrivateDns, GOOGLE_PRIVATE_DNS_SERVER,
- PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
+ networkForPrivateDns, GOOGLE_PRIVATE_DNS_SERVER, true);
final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
mDns.rawQuery(network, TEST_NX_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
executor, null, callback);
@@ -688,9 +667,7 @@
final Network[] testNetworks = getTestableNetworks();
// Set an invalid private DNS server
- Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
- Settings.Global.putString(mCR,
- Settings.Global.PRIVATE_DNS_SPECIFIER, INVALID_PRIVATE_DNS_SERVER);
+ mCtsNetUtils.setPrivateDnsStrictMode(INVALID_PRIVATE_DNS_SERVER);
final String msg = "Test PrivateDnsBypass " + TEST_DOMAIN;
for (Network network : testNetworks) {
// This test cannot be ran with null network because we need to explicitly pass a
@@ -699,7 +676,7 @@
// wait for private DNS setting propagating
mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
- network, INVALID_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS, false);
+ network, INVALID_PRIVATE_DNS_SERVER, false);
final CountDownLatch latch = new CountDownLatch(1);
final DnsResolver.Callback<List<InetAddress>> errorCallback =
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
index f123187..985e313 100644
--- a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
@@ -41,7 +41,6 @@
private static final String TAG = "MultinetworkNativeApiTest";
static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google";
- static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 2_000;
/**
* @return 0 on success
@@ -69,7 +68,7 @@
mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
mCR = getContext().getContentResolver();
mCtsNetUtils = new CtsNetUtils(getContext());
- storePrivateDnsSetting();
+ mCtsNetUtils.storePrivateDnsSetting();
}
@Override
@@ -77,18 +76,6 @@
super.tearDown();
}
- private void storePrivateDnsSetting() {
- // Store private DNS setting
- mOldMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
- mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
- }
-
- private void restorePrivateDnsSetting() {
- // restore private DNS setting
- Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
- Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
- }
-
private Network[] getTestableNetworks() {
final ArrayList<Network> testableNetworks = new ArrayList<Network>();
for (Network network : mCM.getAllNetworks()) {
@@ -239,17 +226,15 @@
// Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
// b/144521720
try {
- Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
- Settings.Global.putString(mCR,
- Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
+ mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
for (Network network : getTestableNetworks()) {
// Wait for private DNS setting to propagate.
mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
- network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS, true);
+ network, GOOGLE_PRIVATE_DNS_SERVER, true);
runResNnxDomainCheck(network.getNetworkHandle());
}
} finally {
- restorePrivateDnsSetting();
+ mCtsNetUtils.restorePrivateDnsSetting();
}
}
}
diff --git a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
index 5e92b41..d118c8a 100644
--- a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
@@ -18,6 +18,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -33,11 +34,12 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
-import android.net.TelephonyNetworkSpecifier;
+import android.net.UidRange;
import android.net.wifi.WifiNetworkSpecifier;
import android.os.Build;
-import android.os.Process;
import android.os.PatternMatcher;
+import android.os.Process;
+import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
@@ -59,6 +61,20 @@
private static final String TEST_PACKAGE_NAME = "test.package.name";
private static final MacAddress ARBITRARY_ADDRESS = MacAddress.fromString("3:5:8:12:9:2");
+ private class LocalNetworkSpecifier extends NetworkSpecifier {
+ private final int mId;
+
+ LocalNetworkSpecifier(int id) {
+ mId = id;
+ }
+
+ @Override
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+ return other instanceof LocalNetworkSpecifier
+ && mId == ((LocalNetworkSpecifier) other).mId;
+ }
+ }
+
@Test
public void testCapabilities() {
assertTrue(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build()
@@ -71,6 +87,16 @@
verifyNoCapabilities(nr);
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testTemporarilyNotMeteredCapability() {
+ assertTrue(new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED).build()
+ .hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ assertFalse(new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED).build()
+ .hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ }
+
private void verifyNoCapabilities(NetworkRequest nr) {
// NetworkCapabilities.mNetworkCapabilities is defined as type long
final int MAX_POSSIBLE_CAPABILITY = Long.SIZE;
@@ -129,54 +155,108 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.Q)
public void testCanBeSatisfiedBy() {
- final TelephonyNetworkSpecifier specifier1 = new TelephonyNetworkSpecifier.Builder()
- .setSubscriptionId(1234 /* subId */)
- .build();
- final TelephonyNetworkSpecifier specifier2 = new TelephonyNetworkSpecifier.Builder()
- .setSubscriptionId(5678 /* subId */)
- .build();
- final NetworkCapabilities cap = new NetworkCapabilities()
+ final LocalNetworkSpecifier specifier1 = new LocalNetworkSpecifier(1234 /* id */);
+ final LocalNetworkSpecifier specifier2 = new LocalNetworkSpecifier(5678 /* id */);
+
+ final NetworkCapabilities capCellularMmsInternet = new NetworkCapabilities()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_MMS)
.addCapability(NET_CAPABILITY_INTERNET);
- final NetworkCapabilities capDualTransport = new NetworkCapabilities(cap)
- .addTransportType(TRANSPORT_VPN);
- final NetworkCapabilities capWithSpecifier1 =
- new NetworkCapabilities(cap).setNetworkSpecifier(specifier1);
- final NetworkCapabilities capDiffTransportWithSpecifier1 = new NetworkCapabilities()
+ final NetworkCapabilities capCellularVpnMmsInternet =
+ new NetworkCapabilities(capCellularMmsInternet).addTransportType(TRANSPORT_VPN);
+ final NetworkCapabilities capCellularMmsInternetSpecifier1 =
+ new NetworkCapabilities(capCellularMmsInternet).setNetworkSpecifier(specifier1);
+ final NetworkCapabilities capVpnInternetSpecifier1 = new NetworkCapabilities()
.addCapability(NET_CAPABILITY_INTERNET)
.addTransportType(TRANSPORT_VPN)
.setNetworkSpecifier(specifier1);
+ final NetworkCapabilities capCellularMmsInternetMatchallspecifier =
+ new NetworkCapabilities(capCellularMmsInternet)
+ .setNetworkSpecifier(new MatchAllNetworkSpecifier());
+ final NetworkCapabilities capCellularMmsInternetSpecifier2 =
+ new NetworkCapabilities(capCellularMmsInternet).setNetworkSpecifier(specifier2);
- final NetworkRequest requestWithSpecifier1 = new NetworkRequest.Builder()
+ final NetworkRequest requestCellularInternetSpecifier1 = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.setNetworkSpecifier(specifier1)
.build();
- assertFalse(requestWithSpecifier1.canBeSatisfiedBy(null));
- assertFalse(requestWithSpecifier1.canBeSatisfiedBy(new NetworkCapabilities()));
- assertTrue(requestWithSpecifier1.canBeSatisfiedBy(new NetworkCapabilities(cap)
- .setNetworkSpecifier(new MatchAllNetworkSpecifier())));
- assertTrue(requestWithSpecifier1.canBeSatisfiedBy(cap));
- assertTrue(requestWithSpecifier1.canBeSatisfiedBy(capWithSpecifier1));
- assertTrue(requestWithSpecifier1.canBeSatisfiedBy(capDualTransport));
- assertFalse(requestWithSpecifier1.canBeSatisfiedBy(
- new NetworkCapabilities(cap).setNetworkSpecifier(specifier2)));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(null));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(new NetworkCapabilities()));
+ assertTrue(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+ capCellularMmsInternetMatchallspecifier));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(capCellularMmsInternet));
+ assertTrue(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+ capCellularMmsInternetSpecifier1));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(capCellularVpnMmsInternet));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+ capCellularMmsInternetSpecifier2));
- final NetworkRequest request = new NetworkRequest.Builder()
+ final NetworkRequest requestCellularInternet = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.build();
- assertTrue(request.canBeSatisfiedBy(cap));
- assertTrue(request.canBeSatisfiedBy(capWithSpecifier1));
- assertTrue(request.canBeSatisfiedBy(
- new NetworkCapabilities(cap).setNetworkSpecifier(specifier2)));
- assertFalse(request.canBeSatisfiedBy(capDiffTransportWithSpecifier1));
- assertTrue(request.canBeSatisfiedBy(capDualTransport));
+ assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternet));
+ assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternetSpecifier1));
+ assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternetSpecifier2));
+ assertFalse(requestCellularInternet.canBeSatisfiedBy(capVpnInternetSpecifier1));
+ assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularVpnMmsInternet));
+ }
- assertEquals(requestWithSpecifier1.canBeSatisfiedBy(capWithSpecifier1),
- new NetworkCapabilities(capWithSpecifier1)
- .satisfiedByNetworkCapabilities(capWithSpecifier1));
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testInvariantInCanBeSatisfiedBy() {
+ // Test invariant that result of NetworkRequest.canBeSatisfiedBy() should be the same with
+ // NetworkCapabilities.satisfiedByNetworkCapabilities().
+ final LocalNetworkSpecifier specifier1 = new LocalNetworkSpecifier(1234 /* id */);
+ final int uid = Process.myUid();
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ final NetworkRequest requestCombination = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .setLinkUpstreamBandwidthKbps(1000)
+ .setNetworkSpecifier(specifier1)
+ .setSignalStrength(-123)
+ .setUids(ranges).build();
+ final NetworkCapabilities capCell = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ assertCorrectlySatisfies(false, requestCombination, capCell);
+
+ final NetworkCapabilities capCellInternet = new NetworkCapabilities.Builder(capCell)
+ .addCapability(NET_CAPABILITY_INTERNET).build();
+ assertCorrectlySatisfies(false, requestCombination, capCellInternet);
+
+ final NetworkCapabilities capCellInternetBW =
+ new NetworkCapabilities.Builder(capCellInternet)
+ .setLinkUpstreamBandwidthKbps(1024).build();
+ assertCorrectlySatisfies(false, requestCombination, capCellInternetBW);
+
+ final NetworkCapabilities capCellInternetBWSpecifier1 =
+ new NetworkCapabilities.Builder(capCellInternetBW)
+ .setNetworkSpecifier(specifier1).build();
+ assertCorrectlySatisfies(false, requestCombination, capCellInternetBWSpecifier1);
+
+ final NetworkCapabilities capCellInternetBWSpecifier1Signal =
+ new NetworkCapabilities.Builder(capCellInternetBWSpecifier1)
+ .setSignalStrength(-123).build();
+ assertCorrectlySatisfies(true, requestCombination,
+ capCellInternetBWSpecifier1Signal);
+
+ final NetworkCapabilities capCellInternetBWSpecifier1SignalUid =
+ new NetworkCapabilities.Builder(capCellInternetBWSpecifier1Signal)
+ .setOwnerUid(uid)
+ .setAdministratorUids(new int [] {uid}).build();
+ assertCorrectlySatisfies(true, requestCombination,
+ capCellInternetBWSpecifier1SignalUid);
+ }
+
+ private void assertCorrectlySatisfies(boolean expect, NetworkRequest request,
+ NetworkCapabilities nc) {
+ assertEquals(expect, request.canBeSatisfiedBy(nc));
+ assertEquals(
+ request.canBeSatisfiedBy(nc),
+ request.networkCapabilities.satisfiedByNetworkCapabilities(nc));
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
index 6214f89..f39b184 100644
--- a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -16,6 +16,8 @@
package android.net.cts.util;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -27,6 +29,7 @@
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.ContentResolver;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
@@ -38,6 +41,7 @@
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
+import android.provider.Settings;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
@@ -58,6 +62,7 @@
private static final int SOCKET_TIMEOUT_MS = 2000;
private static final int PRIVATE_DNS_PROBE_MS = 1_000;
+ public static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 6_000;
public static final int HTTP_PORT = 80;
public static final String TEST_HOST = "connectivitycheck.gstatic.com";
public static final String HTTP_REQUEST =
@@ -68,15 +73,19 @@
public static final String NETWORK_CALLBACK_ACTION =
"ConnectivityManagerTest.NetworkCallbackAction";
- private Context mContext;
- private ConnectivityManager mCm;
- private WifiManager mWifiManager;
+ private final Context mContext;
+ private final ConnectivityManager mCm;
+ private final ContentResolver mCR;
+ private final WifiManager mWifiManager;
private TestNetworkCallback mCellNetworkCallback;
+ private String mOldPrivateDnsMode;
+ private String mOldPrivateDnsSpecifier;
public CtsNetUtils(Context context) {
mContext = context;
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mCR = context.getContentResolver();
}
// Toggle WiFi twice, leaving it in the state it started in
@@ -107,6 +116,8 @@
boolean connected = false;
try {
SystemUtil.runShellCommand("svc wifi enable");
+ SystemUtil.runWithShellPermissionIdentity(() -> mWifiManager.reconnect(),
+ NETWORK_SETTINGS);
// Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
wifiNetwork = callback.waitForAvailable();
assertNotNull(wifiNetwork);
@@ -246,9 +257,51 @@
return s;
}
+ public void storePrivateDnsSetting() {
+ // Store private DNS setting
+ mOldPrivateDnsMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
+ mOldPrivateDnsSpecifier = Settings.Global.getString(mCR,
+ Settings.Global.PRIVATE_DNS_SPECIFIER);
+ // It's possible that there is no private DNS default value in Settings.
+ // Give it a proper default mode which is opportunistic mode.
+ if (mOldPrivateDnsMode == null) {
+ mOldPrivateDnsSpecifier = "";
+ mOldPrivateDnsMode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+ Settings.Global.putString(mCR,
+ Settings.Global.PRIVATE_DNS_SPECIFIER, mOldPrivateDnsSpecifier);
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldPrivateDnsMode);
+ }
+ }
+
+ public void restorePrivateDnsSetting() throws InterruptedException {
+ if (mOldPrivateDnsMode == null || mOldPrivateDnsSpecifier == null) {
+ return;
+ }
+ // restore private DNS setting
+ if ("hostname".equals(mOldPrivateDnsMode)) {
+ setPrivateDnsStrictMode(mOldPrivateDnsSpecifier);
+ awaitPrivateDnsSetting("restorePrivateDnsSetting timeout",
+ mCm.getActiveNetwork(),
+ mOldPrivateDnsSpecifier, true);
+ } else {
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldPrivateDnsMode);
+ }
+ }
+
+ public void setPrivateDnsStrictMode(String server) {
+ // To reduce flake rate, set PRIVATE_DNS_SPECIFIER before PRIVATE_DNS_MODE. This ensures
+ // that if the previous private DNS mode was not "hostname", the system only sees one
+ // EVENT_PRIVATE_DNS_SETTINGS_CHANGED event instead of two.
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, server);
+ final String mode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
+ // If current private DNS mode is "hostname", we only need to set PRIVATE_DNS_SPECIFIER.
+ if (!"hostname".equals(mode)) {
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
+ }
+ }
+
public void awaitPrivateDnsSetting(@NonNull String msg, @NonNull Network network,
- @NonNull String server, int timeoutMs,
- boolean requiresValidatedServers) throws InterruptedException {
+ @NonNull String server, boolean requiresValidatedServers) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
NetworkCallback callback = new NetworkCallback() {
@@ -263,7 +316,7 @@
}
};
mCm.registerNetworkCallback(request, callback);
- assertTrue(msg, latch.await(timeoutMs, TimeUnit.MILLISECONDS));
+ assertTrue(msg, latch.await(PRIVATE_DNS_SETTING_TIMEOUT_MS, TimeUnit.MILLISECONDS));
mCm.unregisterNetworkCallback(callback);
// Wait some time for NetworkMonitor's private DNS probe to complete. If we do not do
// this, then the test could complete before the NetworkMonitor private DNS probe
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
index 37f90e9..ef2a0c1 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -113,15 +113,6 @@
String extensions = mActivity.getExtensionsString();
- final String es30RequiredList[] = {
- "OES_EGL_image_external_essl3"
- };
-
- for (int i = 0; i < es30RequiredList.length; ++i) {
- assertTrue("OpenGL ES version 3.0+ is missing extension " + es30RequiredList[i],
- hasExtension(extensions, es30RequiredList[i]));
- }
-
if (getMajorVersion(reportedVersion) != 3 || getMinorVersion(reportedVersion) < 1)
return;
diff --git a/tests/tests/os/assets/platform_versions.txt b/tests/tests/os/assets/platform_versions.txt
index 331bae0..3762249 100644
--- a/tests/tests/os/assets/platform_versions.txt
+++ b/tests/tests/os/assets/platform_versions.txt
@@ -1 +1 @@
-R
+S
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index 00cb97d..ca0f84a 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -223,6 +223,8 @@
Pattern.compile("^([0-9A-Za-z._-]+)$");
private static final Pattern SERIAL_NUMBER_PATTERN =
Pattern.compile("^([0-9A-Za-z]{6,20})$");
+ private static final Pattern SKU_PATTERN =
+ Pattern.compile("^([0-9A-Za-z.,_-]+)$");
private static final Pattern TAGS_PATTERN =
Pattern.compile("^([0-9A-Za-z.,_-]+)$");
private static final Pattern TYPE_PATTERN =
@@ -254,6 +256,8 @@
assertTrue(SERIAL_NUMBER_PATTERN.matcher(Build.SERIAL).matches());
+ assertTrue(SKU_PATTERN.matcher(Build.SKU).matches());
+
assertTrue(TAGS_PATTERN.matcher(Build.TAGS).matches());
// No format requirements stated in CDD for Build.TIME
diff --git a/tests/tests/provider/src/android/provider/cts/SearchIndexableResourceTest.java b/tests/tests/provider/src/android/provider/cts/SearchIndexableResourceTest.java
new file mode 100644
index 0000000..7a7a954
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/SearchIndexableResourceTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.provider.SearchIndexableResource;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SearchIndexableResourceTest {
+
+ private static final int RANK = 3;
+ private static final int XML_RES_ID = 4;
+ private static final String CLASS_NAME = "testClassName";
+ private static final int ICON_RES_ID = 5;
+
+ @Test
+ public void testConstructor() {
+ SearchIndexableResource resource = new SearchIndexableResource(RANK, XML_RES_ID, CLASS_NAME,
+ ICON_RES_ID);
+
+ assertEquals(RANK, resource.rank);
+ assertEquals(XML_RES_ID, resource.xmlResId);
+ assertEquals(CLASS_NAME, resource.className);
+ assertEquals(ICON_RES_ID, resource.iconResId);
+ }
+}
diff --git a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
index 93fc4d9..f798bb8 100644
--- a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
@@ -86,6 +86,7 @@
"/sdcard/WindowInsetsBehaviorTests";
private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
private static final String ARGUMENT_KEY_FORCE_ENABLE = "force_enable_gesture_navigation";
+ private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
private static final int STEPS = 10;
// The minimum value of the system gesture exclusion limit is 200 dp. The value here should be
@@ -703,6 +704,8 @@
public void swipeOutsideLimit_systemUiVisible_allEventsCanceled() throws Throwable {
assumeTrue(hasSystemGestureFeature());
+ assumeGestureNavigationMode();
+
final int swipeCount = 1;
final boolean insideLimit = false;
testSystemGestureExclusionLimit(swipeCount, insideLimit, SYSTEM_UI_FLAG_VISIBLE);
@@ -779,6 +782,14 @@
assumeTrue("Gesture navigation required.", insets[0].left > 0);
}
+ private void assumeGestureNavigationMode() {
+ // TODO: b/153032202 consider the CTS on GSI case.
+ Resources res = mTargetContext.getResources();
+ int naviMode = res.getIdentifier(NAV_BAR_INTERACTION_MODE_RES_NAME, "integer", "android");
+
+ assumeTrue("Gesture navigation required", naviMode == 2);
+ }
+
/**
* Set system UI visibility and wait for it is applied by the system.
*
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index 5505c2c7..12c0fe2 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -505,11 +505,15 @@
placeAndVerifyCall(null);
}
+ void placeAndVerifyCallByRedirection(boolean wasCancelled) {
+ placeAndVerifyCallByRedirection(null, wasCancelled);
+ }
+
/**
* Puts Telecom in a state where there is an active call provided by the
* {@link CtsConnectionService} which can be tested.
*/
- void placeAndVerifyCallByRedirection(boolean wasCancelled) {
+ void placeAndVerifyCallByRedirection(Bundle extras, boolean wasCancelled) {
int currentCallCount = (getInCallService() == null) ? 0 : getInCallService().getCallCount();
int currentConnections = getNumberOfConnections();
// We expect a new connection if it wasn't cancelled.
@@ -517,7 +521,7 @@
currentConnections++;
currentCallCount++;
}
- placeAndVerifyCall(null, VideoProfile.STATE_AUDIO_ONLY, currentConnections,
+ placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY, currentConnections,
currentCallCount);
// Ensure the new outgoing call broadcast fired for the outgoing call.
assertOutgoingCallBroadcastReceived(true);
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
index 2fcedf5..dd0a5cf 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java
@@ -27,6 +27,7 @@
import android.content.ServiceConnection;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -58,7 +59,18 @@
private static final Uri SAMPLE_HANDLE = Uri.fromParts(PhoneAccount.SCHEME_TEL, "0001112222",
null);
-
+ private static final Uri SAMPLE_HANDLE_WITH_POST_DIAL = new Uri.Builder()
+ .scheme(PhoneAccount.SCHEME_TEL)
+ .encodedOpaquePart("6505551212,1234567890")
+ .build();
+ private static final Uri SAMPLE_REDIRECT_HANDLE = new Uri.Builder()
+ .scheme(PhoneAccount.SCHEME_TEL)
+ .encodedOpaquePart("6505551213")
+ .build();
+ private static final Uri SAMPLE_REDIRECT_HANDLE_WITH_POST_DIAL = new Uri.Builder()
+ .scheme(PhoneAccount.SCHEME_TEL)
+ .encodedOpaquePart("6505551213,1234567890")
+ .build();
private static final int ASYNC_TIMEOUT = 10000;
private RoleManager mRoleManager;
private Handler mHandler;
@@ -120,6 +132,31 @@
assertTrue(Call.STATE_DISCONNECTED != mCall.getState());
}
+ /**
+ * Verifies that post-dial digits will be re-added to a number after redirection.
+ * @throws Exception
+ */
+ public void testRedirectedCallWithPostDialDigits() throws Exception {
+ if (!shouldTestTelecom(mContext)) {
+ return;
+ }
+ mCallRedirectionServiceController.setRedirectCall(SAMPLE_REDIRECT_HANDLE, null, false);
+ Bundle extras = new Bundle();
+ extras.putParcelable(TestUtils.EXTRA_PHONE_NUMBER, SAMPLE_HANDLE_WITH_POST_DIAL);
+ placeAndVerifyCallByRedirection(extras, false /* cancelledByCallRedirection */);
+ mInCallService = mInCallCallbacks.getService();
+ assertCallGatewayConstructed(mInCallService.getLastCall(), true);
+ mCall = mInCallService.getLastCall();
+ assertEquals(SAMPLE_REDIRECT_HANDLE_WITH_POST_DIAL,
+ mCall.getDetails().getGatewayInfo().getGatewayAddress());
+ assertEquals(SAMPLE_HANDLE_WITH_POST_DIAL,
+ mCall.getDetails().getGatewayInfo().getOriginalAddress());
+ assertEquals(SAMPLE_HANDLE_WITH_POST_DIAL,
+ mCall.getDetails().getHandle());
+ assertEquals(TestUtils.TEST_PHONE_ACCOUNT_HANDLE, mCall.getDetails().getAccountHandle());
+ assertTrue(Call.STATE_DISCONNECTED != mCall.getState());
+ }
+
public void testRedirectedCallWithRedirectedPhoneAccount()
throws Exception {
if (!shouldTestTelecom(mContext)) {
diff --git a/tests/tests/telephony/current/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsDownloadService.java b/tests/tests/telephony/current/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsDownloadService.java
index 30447b8..8a186c9 100644
--- a/tests/tests/telephony/current/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsDownloadService.java
+++ b/tests/tests/telephony/current/EmbmsMiddlewareTestApp/src/android/telephony/cts/embmstestapp/CtsDownloadService.java
@@ -67,6 +67,7 @@
public static final String METHOD_INITIALIZE = "initialize";
public static final String METHOD_REQUEST_UPDATE_FILE_SERVICES =
"requestUpdateFileServices";
+ public static final String METHOD_ADD_SERVICE_ANNOUNCEMENT = "addServiceAnnouncementFile";
public static final String METHOD_SET_TEMP_FILE_ROOT = "setTempFileRootDirectory";
public static final String METHOD_RESET_DOWNLOAD_KNOWLEDGE = "resetDownloadKnowledge";
public static final String METHOD_GET_DOWNLOAD_STATUS = "getDownloadStatus";
@@ -82,6 +83,7 @@
public static final String ARGUMENT_DOWNLOAD_REQUEST = "downloadRequest";
public static final String ARGUMENT_FILE_INFO = "fileInfo";
public static final String ARGUMENT_RESULT_CODE = "resultCode";
+ public static final String ARGUMENT_SERVICE_ANNOUNCEMENT_FILE = "serviceAnnouncementFile";
public static final String CONTROL_INTERFACE_ACTION =
"android.telephony.cts.embmstestapp.ACTION_CONTROL_MIDDLEWARE";
@@ -256,6 +258,16 @@
}
@Override
+ public int addServiceAnnouncementFile(int subscriptionId, byte[] announcementFile) {
+ Bundle b = new Bundle();
+ b.putString(METHOD_NAME, METHOD_ADD_SERVICE_ANNOUNCEMENT);
+ b.putInt(ARGUMENT_SUBSCRIPTION_ID, subscriptionId);
+ b.putByteArray(ARGUMENT_SERVICE_ANNOUNCEMENT_FILE, announcementFile);
+ mReceivedCalls.add(b);
+ return MbmsErrors.SUCCESS;
+ }
+
+ @Override
public int cancelDownload(DownloadRequest request) {
Bundle b = new Bundle();
b.putString(METHOD_NAME, METHOD_CANCEL_DOWNLOAD);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CdmaSmsCbProgramDataTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CdmaSmsCbProgramDataTest.java
new file mode 100644
index 0000000..9aee14d
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CdmaSmsCbProgramDataTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static android.telephony.cdma.CdmaSmsCbProgramData.ALERT_OPTION_NO_ALERT;
+import static android.telephony.cdma.CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;
+import static android.telephony.cdma.CdmaSmsCbProgramData.OPERATION_CLEAR_CATEGORIES;
+
+import static org.junit.Assert.assertEquals;
+
+import android.telephony.cdma.CdmaSmsCbProgramData;
+
+import com.android.internal.telephony.cdma.sms.BearerData;
+
+import org.junit.Test;
+
+public class CdmaSmsCbProgramDataTest {
+
+ private static final int OPERATION = OPERATION_CLEAR_CATEGORIES;
+ private static final int CATEGORY = CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;
+ private static final int LANGUAGE = BearerData.LANGUAGE_ENGLISH;
+ private static final int MAX_MESSAGES = 10;
+ private static final int ALERT_OPTION = ALERT_OPTION_NO_ALERT;
+ private static final String CATEGORY_NAME = "category_name";
+
+ @Test
+ public void testCdmaSmsCbProgramDataConstructorAndGetters() {
+ CdmaSmsCbProgramData data = new CdmaSmsCbProgramData(
+ OPERATION,
+ CATEGORY,
+ LANGUAGE,
+ MAX_MESSAGES,
+ ALERT_OPTION,
+ CATEGORY_NAME
+ );
+
+ assertEquals(OPERATION, data.getOperation());
+ assertEquals(CATEGORY, data.getCategory());
+ assertEquals(LANGUAGE, data.getLanguage());
+ assertEquals(MAX_MESSAGES, data.getMaxMessages());
+ assertEquals(ALERT_OPTION, data.getAlertOption());
+ assertEquals(CATEGORY_NAME, data.getCategoryName());
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastServiceTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastServiceTest.java
new file mode 100644
index 0000000..bef1e8d
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellBroadcastServiceTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static org.junit.Assert.assertNull;
+
+import android.os.Bundle;
+import android.telephony.CellBroadcastService;
+import android.telephony.cdma.CdmaSmsCbProgramData;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+public class CellBroadcastServiceTest {
+
+ @Test
+ public void testConstructor() {
+ CellBroadcastService testCellBroadcastService = new CellBroadcastService() {
+ @Override
+ public void onGsmCellBroadcastSms(int slotIndex, byte[] message) {
+ // do nothing
+ }
+
+ @Override
+ public void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+ int serviceCategory) {
+ // do nothing
+ }
+
+ @Override
+ public void onCdmaScpMessage(int slotIndex, List<CdmaSmsCbProgramData> smsCbProgramData,
+ String originatingAddress, Consumer<Bundle> callback) {
+ // do nothing
+ }
+
+ @Override
+ public CharSequence getCellBroadcastAreaInfo(int slotIndex) {
+ return null;
+ }
+ };
+
+ assertNull(testCellBroadcastService.getCellBroadcastAreaInfo(0));
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index 41438fc..2ed4a07 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -382,30 +382,32 @@
private void verifyCellIdentityCdma(CellIdentityCdma cdma, boolean isRegistered) {
int networkId = cdma.getNetworkId();
assertTrue("getNetworkId() out of range [0,65535], networkId=" + networkId,
- networkId == Integer.MAX_VALUE || (networkId >= 0 && networkId <= NETWORK_ID));
+ networkId == CellInfo.UNAVAILABLE || (networkId >= 0 && networkId <= NETWORK_ID));
int systemId = cdma.getSystemId();
assertTrue("getSystemId() out of range [0,32767], systemId=" + systemId,
- systemId == Integer.MAX_VALUE || (systemId >= 0 && systemId <= SYSTEM_ID));
+ systemId == CellInfo.UNAVAILABLE || (systemId >= 0 && systemId <= SYSTEM_ID));
int basestationId = cdma.getBasestationId();
assertTrue("getBasestationId() out of range [0,65535], basestationId=" + basestationId,
- basestationId == Integer.MAX_VALUE
+ basestationId == CellInfo.UNAVAILABLE
|| (basestationId >= 0 && basestationId <= BASESTATION_ID));
int longitude = cdma.getLongitude();
assertTrue("getLongitude() out of range [-2592000,2592000], longitude=" + longitude,
- longitude == Integer.MAX_VALUE
+ longitude == CellInfo.UNAVAILABLE
|| (longitude >= -LONGITUDE && longitude <= LONGITUDE));
int latitude = cdma.getLatitude();
assertTrue("getLatitude() out of range [-1296000,1296000], latitude=" + latitude,
- latitude == Integer.MAX_VALUE || (latitude >= -LATITUDE && latitude <= LATITUDE));
+ latitude == CellInfo.UNAVAILABLE
+ || (latitude >= -LATITUDE && latitude <= LATITUDE));
if (isRegistered) {
- assertTrue("SID is required for registered cells", systemId != Integer.MAX_VALUE);
- assertTrue("NID is required for registered cells", networkId != Integer.MAX_VALUE);
- assertTrue("BSID is required for registered cells", basestationId != Integer.MAX_VALUE);
+ assertTrue("SID is required for registered cells", systemId != CellInfo.UNAVAILABLE);
+ assertTrue("NID is required for registered cells", networkId != CellInfo.UNAVAILABLE);
+ assertTrue("BSID is required for registered cells",
+ basestationId != CellInfo.UNAVAILABLE);
}
}
@@ -447,7 +449,7 @@
int evdoSnr = cdma.getEvdoSnr();
assertTrue("getEvdoSnr() out of range [0,8], evdoSnr=" + evdoSnr,
- (evdoSnr == Integer.MAX_VALUE) || (evdoSnr >= 0 && evdoSnr <= 8));
+ (evdoSnr == CellInfo.UNAVAILABLE) || (evdoSnr >= 0 && evdoSnr <= 8));
}
private void verifyCellSignalStrengthCdmaParcel(CellSignalStrengthCdma cdma) {
@@ -461,9 +463,9 @@
private static void verifyPlmnInfo(String mccStr, String mncStr, int mcc, int mnc) {
// If either int value is invalid, all values must be invalid
- if (mcc == Integer.MAX_VALUE) {
+ if (mcc == CellInfo.UNAVAILABLE) {
assertTrue("MNC and MNC must always be reported together.",
- mnc == Integer.MAX_VALUE && mccStr == null && mncStr == null);
+ mnc == CellInfo.UNAVAILABLE && mccStr == null && mncStr == null);
return;
}
@@ -552,7 +554,7 @@
if (mRadioHalVersion >= RADIO_HAL_VERSION_1_5) {
int[] bands = nr.getBands();
for (int band: bands) {
- assertTrue("getBand out of range [1, 95] or [257, 261]",
+ assertTrue("getBand out of range [1, 95] or [257, 261], band = " + band,
(band >= BAND_FR1_MIN_NR && band <= BAND_FR1_MAX_NR)
|| (band >= BAND_FR2_MIN_NR && band <= BAND_FR2_MAX_NR));
}
@@ -560,7 +562,7 @@
// If the cell is reported as registered, then all the logical cell info must be reported
if (isRegistered) {
- assertTrue("TAC is required for registered cells", tac != Integer.MAX_VALUE);
+ assertTrue("TAC is required for registered cells", tac != CellInfo.UNAVAILABLE);
assertTrue("MCC is required for registered cells", nr.getMccString() != null);
assertTrue("MNC is required for registered cells", nr.getMncString() != null);
}
@@ -576,17 +578,18 @@
assertTrue("getCsiRsrp() out of range [-140, -44] | Integer.MAX_INTEGER, csiRsrp = "
+ csiRsrp, -140 <= csiRsrp && csiRsrp <= -44
- || csiRsrp == Integer.MAX_VALUE);
+ || csiRsrp == CellInfo.UNAVAILABLE);
assertTrue("getCsiRsrq() out of range [-20, -3] | Integer.MAX_INTEGER, csiRsrq = "
- + csiRsrq, -20 <= csiRsrq && csiRsrq <= -3 || csiRsrq == Integer.MAX_VALUE);
+ + csiRsrq, -20 <= csiRsrq && csiRsrq <= -3 || csiRsrq == CellInfo.UNAVAILABLE);
assertTrue("getCsiSinr() out of range [-23, 40] | Integer.MAX_INTEGER, csiSinr = "
- + csiSinr, -23 <= csiSinr && csiSinr <= 40 || csiSinr == Integer.MAX_VALUE);
+ + csiSinr, -23 <= csiSinr && csiSinr <= 40 || csiSinr == CellInfo.UNAVAILABLE);
assertTrue("getSsRsrp() out of range [-140, -44] | Integer.MAX_INTEGER, ssRsrp = "
- + ssRsrp, -140 <= ssRsrp && ssRsrp <= -44 || ssRsrp == Integer.MAX_VALUE);
+ + ssRsrp, -140 <= ssRsrp && ssRsrp <= -44
+ || ssRsrp == CellInfo.UNAVAILABLE);
assertTrue("getSsRsrq() out of range [-20, -3] | Integer.MAX_INTEGER, ssRsrq = "
- + ssRsrq, -20 <= ssRsrq && ssRsrq <= -3 || ssRsrq == Integer.MAX_VALUE);
+ + ssRsrq, -20 <= ssRsrq && ssRsrq <= -3 || ssRsrq == CellInfo.UNAVAILABLE);
assertTrue("getSsSinr() out of range [-23, 40] | Integer.MAX_INTEGER, ssSinr = "
- + ssSinr, -23 <= ssSinr && ssSinr <= 40 || ssSinr == Integer.MAX_VALUE);
+ + ssSinr, -23 <= ssSinr && ssSinr <= 40 || ssSinr == CellInfo.UNAVAILABLE);
}
private void verifyCellInfoLteParcelandHashcode(CellInfoLte lte) {
@@ -605,23 +608,23 @@
// Cell identity ranges from 0 to 268435456.
int ci = lte.getCi();
assertTrue("getCi() out of range [0,268435456], ci=" + ci,
- (ci == Integer.MAX_VALUE) || (ci >= 0 && ci <= CI));
+ (ci == CellInfo.UNAVAILABLE) || (ci >= 0 && ci <= CI));
// Verify LTE physical cell id information.
// Only physical cell id is available for LTE neighbor.
int pci = lte.getPci();
// Physical cell id should be within [0, 503].
assertTrue("getPci() out of range [0, 503], pci=" + pci,
- (pci== Integer.MAX_VALUE) || (pci >= 0 && pci <= PCI));
+ (pci == CellInfo.UNAVAILABLE) || (pci >= 0 && pci <= PCI));
// Tracking area code ranges from 0 to 65535.
int tac = lte.getTac();
assertTrue("getTac() out of range [0,65535], tac=" + tac,
- (tac == Integer.MAX_VALUE) || (tac >= 0 && tac <= TAC));
+ (tac == CellInfo.UNAVAILABLE) || (tac >= 0 && tac <= TAC));
int bw = lte.getBandwidth();
assertTrue("getBandwidth out of range [1400, 20000] | Integer.Max_Value, bw=",
- bw == Integer.MAX_VALUE || bw >= BANDWIDTH_LOW && bw <= BANDWIDTH_HIGH);
+ bw == CellInfo.UNAVAILABLE || bw >= BANDWIDTH_LOW && bw <= BANDWIDTH_HIGH);
int earfcn = lte.getEarfcn();
// Reference 3GPP 36.101 Table 5.7.3-1
@@ -630,7 +633,7 @@
// out of the range of the narrowest 1.4Mhz deployment.
int minEarfcn = 7;
int maxEarfcn = EARFCN_MAX - 7;
- if (bw != Integer.MAX_VALUE) {
+ if (bw != CellInfo.UNAVAILABLE) {
// The number of channels used by a cell is equal to the cell bandwidth divided
// by the channel raster (bandwidth of a channel). The center channel is the channel
// the n/2-th channel where n is the number of channels, and since it is the center
@@ -644,12 +647,12 @@
}
assertTrue(
"getEarfcn() out of range [" + minEarfcn + "," + maxEarfcn + "], earfcn=" + earfcn,
- earfcn == Integer.MAX_VALUE || (earfcn >= minEarfcn && earfcn <= maxEarfcn));
+ earfcn == CellInfo.UNAVAILABLE || (earfcn >= minEarfcn && earfcn <= maxEarfcn));
if (mRadioHalVersion >= RADIO_HAL_VERSION_1_5) {
int[] bands = lte.getBands();
for (int band: bands) {
- assertTrue("getBand out of range [1, 88]",
+ assertTrue("getBand out of range [1, 88], band = " + band,
band >= BAND_MIN_LTE && band <= BAND_MAX_LTE);
}
}
@@ -666,17 +669,27 @@
verifyCsgInfo(lte.getClosedSubscriberGroupInfo());
+ verifyCellIdentityLteLocationSanitation(lte);
+
// If the cell is reported as registered, then all the logical cell info must be reported
if (isRegistered) {
- assertTrue("TAC is required for registered cells", tac != Integer.MAX_VALUE);
- assertTrue("CID is required for registered cells", ci != Integer.MAX_VALUE);
+ assertTrue("TAC is required for registered cells", tac != CellInfo.UNAVAILABLE);
+ assertTrue("CID is required for registered cells", ci != CellInfo.UNAVAILABLE);
assertTrue("MCC is required for registered cells",
- lte.getMccString() != null || lte.getMcc() != Integer.MAX_VALUE);
+ lte.getMccString() != null || lte.getMcc() != CellInfo.UNAVAILABLE);
assertTrue("MNC is required for registered cells",
- lte.getMncString() != null || lte.getMnc() != Integer.MAX_VALUE);
+ lte.getMncString() != null || lte.getMnc() != CellInfo.UNAVAILABLE);
}
}
+ private void verifyCellIdentityLteLocationSanitation(CellIdentityLte lte) {
+ CellIdentityLte sanitized = lte.sanitizeLocationInfo();
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getCi());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getEarfcn());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getPci());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getTac());
+ }
+
private void verifyCellIdentityLteParcel(CellIdentityLte lte) {
Parcel p = Parcel.obtain();
lte.writeToParcel(p, 0);
@@ -689,32 +702,32 @@
private void verifyCellSignalStrengthLte(CellSignalStrengthLte cellSignalStrengthLte) {
verifyRssiDbm(cellSignalStrengthLte.getDbm());
- //Integer.MAX_VALUE indicates an unavailable field
+ //ICellInfo.UNAVAILABLE indicates an unavailable field
int rsrp = cellSignalStrengthLte.getRsrp();
// RSRP is being treated as RSSI in LTE (they are similar but not quite right)
// so reusing the constants here.
assertTrue("getRsrp() out of range, rsrp=" + rsrp, rsrp >= MIN_RSRP && rsrp <= MAX_RSRP);
int rsrq = cellSignalStrengthLte.getRsrq();
- assertTrue("getRsrq() out of range | Integer.MAX_VALUE, rsrq=" + rsrq,
- rsrq == Integer.MAX_VALUE || (rsrq >= MIN_RSRQ && rsrq <= MAX_RSRQ));
+ assertTrue("getRsrq() out of range | CellInfo.UNAVAILABLE, rsrq=" + rsrq,
+ rsrq == CellInfo.UNAVAILABLE || (rsrq >= MIN_RSRQ && rsrq <= MAX_RSRQ));
int rssi = cellSignalStrengthLte.getRssi();
- assertTrue("getRssi() out of range [-113, -51] or Integer.MAX_VALUE if unknown, rssi="
+ assertTrue("getRssi() out of range [-113, -51] or CellInfo.UNAVAILABLE if unknown, rssi="
+ rssi, rssi == CellInfo.UNAVAILABLE
|| (rssi >= MIN_LTE_RSSI && rssi <= MAX_LTE_RSSI));
int rssnr = cellSignalStrengthLte.getRssnr();
- assertTrue("getRssnr() out of range | Integer.MAX_VALUE, rssnr=" + rssnr,
- rssnr == Integer.MAX_VALUE || (rssnr >= MIN_RSSNR && rssnr <= MAX_RSSNR));
+ assertTrue("getRssnr() out of range | CellInfo.UNAVAILABLE, rssnr=" + rssnr,
+ rssnr == CellInfo.UNAVAILABLE || (rssnr >= MIN_RSSNR && rssnr <= MAX_RSSNR));
int cqi = cellSignalStrengthLte.getCqi();
- assertTrue("getCqi() out of range | Integer.MAX_VALUE, cqi=" + cqi,
- cqi == Integer.MAX_VALUE || (cqi >= MIN_CQI && cqi <= MAX_CQI));
+ assertTrue("getCqi() out of range | CellInfo.UNAVAILABLE, cqi=" + cqi,
+ cqi == CellInfo.UNAVAILABLE || (cqi >= MIN_CQI && cqi <= MAX_CQI));
int ta = cellSignalStrengthLte.getTimingAdvance();
- assertTrue("getTimingAdvance() invalid [0-1282] | Integer.MAX_VALUE, ta=" + ta,
- ta == Integer.MAX_VALUE || (ta >= 0 && ta <=1282));
+ assertTrue("getTimingAdvance() invalid [0-1282] | CellInfo.UNAVAILABLE, ta=" + ta,
+ ta == CellInfo.UNAVAILABLE || (ta >= 0 && ta <= 1282));
int level = cellSignalStrengthLte.getLevel();
assertTrue("getLevel() out of range [0,4], level=" + level, level >= 0 && level <= 4);
@@ -725,7 +738,8 @@
int timingAdvance = cellSignalStrengthLte.getTimingAdvance();
assertTrue("getTimingAdvance() out of range [0,1282], timingAdvance=" + timingAdvance,
- timingAdvance == Integer.MAX_VALUE || (timingAdvance >= 0 && timingAdvance <= 1282));
+ timingAdvance == CellInfo.UNAVAILABLE
+ || (timingAdvance >= 0 && timingAdvance <= 1282));
if (mRadioHalVersion >= RADIO_HAL_VERSION_1_2) {
assertTrue("RSRP Must be valid for LTE",
@@ -767,17 +781,17 @@
int lac = wcdma.getLac();
assertTrue("getLac() out of range [0, 65535], lac=" + lac,
- (lac >= 0 && lac <= LAC) || lac == Integer.MAX_VALUE);
+ (lac >= 0 && lac <= LAC) || lac == CellInfo.UNAVAILABLE);
int cid = wcdma.getCid();
assertTrue("getCid() out of range [0, 268435455], cid=" + cid,
- (cid >= 0 && cid <= CID_UMTS) || cid == Integer.MAX_VALUE);
+ (cid >= 0 && cid <= CID_UMTS) || cid == CellInfo.UNAVAILABLE);
// Verify wcdma primary scrambling code information.
// Primary scrambling code should be within [0, 511].
int psc = wcdma.getPsc();
assertTrue("getPsc() out of range [0, 511], psc=" + psc,
- (psc >= 0 && psc <= PSC) || psc == Integer.MAX_VALUE);
+ (psc >= 0 && psc <= PSC) || psc == CellInfo.UNAVAILABLE);
String mobileNetworkOperator = wcdma.getMobileNetworkOperator();
assertTrue("getMobileNetworkOperator() out of range [0, 999999], mobileNetworkOperator="
@@ -800,12 +814,12 @@
// If the cell is reported as registered, then all the logical cell info must be reported
if (isRegistered) {
- assertTrue("LAC is required for registered cells", lac != Integer.MAX_VALUE);
- assertTrue("CID is required for registered cells", cid != Integer.MAX_VALUE);
+ assertTrue("LAC is required for registered cells", lac != CellInfo.UNAVAILABLE);
+ assertTrue("CID is required for registered cells", cid != CellInfo.UNAVAILABLE);
assertTrue("MCC is required for registered cells",
- wcdma.getMccString() != null || wcdma.getMcc() != Integer.MAX_VALUE);
+ wcdma.getMccString() != null || wcdma.getMcc() != CellInfo.UNAVAILABLE);
assertTrue("MNC is required for registered cells",
- wcdma.getMncString() != null || wcdma.getMnc() != Integer.MAX_VALUE);
+ wcdma.getMncString() != null || wcdma.getMnc() != CellInfo.UNAVAILABLE);
}
}
@@ -884,15 +898,15 @@
// Local area code and cellid should be with [0, 65535].
int lac = gsm.getLac();
assertTrue("getLac() out of range [0, 65535], lac=" + lac,
- lac == Integer.MAX_VALUE || (lac >= 0 && lac <= LAC));
+ lac == CellInfo.UNAVAILABLE || (lac >= 0 && lac <= LAC));
int cid = gsm.getCid();
assertTrue("getCid() out range [0, 65535], cid=" + cid,
- cid== Integer.MAX_VALUE || (cid >= 0 && cid <= CID_GSM));
+ cid == CellInfo.UNAVAILABLE || (cid >= 0 && cid <= CID_GSM));
int arfcn = gsm.getArfcn();
// Reference 3GPP 45.005 Table 2-2
assertTrue("getArfcn() out of range [0,1024], arfcn=" + arfcn,
- arfcn == Integer.MAX_VALUE || (arfcn >= 0 && arfcn <= ARFCN));
+ arfcn == CellInfo.UNAVAILABLE || (arfcn >= 0 && arfcn <= ARFCN));
String mobileNetworkOperator = gsm.getMobileNetworkOperator();
assertTrue("getMobileNetworkOperator() out of range [0, 999999], mobileNetworkOperator="
@@ -910,12 +924,12 @@
// If the cell is reported as registered, then all the logical cell info must be reported
if (isRegistered) {
- assertTrue("LAC is required for registered cells", lac != Integer.MAX_VALUE);
- assertTrue("CID is required for registered cells", cid != Integer.MAX_VALUE);
+ assertTrue("LAC is required for registered cells", lac != CellInfo.UNAVAILABLE);
+ assertTrue("CID is required for registered cells", cid != CellInfo.UNAVAILABLE);
assertTrue("MCC is required for registered cells",
- gsm.getMccString() != null || gsm.getMcc() != Integer.MAX_VALUE);
+ gsm.getMccString() != null || gsm.getMcc() != CellInfo.UNAVAILABLE);
assertTrue("MNC is required for registered cells",
- gsm.getMncString() != null || gsm.getMnc() != Integer.MAX_VALUE);
+ gsm.getMncString() != null || gsm.getMnc() != CellInfo.UNAVAILABLE);
}
}
@@ -935,8 +949,8 @@
assertTrue("getLevel() out of range [0,4], level=" + level, level >= 0 && level <= 4);
int ta = gsm.getTimingAdvance();
- assertTrue("getTimingAdvance() out of range [0,219] | Integer.MAX_VALUE, ta=" + ta,
- ta == Integer.MAX_VALUE || (ta >= 0 && ta <= 219));
+ assertTrue("getTimingAdvance() out of range [0,219] | CellInfo.UNAVAILABLE, ta=" + ta,
+ ta == CellInfo.UNAVAILABLE || (ta >= 0 && ta <= 219));
assertEquals(gsm.getDbm(), gsm.getRssi());
@@ -994,11 +1008,11 @@
int lac = tdscdma.getLac();
assertTrue("getLac() out of range [0, 65535], lac=" + lac,
- (lac >= 0 && lac <= LAC) || lac == Integer.MAX_VALUE);
+ (lac >= 0 && lac <= LAC) || lac == CellInfo.UNAVAILABLE);
int cid = tdscdma.getCid();
assertTrue("getCid() out of range [0, 268435455], cid=" + cid,
- (cid >= 0 && cid <= CID_UMTS) || cid == Integer.MAX_VALUE);
+ (cid >= 0 && cid <= CID_UMTS) || cid == CellInfo.UNAVAILABLE);
// Verify tdscdma primary scrambling code information.
// Primary scrambling code should be within [0, 511].
@@ -1026,8 +1040,8 @@
// If the cell is reported as registered, then all the logical cell info must be reported
if (isRegistered) {
- assertTrue("LAC is required for registered cells", lac != Integer.MAX_VALUE);
- assertTrue("CID is required for registered cells", cid != Integer.MAX_VALUE);
+ assertTrue("LAC is required for registered cells", lac != CellInfo.UNAVAILABLE);
+ assertTrue("CID is required for registered cells", cid != CellInfo.UNAVAILABLE);
assertTrue("MCC is required for registered cells", tdscdma.getMccString() != null);
assertTrue("MNC is required for registered cells", tdscdma.getMncString() != null);
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/DataCallResponseTest.java b/tests/tests/telephony/current/src/android/telephony/cts/DataCallResponseTest.java
new file mode 100644
index 0000000..1ec1c81
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/DataCallResponseTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.os.Parcel;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
+
+import org.junit.Test;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.List;
+
+public class DataCallResponseTest {
+ private static final int CAUSE = 0;
+ private static final int RETRY = -1;
+ private static final int ID = 1;
+ private static final int LINK_STATUS = 2;
+ private static final int PROTOCOL_TYPE = ApnSetting.PROTOCOL_IP;
+ private static final String IF_NAME = "IF_NAME";
+ private static final List<LinkAddress> ADDRESSES = Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress("99.88.77.66"), 0));
+ private static final List<InetAddress> DNSES =
+ Arrays.asList(InetAddresses.parseNumericAddress("55.66.77.88"));
+ private static final List<InetAddress> GATEWAYS =
+ Arrays.asList(InetAddresses.parseNumericAddress("11.22.33.44"));
+ private static final List<InetAddress> PCSCFS =
+ Arrays.asList(InetAddresses.parseNumericAddress("22.33.44.55"));
+ private static final int MTU_V4 = 1440;
+ private static final int MTU_V6 = 1400;
+
+ @Test
+ public void testConstructorAndGetters() {
+ DataCallResponse response = new DataCallResponse.Builder()
+ .setCause(CAUSE)
+ .setSuggestedRetryTime(RETRY)
+ .setId(ID)
+ .setLinkStatus(LINK_STATUS)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setInterfaceName(IF_NAME)
+ .setAddresses(ADDRESSES)
+ .setDnsAddresses(DNSES)
+ .setGatewayAddresses(GATEWAYS)
+ .setPcscfAddresses(PCSCFS)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .build();
+
+ assertThat(response.getCause()).isEqualTo(CAUSE);
+ assertThat(response.getSuggestedRetryTime()).isEqualTo(RETRY);
+ assertThat(response.getId()).isEqualTo(ID);
+ assertThat(response.getLinkStatus()).isEqualTo(LINK_STATUS);
+ assertThat(response.getProtocolType()).isEqualTo(PROTOCOL_TYPE);
+ assertThat(response.getInterfaceName()).isEqualTo(IF_NAME);
+ assertThat(response.getAddresses()).isEqualTo(ADDRESSES);
+ assertThat(response.getDnsAddresses()).isEqualTo(DNSES);
+ assertThat(response.getGatewayAddresses()).isEqualTo(GATEWAYS);
+ assertThat(response.getPcscfAddresses()).isEqualTo(PCSCFS);
+ assertThat(response.getMtuV4()).isEqualTo(MTU_V4);
+ assertThat(response.getMtuV6()).isEqualTo(MTU_V6);
+ }
+
+ @Test
+ public void testEquals() {
+ DataCallResponse response = new DataCallResponse.Builder()
+ .setCause(CAUSE)
+ .setSuggestedRetryTime(RETRY)
+ .setId(ID)
+ .setLinkStatus(LINK_STATUS)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setInterfaceName(IF_NAME)
+ .setAddresses(ADDRESSES)
+ .setDnsAddresses(DNSES)
+ .setGatewayAddresses(GATEWAYS)
+ .setPcscfAddresses(PCSCFS)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .build();
+
+ DataCallResponse equalsResponse = new DataCallResponse.Builder()
+ .setCause(CAUSE)
+ .setSuggestedRetryTime(RETRY)
+ .setId(ID)
+ .setLinkStatus(LINK_STATUS)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setInterfaceName(IF_NAME)
+ .setAddresses(ADDRESSES)
+ .setDnsAddresses(DNSES)
+ .setGatewayAddresses(GATEWAYS)
+ .setPcscfAddresses(PCSCFS)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .build();
+
+ assertThat(response).isEqualTo(equalsResponse);
+ }
+
+ @Test
+ public void testNotEquals() {
+ DataCallResponse response = new DataCallResponse.Builder()
+ .setCause(CAUSE)
+ .setSuggestedRetryTime(RETRY)
+ .setId(ID)
+ .setLinkStatus(LINK_STATUS)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setInterfaceName(IF_NAME)
+ .setAddresses(ADDRESSES)
+ .setDnsAddresses(DNSES)
+ .setGatewayAddresses(GATEWAYS)
+ .setPcscfAddresses(PCSCFS)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .build();
+
+ DataCallResponse notEqualsResponse = new DataCallResponse.Builder()
+ .setCause(1)
+ .setSuggestedRetryTime(-1)
+ .setId(1)
+ .setLinkStatus(3)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setInterfaceName(IF_NAME)
+ .setAddresses(ADDRESSES)
+ .setDnsAddresses(DNSES)
+ .setGatewayAddresses(GATEWAYS)
+ .setPcscfAddresses(PCSCFS)
+ .setMtuV4(1441)
+ .setMtuV6(1440)
+ .build();
+
+ assertThat(response).isNotEqualTo(notEqualsResponse);
+ assertThat(response).isNotEqualTo(null);
+ assertThat(response).isNotEqualTo(new String[1]);
+ }
+
+ @Test
+ public void testParcel() {
+ DataCallResponse response = new DataCallResponse.Builder()
+ .setCause(CAUSE)
+ .setSuggestedRetryTime(RETRY)
+ .setId(ID)
+ .setLinkStatus(LINK_STATUS)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setInterfaceName(IF_NAME)
+ .setAddresses(ADDRESSES)
+ .setDnsAddresses(DNSES)
+ .setGatewayAddresses(GATEWAYS)
+ .setPcscfAddresses(PCSCFS)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .build();
+
+ Parcel stateParcel = Parcel.obtain();
+ response.writeToParcel(stateParcel, 0);
+ stateParcel.setDataPosition(0);
+
+ DataCallResponse parcelResponse = DataCallResponse.CREATOR.createFromParcel(stateParcel);
+ assertThat(response).isEqualTo(parcelResponse);
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java b/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java
new file mode 100644
index 0000000..5740272
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataProfile;
+
+import org.junit.Test;
+
+public class DataProfileTest {
+ private static final int PROFILE_ID = 1;
+ private static final String APN = "FAKE_APN";
+ private static final int PROTOCOL_TYPE = ApnSetting.PROTOCOL_IP;
+ private static final int AUTH_TYPE = ApnSetting.AUTH_TYPE_NONE;
+ private static final String USER_NAME = "USER_NAME";
+ private static final String PASSWORD = "PASSWORD";
+ private static final int TYPE = DataProfile.TYPE_3GPP2;
+ private static final boolean IS_ENABLED = true;
+ private static final int APN_BITMASK = 1;
+ private static final int ROAMING_PROTOCOL_TYPE = ApnSetting.PROTOCOL_IP;
+ private static final int BEARER_BITMASK = 14;
+ private static final int MTU_V4 = 1440;
+ private static final int MTU_V6 = 1400;
+ private static final boolean IS_PREFERRED = true;
+ private static final boolean IS_PERSISTENT = true;
+
+ @Test
+ public void testConstructorAndGetters() {
+ DataProfile profile = new DataProfile.Builder()
+ .setProfileId(PROFILE_ID)
+ .setApn(APN)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setAuthType(AUTH_TYPE)
+ .setUserName(USER_NAME)
+ .setPassword(PASSWORD)
+ .setType(TYPE)
+ .enable(IS_ENABLED)
+ .setSupportedApnTypesBitmask(APN_BITMASK)
+ .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+ .setBearerBitmask(BEARER_BITMASK)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .setPreferred(IS_PREFERRED)
+ .setPersistent(IS_PERSISTENT)
+ .build();
+
+ assertThat(profile.getProfileId()).isEqualTo(PROFILE_ID);
+ assertThat(profile.getApn()).isEqualTo(APN);
+ assertThat(profile.getProtocolType()).isEqualTo(PROTOCOL_TYPE);
+ assertThat(profile.getAuthType()).isEqualTo(AUTH_TYPE);
+ assertThat(profile.getUserName()).isEqualTo(USER_NAME);
+ assertThat(profile.getPassword()).isEqualTo(PASSWORD);
+ assertThat(profile.getType()).isEqualTo(TYPE);
+ assertThat(profile.isEnabled()).isEqualTo(IS_ENABLED);
+ assertThat(profile.getSupportedApnTypesBitmask()).isEqualTo(APN_BITMASK);
+ assertThat(profile.getRoamingProtocolType()).isEqualTo(ROAMING_PROTOCOL_TYPE);
+ assertThat(profile.getBearerBitmask()).isEqualTo(BEARER_BITMASK);
+ assertThat(profile.getMtuV4()).isEqualTo(MTU_V4);
+ assertThat(profile.getMtuV6()).isEqualTo(MTU_V6);
+ assertThat(profile.isPreferred()).isEqualTo(IS_PREFERRED);
+ assertThat(profile.isPersistent()).isEqualTo(IS_PERSISTENT);
+ }
+
+ @Test
+ public void testEquals() {
+ DataProfile profile = new DataProfile.Builder()
+ .setProfileId(PROFILE_ID)
+ .setApn(APN)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setAuthType(AUTH_TYPE)
+ .setUserName(USER_NAME)
+ .setPassword(PASSWORD)
+ .setType(TYPE)
+ .enable(IS_ENABLED)
+ .setSupportedApnTypesBitmask(APN_BITMASK)
+ .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+ .setBearerBitmask(BEARER_BITMASK)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .setPreferred(IS_PREFERRED)
+ .setPersistent(IS_PERSISTENT)
+ .build();
+
+ DataProfile equalsProfile = new DataProfile.Builder()
+ .setProfileId(PROFILE_ID)
+ .setApn(APN)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setAuthType(AUTH_TYPE)
+ .setUserName(USER_NAME)
+ .setPassword(PASSWORD)
+ .setType(TYPE)
+ .enable(IS_ENABLED)
+ .setSupportedApnTypesBitmask(APN_BITMASK)
+ .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+ .setBearerBitmask(BEARER_BITMASK)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .setPreferred(IS_PREFERRED)
+ .setPersistent(IS_PERSISTENT)
+ .build();
+
+ assertThat(profile).isEqualTo(equalsProfile);
+ }
+
+ @Test
+ public void testNotEquals() {
+ DataProfile profile = new DataProfile.Builder()
+ .setProfileId(PROFILE_ID)
+ .setApn(APN)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setAuthType(AUTH_TYPE)
+ .setUserName(USER_NAME)
+ .setPassword(PASSWORD)
+ .setType(TYPE)
+ .enable(IS_ENABLED)
+ .setSupportedApnTypesBitmask(APN_BITMASK)
+ .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+ .setBearerBitmask(BEARER_BITMASK)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .setPreferred(IS_PREFERRED)
+ .setPersistent(IS_PERSISTENT)
+ .build();
+
+ DataProfile notEqualsProfile = new DataProfile.Builder()
+ .setProfileId(0)
+ .setApn(APN)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setAuthType(AUTH_TYPE)
+ .setUserName(USER_NAME)
+ .setPassword(PASSWORD)
+ .setType(TYPE)
+ .enable(false)
+ .setSupportedApnTypesBitmask(2)
+ .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+ .setBearerBitmask(3)
+ .setMtuV4(1441)
+ .setMtuV6(1401)
+ .setPreferred(false)
+ .setPersistent(false)
+ .build();
+
+ assertThat(profile).isNotEqualTo(notEqualsProfile);
+ assertThat(profile).isNotEqualTo(null);
+ assertThat(profile).isNotEqualTo(new String[1]);
+ }
+
+ @Test
+ public void testParcel() {
+ DataProfile profile = new DataProfile.Builder()
+ .setProfileId(PROFILE_ID)
+ .setApn(APN)
+ .setProtocolType(PROTOCOL_TYPE)
+ .setAuthType(AUTH_TYPE)
+ .setUserName(USER_NAME)
+ .setPassword(PASSWORD)
+ .setType(TYPE)
+ .enable(IS_ENABLED)
+ .setSupportedApnTypesBitmask(APN_BITMASK)
+ .setRoamingProtocolType(ROAMING_PROTOCOL_TYPE)
+ .setBearerBitmask(BEARER_BITMASK)
+ .setMtuV4(MTU_V4)
+ .setMtuV6(MTU_V6)
+ .setPreferred(IS_PREFERRED)
+ .setPersistent(IS_PERSISTENT)
+ .build();
+
+ Parcel stateParcel = Parcel.obtain();
+ profile.writeToParcel(stateParcel, 0);
+ stateParcel.setDataPosition(0);
+
+ DataProfile parcelProfile = DataProfile.CREATOR.createFromParcel(stateParcel);
+ assertThat(profile).isEqualTo(parcelProfile);
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
index 864cbac..782eeba 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -43,6 +43,7 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SmsManager;
+import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
@@ -89,6 +90,7 @@
private boolean mOnBarringInfoChangedCalled;
private boolean mSecurityExceptionThrown;
private boolean mOnRegistrationFailedCalled;
+ private boolean mOnTelephonyDisplayInfoChanged;
@RadioPowerState private int mRadioPowerState;
@SimActivationState private int mVoiceActivationState;
private BarringInfo mBarringInfo;
@@ -693,6 +695,37 @@
}
@Test
+ public void testOnDisplayInfoChanged() throws Exception {
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+ return;
+ }
+ assertThat(mOnTelephonyDisplayInfoChanged).isFalse();
+
+ mHandler.post(() -> {
+ mListener = new PhoneStateListener() {
+ @Override
+ public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfo) {
+ synchronized (mLock) {
+ mOnTelephonyDisplayInfoChanged = true;
+ mLock.notify();
+ }
+ }
+ };
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.listen(mListener,
+ PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED));
+ });
+
+ synchronized (mLock) {
+ if (!mOnTelephonyDisplayInfoChanged) {
+ mLock.wait(WAIT_TIME);
+ }
+ }
+ assertTrue(mOnTelephonyDisplayInfoChanged);
+ }
+
+ @Test
public void testOnCallForwardingIndicatorChanged() throws Throwable {
if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsCbCmasInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsCbCmasInfoTest.java
new file mode 100644
index 0000000..062f690
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsCbCmasInfoTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static android.telephony.SmsCbCmasInfo.CMAS_CATEGORY_SAFETY;
+import static android.telephony.SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY;
+import static android.telephony.SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
+import static android.telephony.SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EVACUATE;
+import static android.telephony.SmsCbCmasInfo.CMAS_SEVERITY_SEVERE;
+import static android.telephony.SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE;
+
+import android.telephony.SmsCbCmasInfo;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SmsCbCmasInfoTest {
+
+ private static final int MESSAGE_CLASS = CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
+ private static final int CATEGORY = CMAS_CATEGORY_SAFETY;
+ private static final int RESPONSE_TYPE = CMAS_RESPONSE_TYPE_EVACUATE;
+ private static final int SEVERITY = CMAS_SEVERITY_SEVERE;
+ private static final int URGENCY = CMAS_URGENCY_IMMEDIATE;
+ private static final int CERTAINTY = CMAS_CERTAINTY_LIKELY;
+
+ @Test
+ public void testSmsCbCmasInfo() {
+ SmsCbCmasInfo info = new SmsCbCmasInfo(
+ MESSAGE_CLASS,
+ CATEGORY,
+ RESPONSE_TYPE,
+ SEVERITY,
+ URGENCY,
+ CERTAINTY
+ );
+
+ Assert.assertEquals(MESSAGE_CLASS, info.getMessageClass());
+ Assert.assertEquals(CATEGORY, info.getCategory());
+ Assert.assertEquals(RESPONSE_TYPE, info.getResponseType());
+ Assert.assertEquals(SEVERITY, info.getSeverity());
+ Assert.assertEquals(URGENCY, info.getUrgency());
+ Assert.assertEquals(CERTAINTY, info.getCertainty());
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsCbMessageTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsCbMessageTest.java
index b157ab0..e038fef 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsCbMessageTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsCbMessageTest.java
@@ -49,6 +49,7 @@
private static final int TEST_SERIAL = 1234;
private static final String TEST_PLMN = "111222";
private static final SmsCbLocation TEST_LOCATION = new SmsCbLocation(TEST_PLMN, -1, -1);
+ private static final int TEST_DCS = 0;
private static final int TEST_SERVICE_CATEGORY = 4097;
private static final String TEST_LANGUAGE = "en";
private static final String TEST_BODY = "test body";
@@ -79,9 +80,9 @@
}
});
mSmsCbMessage = new SmsCbMessage(TEST_MESSAGE_FORMAT, TEST_GEO_SCOPE, TEST_SERIAL,
- TEST_LOCATION, TEST_SERVICE_CATEGORY, TEST_LANGUAGE, 0, TEST_BODY, TEST_PRIORITY,
- TEST_ETWS_INFO, null, TEST_MAX_WAIT_TIME, TEST_GEOS, TEST_RECEIVED_TIME,
- TEST_SLOT, TEST_SUB_ID);
+ TEST_LOCATION, TEST_SERVICE_CATEGORY, TEST_LANGUAGE, TEST_DCS, TEST_BODY,
+ TEST_PRIORITY, TEST_ETWS_INFO, null, TEST_MAX_WAIT_TIME, TEST_GEOS,
+ TEST_RECEIVED_TIME, TEST_SLOT, TEST_SUB_ID);
}
@Test
@@ -156,6 +157,11 @@
}
@Test
+ public void testDataCodingScheme() {
+ assertEquals(TEST_DCS, mSmsCbMessage.getDataCodingScheme());
+ }
+
+ @Test
public void testCmasInfo() {
final SmsCbCmasInfo info =
new SmsCbCmasInfo(SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST,
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
index fd46eef..d697aab 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
@@ -57,8 +57,10 @@
import android.os.SystemClock;
import android.provider.Telephony;
import android.telephony.SmsManager;
+import android.telephony.SmsCbMessage;
import android.telephony.SmsMessage;
import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaSmsCbProgramData;
import android.text.TextUtils;
import android.util.Log;
@@ -660,6 +662,36 @@
}
}
+ @Test
+ public void testDisableCellBroadcastRange() {
+ try {
+ int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP;
+ executeWithShellPermissionIdentity(() -> {
+ getSmsManager().disableCellBroadcastRange(
+ CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
+ CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+ ranType);
+ });
+ } catch (Exception e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testEnableCellBroadcastRange() {
+ try {
+ int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP;
+ executeWithShellPermissionIdentity(() -> {
+ getSmsManager().enableCellBroadcastRange(
+ CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
+ CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
+ ranType);
+ });
+ } catch (Exception e) {
+ // expected
+ }
+ }
+
protected ArrayList<String> divideMessage(String text) {
return getSmsManager().divideMessage(text);
}
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 7c8c9d46..cf7bfef 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
@@ -26,7 +26,9 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.telephony.SmsManager;
import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import org.junit.Before;
@@ -343,6 +345,45 @@
assertEquals(2, result[0]);
}
+ @Test
+ public void testGetSmsPdu() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ SmsMessage.SubmitPdu smsPdu;
+ String scAddress = null;
+ String destinationAddress = null;
+ String message = null;
+
+ // Null message, null destination
+ smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ SmsManager.STATUS_ON_ICC_READ,
+ scAddress, destinationAddress, message, System.currentTimeMillis());
+ assertNull(smsPdu);
+
+ message = "This is a test message";
+
+ // Non-null message, null destination
+ smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ SmsManager.STATUS_ON_ICC_READ,
+ scAddress, destinationAddress, message, System.currentTimeMillis());
+ assertNull(smsPdu);
+
+ if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
+ // TODO: temp workaround, OCTET encoding for EMS not properly supported
+ return;
+ }
+
+ scAddress = "1650253000";
+ destinationAddress = "18004664411";
+ message = "This is a test message";
+ smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ SmsManager.STATUS_ON_ICC_READ,
+ scAddress, destinationAddress, message, System.currentTimeMillis());
+ assertNotNull(smsPdu);
+ }
+
private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index 72ac137..7cd5a53 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -18,13 +18,14 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -206,12 +207,16 @@
if (!isSupported()) return;
List<SubscriptionInfo> subList = mSm.getActiveSubscriptionInfoList();
+ int[] idList = mSm.getActiveSubscriptionIdList();
// Assert when there is no sim card present or detected
assertNotNull("Active subscriber required", subList);
+ assertNotNull("Active subscriber required", idList);
assertFalse("Active subscriber required", subList.isEmpty());
+ assertNotEquals("Active subscriber required", 0, idList.length);
for (int i = 0; i < subList.size(); i++) {
assertTrue(subList.get(i).getSubscriptionId() >= 0);
assertTrue(subList.get(i).getSimSlotIndex() >= 0);
+ assertTrue(ArrayUtils.contains(idList, subList.get(i).getSubscriptionId()));
if (i >= 1) {
assertTrue(subList.get(i - 1).getSimSlotIndex()
<= subList.get(i).getSimSlotIndex());
@@ -336,12 +341,13 @@
mSm.setSubscriptionPlans(mSubId, Arrays.asList(buildValidSubscriptionPlan()));
// Cellular is metered by default
- assertFalse(cm.getNetworkCapabilities(net).hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(cm.getNetworkCapabilities(net).hasCapability(
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED));
- // Override should make it go unmetered
+ // Override should make it go temporarily unmetered
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
- return caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ return caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
});
mSm.setSubscriptionOverrideUnmetered(mSubId, true, 0);
assertTrue(latch.await(10, TimeUnit.SECONDS));
@@ -350,7 +356,7 @@
// Clearing override should make it go metered
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
- return !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ return !caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
});
mSm.setSubscriptionOverrideUnmetered(mSubId, false, 0);
assertTrue(latch.await(10, TimeUnit.SECONDS));
@@ -371,7 +377,8 @@
mSm.setSubscriptionPlans(mSubId, Arrays.asList(buildValidSubscriptionPlan()));
// Cellular is metered by default
- assertFalse(cm.getNetworkCapabilities(net).hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(cm.getNetworkCapabilities(net).hasCapability(
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED));
SubscriptionPlan unmeteredPlan = SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
@@ -384,7 +391,7 @@
// Unmetered plan should make it go unmetered
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
- return caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ return caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
});
mSm.setSubscriptionPlans(mSubId, Arrays.asList(unmeteredPlan));
assertTrue(latch.await(10, TimeUnit.SECONDS));
@@ -393,7 +400,7 @@
// Metered plan should make it go metered
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
- return !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ return !caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
});
mSm.setSubscriptionPlans(mSubId, Arrays.asList(buildValidSubscriptionPlan()));
assertTrue(latch.await(10, TimeUnit.SECONDS));
@@ -485,6 +492,20 @@
}
@Test
+ public void testSubscriptionPlanResetNetworkTypes() {
+ SubscriptionPlan plan = SubscriptionPlan.Builder
+ .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
+ Period.ofMonths(1))
+ .setTitle("CTS")
+ .setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_LTE})
+ .setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
+ .setDataUsage(500_000_000, System.currentTimeMillis())
+ .resetNetworkTypes()
+ .build();
+ assertEquals(plan, buildValidSubscriptionPlan());
+ }
+
+ @Test
public void testSubscriptionGrouping() throws Exception {
if (!isSupported()) return;
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 5d8219c..acf5ec7 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -37,16 +37,21 @@
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.net.ConnectivityManager;
+import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserManager;
+import android.provider.Settings;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -524,6 +529,7 @@
mTelephonyManager.getCarrierConfig();
mTelephonyManager.isVoiceCapable();
mTelephonyManager.isSmsCapable();
+ mTelephonyManager.isLteCdmaEvdoGsmWcdmaEnabled();
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.isDataConnectionAllowed());
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
@@ -1137,6 +1143,73 @@
}
@Test
+ public void testResetSettings() throws Exception {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ Log.d(TAG, "skipping test on device without FEATURE_TELEPHONY present");
+ return;
+ }
+
+ UserManager userManager = getContext().getSystemService(UserManager.class);
+
+ boolean canChangeMobileNetworkSettings = userManager != null
+ && !userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+ assertTrue("Primary user must be able to configure mobile networks to pass this test",
+ canChangeMobileNetworkSettings);
+
+ //First check permissions are correct
+ try {
+ mTelephonyManager.resetSettings();
+ fail("TelephonyManager#resetSettings requires the"
+ + " android.Manifest.permission.NETWORK_SETTINGS permission");
+ } catch (SecurityException e) {
+ //expected
+ }
+ // and then do a reset to move data to default.
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ TelephonyManager::resetSettings, "android.permission.NETWORK_SETTINGS");
+ } catch (SecurityException e) {
+ fail("TelephonyManager#resetSettings requires the"
+ + " android.Manifest.permission.NETWORK_SETTINGS permission");
+ }
+
+ LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(2);
+ final ContentObserver mobileDataChangeObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ queue.offer(isDataEnabled());
+ }
+ };
+
+ getContext().getContentResolver().registerContentObserver(
+ getObservableDataEnabledUri(mTestSub), /* notifyForDescendants= */ false,
+ mobileDataChangeObserver);
+ boolean defaultDataSetting = isDataEnabled();
+
+ // set data to not the default!
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ tm -> tm.setDataEnabled(!defaultDataSetting));
+ Boolean dataChangedResult = queue.poll(TOLERANCE, TimeUnit.MILLISECONDS);
+ assertNotNull("Data setting was not changed", dataChangedResult);
+ assertEquals("Data enable change didn't work", !defaultDataSetting,
+ dataChangedResult);
+
+ // and then do a reset to move data to default again.
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ TelephonyManager::resetSettings, "android.permission.NETWORK_SETTINGS");
+ } catch (SecurityException e) {
+ fail("TelephonyManager#resetSettings requires the"
+ + " android.Manifest.permission.NETWORK_SETTINGS permission");
+ }
+ dataChangedResult = queue.poll(TOLERANCE, TimeUnit.MILLISECONDS);
+ assertNotNull("Data setting was not changed", dataChangedResult);
+ assertEquals("resetSettings did not reset default data", defaultDataSetting,
+ dataChangedResult);
+ }
+
+ @Test
public void testGetServiceState() throws InterruptedException {
if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
@@ -1999,7 +2072,7 @@
return;
}
if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) {
- fail("This test requires two SIM cards.");
+ return;
}
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setPreferredOpportunisticDataSubscription(
@@ -2135,7 +2208,7 @@
return;
}
if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) {
- fail("This test requires two SIM cards.");
+ return;
}
List<SubscriptionInfo> subscriptionInfoList =
@@ -2276,6 +2349,28 @@
}
@Test
+ public void testIsIccLockEnabled() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ // verify SecurityException
+ try {
+ mTelephonyManager.isIccLockEnabled();
+ fail("testIsIccLockEnabled: Expected SecurityException on isIccLockEnabled");
+ } catch (SecurityException se) {
+ // expected
+ }
+
+ // test with permission
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mTelephonyManager, (tm) -> tm.isIccLockEnabled());
+ } catch (SecurityException se) {
+ fail("testIsIccLockEnabled: SecurityException not expected");
+ }
+ }
+
+ @Test
public void testIsDataEnabledForApn() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
@@ -2298,6 +2393,29 @@
}
@Test
+ public void testIsTetheringApnRequired() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ // verify SecurityException
+ try {
+ mTelephonyManager.isTetheringApnRequired();
+ fail("testIsTetheringApnRequired: Expected SecurityException on "
+ + "isTetheringApnRequired");
+ } catch (SecurityException se) {
+ // expected
+ }
+
+ // test with permission
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mTelephonyManager, (tm) -> tm.isTetheringApnRequired());
+ } catch (SecurityException se) {
+ fail("testIsIccLockEnabled: SecurityException not expected");
+ }
+ }
+
+ @Test
public void testGetCarrierInfoForImsiEncryption() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
@@ -2480,6 +2598,68 @@
}
}
+ @Test
+ public void testOpportunisticNetworkState() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ tm -> tm.isOpportunisticNetworkEnabled());
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ tm -> tm.setOpportunisticNetworkState(true));
+ assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ tm -> tm.isOpportunisticNetworkEnabled()));
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ tm -> tm.setOpportunisticNetworkState(false));
+ assertFalse(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ tm -> tm.isOpportunisticNetworkEnabled()));
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ tm -> tm.setOpportunisticNetworkState(isEnabled));
+ }
+
+ @Test
+ public void testGetSimApplicationState() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ int simApplicationState = mTelephonyManager.getSimApplicationState();
+ assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+ TelephonyManager.SIM_STATE_PIN_REQUIRED,
+ TelephonyManager.SIM_STATE_PUK_REQUIRED,
+ TelephonyManager.SIM_STATE_NETWORK_LOCKED,
+ TelephonyManager.SIM_STATE_NOT_READY,
+ TelephonyManager.SIM_STATE_PERM_DISABLED,
+ TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState));
+ }
+
+ @Test
+ public void testGetSimCardState() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ int simCardState = mTelephonyManager.getSimCardState();
+ assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+ TelephonyManager.SIM_STATE_ABSENT,
+ TelephonyManager.SIM_STATE_CARD_IO_ERROR,
+ TelephonyManager.SIM_STATE_CARD_RESTRICTED,
+ TelephonyManager.SIM_STATE_PRESENT).contains(simCardState));
+ }
+
+
+ private boolean isDataEnabled() {
+ return ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ TelephonyManager::isDataEnabled);
+ }
+
+ private Uri getObservableDataEnabledUri(int subId) {
+ Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
+ if (mTelephonyManager.getActiveModemCount() != 1) {
+ uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId);
+ }
+ return uri;
+ }
+
/**
* Validate Emergency Number address that only contains the dialable character.
*
diff --git a/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java b/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java
index ecb11ee..d6fd098 100644
--- a/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java
@@ -29,11 +29,12 @@
import android.telephony.mbms.FileServiceInfo;
import android.telephony.mbms.MbmsErrors;
+import org.junit.Test;
+
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import org.junit.Test;
public class MbmsDownloadSessionTest extends MbmsDownloadTestBase {
@@ -90,6 +91,15 @@
}
@Test
+ public void testAddServiceAnnouncementFile() throws Exception {
+ byte[] sampleAnnouncementFile = "<xml></xml>".getBytes();
+ mDownloadSession.addServiceAnnouncementFile(sampleAnnouncementFile);
+ List<Bundle> addServiceAnnouncementCalls =
+ getMiddlewareCalls(CtsDownloadService.METHOD_ADD_SERVICE_ANNOUNCEMENT);
+ assertEquals(1, addServiceAnnouncementCalls.size());
+ }
+
+ @Test
public void testSetTempFileDirectory() throws Exception {
String tempFileDirName = "CTSTestDir";
File tempFileRootDirectory = new File(mContext.getFilesDir(), tempFileDirName);
diff --git a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
index feda07b..a9097bb 100644
--- a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
@@ -27,6 +27,7 @@
import android.content.IntentFilter;
import android.telephony.TelephonyManager;
import android.telephony.euicc.DownloadableSubscription;
+import android.telephony.euicc.EuiccCardManager;
import android.telephony.euicc.EuiccInfo;
import android.telephony.euicc.EuiccManager;
@@ -51,6 +52,7 @@
private static final String ACTION_DOWNLOAD_SUBSCRIPTION = "cts_download_subscription";
private static final String ACTION_DELETE_SUBSCRIPTION = "cts_delete_subscription";
private static final String ACTION_SWITCH_TO_SUBSCRIPTION = "cts_switch_to_subscription";
+ private static final String ACTION_ERASE_SUBSCRIPTIONS = "cts_erase_subscriptions";
private static final String ACTION_START_TEST_RESOLUTION_ACTIVITY =
"cts_start_test_resolution_activity";
private static final String ACTIVATION_CODE = "1$LOCALHOST$04386-AGYFT-A74Y8-3F815";
@@ -60,6 +62,7 @@
ACTION_DOWNLOAD_SUBSCRIPTION,
ACTION_DELETE_SUBSCRIPTION,
ACTION_SWITCH_TO_SUBSCRIPTION,
+ ACTION_ERASE_SUBSCRIPTIONS,
ACTION_START_TEST_RESOLUTION_ACTIVITY,
};
@@ -205,6 +208,37 @@
}
@Test
+ public void testEraseSubscriptions() {
+ // test disabled state only for now
+ if (mEuiccManager.isEnabled()) {
+ return;
+ }
+
+ // set up CountDownLatch and receiver
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ mCallbackReceiver = new CallbackReceiver(countDownLatch);
+ getContext()
+ .registerReceiver(
+ mCallbackReceiver, new IntentFilter(ACTION_ERASE_SUBSCRIPTIONS));
+
+ // call eraseSubscriptions()
+ PendingIntent callbackIntent = createCallbackIntent(ACTION_ERASE_SUBSCRIPTIONS);
+ mEuiccManager.eraseSubscriptions(EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
+ callbackIntent);
+
+ // wait for callback
+ try {
+ countDownLatch.await(CALLBACK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail(e.toString());
+ }
+
+ // verify correct result code is received
+ assertEquals(
+ EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, mCallbackReceiver.getResultCode());
+ }
+
+ @Test
public void testStartResolutionActivity() {
// set up CountDownLatch and receiver
CountDownLatch countDownLatch = new CountDownLatch(1);
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 5e28767..c665116 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -1672,6 +1672,45 @@
overrideCarrierConfig(null);
}
+ @Test
+ public void testProvisioningManagerRcsProvisioningCaps() throws Exception {
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+
+ triggerFrameworkConnectToCarrierImsService();
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true);
+ bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, true);
+ overrideCarrierConfig(bundle);
+
+ ProvisioningManager provisioningManager =
+ ProvisioningManager.createForSubscriptionId(sTestSub);
+
+ final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ automan.adoptShellPermissionIdentity();
+ boolean provisioningStatus = provisioningManager.getRcsProvisioningStatusForCapability(
+ RCS_CAP_PRESENCE);
+ provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+ !provisioningStatus);
+ // Make sure the change in provisioning status is correctly returned.
+ assertEquals(!provisioningStatus,
+ provisioningManager.getRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE));
+ // TODO: Enhance test to make sure the provisioning change is also sent to the
+ // ImsService
+
+ // set back to current status
+ provisioningManager.setRcsProvisioningStatusForCapability(RCS_CAP_PRESENCE,
+ provisioningStatus);
+ } finally {
+ automan.dropShellPermissionIdentity();
+ }
+
+ overrideCarrierConfig(null);
+ }
+
private void verifyIntKey(ProvisioningManager pm,
LinkedBlockingQueue<Pair<Integer, Integer>> intQueue, int key, int value)
throws Exception {
@@ -1700,6 +1739,10 @@
TestImsService.LATCH_CREATE_MMTEL));
assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
TestImsService.LATCH_MMTEL_READY));
+ int serviceSlot = sServiceConnector.getCarrierService().getMmTelFeature().getSlotIndex();
+ assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+ + "assigned slot (" + serviceSlot + "+ for the associated MmTelFeature",
+ sTestSlot, serviceSlot);
// Wait until ImsSmsDispatcher connects and calls onReady.
assertTrue(sServiceConnector.getCarrierService().getMmTelFeature().getSmsImplementation()
.waitForOnReadyLatch());
@@ -1729,6 +1772,10 @@
// Make sure the RcsFeature was created in the test service.
assertNotNull("Device ImsService created, but TestDeviceImsService#createRcsFeature was not"
+ "called!", sServiceConnector.getCarrierService().getRcsFeature());
+ int serviceSlot = sServiceConnector.getCarrierService().getRcsFeature().getSlotIndex();
+ assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+ + "assigned slot (" + serviceSlot + "+ for the associated RcsFeature",
+ sTestSlot, serviceSlot);
}
private void triggerFrameworkConnectToCarrierImsService() throws Exception {
@@ -1744,6 +1791,33 @@
.waitForLatchCountdown(TestImsService.LATCH_MMTEL_READY));
assertNotNull("ImsService created, but ImsService#createMmTelFeature was not called!",
sServiceConnector.getCarrierService().getMmTelFeature());
+ int serviceSlot = sServiceConnector.getCarrierService().getMmTelFeature().getSlotIndex();
+ assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+ + "assigned slot (" + serviceSlot + "+ for the associated MmTelFeature",
+ sTestSlot, serviceSlot);
+ }
+
+ // Waiting for ImsRcsManager to become public before implementing RegistrationManager,
+ // Duplicate these methods for now.
+ private void verifyRegistrationState(ImsRcsManager regManager, int expectedState)
+ throws Exception {
+ LinkedBlockingQueue<Integer> mQueue = new LinkedBlockingQueue<>();
+ assertTrue(ImsUtils.retryUntilTrue(() -> {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(regManager,
+ (m) -> m.getRegistrationState(getContext().getMainExecutor(), mQueue::offer));
+ return waitForIntResult(mQueue) == expectedState;
+ }));
+ }
+
+ // Waiting for ImsRcsManager to become public before implementing RegistrationManager,
+ // Duplicate these methods for now.
+ private void verifyRegistrationTransportType(ImsRcsManager regManager,
+ int expectedTransportType) throws Exception {
+ LinkedBlockingQueue<Integer> mQueue = new LinkedBlockingQueue<>();
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(regManager,
+ (m) -> m.getRegistrationTransportType(getContext().getMainExecutor(),
+ mQueue::offer));
+ assertEquals(expectedTransportType, waitForIntResult(mQueue));
}
private void verifyRegistrationState(RegistrationManager regManager, int expectedState)
diff --git a/tests/tests/tethering/AndroidTest.xml b/tests/tests/tethering/AndroidTest.xml
index 25051ba..d0a2bce 100644
--- a/tests/tests/tethering/AndroidTest.xml
+++ b/tests/tests/tethering/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Config for CTS Tethering test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="networking" />
+ <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
diff --git a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
index bbb9403..8665c7e 100644
--- a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -26,7 +26,6 @@
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
@@ -59,7 +58,10 @@
import android.net.cts.util.CtsNetUtils;
import android.net.wifi.WifiManager;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.annotation.NonNull;
@@ -676,6 +678,26 @@
mTM.requestLatestTetheringEntitlementResult(
TETHERING_WIFI, false, c -> c.run(), null);
} catch (IllegalArgumentException expect) { }
+
+ // Override carrier config to ignore entitlement check.
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, false);
+ overrideCarrierConfig(bundle);
+
+ // Verify that requestLatestTetheringEntitlementResult() can get entitlement
+ // result TETHER_ERROR_NO_ERROR due to provisioning bypassed.
+ assertEntitlementResult(listener -> mTM.requestLatestTetheringEntitlementResult(
+ TETHERING_WIFI, false, c -> c.run(), listener), TETHER_ERROR_NO_ERROR);
+
+ // Reset carrier config.
+ overrideCarrierConfig(null);
+ }
+
+ private void overrideCarrierConfig(PersistableBundle bundle) {
+ final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ final int subId = SubscriptionManager.getDefaultSubscriptionId();
+ configManager.overrideConfig(subId, bundle);
}
@Test
diff --git a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
index 1d3a5ed..7a512af 100644
--- a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
@@ -38,7 +38,9 @@
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -48,6 +50,7 @@
import android.widget.ProgressBar;
import android.widget.cts.util.TestUtils;
+import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ActivityTestRule;
@@ -696,6 +699,30 @@
assertEquals(maxHeight, mProgressBar.getMaxHeight());
}
+ @Test
+ public void testAnimationNotOverwriteNewProgress() throws Throwable {
+ mActivityRule.runOnUiThread(() -> {
+ mProgressBarHorizontal.setMin(0);
+ mProgressBarHorizontal.setMax(10);
+ mProgressBarHorizontal.setProgress(10, true);
+ mProgressBarHorizontal.setProgress(0, false);
+ });
+ // wait for completion of animation.
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ SystemClock.sleep(80);
+ assertEquals(0, mProgressBarHorizontal.getProgress());
+ // test level of progress drawable
+ Drawable progressDrawable = mProgressBarHorizontal.getCurrentDrawable();
+ if (progressDrawable instanceof LayerDrawable) {
+ Drawable d = ((LayerDrawable) progressDrawable)
+ .findDrawableByLayerId(android.R.id.progress);
+ if (d != null) {
+ progressDrawable = d;
+ }
+ }
+ assertEquals(0, progressDrawable.getLevel());
+ }
+
/*
* Mock class for ProgressBar to test protected methods
*/