Merge "Adding KEY_ALLOW_HOLD_VIDEO_CALL_BOOL is false" into main
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index ed09268..dcaa9bf 100755
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -355,7 +355,7 @@
logging.info('Capturing an image to check the test scene')
cap = cam.do_capture(req, fmt)
img = image_processing_utils.convert_capture_to_rgb_image(cap)
- img_name = os.path.join(out_path, f'test_{scene}.jpg')
+ img_name = os.path.join(out_path, f'test_{scene.replace("/", "_")}.jpg')
logging.info('Please check scene setup in %s', img_name)
image_processing_utils.write_image(img, img_name)
choice = input(f'Is the image okay for ITS {scene}? (Y/N)').lower()
diff --git a/apps/CtsVerifier/Android.bp b/apps/CtsVerifier/Android.bp
index a799080..dbccbf8 100644
--- a/apps/CtsVerifier/Android.bp
+++ b/apps/CtsVerifier/Android.bp
@@ -217,6 +217,7 @@
":CtsVerifier",
":camera-its",
":camera-webcam-test",
+ ":multidevice-test",
],
tools: [
"soong_zip",
@@ -225,7 +226,7 @@
out: ["android-cts-verifier.zip"],
cmd: "echo $(locations :cts_apps_to_include) $(location :CtsVerifier) $(location :android-cts-verifier-notice) > $(genDir)/list &&" +
" $(location soong_zip) -o $(genDir)/cts-verifier.zip -j -P android-cts-verifier -l $(genDir)/list &&" +
- " $(location merge_zips) $(out) $(genDir)/cts-verifier.zip $(location :camera-its) $(location :camera-webcam-test)",
+ " $(location merge_zips) $(out) $(genDir)/cts-verifier.zip $(location :camera-its) $(location :camera-webcam-test) $(location :multidevice-test)",
dists: [
{
targets: ["cts"],
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index c204518..b967e23 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2292,22 +2292,6 @@
<meta-data android:name="NonApiTest" android:value="Helper class. List test activities" />
</activity>
- <activity android:name="com.android.cts.verifier.nfc.hce.HceReaderTestActivity"
- android:label="@string/nfc_hce_reader_tests"
- android:configChanges="keyboardHidden|orientation|screenSize">
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="NonApiTest" android:value="Helper class. List test activities" />
- </activity>
-
- <activity android:name="com.android.cts.verifier.nfc.hce.HceEmulatorTestActivity"
- android:label="@string/nfc_hce_emulator_tests"
- android:configChanges="keyboardHidden|orientation|screenSize">
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="NonApiTest" android:value="Helper class. List test activities" />
- </activity>
-
<activity android:name="com.android.cts.verifier.nfc.hcef.HceFReaderTestActivity"
android:label="@string/nfc_hce_f_reader_tests"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -2348,279 +2332,6 @@
<meta-data android:name="CddTest" android:value="7.3.3/C-1-2" />
</activity>
- <activity android:name=".nfc.hce.DefaultRouteEmulatorActivity"
- android:label="@string/nfc_hce_default_route_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
-
- </activity>
-
- <activity android:name=".nfc.hce.ProtocolParamsEmulatorActivity"
- android:label="@string/nfc_hce_protocol_params_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.SinglePaymentEmulatorActivity"
- android:label="@string/nfc_hce_single_payment_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="3.2.3.5/C-3-2|7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.SimpleReaderActivity"
- android:label="@string/nfc_hce_single_payment_reader"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-1-2" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.NfcAdapter#enableReaderMode|
- android.nfc.NfcAdapter#disableReaderMode|
- android.nfc.tech.IsoDep#get|
- android.nfc.tech.IsoDep#connect|
- android.nfc.tech.IsoDep#setTimeout|
- android.nfc.tech.IsoDep#transceive" />
- </activity>
-
- <activity android:name=".nfc.hce.ProtocolParamsReaderActivity"
- android:label="@string/nfc_hce_protocol_params_reader"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-1-2" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.NfcAdapter#enableReaderMode|
- android.nfc.Tag#getId|
- android.nfc.tech.NfcA#close|
- android.nfc.tech.NfcA#connect|
- android.nfc.tech.NfcA#get|
- android.nfc.tech.NfcA#getAtqa|
- android.nfc.tech.NfcA#getSak|
- android.nfc.tech.NfcA#transceive" />
- </activity>
-
- <activity android:name=".nfc.hce.ObserveModeReaderActivity"
- android:label="@string/nfc_observe_mode_reader">
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.NfcAdapter#disallowTransaction"/>
- </activity>
-
- <activity android:name=".nfc.hce.ObserveModeEmulatorActivity"
- android:label="@string/nfc_observe_mode_emulator">
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="GmsTest" android:value="GMS-VSR-3.2.8-001" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.NfcAdapter#disallowTransaction|
- android.nfc.NfcAdapter#allowTransaction|
- android.nfc.cardemulation.HostApduService#KEY_POLLING_LOOP_TYPE|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_A|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_ON|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_OFF"/>
- </activity>
-
- <activity android:name=".nfc.hce.PollingLoopReaderActivity"
- android:label="@string/nfc_polling_loop_a_reader">
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="GmsTest" android:value="GMS-VSR-3.2.8-001" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.NfcAdapter#disallowTransaction|
- android.nfc.NfcAdapter#allowTransaction|
- android.nfc.cardemulation.HostApduService#KEY_POLLING_LOOP_TYPE|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_B|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_ON|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_OFF"/>
- </activity>
-
- <activity android:name=".nfc.hce.PollingLoopEmulatorActivity"
- android:label="@string/nfc_polling_loop_a_emulator">
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="GmsTest" android:value="GMS-VSR-3.2.8-001" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.NfcAdapter#disallowTransaction|
- android.nfc.NfcAdapter#allowTransaction|
- android.nfc.cardemulation.HostApduService#KEY_POLLING_LOOP_TYPE|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_A|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_B|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_ON|
- android.nfc.cardemulation.HostApduService#POLLING_LOOP_TYPE_OFF"/>
- </activity>
-
- <activity android:name=".nfc.hce.DualPaymentEmulatorActivity"
- android:label="@string/nfc_hce_dual_payment_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="3.2.3.5/C-3-2|7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.ChangeDefaultEmulatorActivity"
- android:label="@string/nfc_hce_change_default_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="3.2.3.5/C-3-2|7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.SingleNonPaymentEmulatorActivity"
- android:label="@string/nfc_hce_single_non_payment_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.DualNonPaymentEmulatorActivity"
- android:label="@string/nfc_hce_dual_non_payment_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.ConflictingNonPaymentEmulatorActivity"
- android:label="@string/nfc_hce_conflicting_non_payment_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.ForegroundNonPaymentEmulatorActivity"
- android:label="@string/nfc_hce_foreground_non_payment_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.cardemulation.CardEmulation#setPreferredService|
- android.nfc.cardemulation.CardEmulation#unsetPreferredService" />
- </activity>
-
- <activity android:name=".nfc.hce.ForegroundPaymentEmulatorActivity"
- android:label="@string/nfc_hce_foreground_payment_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="3.2.3.5/C-3-1,C-3-2|7.4.4/C-2-1,C-2-2" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.cardemulation.CardEmulation#categoryAllowsForegroundPreference" />
- </activity>
-
- <activity android:name=".nfc.hce.OffHostEmulatorActivity"
- android:label="@string/nfc_hce_offhost_service_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.OnAndOffHostEmulatorActivity"
- android:label="@string/nfc_hce_on_and_offhost_service_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.ThroughputEmulatorActivity"
- android:label="@string/nfc_hce_throughput_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.TapTestEmulatorActivity"
- android:label="@string/nfc_hce_tap_test_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.DynamicAidEmulatorActivity"
- android:label="@string/nfc_hce_payment_dynamic_aids_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="3.2.3.5/C-3-2|7.4.4/C-2-1,C-2-2" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.cardemulation.CardEmulation#registerAidsForService" />
- </activity>
-
- <activity android:name=".nfc.hce.LargeNumAidsEmulatorActivity"
- android:label="@string/nfc_hce_large_num_aids_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.cardemulation.CardEmulation#registerAidsForService" />
- </activity>
-
- <activity android:name=".nfc.hce.PrefixPaymentEmulatorActivity"
- android:label="@string/nfc_hce_payment_prefix_aids_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="3.2.3.5/C-3-2|7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.PrefixPaymentEmulator2Activity"
- android:label="@string/nfc_hce_payment_prefix_aids_emulator_2"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="3.2.3.5/C-3-2|7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.DualNonPaymentPrefixEmulatorActivity"
- android:label="@string/nfc_hce_other_prefix_aids_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.cardemulation.CardEmulation#registerAidsForService" />
- </activity>
-
- <activity android:name=".nfc.hce.ConflictingNonPaymentPrefixEmulatorActivity"
- android:label="@string/nfc_hce_other_conflicting_prefix_aids_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- <meta-data android:name="ApiTest"
- android:value="android.nfc.cardemulation.CardEmulation#registerAidsForService" />
- </activity>
-
- <activity android:name=".nfc.hce.ScreenOnOnlyOffHostEmulatorActivity"
- android:label="@string/nfc_screen_on_only_offhost_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="7.4.4/C-2-1,C-2-2" />
- </activity>
-
- <activity android:name=".nfc.hce.ScreenOffPaymentEmulatorActivity"
- android:label="@string/nfc_screen_off_hce_payment_emulator"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- <meta-data android:name="CddTest" android:value="3.2.3.5/C-3-2|7.4.4/C-2-1,C-2-2" />
- </activity>
-
<activity android:name=".nfc.hcef.HceFEmulatorActivity"
android:label="@string/nfc_hce_f_emulator"
android:configChanges="keyboardHidden|orientation|screenSize" >
@@ -2679,162 +2390,6 @@
android:value="android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED" />
</activity>
- <!-- services used for testing NFC host-based card emulation -->
- <service android:name=".nfc.hce.PaymentService1" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_1"/>
- </service>
- <service android:name=".nfc.hce.PaymentService2" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_2"/>
- </service>
- <service android:name=".nfc.hce.TransportService1" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/transport_aid_list_1"/>
- </service>
- <service android:name=".nfc.hce.TransportService2" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/transport_aid_list_2"/>
- </service>
- <service android:name=".nfc.hce.AccessService" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/access_aid_list"/>
- </service>
- <service android:name=".nfc.hce.ThroughputService" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/throughput_aid_list"/>
- </service>
-
- <service android:name=".nfc.hce.OffHostService" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service" android:resource="@xml/offhost_aid_list"/>
- </service>
- <service android:name=".nfc.hce.PaymentServiceDynamicAids" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_1"/>
- </service>
- <service android:name=".nfc.hce.PrefixPaymentService1" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_prefix_aid_list"/>
- </service>
- <service android:name=".nfc.hce.PrefixPaymentService2" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_prefix_aid_list_2"/>
- </service>
- <service android:name=".nfc.hce.PrefixTransportService1" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/transport_prefix_aid_list_1"/>
- </service>
- <service android:name=".nfc.hce.PrefixTransportService2" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/transport_prefix_aid_list_2"/>
- </service>
- <service android:name=".nfc.hce.PrefixAccessService" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/access_prefix_aid_list"/>
- </service>
- <service android:name=".nfc.hce.LargeNumAidsService" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_1"/>
- </service>
- <service android:name=".nfc.hce.ScreenOnOnlyOffHostService" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service" android:resource="@xml/screen_on_only_offhost_aid_list"/>
- </service>
- <service android:name=".nfc.hce.ScreenOffPaymentService" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/screen_off_payment_aid_list"/>
- </service>
- <service android:name=".nfc.hce.PollingLoopService" android:exported="true"
- android:permission="android.permission.BIND_NFC_SERVICE"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/access_aid_list"/>
- </service>
-
<service
android:name=".nfc.hcef.MyHostFelicaService"
android:enabled="true"
@@ -4786,6 +4341,18 @@
android:value="android.provider.AlarmClock#ACTION_SHOW_ALARMS|android.provider.AlarmClock#ACTION_SET_ALARM|android.provider.AlarmClock#ACTION_SET_TIMER" />
</activity>
+ <activity android:name=".multidevice.MultiDeviceTestsActivity"
+ android:exported="true"
+ android:label="@string/nfc_tests_title">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_multidevice" />
+ <meta-data android:name="display_mode"
+ android:value="single_display_mode" />
+ </activity>
+
<!-- TODO: enable when not requiring to tap the screen and timeouts are tuned -->
<!-- Removed from initial L release
diff --git a/apps/CtsVerifier/res/layout-small/positive_device_owner.xml b/apps/CtsVerifier/res/layout-small/positive_device_owner.xml
index 6a9274c..719f58a 100644
--- a/apps/CtsVerifier/res/layout-small/positive_device_owner.xml
+++ b/apps/CtsVerifier/res/layout-small/positive_device_owner.xml
@@ -43,7 +43,7 @@
<ListView
android:id="@+id/android:list"
android:layout_width="match_parent"
- android:layout_height="1200dp"/>
+ android:layout_height="1400dp"/>
</LinearLayout>
</ScrollView>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 420e63d..958d618 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -84,6 +84,7 @@
<string name="test_category_display_cutout">DisplayCutout</string>
<string name="test_category_other">Other</string>
<string name="test_category_device_controls">Device controls</string>
+ <string name="test_category_multidevice">Multi Device</string>
<string name="clear">Clear</string>
<string name="test_results_clear_title">Remove all test results?</string>
<string name="test_results_clear_yes">Yes</string>
@@ -1286,11 +1287,8 @@
<string name="nfc_result_message">Written data:\n%1$s\n\nRead data:\n%2$s</string>
<string name="nfc_ndef_content">Id: %1$s\nMime: %2$s\nPayload: %3$s</string>
- <string name="nfc_hce">Host-based card emulation</string>
<string name="nfc_hce_f">Host-based Felica card emulation</string>
- <string name="nfc_hce_reader_tests">HCE reader tests</string>
<string name="nfc_hce_f_reader_tests">HCE Felica reader tests</string>
- <string name="nfc_hce_emulator_tests">HCE emulator tests</string>
<string name="nfc_hce_f_emulator_tests">HCE Felica emulator tests</string>
<string name="nfc_hce_f_emulator">HCE Felica emulator</string>
<string name="nfc_hce_f_reader">HCE Felica reader</string>
@@ -1309,131 +1307,9 @@
in the "HCE reader tests" section. The "reader test" acts as a NFC terminal/POS
and makes sure the device-under-test implements card emulation correctly.
</string>
- <string name="nfc_hce_type_selection">By default HCE applications must run on type A. If your device is restricted to type B (for example, because of a type B only UICC), select "Type B" from the drop-down box above. Note that all tests must be completed in the same mode (either "Type A" or "Type B").</string>
- <string name="nfc_hce_please_wait">Please wait</string>
- <string name="nfc_hce_setting_up">Setting up card emulation services...</string>
- <string name="nfc_hce_default_route_emulator">Default route (Emulator)</string>
- <string name="nfc_hce_default_route_reader">Default route (Reader)</string>
- <string name="nfc_hce_default_route_emulator_help">This test verifies that the default route for ISO-DEP (ISO14443-4) frames is the host CPU. It does this by selecting an AID that any Android HCE phone will respond to if the select command is routed to the host. Please verify that there is no rule in the routing table that points this AID to the host. This test may be passed if the "PASS" button on the reader side lights up after tapping the devices together.</string>
-
- <string name="nfc_hce_protocol_params_emulator">Protocol parameters (Emulator)</string>
- <string name="nfc_hce_protocol_params_reader">Protocol parameters (Reader)</string>
- <string name="nfc_hce_protocol_params_emulator_help">This test verifies that the Nfc-A and ISO-DEP protocol parameters are being set correctly. The test may be passed when no FAIL entries show up in the results below. Note that the reader device may be different from the device under test - the DUT itself does not need to be able to act as the reader for this test. \n\n Note that for each test there are 3 possible outcomes:\n1) OK -> test has passed;\n2) FAIL -> test has failed and this must be fixed;\n3) FAIL EMVCO -> this protocol parameter is deviating from the requirements in the EMV Contactless Communication Protocol specification.\n\nWhile it is allowed to ship a HCE implementation with EMVCo failures, it may not perform optimal when EMVco based payment HCE apps are run on the device.</string>
-
- <string name="nfc_hce_single_payment_emulator">Single payment (Emulator)</string>
- <string name="nfc_hce_single_payment_reader">Single payment (Reader)</string>
-
- <string name="nfc_hce_dual_payment_emulator">Two payment services (Emulator)</string>
- <string name="nfc_hce_dual_payment_reader">Two payment services (Reader)</string>
-
- <string name="nfc_hce_change_default_emulator">Change default payment service (Emulator)</string>
- <string name="nfc_hce_change_default_reader">Change default payment service (Reader)</string>
-
- <string name="nfc_hce_tap_reader_title">Tap reader</string>
- <string name="nfc_hce_tap_reader_message">Select the corresponding reader test on the remote device, click OK on this dialog, and tap devices together. The pass button will be enabled if the test passes.</string>
-
- <string name="nfc_hce_single_non_payment_emulator">Single non-payment (Emulator)</string>
- <string name="nfc_hce_single_non_payment_reader">Single non-payment (Reader)</string>
-
- <string name="nfc_hce_dual_non_payment_emulator">Two non-payment services (Emulator)</string>
- <string name="nfc_hce_dual_non_payment_reader">Two non-payment services (Reader)</string>
-
- <string name="nfc_hce_conflicting_non_payment_emulator">Two conflicting non-payment services (Emulator)</string>
- <string name="nfc_hce_conflicting_non_payment_reader">Two conflicting non-payment services (Reader)</string>
-
- <string name="nfc_hce_foreground_non_payment_emulator">Foreground override non-payment services (Emulator)</string>
- <string name="nfc_hce_foreground_non_payment_reader">Foreground override non-payment services (Reader)</string>
- <string name="nfc_hce_foreground_non_payment_help">This test enables two non-payment services with conflicting AIDs. It then uses Androids API to allow the foreground app to set a preference for a specific service. This should prevent a popup dialog from showing. If you see a popup dialog during this asking you to select an app, this test has failed.</string>
-
- <string name="nfc_hce_foreground_payment_emulator">Foreground override payment services (Emulator)</string>
- <string name="nfc_hce_foreground_payment_reader">Foreground override payment services (Reader)</string>
- <string name="nfc_hce_foreground_payment_help">This test enables two payment services, and asks you to set one as the default service. It then uses Androids API to allow the foreground app to set a preference for the non-default service. This will cause the non-default payment service to be invoked.</string>
- <string name="nfc_hce_change_favor_foreground">This test requires the \"Use default\" setting to be set to \"Except when another payment app is open\". Tap OK to go to Tap and Pay settings and make sure the \"Use default\" setting is set to \"Except when another payment app is open\".</string>
- <string name="nfc_hce_offhost_service_emulator">Off-host service (Emulator)</string>
- <string name="nfc_hce_offhost_service_reader">Off-host service (Reader)</string>
- <string name="nfc_hce_offhost_emulator_help">This test enables a service that declares some AIDs to reside off-host. This test only needs to be passed if your device has a secure element (either embedded or UICC.). The responses from the secure element are not verified by the test - it is up to the tester to verify the responses are as expected.</string>
-
- <string name="nfc_hce_on_and_offhost_service_emulator">On and off-host services (Emulator)</string>
- <string name="nfc_hce_on_and_offhost_service_reader">On and off-host services (Reader)</string>
- <string name="nfc_hce_on_and_offhost_emulator_help">This test enables a service that declares some AIDs to reside off-host, and another service that runs on host. It then first sends an APDU sequence that goes off-host, and subsequently some APDUs that should go to the on-host service. This test only needs to be passed if your device has a secure element (either embedded or UICC.). The pass button will be enabled if the on-host sequence is performed as expected. The responses from the secure element are not verified by the test - it is up to the tester to verify the responses are as expected. </string>
-
- <string name="nfc_hce_tap_test_emulator">50 successful taps test (Emulator)</string>
- <string name="nfc_hce_tap_test_reader">50 successful taps test (Reader)</string>
- <string name="nfc_hce_tap_test_emulator_help">This test requires you to complete at least 50 HCE taps, to ensure stability of the HCE feature. The NFC service and controller should not crash or hang during any of the 50 taps.</string>
- <string name="nfc_hce_throughput_emulator">HCE throughput test (Emulator)</string>
- <string name="nfc_hce_throughput_reader">HCE throughput test (Reader)</string>
- <string name="nfc_hce_throughput_emulator_help">This test verifies that your HCE implementation can reach a decent throughput. While Android does not have any requirements on HCE performance, many HCE applications such as transport and payment apps do. If the average APDU roundtrip time is more than 50ms, please take a look at your implementation to see where the delay is coming from.</string>
- <string name="nfc_hce_change_preinstalled_wallet">The device has an installed payment application that is currently set as default. To complete the test, you will be asked whether you want to make another app the default app. Select yes.</string>
- <string name="nfc_hce_change_default_wallet">The device has an installed Wallet application that is currently set as default. To complete the test, you will be asked whether you want to make another app the default Wallet app. Select yes. Then make sure to select CTSVerifier app. Then hit OK. </string>
- <string name="nfc_hce_change_default_help">You will now be asked whether you want to make Payment Service #1 the default app. Select yes.</string>
- <string name="nfc_hce_conflicting_non_payment_help">When tapping the first time, you will be shown a dialog that asks you to choose between TransportService #1 and TransportService #2. Select TransportService #2. Verify a dialog is shown that asks you to tap again to complete. Now tap again, and if communication with TransportService #2 is successfull the pass button will be enabled."</string>
-
- <string name="nfc_hce_payment_dynamic_aids_emulator">Dynamic payment AIDs (Emulator)</string>
- <string name="nfc_hce_payment_dynamic_aids_reader">Dynamic payment AIDs (Reader)</string>
- <string name="nfc_hce_payment_dynamic_aids_help">This test tries to register dynamic AIDs for a payment service.</string>
-
- <string name="nfc_hce_large_num_aids_emulator">Large number of AIDs (Emulator)</string>
- <string name="nfc_hce_large_num_aids_reader">Large number of AIDs (Reader)</string>
- <string name="nfc_hce_large_num_aids_help">This test tries to register a large number of different AIDs, to make sure there are no limitations on the maximum amount of HCE apps on the device. Note that this test may take a few seconds to complete; please be patient.</string>
-
- <string name="nfc_hce_payment_prefix_aids_emulator">Payment prefix AIDs (Emulator)</string>
- <string name="nfc_hce_payment_prefix_aids_reader">Payment prefix AIDs (Reader)</string>
- <string name="nfc_hce_payment_prefix_aids_help">This test statically registers prefix AIDs for a payment service.</string>
-
- <string name="nfc_hce_payment_prefix_aids_emulator_2">Payment prefix AIDs 2 (Emulator)</string>
- <string name="nfc_hce_payment_prefix_aids_reader_2">Payment prefix AIDs 2 (Reader)</string>
-
- <string name="nfc_observe_mode_emulator">Observe Mode (Emulator)</string>
- <string name="nfc_observe_mode_reader">Observe Mode (Reader)</string>
- <string name="nfc_observe_mode_emulator_help">In observe mode there should not be any response from the emulator to the reader. If the emulator detects the reader and does not receive a command, it will enable the pass button and that represents a pass or the test.</string>
- <string name="nfc_observe_mode_reader_help">In observe mode there should not be any response from the emulator to the reader. If the reader detects a response it will disable the pass button and this is a failure.</string>
-
- <string name="nfc_polling_loop_a_emulator">Polling Loop A (Emulator)</string>
- <string name="nfc_polling_loop_a_reader">Polling Loop A (Reader)</string>
- <string name="nfc_polling_loop_b_emulator">Polling Loop B (Emulator)</string>
- <string name="nfc_polling_loop_b_reader">Polling Loop B (Reader)</string>
- <string name="nfc_polling_loop_ab_emulator">Polling Loop A and B (Emulator)</string>
- <string name="nfc_polling_loop_custom_emulator">Polling Loop Custom (Emulator)</string>
- <string name="nfc_polling_loop_ab_reader">Polling Loop A and B (Reader)</string>
- <string name="nfc_polling_loop_a_emulator_help">The emulator will wait until it sees 3 NFC-A polling frames before responding. If it sees another type of frame, it will fail.</string>
- <string name="nfc_polling_loop_a_reader_help">The reader will poll for NFC-A.</string>
- <string name="nfc_polling_loop_b_emulator_help">The emulator will wait until it sees 3 NFC-B polling frames before responding. If it sees another type of frame, it will fail.</string>
- <string name="nfc_polling_loop_b_reader_help">The reader will poll for NFC-B.</string>
- <string name="nfc_polling_loop_ab_emulator_help">The emulator will wait until it sees 3 NFC-A polling frames and 3 NFC-B polling frames before responding. If it sees another type of frame, it will fail.</string>
- <string name="nfc_polling_loop_ab_reader_help">The reader will poll for NFC-A and NFC-B.</string>
- <string name="nfc_polling_loop_custom_emulator_help">Please connect the PN532 to your linux machine and follow the instructions. The emulator will wait until it sees the specified custom polling frame.</string>
-
- <string name="nfc_hce_other_prefix_aids_emulator">Other prefix AIDs (Emulator)</string>
- <string name="nfc_hce_other_prefix_aids_reader">Other prefix AIDs (Reader)</string>
- <string name="nfc_hce_other_prefix_aids_help">This test dynamically registers prefix AIDs for a non-payment service.</string>
-
- <string name="nfc_hce_other_conflicting_prefix_aids_emulator">Conflicting non-payment prefix AIDs (Emulator)</string>
- <string name="nfc_hce_other_conflicting_prefix_aids_reader">Conflicting non-payment prefix AIDs (Reader)</string>
- <string name="nfc_hce_other_conflicting_prefix_aids_help">This test registers conflicting prefix AIDs and makes sure AID conflict detection with prefix AIDs works properly. When tapping the first time, you will be shown a dialog that asks you to choose between TransportService #1 and TransportService #2. Select TransportService #2. Verify a dialog is shown that asks you to tap again to complete. Now tap again, and if communication with TransportService #2 is successfull the pass button will be enabled."</string>
-
- <string name="nfc_screen_on_only_offhost_emulator">Screen on only Off-host Service (Emulator)</string>
- <string name="nfc_screen_on_only_offhost_reader">Screen on only Off-host Service (Reader)</string>
- <string name="nfc_screen_on_only_offhost_help">This test enables a service that declares some AIDs to reside off-host. This test only needs to be passed if your device has a secure element. The responses from the secure element are not verified by the test - it is up to the tester to verify the responses are as expected. The expected behavior is the AID can be selected only when the device is in screen on state.</string>
-
- <string name="nfc_screen_off_hce_payment_emulator">Screen off HCE Payment (Emulator)</string>
- <string name="nfc_screen_off_hce_payment_reader">Screen off HCE Payment (Reader)</string>
- <string name="nfc_screen_off_hce_payment_help">This test verifies emulator responds when device is in screen off state. Please put the emulator to screen off state.</string>
-
- <string name="nfc_payment_service_desc">NFC Payment service</string>
- <string name="ppse">PPSE</string>
- <string name="mastercard">MasterCard</string>
- <string name="visa">Visa</string>
- <string name="paymentService1">Payment Service #1</string>
- <string name="paymentService2">Payment Service #2</string>
- <string name="transportService1">TransportService #1</string>
- <string name="transportService2">TransportService #2</string>
- <string name="accessService">AccessService</string>
- <string name="offhostService">OffhostService</string>
<string name="felicaservice">Felica Service</string>
<string name="UiccTransactionEventService">Uicc Transaction Event Service</string>
- <string name="screenOffPaymentService">Screen Off Payment Service</string>
- <string name="screenOnOnlyOffHostService">Screen On Only OffHost Service</string>
<string name="nfc_offhost_uicc">Offhost UICC-based card emulation</string>
<string name="nfc_offhost_uicc_emulator_tests">Offhost card emulation emulator tests</string>
@@ -3310,6 +3186,18 @@
\nThen click the \"Change password\" button to change it. You should be prompted for the current password first. If you are not, then mark the test as failed.
</string>
+ <!-- Strings for NFC Tests. -->
+ <string name="nfc_tests_title">NFC Tests</string>
+ <string name="nfc_tests_dialog_title">NFC Tests Dialog</string>
+ <string name="nfc_tests_dialog_content">
+ The NFC tests.\n
+ The DUT \"must\" be connected to a host to run these tests.\n
+ Please follow the detailed instruction of the tests to execute scripts and get results.\n
+ The tests will be set as passing if the DUT is not supported with NFC.\n
+ See the instruction in the following link.\n
+ https://source.android.com/docs/compatibility/cts/ctsv-multidevice
+ </string>
+
<!-- String for Projection Tests -->
<string name="test_category_projection">Projection Tests</string>
<string name="projection_service_name">Projection Service</string>
diff --git a/apps/CtsVerifier/res/xml/access_aid_list.xml b/apps/CtsVerifier/res/xml/access_aid_list.xml
deleted file mode 100644
index 1303f13..0000000
--- a/apps/CtsVerifier/res/xml/access_aid_list.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/transportService1">
- <aid-group>
- <aid-filter android:name="F005060708"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/access_prefix_aid_list.xml b/apps/CtsVerifier/res/xml/access_prefix_aid_list.xml
deleted file mode 100644
index 915c041..0000000
--- a/apps/CtsVerifier/res/xml/access_prefix_aid_list.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/accessService">
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/empty_aid_list.xml b/apps/CtsVerifier/res/xml/empty_aid_list.xml
deleted file mode 100644
index 0646fd9..0000000
--- a/apps/CtsVerifier/res/xml/empty_aid_list.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/transportService1">
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/offhost_aid_list.xml b/apps/CtsVerifier/res/xml/offhost_aid_list.xml
deleted file mode 100644
index 8329955..0000000
--- a/apps/CtsVerifier/res/xml/offhost_aid_list.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/offhostService">
- <aid-group>
- <aid-filter android:name="A000000151000000"/>
- <aid-filter android:name="A000000444000000"/>
- </aid-group>
-</offhost-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/payment_aid_list_1.xml b/apps/CtsVerifier/res/xml/payment_aid_list_1.xml
deleted file mode 100644
index 37344ff..0000000
--- a/apps/CtsVerifier/res/xml/payment_aid_list_1.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/paymentService1"
- android:apduServiceBanner="@drawable/nfc_hce_banner">
- <aid-group android:description="@string/paymentService1" android:category="payment">
- <aid-filter android:name="325041592E5359532E4444463031"
- android:description="@string/ppse"/>
- <aid-filter android:name="A0000000041010"
- android:description="@string/mastercard"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/payment_aid_list_2.xml b/apps/CtsVerifier/res/xml/payment_aid_list_2.xml
deleted file mode 100644
index 7d60988..0000000
--- a/apps/CtsVerifier/res/xml/payment_aid_list_2.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/paymentService2"
- android:apduServiceBanner="@drawable/nfc_hce_banner">
- <aid-group android:description="@string/paymentService2" android:category="payment">
- <aid-filter android:name="325041592E5359532E4444463031"
- android:description="@string/ppse"/>
- <aid-filter android:name="A0000000041010"
- android:description="@string/mastercard"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/payment_empty_aid_list.xml b/apps/CtsVerifier/res/xml/payment_empty_aid_list.xml
deleted file mode 100644
index 6daf5ea..0000000
--- a/apps/CtsVerifier/res/xml/payment_empty_aid_list.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/paymentService1" android:apduServiceBanner="@drawable/nfc_hce_banner">
- <aid-group android:description="@string/paymentService1" android:category="payment">
- <aid-filter android:name="325041592E5359532E4444463031"
- android:description="@string/ppse"/>
- <aid-filter android:name="A0000000041010"
- android:description="@string/mastercard"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/payment_prefix_aid_list.xml b/apps/CtsVerifier/res/xml/payment_prefix_aid_list.xml
deleted file mode 100644
index cea5600..0000000
--- a/apps/CtsVerifier/res/xml/payment_prefix_aid_list.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/paymentService1" android:apduServiceBanner="@drawable/nfc_hce_banner">
- <aid-group android:description="@string/paymentService1" android:category="payment">
- <aid-filter android:name="325041592E5359532E4444463031"
- android:description="@string/ppse"/>
- <aid-prefix-filter android:name="A000000004"
- android:description="@string/mastercard"/>
- <aid-prefix-filter android:name="A000000003"
- android:description="@string/visa"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/payment_prefix_aid_list_2.xml b/apps/CtsVerifier/res/xml/payment_prefix_aid_list_2.xml
deleted file mode 100644
index 67b4d90..0000000
--- a/apps/CtsVerifier/res/xml/payment_prefix_aid_list_2.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/paymentService1" android:apduServiceBanner="@drawable/nfc_hce_banner">
- <aid-group android:description="@string/paymentService1" android:category="payment">
- <aid-filter android:name="325041592E5359532E4444463031"
- android:description="@string/ppse"/>
- <aid-prefix-filter android:name="A00000000410"
- android:description="@string/mastercard"/>
- <aid-prefix-filter android:name="A0000000041010"
- android:description="@string/mastercard"/>
- <aid-prefix-filter android:name="A00000000310"
- android:description="@string/visa"/>
- </aid-group>
- <aid-group android:description="@string/transportService1" android:category="other">
- <aid-prefix-filter android:name="F0000000FF"
- android:description="@string/transportService1"/>
- <aid-prefix-filter android:name="F000000000"
- android:description="@string/transportService1"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/screen_off_payment_aid_list.xml b/apps/CtsVerifier/res/xml/screen_off_payment_aid_list.xml
deleted file mode 100644
index 562a5cd..0000000
--- a/apps/CtsVerifier/res/xml/screen_off_payment_aid_list.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/screenOffPaymentService"
- android:requireDeviceScreenOn="false"
- android:requireDeviceUnlock="false"
- android:apduServiceBanner="@drawable/nfc_hce_banner">
- <aid-group android:description="@string/screenOffPaymentService" android:category="payment">
- <aid-filter android:name="325041592E5359532E4444463031"
- android:description="@string/ppse"/>
- <aid-filter android:name="A0000000041010"
- android:description="@string/mastercard"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/screen_on_only_offhost_aid_list.xml b/apps/CtsVerifier/res/xml/screen_on_only_offhost_aid_list.xml
deleted file mode 100644
index 318399b..0000000
--- a/apps/CtsVerifier/res/xml/screen_on_only_offhost_aid_list.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/screenOnOnlyOffHostService"
- android:requireDeviceScreenOn="true"
- android:requireDeviceUnlock="true"
- android:secureElementName="SIM">
- <aid-group
- android:category="other"
- android:description="@string/screenOnOnlyOffHostService" >
-
- <aid-filter android:name="A000000476416E64726F696443545340"/>
- </aid-group>
-</offhost-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/throughput_aid_list.xml b/apps/CtsVerifier/res/xml/throughput_aid_list.xml
deleted file mode 100644
index 7f49eb3..0000000
--- a/apps/CtsVerifier/res/xml/throughput_aid_list.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/transportService1">
- <aid-group>
- <aid-filter android:name="F0010203040607FF"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/transport_aid_list_1.xml b/apps/CtsVerifier/res/xml/transport_aid_list_1.xml
deleted file mode 100644
index 9396391..0000000
--- a/apps/CtsVerifier/res/xml/transport_aid_list_1.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/transportService1">
- <aid-group>
- <aid-filter android:name="F001020304"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/transport_aid_list_2.xml b/apps/CtsVerifier/res/xml/transport_aid_list_2.xml
deleted file mode 100644
index 9e20ff3..0000000
--- a/apps/CtsVerifier/res/xml/transport_aid_list_2.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/transportService2">
- <aid-group>
- <aid-filter android:name="F001020304"/>
- </aid-group>
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/transport_prefix_aid_list_1.xml b/apps/CtsVerifier/res/xml/transport_prefix_aid_list_1.xml
deleted file mode 100644
index 0646fd9..0000000
--- a/apps/CtsVerifier/res/xml/transport_prefix_aid_list_1.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/transportService1">
-</host-apdu-service>
diff --git a/apps/CtsVerifier/res/xml/transport_prefix_aid_list_2.xml b/apps/CtsVerifier/res/xml/transport_prefix_aid_list_2.xml
deleted file mode 100644
index 44b1772..0000000
--- a/apps/CtsVerifier/res/xml/transport_prefix_aid_list_2.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/transportService2">
-</host-apdu-service>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ArrayTestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ArrayTestListAdapter.java
index 29440f0..838ca3d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ArrayTestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ArrayTestListAdapter.java
@@ -22,8 +22,8 @@
import java.util.List;
/**
- * {@link TestListAdapter} that works like {@link android.widget.ArrayAdapter}
- * where items can be added by calling {@link #add(TestListItem)} repeatedly.
+ * {@link TestListAdapter} that works like {@link android.widget.ArrayAdapter} where items can be
+ * added by calling {@link #add(TestListItem)} or {@code #addAll(List<TestListItem>)}.
*/
public class ArrayTestListAdapter extends TestListAdapter {
@@ -33,13 +33,20 @@
super(context);
}
+ @Override
+ protected List<TestListItem> getRows() {
+ return mRows;
+ }
+
+ /** Adds a new {@link TestListItem} to the adapter. */
public void add(TestListItem item) {
mRows.add(item);
notifyDataSetChanged();
}
- @Override
- protected List<TestListItem> getRows() {
- return mRows;
+ /** Adds a list of {@link TestListItem}s to the adapter. */
+ public void addAll(List<TestListItem> items) {
+ mRows.addAll(items);
+ notifyDataSetChanged();
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/HostTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/HostTestsActivity.java
new file mode 100644
index 0000000..7bd98c6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/HostTestsActivity.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.DataSetObserver;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ListView;
+
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * General activity to support host-side tests in CtsVerifier.
+ *
+ * <ul>
+ * <li>Show a list of tests.
+ * <li>Register a BroadcastReceiver to recod results of tests executed on the host.
+ * <li>Parse and update the results of the tests.
+ * </ul>
+ */
+public class HostTestsActivity extends PassFailButtons.TestListActivity {
+
+ private static final String TAG = "HostTestsActivity";
+ // The action to identify the broadcast Intent.
+ private static final String ACTION_HOST_TEST_RESULT =
+ "com.android.cts.verifier.ACTION_HOST_TEST_RESULT";
+ // The key of the test results passed as the extra data of a broadcast Intent.
+ private static final String EXTRA_HOST_TEST_RESULT =
+ "com.android.cts.verifier.extra.HOST_TEST_RESULT";
+ // The key for a test result in the JSON data.
+ private static final String TEST_RESULT_KEY = "result";
+ // Represents a pass test result.
+ private static final String TEST_RESULT_PASS = "PASS";
+ // Represents a fail test result.
+ private static final String TEST_RESULT_FAIL = "FAIL";
+
+ /** Represents a host-side test case in CtsVerifier. It's a test without an {@link Intent}. */
+ public static final class HostTestListItem extends TestListItem {
+
+ /**
+ * Creates a test case shown in the UI with required test name and ID.
+ *
+ * @param testName name of the test shown in the UI
+ * @param testId ID of the test to record its result in test report
+ */
+ public HostTestListItem(String testName, String testId) {
+ super(
+ testName,
+ testId,
+ /* intent= */ null,
+ /* requiredFeatures= */ null,
+ /* excludedFeatures= */ null,
+ /* applicableFeatures= */ null);
+ }
+
+ @Override
+ boolean isTest() {
+ return true;
+ }
+ }
+
+ /** Represents a category and its belonging tests. */
+ public static final class HostTestCategory {
+ // The title of the category.
+ private final String mTitle;
+ // Test name -> test ID mappings of all tests of this category.
+ private final TreeMap<String, String> mTests;
+ // IDs of all tests of this category.
+ private final Set<String> mTestIds;
+
+ public HostTestCategory(String title) {
+ mTitle = title;
+ mTests = new TreeMap<>();
+ mTestIds = new HashSet<>();
+ }
+
+ /** Adds a test that belongs to this category. */
+ public HostTestCategory addTest(String testName, String testId) {
+ mTests.put(testName, testId);
+ mTestIds.add(testId);
+ return this;
+ }
+
+ /** Generates a list of {@link TestListItem}s to render this test category in the UI. */
+ public List<TestListItem> generateTestListItems() {
+ List<TestListItem> testListItems = new ArrayList<>();
+ testListItems.add(TestListItem.newCategory(mTitle));
+ for (Map.Entry<String, String> entry : mTests.entrySet()) {
+ testListItems.add(new HostTestListItem(entry.getKey(), entry.getValue()));
+ }
+ return testListItems;
+ }
+
+ /** Gets the IDs of all tests that belong to this test category. */
+ public Set<String> getTestIds() {
+ return mTestIds;
+ }
+ }
+
+ /**
+ * The {@link BroadcastReceiver} to receive the broadcast {@link Intent} to update
+ * status/results of tests.
+ *
+ * <p>The result data is in JSON format by default, a sample result data is: { "test_id_1": {
+ * "result":"PASS" }, "test_id_2": { "result":"FAIL" } }
+ */
+ public final class ResultsReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_HOST_TEST_RESULT.equals(intent.getAction())) {
+ Log.i(TAG, "Parsing test results...");
+ String testResults = intent.getStringExtra(EXTRA_HOST_TEST_RESULT);
+ if (testResults == null || testResults.isEmpty()) {
+ Log.i(TAG, EXTRA_HOST_TEST_RESULT + " is empty in the Intent.");
+ return;
+ }
+ JSONObject jsonResults;
+ try {
+ jsonResults = new JSONObject(testResults);
+ } catch (JSONException e) {
+ Log.e(TAG, "Error parsing json result string: " + testResults, e);
+ return;
+ }
+ Log.i(TAG, "Parsed test results: " + jsonResults.toString());
+ Iterator<String> testIds = jsonResults.keys();
+ while (testIds.hasNext()) {
+ String testId = testIds.next();
+ if (!mAllTestIds.contains(testId)) {
+ Log.e(
+ TAG,
+ "Unknown test ID "
+ + testId
+ + " that doesn't belong to this activity.");
+ continue;
+ }
+ String result;
+ try {
+ result = jsonResults.getJSONObject(testId).getString(TEST_RESULT_KEY);
+ } catch (JSONException e) {
+ Log.e(TAG, "Error getting result of test " + testId);
+ continue;
+ }
+ if (TEST_RESULT_PASS.equals(result)) {
+ updateTestResult(TestResult.TEST_RESULT_PASSED, testId);
+ } else if (TEST_RESULT_FAIL.equals(result)) {
+ updateTestResult(TestResult.TEST_RESULT_FAILED, testId);
+ } else {
+ Log.e(TAG, "Unrecognized result " + result + " for test " + testId);
+ }
+ }
+ } else {
+ Log.e(TAG, "Unknown Intent action " + intent.getAction());
+ }
+ }
+ }
+
+ // The resource ID of the title of the dialog to show when entering the activity first time.
+ private final int mTitleId;
+ // The resource ID of the message of the dialog to show when entering the activity first time.
+ private final int mMessageId;
+ // All test categories to render in this activity.
+ private final List<HostTestCategory> mHostTestCategories;
+ // IDs of all tests belong to this activity.
+ private final Set<String> mAllTestIds;
+ // The adapter to render all tests in a list.
+ private ArrayTestListAdapter mTestListAdapter;
+ // The receiver to update test results via broadcast.
+ private final ResultsReceiver mResultsReceiver = new ResultsReceiver();
+ private boolean mReceiverRegistered = false;
+
+ public HostTestsActivity(int titleId, int messageId, HostTestCategory... hostTestCategories) {
+ mTitleId = titleId;
+ mMessageId = messageId;
+ mHostTestCategories = new ArrayList<>(Arrays.asList(hostTestCategories));
+ mAllTestIds = new HashSet<>();
+ for (HostTestCategory testCategory : hostTestCategories) {
+ mAllTestIds.addAll(testCategory.getTestIds());
+ }
+ }
+
+ @Override
+ protected void handleItemClick(ListView l, View v, int position, long id) {
+ // Does nothing. All tests are executed in host side.
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.pass_fail_list);
+ setInfoResources(mTitleId, mMessageId, 0);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ mTestListAdapter = new ArrayTestListAdapter(this);
+ for (HostTestCategory testCategory : mHostTestCategories) {
+ mTestListAdapter.addAll(testCategory.generateTestListItems());
+ }
+ mTestListAdapter.registerDataSetObserver(
+ new DataSetObserver() {
+
+ @Override
+ public void onChanged() {
+ updatePassButton();
+ }
+ });
+ setTestListAdapter(mTestListAdapter);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ Log.i(TAG, "Registering broadcast receivers...");
+ IntentFilter filter = new IntentFilter(ACTION_HOST_TEST_RESULT);
+ registerReceiver(mResultsReceiver, filter, Context.RECEIVER_EXPORTED);
+ mReceiverRegistered = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ Log.i(TAG, "Unregistering broadcast receivers...");
+ if (mReceiverRegistered) {
+ unregisterReceiver(mResultsReceiver);
+ mReceiverRegistered = false;
+ }
+ }
+
+ /** Updates a test with the given test result. */
+ private void updateTestResult(int testResult, String testId) {
+ Intent resultIntent = new Intent();
+ TestResult.addResultData(
+ resultIntent,
+ testResult,
+ testId,
+ /* testDetails= */ null,
+ /* reportLog= */ null,
+ /* historyCollection= */ null);
+ handleLaunchTestResult(RESULT_OK, resultIntent);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/multidevice/MultiDeviceTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/multidevice/MultiDeviceTestsActivity.java
new file mode 100644
index 0000000..01aff28
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/multidevice/MultiDeviceTestsActivity.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier.multidevice;
+
+import com.android.cts.verifier.HostTestsActivity;
+import com.android.cts.verifier.HostTestsActivity.HostTestCategory;
+import com.android.cts.verifier.R;
+
+/** Activity for general multi-device tests in CtsVerifier. */
+public class MultiDeviceTestsActivity extends HostTestsActivity {
+ private static final String TAG = "MultiDeviceTestsActivity";
+
+ public MultiDeviceTestsActivity() {
+ super(
+ R.string.nfc_tests_dialog_title,
+ R.string.nfc_tests_dialog_content,
+ new HostTestCategory("Multidevice Tests")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_conflicting_non_payment",
+ "CtsNfcHceMultiDeviceTestCases#test_conflicting_non_payment")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_conflicting_non_payment_prefix",
+ "CtsNfcHceMultiDeviceTestCases#test_conflicting_non_payment_prefix")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_dual_non_payment",
+ "CtsNfcHceMultiDeviceTestCases#test_dual_non_payment")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_dual_payment_service",
+ "CtsNfcHceMultiDeviceTestCases#test_dual_payment_service")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_dynamic_aid_emulator",
+ "CtsNfcHceMultiDeviceTestCases#test_dynamic_aid_emulator")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_foreground_non_payment",
+ "CtsNfcHceMultiDeviceTestCases#test_foreground_non_payment")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_foreground_payment_emulator",
+ "CtsNfcHceMultiDeviceTestCases#test_foreground_payment_emulator")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_large_num_aids",
+ "CtsNfcHceMultiDeviceTestCases#test_large_num_aids")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_offhost_service",
+ "CtsNfcHceMultiDeviceTestCases#test_offhost_service")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_on_and_offhost_service",
+ "CtsNfcHceMultiDeviceTestCases#test_on_and_offhost_service")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_other_prefix",
+ "CtsNfcHceMultiDeviceTestCases#test_other_prefix")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_payment_prefix_emulator",
+ "CtsNfcHceMultiDeviceTestCases#test_payment_prefix_emulator")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_prefix_payment_emulator_2",
+ "CtsNfcHceMultiDeviceTestCases#test_prefix_payment_emulator_2")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_protocol_params",
+ "CtsNfcHceMultiDeviceTestCases#test_protocol_params")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_screen_off_payment",
+ "CtsNfcHceMultiDeviceTestCases#test_screen_off_payment")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_screen_on_only_off_host_service",
+ "CtsNfcHceMultiDeviceTestCases#test_screen_on_only_off_host_service")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_single_non_payment_service",
+ "CtsNfcHceMultiDeviceTestCases#test_single_non_payment_service")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_single_non_payment_service_with_listen_tech_disabled",
+ "CtsNfcHceMultiDeviceTestCases#test_single_non_payment_service_with_listen_tech_disabled")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_single_non_payment_service_with_listen_tech_poll_tech_mismatch",
+ "CtsNfcHceMultiDeviceTestCases#test_single_non_payment_service_with_listen_tech_poll_tech_mismatch")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_single_payment_service",
+ "CtsNfcHceMultiDeviceTestCases#test_single_payment_service")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_tap_50_times",
+ "CtsNfcHceMultiDeviceTestCases#test_tap_50_times")
+ .addTest(
+ "CtsNfcHceMultiDeviceTestCases#test_throughput",
+ "CtsNfcHceMultiDeviceTestCases#test_throughput"));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/multidevice/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/multidevice/OWNERS
new file mode 100644
index 0000000..3c9070b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/multidevice/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 1332816
+
+# xTS Infra (android-xts-infra@google.com)
+liutongbo@google.com
+wenshan@google.com
+kgui@google.com
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java
index 5a9d107..fc6b15f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java
@@ -42,50 +42,6 @@
.create();
}
- public static AlertDialog createHceTapReaderDialog(final Context context, String message) {
- String baseString = context.getString(R.string.nfc_hce_tap_reader_message);
- return new AlertDialog.Builder(context)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(R.string.nfc_hce_tap_reader_title)
- .setMessage(message != null ? message + "\n\n" + baseString : baseString)
- .setPositiveButton("OK", null)
- .create();
- }
-
- public static AlertDialog createChangeForegroundDialog(final Context context) {
- return new AlertDialog.Builder(context)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(R.string.nfc_hce_tap_reader_title)
- .setMessage(context.getString(R.string.nfc_hce_change_favor_foreground))
- .setPositiveButton("OK", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS);
- context.startActivity(intent);
- }
- })
- .create();
- }
-
- /**
- * SecureNfcEnabled dialog
- */
- public static AlertDialog createSecureNfcEnabledDialog(final Context context) {
- return new AlertDialog.Builder(context)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(R.string.secure_nfc_enabled)
- .setMessage(R.string.secure_nfc_enabled_message)
- .setPositiveButton(R.string.nfc_settings, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Settings.ACTION_NFC_SETTINGS);
- context.startActivity(intent);
- }
- })
- .create();
- }
-
-
private NfcDialogs() {
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
index a0eb2ea..9e7255b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
@@ -27,8 +27,6 @@
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.cts.verifier.TestListAdapter.TestListItem;
-import com.android.cts.verifier.nfc.hce.HceEmulatorTestActivity;
-import com.android.cts.verifier.nfc.hce.HceReaderTestActivity;
import com.android.cts.verifier.nfc.hcef.HceFEmulatorTestActivity;
import com.android.cts.verifier.nfc.hcef.HceFReaderTestActivity;
import com.android.cts.verifier.nfc.offhost.OffhostUiccEmulatorTestActivity;
@@ -64,18 +62,6 @@
}
}
- if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
- adapter.add(TestListItem.newCategory(this, R.string.nfc_hce));
- if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_reader_tests,
- HceReaderTestActivity.class.getName(),
- new Intent(this, HceReaderTestActivity.class), null));
- }
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_emulator_tests,
- HceEmulatorTestActivity.class.getName(),
- new Intent(this, HceEmulatorTestActivity.class), null));
- }
-
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_f));
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/AccessService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/AccessService.java
deleted file mode 100644
index 51eb7ec..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/AccessService.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class AccessService extends HceService {
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- AccessService.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.ACCESS_AID, true),
- HceUtils.buildCommandApdu("80CA01F000", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "123456789000",
- "1481148114819000"
- };
-
- public AccessService() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
deleted file mode 100644
index fb28725..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/BaseEmulatorActivity.java
+++ /dev/null
@@ -1,253 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import static android.content.Context.RECEIVER_EXPORTED;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.app.role.RoleManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.nfc.NfcAdapter;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@TargetApi(19)
-public abstract class BaseEmulatorActivity extends PassFailButtons.Activity {
- static final String TAG = "BaseEmulatorActivity";
- NfcAdapter mAdapter;
- CardEmulation mCardEmulation;
- RoleManager mRoleManager;
- ProgressDialog mSetupDialog;
- ComponentName mMakingDefault;
-
- final ArrayList<ComponentName> SERVICES = new ArrayList<ComponentName>(
- Arrays.asList(
- PaymentService1.COMPONENT,
- PaymentService2.COMPONENT,
- TransportService1.COMPONENT,
- TransportService2.COMPONENT,
- AccessService.COMPONENT,
- ThroughputService.COMPONENT,
- OffHostService.COMPONENT,
- PollingLoopService.COMPONENT,
- PaymentServiceDynamicAids.COMPONENT,
- PrefixPaymentService1.COMPONENT,
- PrefixPaymentService2.COMPONENT,
- PrefixTransportService1.COMPONENT,
- PrefixTransportService2.COMPONENT,
- PrefixAccessService.COMPONENT,
- LargeNumAidsService.COMPONENT,
- ScreenOnOnlyOffHostService.COMPONENT,
- ScreenOffPaymentService.COMPONENT)
- );
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mAdapter = NfcAdapter.getDefaultAdapter(this);
- mCardEmulation = CardEmulation.getInstance(mAdapter);
- IntentFilter filter = new IntentFilter(HceUtils.ACTION_APDU_SEQUENCE_COMPLETE);
- registerReceiver(mReceiver, filter, RECEIVER_EXPORTED);
- mRoleManager = getSystemService(RoleManager.class);
- }
-
- abstract void onServicesSetup(boolean result);
-
- abstract void onApduSequenceComplete(ComponentName component, long duration);
-
- void onApduSequenceError() {
-
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- unregisterReceiver(mReceiver);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- protected boolean isWalletRoleHeld() {
- return mRoleManager.isRoleHeld(RoleManager.ROLE_WALLET);
- }
-
- protected boolean isWalletRoleAvailable() {
- return mRoleManager.isRoleAvailable(RoleManager.ROLE_WALLET);
- }
-
- final void setupServices(Context context, ComponentName... components) {
- mSetupDialog = new ProgressDialog(context);
- new SetupServicesTask().execute(components);
- }
-
- final boolean makePaymentDefault(final ComponentName defaultComponent, int stringId) {
- if ((isWalletRoleAvailable() && !isWalletRoleHeld())
- || (!isWalletRoleAvailable()
- && !mCardEmulation.isDefaultServiceForCategory(defaultComponent,
- CardEmulation.CATEGORY_PAYMENT))) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Note");
- builder.setMessage(stringId);
- mMakingDefault = defaultComponent;
- builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (!isWalletRoleAvailable()) {
- Intent changeDefault = new Intent(CardEmulation.ACTION_CHANGE_DEFAULT);
- changeDefault.putExtra(CardEmulation.EXTRA_CATEGORY,
- CardEmulation.CATEGORY_PAYMENT);
- changeDefault.putExtra(CardEmulation.EXTRA_SERVICE_COMPONENT,
- defaultComponent);
- changeDefault.putExtra(Intent.EXTRA_USER,
- UserHandle.getUserHandleForUid(getApplicationInfo().uid));
- startActivityForResult(changeDefault, 0);
- } else {
- Intent roleRequestIntent = mRoleManager
- .createRequestRoleIntent(RoleManager.ROLE_WALLET);
- startActivityForResult(roleRequestIntent, 0);
- }
- }
- });
- builder.show();
- return true;
- } else {
- return false;
- }
- }
-
- final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (HceUtils.ACTION_APDU_SEQUENCE_COMPLETE.equals(action)) {
- // Get component whose sequence was completed
- ComponentName component = intent.getParcelableExtra(HceUtils.EXTRA_COMPONENT);
- long duration = intent.getLongExtra(HceUtils.EXTRA_DURATION, 0);
- if (component != null) {
- onApduSequenceComplete(component, duration);
- }
- } else if (HceUtils.ACTION_APDU_SEQUENCE_ERROR.equals(action)) {
- onApduSequenceError();
- }
- }
- };
-
- private class SetupServicesTask extends AsyncTask<ComponentName, Void, Boolean> {
- @Override
- protected void onPostExecute(Boolean result) {
- super.onPostExecute(result);
- mSetupDialog.dismiss();
- onServicesSetup(result);
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- mSetupDialog.setTitle(R.string.nfc_hce_please_wait);
- mSetupDialog.setMessage(getString(R.string.nfc_hce_setting_up));
- mSetupDialog.setCancelable(false);
- mSetupDialog.show();
- }
-
- @Override
- protected Boolean doInBackground(ComponentName... components) {
- List<ComponentName> enableComponents = Arrays.asList(components);
- for (ComponentName component : SERVICES) {
- if (enableComponents.contains(component)) {
- Log.d(TAG, "Enabling component " + component);
- HceUtils.enableComponent(getPackageManager(), component);
- } else {
- Log.d(TAG, "Disabling component " + component);
- HceUtils.disableComponent(getPackageManager(), component);
- }
- }
- // This is a trick to invalidate the HCE cache and avoid
- // having to wait for PackageManager broadcasts to NFCService.
- ComponentName bogusComponent = new ComponentName("com.android.cts.verifier",
- "com.android.cts.verifier.nfc.hce.BogusService");
- mCardEmulation.isDefaultServiceForCategory(bogusComponent,
- CardEmulation.CATEGORY_PAYMENT);
- return true;
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == Activity.RESULT_OK) {
- // Verify it's default
- if (!isWalletRoleAvailable()) {
- if (!mCardEmulation.isDefaultServiceForCategory(mMakingDefault,
- CardEmulation.CATEGORY_PAYMENT)) {
- // Popup dialog-box
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Test failed.");
- builder.setMessage("The service was not made the default service according "
- + "to CardEmulation.getDefaultServiceForCategory(), verify the make "
- + "default implementation is correct.");
- builder.setPositiveButton("OK", null);
- builder.show();
- onPaymentDefaultResult(mMakingDefault, false);
- } else {
- onPaymentDefaultResult(mMakingDefault, true);
- }
- } else if (!isWalletRoleHeld()) {
- roleSelectionFailed();
- }
- } else {
- if (!isWalletRoleAvailable()) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Test failed.");
- builder.setMessage("You clicked no");
- builder.setPositiveButton("OK", null);
- builder.show();
- onPaymentDefaultResult(mMakingDefault, false);
- } else if (!isWalletRoleHeld()) {
- roleSelectionFailed();
- }
- }
- }
-
- private void roleSelectionFailed() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Test failed.");
- builder.setMessage("You selected the wrong app or 'None' to be the "
- + "wallet role holder.");
- builder.setPositiveButton("OK", null);
- builder.show();
- }
-
- void onPaymentDefaultResult(ComponentName component, boolean success) {
-
- };
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ChangeDefaultEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ChangeDefaultEmulatorActivity.java
deleted file mode 100644
index 35dd0cb..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ChangeDefaultEmulatorActivity.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class ChangeDefaultEmulatorActivity extends BaseEmulatorActivity {
- final static int STATE_IDLE = 0;
- final static int STATE_SERVICE1_SETTING_UP = 1;
- final static int STATE_SERVICE2_SETTING_UP = 2;
- final static int STATE_MAKING_SERVICE1_DEFAULT = 3;
- final static int STATE_MAKING_SERVICE2_DEFAULT = 4;
- final static int STATE_DEFAULT_CHANGED = 5;
-
- int mState = STATE_IDLE;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- mState = STATE_SERVICE2_SETTING_UP;
- setupServices(this, PaymentService2.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- if (mState == STATE_SERVICE2_SETTING_UP) {
- mState = STATE_SERVICE1_SETTING_UP;
- setupServices(this, PaymentService1.COMPONENT, PaymentService2.COMPONENT);
- return;
- }
- if (!makePaymentDefault(PaymentService2.COMPONENT,
- R.string.nfc_hce_change_preinstalled_wallet)) {
- // Service 2 is already default, make one default now
- mState = STATE_MAKING_SERVICE1_DEFAULT;
- makePaymentDefault(PaymentService1.COMPONENT, R.string.nfc_hce_change_default_help);
- } else {
- mState = STATE_MAKING_SERVICE2_DEFAULT;
- // will get callback when 2 is made default
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- PaymentService1.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- PaymentService1.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_change_default_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(PaymentService1.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-
- @Override
- void onPaymentDefaultResult(ComponentName component, boolean success) {
- if (mState == STATE_MAKING_SERVICE2_DEFAULT) {
- if (success) {
- mState = STATE_MAKING_SERVICE1_DEFAULT;
- makePaymentDefault(PaymentService1.COMPONENT, R.string.nfc_hce_change_default_help);
- }
- } else if (mState == STATE_MAKING_SERVICE1_DEFAULT) {
- if (success) {
- mState = STATE_DEFAULT_CHANGED;
- NfcDialogs.createHceTapReaderDialog(this, null).show();
- }
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/CommandApdu.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/CommandApdu.java
index 3039efdc..7fb5967 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/CommandApdu.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/CommandApdu.java
@@ -12,10 +12,6 @@
mReachable = reachable;
}
- public boolean isReachable() {
- return mReachable;
- }
-
public String getApdu() {
return mApdu;
}
@@ -27,18 +23,18 @@
public static final Parcelable.Creator<CommandApdu> CREATOR =
new Parcelable.Creator<CommandApdu>() {
- @Override
- public CommandApdu createFromParcel(Parcel source) {
- String apdu = source.readString();
- boolean reachable = source.readInt() != 0 ? true : false;
- return new CommandApdu(apdu, reachable);
- }
+ @Override
+ public CommandApdu createFromParcel(Parcel source) {
+ String apdu = source.readString();
+ boolean reachable = source.readInt() != 0;
+ return new CommandApdu(apdu, reachable);
+ }
- @Override
- public CommandApdu[] newArray(int size) {
- return new CommandApdu[size];
- }
- };
+ @Override
+ public CommandApdu[] newArray(int size) {
+ return new CommandApdu[size];
+ }
+ };
@Override
public void writeToParcel(Parcel dest, int flags) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentEmulatorActivity.java
deleted file mode 100644
index 37d5207..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentEmulatorActivity.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class ConflictingNonPaymentEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, TransportService1.COMPONENT, TransportService2.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_hce_conflicting_non_payment_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- TransportService2.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- TransportService2.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_conflicting_non_payment_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(TransportService2.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentPrefixEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentPrefixEmulatorActivity.java
deleted file mode 100644
index 3b0313a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ConflictingNonPaymentPrefixEmulatorActivity.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-import java.util.ArrayList;
-
-public class ConflictingNonPaymentPrefixEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, PrefixTransportService1.COMPONENT, PrefixTransportService2.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- // Do dynamic AID registration
- ArrayList<String> service_aids = new ArrayList<String>();
- service_aids.add(HceUtils.TRANSPORT_PREFIX_AID + "*");
- mCardEmulation.registerAidsForService(PrefixTransportService1.COMPONENT, CardEmulation.CATEGORY_OTHER, service_aids);
- mCardEmulation.registerAidsForService(PrefixTransportService2.COMPONENT, CardEmulation.CATEGORY_OTHER, service_aids);
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_other_conflicting_prefix_aids_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS, PrefixTransportService2.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES, PrefixTransportService2.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_other_conflicting_prefix_aids_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(PrefixTransportService2.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteEmulatorActivity.java
deleted file mode 100644
index fbf07e7..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DefaultRouteEmulatorActivity.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class DefaultRouteEmulatorActivity extends BaseEmulatorActivity {
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu("A000000476416E64726F6964484345", true),
- };
- public static final String[] APDU_RESPONSE_SEQUENCE = {
- "148100009000"
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_hce_default_route_emulator_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS, APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES, APDU_RESPONSE_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_default_route_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualNonPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualNonPaymentEmulatorActivity.java
deleted file mode 100644
index 0a9362a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualNonPaymentEmulatorActivity.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class DualNonPaymentEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, TransportService2.COMPONENT, AccessService.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this, null).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- // Combine command/response APDU arrays
- CommandApdu[] commandSequences = new CommandApdu[TransportService2.APDU_COMMAND_SEQUENCE.length +
- AccessService.APDU_COMMAND_SEQUENCE.length];
- System.arraycopy(TransportService2.APDU_COMMAND_SEQUENCE, 0, commandSequences, 0,
- TransportService2.APDU_COMMAND_SEQUENCE.length);
- System.arraycopy(AccessService.APDU_COMMAND_SEQUENCE, 0, commandSequences,
- TransportService2.APDU_COMMAND_SEQUENCE.length,
- AccessService.APDU_COMMAND_SEQUENCE.length);
-
- String[] responseSequences = new String[TransportService2.APDU_RESPOND_SEQUENCE.length +
- AccessService.APDU_RESPOND_SEQUENCE.length];
- System.arraycopy(TransportService2.APDU_RESPOND_SEQUENCE, 0, responseSequences, 0,
- TransportService2.APDU_RESPOND_SEQUENCE.length);
- System.arraycopy(AccessService.APDU_RESPOND_SEQUENCE, 0, responseSequences,
- TransportService2.APDU_RESPOND_SEQUENCE.length,
- AccessService.APDU_RESPOND_SEQUENCE.length);
-
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS, commandSequences);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES, responseSequences);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_dual_non_payment_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(TransportService2.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualNonPaymentPrefixEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualNonPaymentPrefixEmulatorActivity.java
deleted file mode 100644
index c02faca..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualNonPaymentPrefixEmulatorActivity.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-import java.util.ArrayList;
-
-public class DualNonPaymentPrefixEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, PrefixTransportService1.COMPONENT, PrefixAccessService.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- // Do dynamic AID registration
- ArrayList<String> service1_aids = new ArrayList<String>();
- service1_aids.add(HceUtils.TRANSPORT_PREFIX_AID + "*");
- ArrayList<String> service2_aids = new ArrayList<String>();
- service2_aids.add(HceUtils.ACCESS_PREFIX_AID + "*");
- mCardEmulation.registerAidsForService(PrefixTransportService1.COMPONENT, CardEmulation.CATEGORY_OTHER, service1_aids);
- mCardEmulation.registerAidsForService(PrefixAccessService.COMPONENT, CardEmulation.CATEGORY_OTHER, service2_aids);
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_other_prefix_aids_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- // Combine command/response APDU arrays
- CommandApdu[] commandSequences = new CommandApdu[PrefixTransportService1.APDU_COMMAND_SEQUENCE.length +
- PrefixAccessService.APDU_COMMAND_SEQUENCE.length];
- System.arraycopy(PrefixTransportService1.APDU_COMMAND_SEQUENCE, 0, commandSequences, 0,
- PrefixTransportService1.APDU_COMMAND_SEQUENCE.length);
- System.arraycopy(PrefixAccessService.APDU_COMMAND_SEQUENCE, 0, commandSequences,
- PrefixTransportService1.APDU_COMMAND_SEQUENCE.length,
- PrefixAccessService.APDU_COMMAND_SEQUENCE.length);
-
- String[] responseSequences = new String[PrefixTransportService1.APDU_RESPOND_SEQUENCE.length +
- PrefixAccessService.APDU_RESPOND_SEQUENCE.length];
- System.arraycopy(PrefixTransportService1.APDU_RESPOND_SEQUENCE, 0, responseSequences, 0,
- PrefixTransportService1.APDU_RESPOND_SEQUENCE.length);
- System.arraycopy(PrefixAccessService.APDU_RESPOND_SEQUENCE, 0, responseSequences,
- PrefixTransportService1.APDU_RESPOND_SEQUENCE.length,
- PrefixAccessService.APDU_RESPOND_SEQUENCE.length);
-
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS, commandSequences);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES, responseSequences);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_other_prefix_aids_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(PrefixAccessService.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualPaymentEmulatorActivity.java
deleted file mode 100644
index beafa85..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DualPaymentEmulatorActivity.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class DualPaymentEmulatorActivity extends BaseEmulatorActivity {
- final static int STATE_IDLE = 0;
- final static int STATE_SERVICE1_SETTING_UP = 1;
- final static int STATE_SERVICE2_SETTING_UP = 2;
- final static int STATE_MAKING_SERVICE2_DEFAULT = 3;
-
- int mState = STATE_IDLE;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- mState = STATE_SERVICE2_SETTING_UP;
- setupServices(this, PaymentService2.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- if (mState == STATE_SERVICE2_SETTING_UP) {
- mState = STATE_SERVICE1_SETTING_UP;
- setupServices(this, PaymentService1.COMPONENT, PaymentService2.COMPONENT);
- return;
- }
- // Verify HCE service 2 is the default
- int stringId = isWalletRoleAvailable() ? R.string.nfc_hce_change_default_wallet
- : R.string.nfc_hce_change_preinstalled_wallet;
- if (makePaymentDefault(PaymentService2.COMPONENT, stringId)) {
- mState = STATE_MAKING_SERVICE2_DEFAULT;
- } else {
- // Already default
- NfcDialogs.createHceTapReaderDialog(this,null).show();
- }
- }
-
- @Override
- void onPaymentDefaultResult(ComponentName component, boolean success) {
- if (success) {
- NfcDialogs.createHceTapReaderDialog(this, null).show();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- /**
- * Accepting isWalletRoleAvailable as a parameter to decide sequences accordingly
- **/
- public static Intent buildReaderIntent(Context context, boolean isWalletRoleAvailable) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- CommandApdu[] commandSequence = isWalletRoleAvailable
- ? PaymentService1.APDU_COMMAND_SEQUENCE : PaymentService2.APDU_COMMAND_SEQUENCE;
- String[] responseSequence = isWalletRoleAvailable
- ? PaymentService1.APDU_RESPOND_SEQUENCE : PaymentService2.APDU_RESPOND_SEQUENCE;
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- commandSequence);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- responseSequence);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_dual_payment_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(isWalletRoleAvailable() ? PaymentService1.COMPONENT
- : PaymentService2.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DynamicAidEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DynamicAidEmulatorActivity.java
deleted file mode 100644
index ae8d50c..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/DynamicAidEmulatorActivity.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-import java.util.ArrayList;
-
-public class DynamicAidEmulatorActivity extends BaseEmulatorActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, PaymentServiceDynamicAids.COMPONENT);
- }
-
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
-
- @Override
- void onServicesSetup(boolean result) {
- ArrayList<String> paymentAids = new ArrayList<String>();
- paymentAids.add(HceUtils.PPSE_AID);
- paymentAids.add(HceUtils.VISA_AID);
- // Register a different set of AIDs for the foreground
- mCardEmulation.registerAidsForService(PaymentServiceDynamicAids.COMPONENT,
- CardEmulation.CATEGORY_PAYMENT, paymentAids);
- // Now make sure it's default
- if (makePaymentDefault(PaymentServiceDynamicAids.COMPONENT,
- R.string.nfc_hce_change_preinstalled_wallet)) {
- // Wait for callback
- } else {
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_payment_dynamic_aids_help)).show();
- }
- }
-
- @Override
- void onPaymentDefaultResult(ComponentName component, boolean success) {
- if (success) {
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_payment_dynamic_aids_help)).show();
- }
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(PaymentServiceDynamicAids.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- PaymentServiceDynamicAids.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- PaymentServiceDynamicAids.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_payment_dynamic_aids_reader));
- return readerIntent;
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ForegroundNonPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ForegroundNonPaymentEmulatorActivity.java
deleted file mode 100644
index e8bf5f1..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ForegroundNonPaymentEmulatorActivity.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class ForegroundNonPaymentEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- setupServices(this, TransportService1.COMPONENT, TransportService2.COMPONENT);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mCardEmulation.unsetPreferredService(this);
- }
-
- @Override
- void onServicesSetup(boolean result) {
- // Tell NFC service we prefer TransportService2
- mCardEmulation.setPreferredService(this, TransportService2.COMPONENT);
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_hce_foreground_non_payment_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- TransportService2.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- TransportService2.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_foreground_non_payment_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(TransportService2.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ForegroundPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ForegroundPaymentEmulatorActivity.java
deleted file mode 100644
index 621a0de..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ForegroundPaymentEmulatorActivity.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class ForegroundPaymentEmulatorActivity extends BaseEmulatorActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (!mCardEmulation.categoryAllowsForegroundPreference(CardEmulation.CATEGORY_PAYMENT)) {
- // Launch tap&pay settings
- NfcDialogs.createChangeForegroundDialog(this).show();
- } else {
- setupServices(this, PaymentService2.COMPONENT, PaymentService1.COMPONENT);
- }
- }
-
- @Override
- void onServicesSetup(boolean result) {
- if (!isWalletRoleAvailable()) {
- if (!makePaymentDefault(PaymentService1.COMPONENT,
- R.string.nfc_hce_change_preinstalled_wallet)) {
- mCardEmulation.setPreferredService(this, PaymentService2.COMPONENT);
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_hce_foreground_payment_help)).show();
- } // else, wait for callback
- } else {
- if (isWalletRoleHeld()) {
- makePaymentDefault(PaymentService1.COMPONENT,
- R.string.nfc_hce_change_preinstalled_wallet);
- }
- mCardEmulation.setPreferredService(this, PaymentService2.COMPONENT);
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_hce_foreground_payment_help)).show();
- }
- }
-
- @Override
- void onPaymentDefaultResult(ComponentName component, boolean success) {
- if (success) {
- NfcDialogs.createHceTapReaderDialog(this, null).show();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mCardEmulation.unsetPreferredService(this);
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- PaymentService2.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- PaymentService2.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_foreground_payment_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(PaymentService2.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
deleted file mode 100644
index 7ec417c..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceEmulatorTestActivity.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.nfc.NfcAdapter;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemProperties;
-
-import com.android.cts.verifier.ArrayTestListAdapter;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.TestListAdapter.TestListItem;
-
-/** Activity that lists all the NFC HCE emulator tests. */
-public class HceEmulatorTestActivity extends PassFailButtons.TestListActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_list);
- setInfoResources(R.string.nfc_test, R.string.nfc_hce_emulator_test_info, 0);
- setPassFailButtonClickListeners();
-
- ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
-
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
- CardEmulation cardEmulation = CardEmulation.getInstance(nfcAdapter);
- if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
- adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_emulator_tests));
-
- /*
- * Only add this test when supported in platform
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_default_route_emulator,
- DefaultRouteEmulatorActivity.class.getName(),
- new Intent(this, DefaultRouteEmulatorActivity.class), null));
- */
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_protocol_params_emulator,
- ProtocolParamsEmulatorActivity.class.getName(),
- new Intent(this, ProtocolParamsEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_payment_emulator,
- SinglePaymentEmulatorActivity.class.getName(),
- new Intent(this, SinglePaymentEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_dual_payment_emulator,
- DualPaymentEmulatorActivity.class.getName(),
- new Intent(this, DualPaymentEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_change_default_emulator,
- ChangeDefaultEmulatorActivity.class.getName(),
- new Intent(this, ChangeDefaultEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_foreground_payment_emulator,
- ForegroundPaymentEmulatorActivity.class.getName(),
- new Intent(this, ForegroundPaymentEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_non_payment_emulator,
- SingleNonPaymentEmulatorActivity.class.getName(),
- new Intent(this, SingleNonPaymentEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_dual_non_payment_emulator,
- DualNonPaymentEmulatorActivity.class.getName(),
- new Intent(this, DualNonPaymentEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_conflicting_non_payment_emulator,
- ConflictingNonPaymentEmulatorActivity.class.getName(),
- new Intent(this, ConflictingNonPaymentEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_foreground_non_payment_emulator,
- ForegroundNonPaymentEmulatorActivity.class.getName(),
- new Intent(this, ForegroundNonPaymentEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_throughput_emulator,
- ThroughputEmulatorActivity.class.getName(),
- new Intent(this, ThroughputEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_tap_test_emulator,
- TapTestEmulatorActivity.class.getName(),
- new Intent(this, TapTestEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_offhost_service_emulator,
- OffHostEmulatorActivity.class.getName(),
- new Intent(this, OffHostEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_on_and_offhost_service_emulator,
- OnAndOffHostEmulatorActivity.class.getName(),
- new Intent(this, OnAndOffHostEmulatorActivity.class), null));
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_large_num_aids_emulator,
- LargeNumAidsEmulatorActivity.class.getName(),
- new Intent(this, LargeNumAidsEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_payment_dynamic_aids_emulator,
- DynamicAidEmulatorActivity.class.getName(),
- new Intent(this, DynamicAidEmulatorActivity.class), null));
-
- if (cardEmulation.supportsAidPrefixRegistration()) {
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_payment_prefix_aids_emulator,
- PrefixPaymentEmulatorActivity.class.getName(),
- new Intent(this, PrefixPaymentEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_payment_prefix_aids_emulator_2,
- PrefixPaymentEmulator2Activity.class.getName(),
- new Intent(this, PrefixPaymentEmulator2Activity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_other_prefix_aids_emulator,
- DualNonPaymentPrefixEmulatorActivity.class.getName(),
- new Intent(this, DualNonPaymentPrefixEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_other_conflicting_prefix_aids_emulator,
- ConflictingNonPaymentPrefixEmulatorActivity.class.getName(),
- new Intent(this, ConflictingNonPaymentPrefixEmulatorActivity.class), null));
- }
- }
- if (nfcAdapter.isObserveModeSupported()) {
- adapter.add(TestListItem.newTest(this, R.string.nfc_observe_mode_emulator,
- ObserveModeEmulatorActivity.class.getName(),
- new Intent(this, ObserveModeEmulatorActivity.class), null));
- adapter.add(TestListItem.newTest(this, R.string.nfc_polling_loop_a_emulator,
- getString(R.string.nfc_polling_loop_a_emulator),
- PollingLoopEmulatorActivity.buildEmulatorIntent(this,
- NfcAdapter.FLAG_READER_NFC_A),
- null));
- adapter.add(TestListItem.newTest(this, R.string.nfc_polling_loop_b_emulator,
- getString(R.string.nfc_polling_loop_b_emulator),
- PollingLoopEmulatorActivity.buildEmulatorIntent(this,
- NfcAdapter.FLAG_READER_NFC_B),
- null));
- adapter.add(TestListItem.newTest(this, R.string.nfc_polling_loop_ab_emulator,
- getString(R.string.nfc_polling_loop_ab_emulator),
- PollingLoopEmulatorActivity.buildEmulatorIntent(this,
- NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B),
- null));
- adapter.add(TestListItem.newTest(this, R.string.nfc_polling_loop_custom_emulator,
- getString(R.string.nfc_polling_loop_custom_emulator),
- PollingLoopEmulatorActivity.buildEmulatorIntent(this,
- NfcAdapter.FLAG_READER_NFC_A, "48656c6c6f20776f726c64"),
- null));
- }
-
- int firstSdk =
- SystemProperties.getInt("ro.product.first_api_level", Build.VERSION_CODES.S);
- if (firstSdk >= Build.VERSION_CODES.S) {
- adapter.add(TestListItem.newTest(this, R.string.nfc_screen_on_only_offhost_emulator,
- ScreenOnOnlyOffHostEmulatorActivity.class.getName(),
- new Intent(this, ScreenOnOnlyOffHostEmulatorActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_screen_off_hce_payment_emulator,
- ScreenOffPaymentEmulatorActivity.class.getName(),
- new Intent(this, ScreenOffPaymentEmulatorActivity.class), null));
- }
- }
-
- setTestListAdapter(adapter);
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
deleted file mode 100644
index e699f3c..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceReaderTestActivity.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.nfc.hce;
-
-import android.app.role.RoleManager;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.nfc.NfcAdapter;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemProperties;
-
-import com.android.cts.verifier.ArrayTestListAdapter;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.TestListAdapter.TestListItem;
-
-/** Activity that lists all the NFC HCE reader tests. */
-public class HceReaderTestActivity extends PassFailButtons.TestListActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_list);
- setInfoResources(R.string.nfc_test, R.string.nfc_hce_reader_test_info, 0);
- setPassFailButtonClickListeners();
- RoleManager roleManager = getSystemService(RoleManager.class);
- ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
- boolean isWalletRoleAvailable = roleManager.isRoleAvailable(RoleManager.ROLE_WALLET);
- if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
- adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_reader_tests));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_protocol_params_reader,
- ProtocolParamsReaderActivity.class.getName(),
- new Intent(this, ProtocolParamsReaderActivity.class), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_payment_reader,
- getString(R.string.nfc_hce_single_payment_reader),
- SinglePaymentEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_dual_payment_reader,
- getString(R.string.nfc_hce_dual_payment_reader),
- DualPaymentEmulatorActivity.buildReaderIntent(this,
- isWalletRoleAvailable), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_change_default_reader,
- getString(R.string.nfc_hce_change_default_reader),
- ChangeDefaultEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_foreground_payment_reader,
- getString(R.string.nfc_hce_foreground_payment_reader),
- ForegroundPaymentEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_single_non_payment_reader,
- getString(R.string.nfc_hce_single_non_payment_reader),
- SingleNonPaymentEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_dual_non_payment_reader,
- getString(R.string.nfc_hce_dual_non_payment_reader),
- DualNonPaymentEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_conflicting_non_payment_reader,
- getString(R.string.nfc_hce_conflicting_non_payment_reader),
- ConflictingNonPaymentEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_foreground_non_payment_reader,
- getString(R.string.nfc_hce_foreground_non_payment_reader),
- ForegroundNonPaymentEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_throughput_reader,
- getString(R.string.nfc_hce_throughput_reader),
- ThroughputEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_tap_test_reader,
- getString(R.string.nfc_hce_tap_test_reader),
- TapTestEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_offhost_service_reader,
- getString(R.string.nfc_hce_offhost_service_reader),
- OffHostEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_on_and_offhost_service_reader,
- getString(R.string.nfc_hce_on_and_offhost_service_reader),
- OnAndOffHostEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_payment_dynamic_aids_reader,
- getString(R.string.nfc_hce_payment_dynamic_aids_reader),
- DynamicAidEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_large_num_aids_reader,
- getString(R.string.nfc_hce_large_num_aids_reader),
- LargeNumAidsEmulatorActivity.buildReaderIntent(this), null));
-
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
- CardEmulation cardEmulation = CardEmulation.getInstance(nfcAdapter);
- if (cardEmulation.supportsAidPrefixRegistration()) {
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_payment_prefix_aids_reader,
- getString(R.string.nfc_hce_payment_prefix_aids_reader),
- PrefixPaymentEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_payment_prefix_aids_reader_2,
- getString(R.string.nfc_hce_payment_prefix_aids_reader_2),
- PrefixPaymentEmulator2Activity.buildReaderIntent(this,
- isWalletRoleAvailable), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_other_prefix_aids_reader,
- getString(R.string.nfc_hce_other_prefix_aids_reader),
- DualNonPaymentPrefixEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_hce_other_conflicting_prefix_aids_reader,
- getString(R.string.nfc_hce_other_conflicting_prefix_aids_reader),
- ConflictingNonPaymentPrefixEmulatorActivity.buildReaderIntent(this), null));
- }
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_observe_mode_reader,
- ObserveModeReaderActivity.class.getName(),
- new Intent(this, ObserveModeReaderActivity.class), null));
- adapter.add(TestListItem.newTest(this, R.string.nfc_polling_loop_a_reader,
- getString(R.string.nfc_polling_loop_a_reader),
- PollingLoopEmulatorActivity.buildReaderIntent(this,
- NfcAdapter.FLAG_READER_NFC_A), null));
- adapter.add(TestListItem.newTest(this, R.string.nfc_polling_loop_b_reader,
- getString(R.string.nfc_polling_loop_b_reader),
- PollingLoopEmulatorActivity.buildReaderIntent(this,
- NfcAdapter.FLAG_READER_NFC_B), null));
- adapter.add(TestListItem.newTest(this, R.string.nfc_polling_loop_ab_reader,
- getString(R.string.nfc_polling_loop_ab_reader),
- PollingLoopEmulatorActivity.buildReaderIntent(this,
- NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B), null));
-
- int firstSdk =
- SystemProperties.getInt("ro.product.first_api_level", Build.VERSION_CODES.S);
- if (firstSdk >= Build.VERSION_CODES.S) {
- adapter.add(TestListItem.newTest(this, R.string.nfc_screen_on_only_offhost_reader,
- getString(R.string.nfc_screen_on_only_offhost_reader),
- ScreenOnOnlyOffHostEmulatorActivity.buildReaderIntent(this), null));
-
- adapter.add(TestListItem.newTest(this, R.string.nfc_screen_off_hce_payment_reader,
- getString(R.string.nfc_screen_off_hce_payment_reader),
- ScreenOffPaymentEmulatorActivity.buildReaderIntent(this), null));
- }
- }
-
- setTestListAdapter(adapter);
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceService.java
deleted file mode 100644
index e9cd121..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceService.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.nfc.cardemulation.HostApduService;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.util.Arrays;
-
-@TargetApi(19)
-public abstract class HceService extends HostApduService {
- final static String TAG = "HceService";
-
- final static int STATE_IDLE = 0;
- final static int STATE_IN_PROGRESS = 1;
- final static int STATE_FAILED = 2;
-
- // Variables below only used on main thread
- CommandApdu[] mCommandApdus = null;
- String[] mResponseApdus = null;
- int mApduIndex = 0;
- int mState = STATE_IDLE;
- long mStartTime;
-
- public void initialize(CommandApdu[] commandApdus, String[] responseApdus) {
- mCommandApdus = commandApdus;
- mResponseApdus = responseApdus;
- }
-
- @Override
- public void onDeactivated(int arg0) {
- mApduIndex = 0;
- mState = STATE_IDLE;
- }
-
- public abstract ComponentName getComponent();
-
- public void onApduSequenceComplete() {
- Intent completionIntent = new Intent(HceUtils.ACTION_APDU_SEQUENCE_COMPLETE);
- completionIntent.putExtra(HceUtils.EXTRA_COMPONENT, getComponent());
- completionIntent.putExtra(HceUtils.EXTRA_DURATION,
- System.currentTimeMillis() - mStartTime);
- sendBroadcast(completionIntent);
- }
-
- public void onApduSequenceError() {
- Intent errorIntent = new Intent(HceUtils.ACTION_APDU_SEQUENCE_ERROR);
- sendBroadcast(errorIntent);
- }
-
- @Override
- public byte[] processCommandApdu(byte[] arg0, Bundle arg1) {
- if (mState == STATE_FAILED) {
- // Don't accept any more APDUs until deactivated
- return null;
- }
-
- if (mState == STATE_IDLE) {
- mState = STATE_IN_PROGRESS;
- mStartTime = System.currentTimeMillis();
- }
-
-
- if (mApduIndex >= mCommandApdus.length) {
- // Skip all APDUs which aren't supposed to reach us
- return null;
- }
-
- do {
- if (!mCommandApdus[mApduIndex].isReachable()) {
- mApduIndex++;
- } else {
- break;
- }
- } while (mApduIndex < mCommandApdus.length);
-
- if (mApduIndex >= mCommandApdus.length) {
- Log.d(TAG, "Ignoring command APDU; protocol complete.");
- // Ignore new APDUs after completion
- return null;
- } else {
-
- if (!Arrays.equals(HceUtils.hexStringToBytes(mCommandApdus[mApduIndex].getApdu()), arg0)) {
- Log.d(TAG, "Unexpected command APDU: " + HceUtils.getHexBytes("", arg0));
- onApduSequenceError();
- return null;
- } else {
- // Send corresponding response APDU
- byte[] responseApdu = HceUtils.hexStringToBytes(mResponseApdus[mApduIndex]);
- mApduIndex++;
- if (mApduIndex == mCommandApdus.length) {
- // Test passed
- onApduSequenceComplete();
- }
- return responseApdu;
- }
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
index a324a4e..6174b85 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/HceUtils.java
@@ -1,46 +1,8 @@
package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-
public final class HceUtils {
- public static final String ACTION_APDU_SEQUENCE_COMPLETE =
- "com.android.cts.verifier.nfc.hce.ACTION_APDU_SEQUENCE_COMPLETE";
- public static final String ACTION_APDU_SEQUENCE_ERROR =
- "com.android.cts.verifier.nfc.hce.ACTION_APDU_SEQUENCE_ERROR";
-
- public static final String EXTRA_COMPONENT = "component";
- public static final String EXTRA_DURATION = "duration";
-
- public static final String PPSE_AID = "325041592E5359532E4444463031";
- public static final String MC_AID = "A0000000041010";
- public static final String VISA_AID = "A0000000030000";
-
- public static final String TRANSPORT_AID = "F001020304";
- public static final String ACCESS_AID = "F005060708";
-
- public static final String TRANSPORT_PREFIX_AID = "F001020304";
- public static final String ACCESS_PREFIX_AID = "F005060708";
-
- public static final String LARGE_NUM_AIDS_PREFIX = "F00102030414";
- public static final String LARGE_NUM_AIDS_POSTFIX ="81";
-
public static final String TRANSACTION_EVENT_AID = "A000000476416E64726F696443545341";
public static final String HCI_CMD = "0025000000";
- public static void enableComponent(PackageManager pm, ComponentName component) {
- pm.setComponentEnabledSetting(
- component,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
- PackageManager.DONT_KILL_APP);
- }
-
- public static void disableComponent(PackageManager pm, ComponentName component) {
- pm.setComponentEnabledSetting(
- component,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- }
public static String getHexBytes(String header, byte[] bytes) {
StringBuilder sb = new StringBuilder();
@@ -62,8 +24,10 @@
}
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
- data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
- + Character.digit(s.charAt(i+1), 16));
+ data[i / 2] =
+ (byte)
+ ((Character.digit(s.charAt(i), 16) << 4)
+ + Character.digit(s.charAt(i + 1), 16));
}
return data;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java
deleted file mode 100644
index db58252..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsEmulatorActivity.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-import java.util.ArrayList;
-
-public class LargeNumAidsEmulatorActivity extends BaseEmulatorActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, LargeNumAidsService.COMPONENT);
- }
-
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
-
- @Override
- void onServicesSetup(boolean result) {
- ArrayList<String> aids = new ArrayList<String>();
- for (int i = 0; i < 256; i++) {
- aids.add(HceUtils.LARGE_NUM_AIDS_PREFIX + String.format("%02X", i) + HceUtils.LARGE_NUM_AIDS_POSTFIX);
- }
- mCardEmulation.registerAidsForService(LargeNumAidsService.COMPONENT,
- CardEmulation.CATEGORY_OTHER, aids);
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(LargeNumAidsService.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- LargeNumAidsService.getCommandSequence());
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- LargeNumAidsService.getResponseSequence());
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_large_num_aids_reader));
- return readerIntent;
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java
deleted file mode 100644
index 5883543..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/LargeNumAidsService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class LargeNumAidsService extends HceService {
- static final String TAG = "LargeNumAidsService";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- LargeNumAidsService.class.getName());
-
- public static final CommandApdu[] getCommandSequence() {
- CommandApdu[] commands = new CommandApdu[256];
- for (int i = 0; i < 256; i++) {
- commands[i] = HceUtils.buildSelectApdu(HceUtils.LARGE_NUM_AIDS_PREFIX + String.format("%02X", i) +
- HceUtils.LARGE_NUM_AIDS_POSTFIX, true);
- }
- return commands;
- }
-
- public static final String[] getResponseSequence() {
- String[] responses = new String[256];
- for (int i = 0; i < 256; i++) {
- responses[i] = "9000" + String.format("%02X", i);
- }
- return responses;
- }
-
- public LargeNumAidsService() {
- initialize(getCommandSequence(), getResponseSequence());
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ObserveModeEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ObserveModeEmulatorActivity.java
deleted file mode 100644
index f0fe88d..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ObserveModeEmulatorActivity.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.nfc.NfcAdapter;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class ObserveModeEmulatorActivity extends BaseEmulatorActivity {
-
- static String sRfOnAction = "com.android.nfc_extras.action.RF_FIELD_ON_DETECTED";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(true);
- mAdapter = NfcAdapter.getDefaultAdapter(this);
- IntentFilter filter = new IntentFilter(sRfOnAction);
- registerReceiver(mFieldStateReceiver, filter, RECEIVER_EXPORTED);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_observe_mode_emulator_help)).show();
- mAdapter.setObserveModeEnabled(true);
- getPassButton().setEnabled(false);
- }
-
- @Override
- void onServicesSetup(boolean result) {
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- getPassButton().setEnabled(false);
- }
-
- final BroadcastReceiver mFieldStateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(sRfOnAction)) {
- getPassButton().setEnabled(true);
- }
- }
- };
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ObserveModeReaderActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ObserveModeReaderActivity.java
deleted file mode 100644
index 8ec8ef8..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ObserveModeReaderActivity.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcAdapter.ReaderCallback;
-import android.nfc.Tag;
-import android.os.Bundle;
-import android.widget.TextView;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-@TargetApi(19)
-public class ObserveModeReaderActivity extends PassFailButtons.Activity implements ReaderCallback {
- public static final String TAG = "ObserveModeReaderActivity";
-
- NfcAdapter mAdapter;
-
- TextView mTextView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
-
- setTitle(R.string.nfc_observe_mode_reader);
-
- mAdapter = NfcAdapter.getDefaultAdapter(this);
- mTextView = (TextView) findViewById(R.id.text);
- mTextView.setTextSize(12.0f);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A
- | NfcAdapter.FLAG_READER_NFC_B, null);
- }
-
- @Override
- public void onTagDiscovered(Tag tag) {
- getPassButton().setEnabled(false);
-
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostEmulatorActivity.java
deleted file mode 100644
index 216f35a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostEmulatorActivity.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class OffHostEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- setupServices(this, OffHostService.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_offhost_emulator_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- OffHostService.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- OffHostService.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_offhost_service_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java
deleted file mode 100644
index d9b18ab..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class OffHostService {
- public static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- OffHostService.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu("A000000151000000", true),
- HceUtils.buildCommandApdu("80CA9F7F00", true),
- HceUtils.buildSelectApdu("A000000444000000", true),
- HceUtils.buildCommandApdu("80CA9F7F00", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "*",
- "*",
- "*",
- "*"
- };
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OnAndOffHostEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OnAndOffHostEmulatorActivity.java
deleted file mode 100644
index d599959..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OnAndOffHostEmulatorActivity.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.nfc.NfcDialogs;
-import com.android.cts.verifier.R;
-
-@TargetApi(19)
-public class OnAndOffHostEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, OffHostService.COMPONENT, AccessService.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_on_and_offhost_emulator_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- // Combine command/response APDU arrays
- CommandApdu[] commandSequences = new CommandApdu[OffHostService.APDU_COMMAND_SEQUENCE.length +
- AccessService.APDU_COMMAND_SEQUENCE.length];
- System.arraycopy(OffHostService.APDU_COMMAND_SEQUENCE, 0, commandSequences, 0,
- OffHostService.APDU_COMMAND_SEQUENCE.length);
- System.arraycopy(AccessService.APDU_COMMAND_SEQUENCE, 0, commandSequences,
- OffHostService.APDU_COMMAND_SEQUENCE.length,
- AccessService.APDU_COMMAND_SEQUENCE.length);
-
- String[] responseSequences = new String[OffHostService.APDU_RESPOND_SEQUENCE.length +
- AccessService.APDU_RESPOND_SEQUENCE.length];
- System.arraycopy(OffHostService.APDU_RESPOND_SEQUENCE, 0, responseSequences, 0,
- OffHostService.APDU_RESPOND_SEQUENCE.length);
- System.arraycopy(AccessService.APDU_RESPOND_SEQUENCE, 0, responseSequences,
- OffHostService.APDU_RESPOND_SEQUENCE.length,
- AccessService.APDU_RESPOND_SEQUENCE.length);
-
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- commandSequences);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- responseSequences);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_on_and_offhost_service_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(AccessService.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService1.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService1.java
deleted file mode 100644
index 80df0cb..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService1.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class PaymentService1 extends HceService {
- static final String TAG = "PaymentService1";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- PaymentService1.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.PPSE_AID, true),
- HceUtils.buildSelectApdu(HceUtils.MC_AID, true),
- HceUtils.buildCommandApdu("80CA01F000", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "FFFF9000",
- "FFEF9000",
- "FFDFFFAABB9000"
- };
-
- public PaymentService1() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService2.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService2.java
deleted file mode 100644
index f2c4835..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentService2.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class PaymentService2 extends HceService {
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- PaymentService2.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.PPSE_AID, true),
- HceUtils.buildSelectApdu(HceUtils.MC_AID, true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "12349000",
- "56789000"
- };
-
- public PaymentService2() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentServiceDynamicAids.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentServiceDynamicAids.java
deleted file mode 100644
index 047a667..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PaymentServiceDynamicAids.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class PaymentServiceDynamicAids extends HceService {
- static final String TAG = "PaymentService1";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- PaymentServiceDynamicAids.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.PPSE_AID, true),
- HceUtils.buildSelectApdu(HceUtils.VISA_AID, true),
- HceUtils.buildCommandApdu("80CA01F000", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "FFFF9000",
- "FF0F9000",
- "FFDFFFAACB9000"
- };
-
- public PaymentServiceDynamicAids() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PollingLoopEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PollingLoopEmulatorActivity.java
deleted file mode 100644
index 84b7e38..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PollingLoopEmulatorActivity.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.nfc.hce;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.nfc.NfcAdapter;
-import android.nfc.cardemulation.PollingFrame;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-import java.util.HexFormat;
-import java.util.List;
-
-public class PollingLoopEmulatorActivity extends BaseEmulatorActivity {
-
- int mNfcTech = 0;
- int mNfcACount = 0;
- int mNfcBCount = 0;
- int mNfcOnCount = 0;
- int mNfcOffCount = 0;
- boolean mAllowedTransaction = false;
- PendingIntent mPendingIntent;
- String mCustomFrame = null;
-
- private static final String NFC_TECH_KEY = "NFC_TECH";
- private static final String NFC_CUSTOM_FRAME = "NFC_CUSTOM_FRAME";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(true);
- mAdapter = NfcAdapter.getDefaultAdapter(this);
- mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
- .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE_UNAUDITED);
- IntentFilter filter = new IntentFilter(PollingLoopService.POLLING_FRAME_ACTION);
- registerReceiver(mFieldStateReceiver, filter, RECEIVER_EXPORTED);
- }
-
- private static String getReaderString(Context context, int nfcTech) {
- if (nfcTech == NfcAdapter.FLAG_READER_NFC_A) {
- return context.getString(R.string.nfc_polling_loop_a_reader);
- } else if (nfcTech == NfcAdapter.FLAG_READER_NFC_B) {
- return context.getString(R.string.nfc_polling_loop_b_reader);
- } else if (nfcTech == (NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B)) {
- return context.getString(R.string.nfc_polling_loop_ab_reader);
- }
- return "error";
- }
-
- private static String getEmulatorString(Context context, int nfcTech, String customFrame) {
- if (customFrame != null) {
- return context.getString(R.string.nfc_polling_loop_custom_emulator);
- }
- if (nfcTech == NfcAdapter.FLAG_READER_NFC_A) {
- return context.getString(R.string.nfc_polling_loop_a_emulator);
- } else if (nfcTech == NfcAdapter.FLAG_READER_NFC_B) {
- return context.getString(R.string.nfc_polling_loop_b_emulator);
- } else if (nfcTech == (NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B)) {
- return context.getString(R.string.nfc_polling_loop_ab_emulator);
- }
- return "error";
- }
-
- private static String getEmulatorHelpString(Context context, int nfcTech, String customFrame) {
- if (customFrame != null) {
- return context.getString(R.string.nfc_polling_loop_custom_emulator_help);
- }
- if (nfcTech == NfcAdapter.FLAG_READER_NFC_A) {
- return context.getString(R.string.nfc_polling_loop_a_emulator_help);
- } else if (nfcTech == NfcAdapter.FLAG_READER_NFC_B) {
- return context.getString(R.string.nfc_polling_loop_a_emulator_help);
- } else if (nfcTech == (NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B)) {
- return context.getString(R.string.nfc_polling_loop_a_emulator_help);
- }
- return "error";
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mNfcTech = getIntent().getIntExtra(NFC_TECH_KEY, NfcAdapter.FLAG_READER_NFC_A);
- mCustomFrame = getIntent().getStringExtra(NFC_CUSTOM_FRAME);
- NfcDialogs.createHceTapReaderDialog(this,
- getEmulatorHelpString(this, mNfcTech, mCustomFrame)).show();
- setTitle(getEmulatorString(this, mNfcTech, mCustomFrame));
- mAdapter.setObserveModeEnabled(true);
- getPassButton().setEnabled(false);
- setupServices(this, PollingLoopService.COMPONENT);
- mCardEmulation.setPreferredService(this,
- new ComponentName(this.getApplicationContext(),
- PollingLoopService.class));
- mNfcACount = 0;
- mNfcBCount = 0;
- mNfcOnCount = 0;
- mNfcOffCount = 0;
- mAllowedTransaction = false;
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (mAllowedTransaction) {
- getPassButton().setEnabled(true);
- } else {
- getPassButton().setEnabled(false);
- }
- }
-
- final BroadcastReceiver mFieldStateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(PollingLoopService.POLLING_FRAME_ACTION)) {
- processPollingFrames(
- intent.getParcelableArrayListExtra(PollingLoopService.POLLING_FRAME_EXTRA,
- PollingFrame.class));
- }
- }
- };
-
- static Intent buildEmulatorIntent(Context context, int nfcTech, String customFrame) {
- Intent intent = buildEmulatorIntent(context, nfcTech);
- intent.putExtra(NFC_CUSTOM_FRAME, customFrame);
- return intent;
- }
-
- static Intent buildEmulatorIntent(Context context, int nfcTech) {
- Intent intent = new Intent(context, PollingLoopEmulatorActivity.class);
- intent.putExtra(NFC_TECH_KEY, nfcTech);
- return intent;
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- mNfcTech = intent.getIntExtra(NFC_TECH_KEY, NfcAdapter.FLAG_READER_NFC_A);
- setIntent(intent);
- setupServices(this, PollingLoopService.COMPONENT);
- }
-
- @Override
- public void startActivity(Intent intent) {
- super.startActivity(intent);
- mNfcTech = intent.getIntExtra(NFC_TECH_KEY, NfcAdapter.FLAG_READER_NFC_A);
- setupServices(this, PollingLoopService.COMPONENT);
- }
-
- void processPollingFrames(List<PollingFrame> frames) {
- for (PollingFrame frame : frames) {
- processPollingFrame(frame);
- }
- if (seenCorrectPollingLoop() && !mAllowedTransaction && mCustomFrame == null) {
- if (mAdapter.setObserveModeEnabled(false)) {
- mAllowedTransaction = true;
- }
- }
- }
-
- private boolean seenCorrectPollingLoop() {
- if (mNfcTech == NfcAdapter.FLAG_READER_NFC_A) {
- if (mNfcACount >= 3 && mNfcBCount == 0 && mNfcOnCount >= 3
- && mNfcOffCount >= 3 && Math.abs(mNfcOffCount - mNfcOnCount) <= 1) {
- return true;
- }
- } else if (mNfcTech == NfcAdapter.FLAG_READER_NFC_B) {
- if (mNfcBCount >= 3 && mNfcACount == 0 && mNfcOnCount >= 3
- && mNfcOffCount >= 3 && Math.abs(mNfcOffCount - mNfcOnCount) <= 1) {
- return true;
- }
- } else if (mNfcTech == (NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B)) {
- if (mNfcACount >= 3 && mNfcBCount >= 3 && mNfcOnCount >= 3
- && mNfcOffCount >= 3 && Math.abs(mNfcOffCount - mNfcOnCount) <= 1) {
- return true;
- }
- }
- return false;
- }
-
- void processPollingFrame(PollingFrame frame) {
- int type = frame.getType();
- if (type == PollingFrame.POLLING_LOOP_TYPE_A) {
- mNfcACount++;
- } else if (type == PollingFrame.POLLING_LOOP_TYPE_B) {
- mNfcBCount++;
- } else if (type == PollingFrame.POLLING_LOOP_TYPE_ON) {
- mNfcOnCount++;
- } else if (type == PollingFrame.POLLING_LOOP_TYPE_OFF) {
- mNfcOffCount++;
- } else if (type == PollingFrame.POLLING_LOOP_TYPE_UNKNOWN) {
- if (mCustomFrame != null && !mAllowedTransaction) {
- byte[] passedData = frame.getData();
- if (mCustomFrame.equals(HexFormat.of().formatHex(passedData))) {
- getPassButton().setEnabled(true);
- }
- }
- }
- }
-
- static Intent buildReaderIntent(Context context, int nfcTech) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_NFC_TECH, nfcTech);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- PollingLoopService.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- PollingLoopService.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL, getReaderString(context, nfcTech));
- return readerIntent;
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PollingLoopService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PollingLoopService.java
deleted file mode 100644
index 0f12fd4..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PollingLoopService.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.nfc.cardemulation.PollingFrame;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PollingLoopService extends HceService {
- static final String TAG = "PollingLoopService";
-
- public static final String POLLING_FRAME_ACTION =
- "com.android.cts.verifier.nfc.hce.POLLING_FRAME_ACTION";
- public static final String POLLING_FRAME_EXTRA = "POLLING_FRAME_EXTRA";
-
- static final ComponentName COMPONENT = new ComponentName("com.android.cts.verifier",
- PollingLoopService.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.ACCESS_AID, true),
- HceUtils.buildCommandApdu("80CA01F000", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "123456789000",
- "1481148114819000"
- };
-
- public PollingLoopService() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-
- @Override
- public void processPollingFrames(List<PollingFrame> frames) {
- Intent pollingFrameIntent = new Intent(POLLING_FRAME_ACTION);
- pollingFrameIntent.putExtra(HceUtils.EXTRA_COMPONENT, getComponent());
- pollingFrameIntent.putExtra(HceUtils.EXTRA_DURATION,
- System.currentTimeMillis() - mStartTime);
- pollingFrameIntent.putExtra(POLLING_FRAME_EXTRA, new ArrayList<PollingFrame>(frames));
- sendBroadcast(pollingFrameIntent);
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixAccessService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixAccessService.java
deleted file mode 100644
index 8cecb56..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixAccessService.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class PrefixAccessService extends HceService {
- static final String TAG = "PrefixAccessService";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- PrefixAccessService.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.ACCESS_PREFIX_AID + "FFFF", true),
- HceUtils.buildSelectApdu(HceUtils.ACCESS_PREFIX_AID + "FFAA", true),
- HceUtils.buildSelectApdu(HceUtils.ACCESS_PREFIX_AID + "FFAABBCCDDEEFF", true),
- HceUtils.buildCommandApdu("80CA010000010203", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "FAFE9000",
- "FAFE25929000",
- "FAFEAABB25929000",
- "FAFEFFAACC25929000"
- };
-
- public PrefixAccessService() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentEmulator2Activity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentEmulator2Activity.java
deleted file mode 100644
index bfb0818..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentEmulator2Activity.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class PrefixPaymentEmulator2Activity extends BaseEmulatorActivity {
- final static int STATE_IDLE = 0;
- final static int STATE_SERVICE1_SETTING_UP = 1;
- final static int STATE_SERVICE2_SETTING_UP = 2;
- final static int STATE_MAKING_SERVICE2_DEFAULT = 3;
-
- int mState = STATE_IDLE;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- mState = STATE_SERVICE2_SETTING_UP;
- setupServices(this, PrefixPaymentService2.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- if (mState == STATE_SERVICE2_SETTING_UP) {
- mState = STATE_SERVICE1_SETTING_UP;
- setupServices(this, PrefixPaymentService1.COMPONENT, PrefixPaymentService2.COMPONENT);
- return;
- }
- // Verify HCE service 2 is the default
- int stringId = isWalletRoleAvailable() ? R.string.nfc_hce_change_default_wallet
- : R.string.nfc_hce_change_preinstalled_wallet;
- if (makePaymentDefault(PrefixPaymentService2.COMPONENT, stringId)) {
- mState = STATE_MAKING_SERVICE2_DEFAULT;
- } else {
- // Already default
- NfcDialogs.createHceTapReaderDialog(this,getString(R.string.nfc_hce_payment_prefix_aids_help)).show();
- }
- }
-
- @Override
- void onPaymentDefaultResult(ComponentName component, boolean success) {
- if (success) {
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_payment_prefix_aids_help)).show();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- /**
- * Accepting isWalletRoleAvailable as a parameter to decide sequences accordingly
- **/
- public static Intent buildReaderIntent(Context context, boolean isWalletRoleAvailable) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- CommandApdu[] commandSequence = isWalletRoleAvailable
- ? PrefixPaymentService1.APDU_COMMAND_SEQUENCE
- : PrefixPaymentService2.APDU_COMMAND_SEQUENCE;
- String[] responseSequence = isWalletRoleAvailable
- ? PrefixPaymentService1.APDU_RESPOND_SEQUENCE
- : PrefixPaymentService2.APDU_RESPOND_SEQUENCE;
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- commandSequence);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- responseSequence);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_payment_prefix_aids_reader_2));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(isWalletRoleAvailable() ? PrefixPaymentService1.COMPONENT
- : PrefixPaymentService2.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentEmulatorActivity.java
deleted file mode 100644
index a028842..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentEmulatorActivity.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class PrefixPaymentEmulatorActivity extends BaseEmulatorActivity {
- final static int STATE_IDLE = 0;
- final static int STATE_SERVICE1_SETTING_UP = 1;
- final static int STATE_SERVICE2_SETTING_UP = 2;
- final static int STATE_MAKING_SERVICE1_DEFAULT = 3;
-
- int mState = STATE_IDLE;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- mState = STATE_SERVICE1_SETTING_UP;
- setupServices(this, PrefixPaymentService1.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- if (mState == STATE_SERVICE1_SETTING_UP) {
- mState = STATE_SERVICE2_SETTING_UP;
- setupServices(this, PrefixPaymentService1.COMPONENT, PrefixPaymentService2.COMPONENT);
- return;
- }
- // Verify HCE service 1 is the default
- if (makePaymentDefault(PrefixPaymentService1.COMPONENT, R.string.nfc_hce_change_preinstalled_wallet)) {
- mState = STATE_MAKING_SERVICE1_DEFAULT;
- } else {
- // Already default
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_payment_prefix_aids_help)).show();
- }
- }
-
- @Override
- void onPaymentDefaultResult(ComponentName component, boolean success) {
- if (success) {
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_hce_payment_prefix_aids_help)).show();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- PrefixPaymentService1.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- PrefixPaymentService1.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_payment_prefix_aids_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(PrefixPaymentService1.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentService1.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentService1.java
deleted file mode 100644
index be6acc2..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentService1.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class PrefixPaymentService1 extends HceService {
- static final String TAG = "PrefixPaymentService1";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- PrefixPaymentService1.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.PPSE_AID, true),
- HceUtils.buildSelectApdu(HceUtils.MC_AID, true),
- HceUtils.buildCommandApdu("80CA01F000", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "F1239000",
- "F4569000",
- "F789FFAABB9000"
- };
-
- public PrefixPaymentService1() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentService2.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentService2.java
deleted file mode 100644
index 9642025..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixPaymentService2.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class PrefixPaymentService2 extends HceService {
- static final String TAG = "PrefixPaymentService2";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- PrefixPaymentService2.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.PPSE_AID, true),
- HceUtils.buildSelectApdu(HceUtils.MC_AID, true),
- HceUtils.buildCommandApdu("80CA02F000", true),
- HceUtils.buildSelectApdu("F0000000FFFFFFFFFFFFFFFFFFFFFFFF", true),
- HceUtils.buildSelectApdu("F000000000", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "FAAA9000",
- "FBBB9000",
- "F789FFCCDD9000",
- "FFBAFEBECA",
- "F0BABEFECA"
- };
-
- public PrefixPaymentService2() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixTransportService1.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixTransportService1.java
deleted file mode 100644
index 5521440..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixTransportService1.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class PrefixTransportService1 extends HceService {
- static final String TAG = "PrefixTransportService1";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- PrefixTransportService1.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.TRANSPORT_PREFIX_AID + "FFFF", true),
- HceUtils.buildSelectApdu(HceUtils.TRANSPORT_PREFIX_AID + "FFAA", true),
- HceUtils.buildSelectApdu(HceUtils.TRANSPORT_PREFIX_AID + "FFAABBCCDDEEFF", true),
- HceUtils.buildCommandApdu("80CA01FFAA", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "25929000",
- "FFEF25929000",
- "FFDFFFAABB25929000",
- "FFDFFFAACC25929000"
- };
-
- public PrefixTransportService1() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixTransportService2.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixTransportService2.java
deleted file mode 100644
index 2235446..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/PrefixTransportService2.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class PrefixTransportService2 extends HceService {
- static final String TAG = "PrefixTransportService2";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- PrefixTransportService2.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.TRANSPORT_PREFIX_AID + "FFFF", true),
- HceUtils.buildSelectApdu(HceUtils.TRANSPORT_PREFIX_AID + "FFAA", true),
- HceUtils.buildSelectApdu(HceUtils.TRANSPORT_PREFIX_AID + "FFAABBCCDDEEFF", true),
- HceUtils.buildCommandApdu("80CA01FFBB", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "36039000",
- "FFBB25929000",
- "FFDFFFBBBB25929000",
- "FFDFFFBBCC25929000"
- };
-
- public PrefixTransportService2() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsEmulatorActivity.java
deleted file mode 100644
index 70842a5..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsEmulatorActivity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class ProtocolParamsEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(true);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_hce_protocol_params_emulator_help)).show();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsReaderActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsReaderActivity.java
deleted file mode 100644
index d3504ff..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ProtocolParamsReaderActivity.java
+++ /dev/null
@@ -1,218 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcAdapter.ReaderCallback;
-import android.nfc.tech.IsoDep;
-import android.nfc.tech.NfcA;
-import android.nfc.Tag;
-import android.os.Bundle;
-import android.widget.TextView;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import java.io.IOException;
-
-@TargetApi(19)
-public class ProtocolParamsReaderActivity extends PassFailButtons.Activity implements ReaderCallback {
- public static final String TAG = "ProtocolParamsReaderActivity";
-
- NfcAdapter mAdapter;
-
- TextView mTextView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
-
- setTitle(R.string.nfc_hce_protocol_params_reader);
-
- mAdapter = NfcAdapter.getDefaultAdapter(this);
- mTextView = (TextView) findViewById(R.id.text);
- mTextView.setTextSize(12.0f);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A |
- NfcAdapter.FLAG_READER_NFC_BARCODE | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
- }
-
- public boolean parseProtocolParameters(StringBuilder sb, byte[] uid,
- short sak, byte[] atqa, byte[] ats) {
-
- boolean success = true;
-
- sb.append("UID: " + HceUtils.getHexBytes(null, uid) + "\n\n");
- sb.append("SAK: 0x" + Integer.toHexString(sak & 0xFF) + "\n");
-
- if ((sak & 0x20) != 0) {
- sb.append(" (OK) ISO-DEP bit (0x20) is set.\n");
- } else {
- success = false;
- sb.append(" (FAIL) ISO-DEP bit (0x20) is NOT set.\n");
- }
-
- if ((sak & 0x40) != 0) {
- sb.append(" (OK) P2P bit (0x40) is set.\n");
- } else {
- sb.append(" (WARN) P2P bit (0x40) is NOT set.\n");
- }
-
- sb.append("\n");
- sb.append("ATQA: " + HceUtils.getHexBytes(null, atqa) + "\n");
- sb.append("\n");
-
- sb.append("ATS: " + HceUtils.getHexBytes(null, ats) + "\n");
- sb.append(" TL: 0x" + Integer.toHexString(ats[0] & 0xFF) + "\n");
- sb.append(" T0: 0x" + Integer.toHexString(ats[1] & 0xFF) + "\n");
-
- boolean ta_present = false;
- boolean tb_present = false;
- boolean tc_present = false;
- int atsIndex = 1;
- if ((ats[atsIndex] & 0x40) != 0) {
- sb.append(" (OK) T(C) is present (bit 7 is set).\n");
- tc_present = true;
- } else {
- success = false;
- sb.append(" (FAIL) T(C) is not present (bit 7 is NOT set).\n");
- }
- if ((ats[atsIndex] & 0x20) != 0) {
- sb.append(" (OK) T(B) is present (bit 6 is set).\n");
- tb_present = true;
- } else {
- success = false;
- sb.append(" (FAIL) T(B) is not present (bit 6 is NOT set).\n");
- }
- if ((ats[atsIndex] & 0x10) != 0) {
- sb.append(" (OK) T(A) is present (bit 5 is set).\n");
- ta_present = true;
- } else {
- success = false;
- sb.append(" (FAIL) T(A) is not present (bit 5 is NOT set).\n");
- }
- int fsc = ats[atsIndex] & 0x0F;
- if (fsc > 8) {
- success = false;
- sb.append(" (FAIL) FSC " + Integer.toString(fsc) + " is > 8\n");
- } else if (fsc < 2) {
- sb.append(" (FAIL EMVCO) FSC " + Integer.toString(fsc) + " is < 2\n");
- } else {
- sb.append(" (OK) FSC = " + Integer.toString(fsc) + "\n");
- }
-
- atsIndex++;
- if (ta_present) {
- sb.append(" TA: 0x" + Integer.toHexString(ats[atsIndex] & 0xff) + "\n");
- if ((ats[atsIndex] & 0x80) != 0) {
- sb.append(" (OK) bit 8 set, indicating only same bit rate divisor.\n");
- } else {
- sb.append(" (FAIL EMVCO) bit 8 NOT set, indicating support for assymetric " +
- "bit rate divisors. EMVCo requires bit 8 set.\n");
- }
- if ((ats[atsIndex] & 0x70) != 0) {
- sb.append(" (FAIL EMVCO) EMVCo requires bits 7 to 5 set to 0.\n");
- } else {
- sb.append(" (OK) bits 7 to 5 indicating only 106 kbit/s L->P supported.\n");
- }
- if ((ats[atsIndex] & 0x7) != 0) {
- sb.append(" (FAIL EMVCO) EMVCo requires bits 3 to 1 set to 0.\n");
- } else {
- sb.append(" (OK) bits 3 to 1 indicating only 106 kbit/s P->L supported.\n");
- }
- atsIndex++;
- }
-
- if (tb_present) {
- sb.append(" TB: 0x" + Integer.toHexString(ats[3] & 0xFF) + "\n");
- int fwi = (ats[atsIndex] & 0xF0) >> 4;
- if (fwi > 8) {
- success = false;
- sb.append(" (FAIL) FWI=" + Integer.toString(fwi) + ", should be <= 8\n");
- } else if (fwi == 8) {
- sb.append(" (FAIL EMVCO) FWI=" + Integer.toString(fwi) +
- ", EMVCo requires <= 7\n");
- } else {
- sb.append(" (OK) FWI=" + Integer.toString(fwi) + "\n");
- }
- int sfgi = ats[atsIndex] & 0x0F;
- if (sfgi > 8) {
- success = false;
- sb.append(" (FAIL) SFGI=" + Integer.toString(sfgi) + ", should be <= 8\n");
- } else {
- sb.append(" (OK) SFGI=" + Integer.toString(sfgi) + "\n");
- }
- atsIndex++;
- }
-
- if (tc_present) {
- sb.append(" TC: 0x" + Integer.toHexString(ats[atsIndex] & 0xFF) + "\n");
- boolean apSupported = (ats[atsIndex] & 0x10) != 0;
- boolean didSupported = (ats[atsIndex] & 0x02) != 0;
- boolean nadSupported = (ats[atsIndex] & 0x01) != 0;
- if (nadSupported) {
- success = false;
- sb.append(" (FAIL) NAD bit is not allowed to be set.\n");
- } else {
- sb.append(" (OK) NAD bit is not set.\n");
- }
- atsIndex++;
- // See if there's any bytes left for general bytes
- if (atsIndex + 1 < ats.length) {
- int bytesToCopy = ats.length - atsIndex;
- byte[] historical_bytes = new byte[bytesToCopy];
- System.arraycopy(ats, atsIndex, historical_bytes, 0, bytesToCopy);
- sb.append("\n(OK) Historical bytes: " +
- HceUtils.getHexBytes(null, historical_bytes));
- }
- }
- return success;
- }
-
- @Override
- public void onTagDiscovered(Tag tag) {
- final StringBuilder sb = new StringBuilder();
- IsoDep isoDep = IsoDep.get(tag);
- NfcA nfcA = NfcA.get(tag);
- boolean success = false;
- if (nfcA == null || isoDep == null) {
- return;
- }
- try {
- nfcA.connect();
- byte[] ats = nfcA.transceive(new byte[] { (byte) 0xE0, (byte)0xF0});
- success = parseProtocolParameters(sb, tag.getId(), nfcA.getSak(), nfcA.getAtqa(), ats);
- } catch (IOException e) {
- sb.insert(0, "Test failed. IOException (did you keep the devices in range?)\n\n.");
- } finally {
- if (success) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTextView.setText(sb.toString());
- getPassButton().setEnabled(true);
- }
- });
- } else {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTextView.setText(sb.toString());
- getPassButton().setEnabled(false);
- }
- });
- }
- try {
- nfcA.transceive(new byte[] {(byte) 0xC2});
- nfcA.close();
- isoDep.connect();
- } catch (IOException e) {
- }
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOffPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOffPaymentEmulatorActivity.java
deleted file mode 100644
index cd01105..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOffPaymentEmulatorActivity.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import static android.content.Context.RECEIVER_EXPORTED;
-
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcManager;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class ScreenOffPaymentEmulatorActivity extends BaseEmulatorActivity {
- final static int STATE_SCREEN_ON = 0;
- final static int STATE_SCREEN_OFF = 1;
- private static final int SECURE_NFC_ENABLED_DIALOG_ID = 1;
- private int mState = STATE_SCREEN_ON;
-
- private ScreenOnOffReceiver mReceiver;
- private NfcAdapter mNfcAdapter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- mState = STATE_SCREEN_ON;
- setupServices(this, ScreenOffPaymentService.COMPONENT);
-
- mReceiver = new ScreenOnOffReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_SCREEN_ON);
- registerReceiver(mReceiver, filter, RECEIVER_EXPORTED);
-
- NfcManager nfcManager = getSystemService(NfcManager.class);
- mNfcAdapter = nfcManager.getDefaultAdapter();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (mNfcAdapter.isSecureNfcSupported() && mNfcAdapter.isSecureNfcEnabled()) {
- showDialog(SECURE_NFC_ENABLED_DIALOG_ID);
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- unregisterReceiver(mReceiver);
- }
-
- @Override
- void onServicesSetup(boolean result) {
- // Verify ScreenOff HCE service is the default
- if (makePaymentDefault(ScreenOffPaymentService.COMPONENT,
- R.string.nfc_hce_change_preinstalled_wallet)) {
- // Wait for callback
- } else {
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_screen_off_hce_payment_help)).show();
- }
- }
-
- @Override
- void onPaymentDefaultResult(ComponentName component, boolean success) {
- if (success) {
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_screen_off_hce_payment_help)).show();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- ScreenOffPaymentService.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- ScreenOffPaymentService.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_screen_off_hce_payment_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(ScreenOffPaymentService.COMPONENT) && mState == STATE_SCREEN_OFF) {
- getPassButton().setEnabled(true);
- }
- }
-
- @Override
- public Dialog onCreateDialog(int id, Bundle args) {
- switch (id) {
- case SECURE_NFC_ENABLED_DIALOG_ID:
- return NfcDialogs.createSecureNfcEnabledDialog(this);
- default:
- return super.onCreateDialog(id, args);
- }
- }
-
- private class ScreenOnOffReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- mState = STATE_SCREEN_OFF;
- } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
- mState = STATE_SCREEN_ON;
- }
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOffPaymentService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOffPaymentService.java
deleted file mode 100644
index 9f562ca..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOffPaymentService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class ScreenOffPaymentService extends HceService {
- static final String TAG = "ScreenOffPaymentService";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- ScreenOffPaymentService.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.PPSE_AID, true),
- HceUtils.buildSelectApdu(HceUtils.MC_AID, true),
- HceUtils.buildCommandApdu("80CA01F000", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "FFFF9000",
- "FFEF9000",
- "FFDFFFAABB9000"
- };
-
- public ScreenOffPaymentService() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOnOnlyOffHostEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOnOnlyOffHostEmulatorActivity.java
deleted file mode 100644
index b9ae298..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOnOnlyOffHostEmulatorActivity.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class ScreenOnOnlyOffHostEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- setupServices(this, ScreenOnOnlyOffHostService.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this, getString(R.string.nfc_screen_on_only_offhost_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- ScreenOnOnlyOffHostService.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- ScreenOnOnlyOffHostService.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_screen_on_only_offhost_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOnOnlyOffHostService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOnOnlyOffHostService.java
deleted file mode 100644
index daebf69..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ScreenOnOnlyOffHostService.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class ScreenOnOnlyOffHostService {
- public static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- ScreenOnOnlyOffHostService.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu("A000000476416E64726F696443545340", true),
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "*",
- };
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SimpleReaderActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SimpleReaderActivity.java
deleted file mode 100644
index 6bcd72c..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SimpleReaderActivity.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.app.AlertDialog;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcAdapter.ReaderCallback;
-import android.nfc.Tag;
-import android.nfc.tech.IsoDep;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Log;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-@TargetApi(19)
-public class SimpleReaderActivity extends PassFailButtons.Activity implements ReaderCallback,
- OnItemSelectedListener {
- public static final String PREFS_NAME = "HceTypePrefs";
-
- public static final String TAG = "SimpleReaderActivity";
- public static final String EXTRA_APDUS = "apdus";
- public static final String EXTRA_RESPONSES = "responses";
- public static final String EXTRA_LABEL = "label";
- public static final String EXTRA_NFC_TECH = "nfc_tech";
-
-
- NfcAdapter mAdapter;
- CommandApdu[] mApdus;
- String[] mResponses;
- String mLabel;
-
- TextView mTextView;
- Spinner mSpinner;
- SharedPreferences mPrefs;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.nfc_hce_reader);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- mLabel = getIntent().getStringExtra(EXTRA_LABEL);
- setTitle(mLabel);
-
- mAdapter = NfcAdapter.getDefaultAdapter(this);
- mTextView = (TextView) findViewById(R.id.text);
- mTextView.setTextSize(12.0f);
- mTextView.setText(R.string.nfc_hce_type_selection);
-
- Spinner spinner = (Spinner) findViewById(R.id.type_ab_selection);
- Intent intent = getIntent();
- if (!intent.hasExtra(EXTRA_NFC_TECH)) {
- ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
- R.array.nfc_types_array, android.R.layout.simple_spinner_item);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spinner.setAdapter(adapter);
- spinner.setOnItemSelectedListener(this);
-
- mPrefs = getSharedPreferences(PREFS_NAME, 0);
- boolean isTypeB = mPrefs.getBoolean("typeB", false);
- if (isTypeB) {
- spinner.setSelection(1);
- }
- } else {
- spinner.setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Intent intent = getIntent();
- int nfc_tech = intent.getIntExtra(EXTRA_NFC_TECH, NfcAdapter.FLAG_READER_NFC_A
- | NfcAdapter.FLAG_READER_NFC_BARCODE | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK);
- mAdapter.enableReaderMode(this, this, nfc_tech, null);
- Parcelable[] apdus = intent.getParcelableArrayExtra(EXTRA_APDUS);
- if (apdus != null) {
- mApdus = new CommandApdu[apdus.length];
- for (int i = 0; i < apdus.length; i++) {
- mApdus[i] = (CommandApdu) apdus[i];
- }
- } else {
- mApdus = null;
- }
- mResponses = intent.getStringArrayExtra(EXTRA_RESPONSES);
- }
-
- @Override
- public void onTagDiscovered(Tag tag) {
- final StringBuilder sb = new StringBuilder();
- IsoDep isoDep = IsoDep.get(tag);
- if (isoDep == null) {
- // TODO dialog box
- return;
- }
-
- try {
- isoDep.connect();
- isoDep.setTimeout(5000);
- int count = 0;
- boolean success = true;
- long startTime = System.currentTimeMillis();
- for (CommandApdu apdu: mApdus) {
- sb.append("Request APDU:\n");
- sb.append(apdu.getApdu() + "\n\n");
- long apduStartTime = System.currentTimeMillis();
- byte[] response = isoDep.transceive(HceUtils.hexStringToBytes(apdu.getApdu()));
- long apduEndTime = System.currentTimeMillis();
- sb.append("Response APDU (in " + Long.toString(apduEndTime - apduStartTime) +
- " ms):\n");
- sb.append(HceUtils.getHexBytes(null, response));
-
- sb.append("\n\n\n");
- boolean wildCard = "*".equals(mResponses[count]);
- byte[] expectedResponse = HceUtils.hexStringToBytes(mResponses[count]);
- Log.d(TAG, HceUtils.getHexBytes("APDU response: ", response));
- if (!wildCard && !Arrays.equals(response, expectedResponse)) {
- Log.d(TAG, "Unexpected APDU response: " + HceUtils.getHexBytes("", response));
- success = false;
- break;
- }
- count++;
- }
- if (success) {
- sb.insert(0, "Total APDU exchange time: " +
- Long.toString(System.currentTimeMillis() - startTime) + " ms.\n\n");
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTextView.setText(sb.toString());
- getPassButton().setEnabled(true);
- }
- });
- } else {
- sb.insert(0, "FAIL. Total APDU exchange time: " +
- Long.toString(System.currentTimeMillis() - startTime) + " ms.\n\n");
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTextView.setText(sb.toString());
- AlertDialog.Builder builder = new AlertDialog.Builder(SimpleReaderActivity.this);
- builder.setTitle("Test failed");
- builder.setMessage("An unexpected response APDU was received, or no APDUs were received at all.");
- builder.setPositiveButton("OK", null);
- builder.show();
- }
- });
- }
- } catch (IOException e) {
- sb.insert(0, "Error while reading: (did you keep the devices in range?)\nPlease try again\n.");
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTextView.setText(sb.toString());
- }
- });
- } finally {
- }
- }
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position,
- long id) {
- Intent intent = getIntent();
- if (!intent.hasExtra(EXTRA_NFC_TECH)) {
- if (position == 0) {
- // Type-A
- mAdapter.disableReaderMode(this);
- mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_A
- | NfcAdapter.FLAG_READER_NFC_BARCODE
- | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
- SharedPreferences.Editor editor = mPrefs.edit();
- editor.putBoolean("typeB", false);
- editor.commit();
- } else {
- // Type-B
- mAdapter.disableReaderMode(this);
- mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_B
- | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
- SharedPreferences.Editor editor = mPrefs.edit();
- editor.putBoolean("typeB", true);
- editor.commit();
- }
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
-
- @Override
- public String getTestId() {
- return mLabel;
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SingleNonPaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SingleNonPaymentEmulatorActivity.java
deleted file mode 100644
index 694199b..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SingleNonPaymentEmulatorActivity.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-public class SingleNonPaymentEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, TransportService1.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this, null).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- TransportService1.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- TransportService1.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_single_non_payment_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(TransportService1.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SinglePaymentEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SinglePaymentEmulatorActivity.java
deleted file mode 100644
index 0f1fba0..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/SinglePaymentEmulatorActivity.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class SinglePaymentEmulatorActivity extends BaseEmulatorActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setupServices(this, PaymentService1.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- // Verify HCE service 1 is the default
- if (makePaymentDefault(PaymentService1.COMPONENT,
- R.string.nfc_hce_change_preinstalled_wallet)) {
- // Wait for callback
- } else {
- NfcDialogs.createHceTapReaderDialog(this, null).show();
- }
- }
-
- @Override
- void onPaymentDefaultResult(ComponentName component, boolean success) {
- if (success) {
- NfcDialogs.createHceTapReaderDialog(this, null).show();
- }
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- PaymentService1.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- PaymentService1.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_single_payment_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(PaymentService1.COMPONENT)) {
- getPassButton().setEnabled(true);
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TapTestEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TapTestEmulatorActivity.java
deleted file mode 100644
index 2eea89c..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TapTestEmulatorActivity.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.TextView;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class TapTestEmulatorActivity extends BaseEmulatorActivity {
- TextView mTextView;
- int mNumTaps;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- mTextView = (TextView) findViewById(R.id.text);
- setupServices(this, TransportService1.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mNumTaps = 0;
- mTextView.setText("Number of successful taps: 0/50.");
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_hce_tap_test_emulator_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- TransportService1.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- TransportService1.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_tap_test_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(TransportService1.COMPONENT)) {
- mNumTaps++;
- if (mNumTaps <= 50) {
- mTextView.setText("Number of successful taps: " + Integer.toString(mNumTaps) +
- "/50.");
- }
- if (mNumTaps >= 50) {
- getPassButton().setEnabled(true);
- }
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputEmulatorActivity.java
deleted file mode 100644
index f050d77..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputEmulatorActivity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.TextView;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.NfcDialogs;
-
-@TargetApi(19)
-public class ThroughputEmulatorActivity extends BaseEmulatorActivity {
- TextView mTextView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_text);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- mTextView = (TextView) findViewById(R.id.text);
- setupServices(this, ThroughputService.COMPONENT);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- void onServicesSetup(boolean result) {
- NfcDialogs.createHceTapReaderDialog(this,
- getString(R.string.nfc_hce_throughput_emulator_help)).show();
- }
-
- public static Intent buildReaderIntent(Context context) {
- Intent readerIntent = new Intent(context, SimpleReaderActivity.class);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_APDUS,
- ThroughputService.APDU_COMMAND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_RESPONSES,
- ThroughputService.APDU_RESPOND_SEQUENCE);
- readerIntent.putExtra(SimpleReaderActivity.EXTRA_LABEL,
- context.getString(R.string.nfc_hce_throughput_reader));
- return readerIntent;
- }
-
- @Override
- void onApduSequenceComplete(ComponentName component, long duration) {
- if (component.equals(ThroughputService.COMPONENT)) {
- long timePerApdu = duration / ThroughputService.APDU_COMMAND_SEQUENCE.length;
- if (duration < 1000) {
- mTextView.setText("PASS. Total duration: " + Long.toString(duration) + " ms " +
- "( " + Long.toString(timePerApdu) + " ms per APDU roundtrip).");
- getPassButton().setEnabled(true);
- } else {
- mTextView.setText("FAIL. Total duration: " + Long.toString(duration) + " ms " +
- "(" + Long.toString(timePerApdu) + " ms per APDU roundtrip)." +
- " Require <= 60ms per APDU roundtrip.");
- }
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputService.java
deleted file mode 100644
index b74f0b7..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/ThroughputService.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class ThroughputService extends HceService {
- static final String TAG = "PaymentService1";
-
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- ThroughputService.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu("F0010203040607FF", true),
- HceUtils.buildCommandApdu("80CA010100", true),
- HceUtils.buildCommandApdu("80CA010200", true),
- HceUtils.buildCommandApdu("80CA010300", true),
- HceUtils.buildCommandApdu("80CA010400", true),
- HceUtils.buildCommandApdu("80CA010500", true),
- HceUtils.buildCommandApdu("80CA010600", true),
- HceUtils.buildCommandApdu("80CA010700", true),
- HceUtils.buildCommandApdu("80CA010800", true),
- HceUtils.buildCommandApdu("80CA010900", true),
- HceUtils.buildCommandApdu("80CA010A00", true),
- HceUtils.buildCommandApdu("80CA010B00", true),
- HceUtils.buildCommandApdu("80CA010C00", true),
- HceUtils.buildCommandApdu("80CA010D00", true),
- HceUtils.buildCommandApdu("80CA010E00", true),
- HceUtils.buildCommandApdu("80CA010F00", true),
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "9000",
- "0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0002FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "0009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "000AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- "000EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9000",
- };
-
- public ThroughputService() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService1.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService1.java
deleted file mode 100644
index c8d8460..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService1.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class TransportService1 extends HceService {
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- TransportService1.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.TRANSPORT_AID, true),
- HceUtils.buildCommandApdu("80CA01E000", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "80CA9000",
- "83947102829000"
- };
-
- public TransportService1() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService2.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService2.java
deleted file mode 100644
index 322a076..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/TransportService2.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.android.cts.verifier.nfc.hce;
-
-import android.content.ComponentName;
-
-public class TransportService2 extends HceService {
- static final ComponentName COMPONENT =
- new ComponentName("com.android.cts.verifier",
- TransportService2.class.getName());
-
- public static final CommandApdu[] APDU_COMMAND_SEQUENCE = {
- HceUtils.buildSelectApdu(HceUtils.TRANSPORT_AID, true),
- HceUtils.buildCommandApdu("80CA01E100", true)
- };
-
- public static final String[] APDU_RESPOND_SEQUENCE = {
- "81CA9000",
- "7483624748FEFE9000"
- };
-
- public TransportService2() {
- initialize(APDU_COMMAND_SEQUENCE, APDU_RESPOND_SEQUENCE);
- }
-
- @Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
index 30ce15f..bdcb590 100755
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
@@ -20,6 +20,8 @@
import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATION;
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_CDMA;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_GSM;
import static android.keystore.cts.CertificateUtils.createCertificate;
import static com.google.common.truth.Truth.assertThat;
@@ -461,15 +463,19 @@
assertWithMessage("Need to be able to read device identifiers")
.that(telephonyService)
.isNotNull();
- imei = telephonyService.getImei(0);
- meid = telephonyService.getMeid(0);
- // If the device has a valid IMEI it must support attestation for it.
- if (imei != null) {
- modesToTest.add(ID_TYPE_IMEI);
+ if (mContext.getPackageManager().hasSystemFeature(FEATURE_TELEPHONY_GSM)) {
+ imei = telephonyService.getImei(0);
+ // If the device has a valid IMEI it must support attestation for it.
+ if (imei != null) {
+ modesToTest.add(ID_TYPE_IMEI);
+ }
}
- // Same for MEID
- if (meid != null) {
- modesToTest.add(ID_TYPE_MEID);
+ if (mContext.getPackageManager().hasSystemFeature(FEATURE_TELEPHONY_CDMA)) {
+ meid = telephonyService.getMeid(0);
+ // Same for MEID
+ if (meid != null) {
+ modesToTest.add(ID_TYPE_MEID);
+ }
}
}
int numCombinations = 1 << modesToTest.size();
diff --git a/hostsidetests/multidevices/Android.bp b/hostsidetests/multidevices/Android.bp
new file mode 100644
index 0000000..b7c59df
--- /dev/null
+++ b/hostsidetests/multidevices/Android.bp
@@ -0,0 +1,81 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Zip all the files in this directory together for merging into cts-verifier.zip.
+// build/envsetup.sh is used as a known file to get the location of the top of the
+// directory.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "multidevice_apps_to_include",
+ srcs: [
+ ":NfcReaderTestApp",
+ ":NfcEmulatorTestApp",
+ ],
+}
+
+filegroup {
+ name: "multidevice_tests_to_include",
+ srcs: [
+ "nfc/*.py",
+ ],
+}
+
+genrule {
+ name: "multidevice-test-apps",
+ srcs: [
+ ":multidevice_apps_to_include",
+ ],
+ tools: ["soong_zip"],
+ out: ["multidevice-test-apps.zip"],
+ cmd: "echo $(locations :multidevice_apps_to_include) >$(genDir)/list && " +
+ "$(location soong_zip) -o $(out) -j -P android-cts-verifier/MultiDevice -l $(genDir)/list",
+}
+
+genrule {
+ name: "multidevice-test-list",
+ srcs: [
+ ":multidevice_tests_to_include",
+ // Placeholder file outside the glob used to find the top of the directory.
+ "build/envsetup.sh",
+ ],
+ tools: ["soong_zip"],
+ out: ["multidevice-test-list.zip"],
+ cmd: "echo $(locations :multidevice_tests_to_include) >$(genDir)/test_list && " +
+ "$(location soong_zip) -o $(out) -P android-cts-verifier/MultiDevice/tests -C $$(dirname $$(dirname $(location build/envsetup.sh))) -l $(genDir)/test_list",
+}
+
+genrule {
+ name: "multidevice-test",
+ srcs: [
+ "tools/*.py",
+ "utils/*.py",
+ "config.yml",
+ // Placeholder file outside the glob used to find the top of the directory.
+ "build/envsetup.sh",
+ ":multidevice-test-list",
+ ":multidevice-test-apps",
+ ],
+ tools: [
+ "soong_zip",
+ "merge_zips",
+ ],
+ out: ["multidevice-test.zip"],
+ cmd: "echo $(locations tools/*.py) $(locations utils/*.py) $(locations build/envsetup.sh) $(locations config.yml) >$(genDir)/list && " +
+ "$(location soong_zip) -o $(genDir)/multidevice-temp.zip -P android-cts-verifier/MultiDevice -C $$(dirname $$(dirname $(location build/envsetup.sh))) -l $(genDir)/list && " +
+ "$(location merge_zips) $(out) $(genDir)/multidevice-temp.zip $(location :multidevice-test-list) $(location :multidevice-test-apps)",
+}
diff --git a/hostsidetests/multidevices/OWNERS b/hostsidetests/multidevices/OWNERS
new file mode 100644
index 0000000..d808e63
--- /dev/null
+++ b/hostsidetests/multidevices/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 1332816
+
+# xTS Infra (android-xts-infra@google.com)
+liutongbo@google.com
+wenshan@google.com
+kgui@google.com
+
+# Mobly team
+angli@google.com
+xianyuanjia@google.com
diff --git a/hostsidetests/multidevices/build/envsetup.sh b/hostsidetests/multidevices/build/envsetup.sh
new file mode 100644
index 0000000..b704334
--- /dev/null
+++ b/hostsidetests/multidevices/build/envsetup.sh
@@ -0,0 +1,42 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file should be sourced from bash. Sets environment variables for
+# running tests, and also checks that a number of dependencies are present
+# (indicating that the setup is correct).
+
+
+[[ "${BASH_SOURCE[0]}" != "${0}" ]] || \
+ { echo ">> Script must be sourced with 'source $0'" >&2; exit 1; }
+
+command -v adb >/dev/null 2>&1 || \
+ echo ">> Require adb executable to be in path" >&2
+
+command -v python >/dev/null 2>&1 || \
+ echo ">> Require python executable to be in path" >&2
+
+python3 -V 2>&1 | grep -q "Python 3.*" || \
+ echo ">> Require python version 3" >&2
+
+for M in mobly
+do
+ python3 -c "import $M" >/dev/null 2>&1 || \
+ echo ">> Require Python $M module" >&2
+done
+
+export PYTHONPATH="$PWD/utils:$PYTHONPATH"
+export PYTHONPATH="$PWD/tests:$PYTHONPATH"
+
+echo -e "\n*****Please execute below adb command on your dut before running the tests*****\n"
+echo -e "adb -s <device_id> shell am compat enable ALLOW_TEST_API_ACCESS com.android.cts.verifier\n\n"
diff --git a/hostsidetests/multidevices/config.yml b/hostsidetests/multidevices/config.yml
new file mode 100644
index 0000000..9a2e34a
--- /dev/null
+++ b/hostsidetests/multidevices/config.yml
@@ -0,0 +1,6 @@
+TestBeds:
+ - Name: cts_nfc_hce_multi_device_test
+ Controllers:
+ AndroidDevice:
+ - serial: "<device-id>"
+ - serial: "<device-id>"
diff --git a/hostsidetests/multidevices/nfc/Android.bp b/hostsidetests/multidevices/nfc/Android.bp
new file mode 100644
index 0000000..860eea7
--- /dev/null
+++ b/hostsidetests/multidevices/nfc/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_team: "trendy_team_fwk_nfc",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_defaults {
+ name: "CtsNfcMultiDevicePythonDefaults",
+ libs: [
+ "mobly",
+ ],
+ test_suites: [
+ // Added this test suite to CTS-V
+ "general-tests",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+}
+
+python_test_host {
+ name: "CtsNfcHceMultiDeviceTestCases",
+ main: "cts_nfc_hce_multi_device_test.py",
+ srcs: ["cts_nfc_hce_multi_device_test.py"],
+ test_config: "AndroidTest.xml",
+ data: [
+ ":NfcReaderTestApp",
+ ":NfcEmulatorTestApp",
+ ],
+ test_options: {
+ unit_test: false,
+ runner: "mobly",
+ },
+ defaults: ["CtsNfcMultiDevicePythonDefaults"],
+}
diff --git a/hostsidetests/multidevices/nfc/AndroidTest.xml b/hostsidetests/multidevices/nfc/AndroidTest.xml
new file mode 100644
index 0000000..523b13a
--- /dev/null
+++ b/hostsidetests/multidevices/nfc/AndroidTest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Config for CTS NFC multi device test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="nfc" />
+ <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" />
+
+ <device name="AndroidDevice">
+ <target_preparer class="AndroidMinSdkVersionCheckDecorator">
+ <option name="min_sdk_version" value="35" />
+ </target_preparer>
+ <target_preparer class="AndroidInstallAppsDecorator" />
+ </device>
+ <device name="AndroidDevice">
+ <target_preparer class="AndroidMinSdkVersionCheckDecorator">
+ <option name="min_sdk_version" value="35" />
+ </target_preparer>
+ <target_preparer class="AndroidInstallAppsDecorator" />
+ </device>
+
+ <test class="MoblyAospPackageTest" />
+
+ <option name="mobly_pkg" key="file" value="CtsNfcHceMultiDeviceTestCases" />
+ <option name="build_apk" key="file" value="NfcReaderTestApp.apk" />
+ <option name="build_apk" key="file" value="NfcEmulatorTestApp.apk" />
+</configuration>
diff --git a/hostsidetests/multidevices/nfc/OWNERS b/hostsidetests/multidevices/nfc/OWNERS
new file mode 100644
index 0000000..35e9713
--- /dev/null
+++ b/hostsidetests/multidevices/nfc/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
diff --git a/hostsidetests/multidevices/nfc/cts_nfc_hce_multi_device_test.py b/hostsidetests/multidevices/nfc/cts_nfc_hce_multi_device_test.py
new file mode 100644
index 0000000..272585e
--- /dev/null
+++ b/hostsidetests/multidevices/nfc/cts_nfc_hce_multi_device_test.py
@@ -0,0 +1,711 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# 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.
+
+# Lint as: python3
+"""CTS Tests that verify NFC HCE features.
+
+These tests require two phones, one acting as a card emulator and the other
+acting as an NFC reader. The two phones should be placed back to back.
+"""
+
+import sys
+import logging
+
+from mobly import asserts
+from mobly import base_test
+from mobly import test_runner
+from mobly import utils
+from mobly.controllers import android_device
+from mobly.snippet import errors
+
+# Timeout to give the NFC service time to perform async actions such as
+# discover tags.
+_NFC_TIMEOUT_SEC = 10
+_NFC_TECH_A_POLLING_ON = (0x1 #NfcAdapter.FLAG_READER_NFC_A
+ | 0x10 #NfcAdapter.FLAG_READER_NFC_BARCODE
+ | 0x80 #NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
+ )
+_NFC_TECH_A_POLLING_OFF = (0x10 #NfcAdapter.FLAG_READER_NFC_BARCODE
+ | 0x80 #NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
+ )
+_NFC_TECH_A_LISTEN_ON = 0x1 #NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_A
+_NFC_TECH_F_LISTEN_ON = 0x4 #NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_F
+_NFC_LISTEN_OFF = 0x0 #NfcAdapter.FLAG_LISTEN_DISABLE
+
+class CtsNfcHceMultiDeviceTestCases(base_test.BaseTestClass):
+ def setup_class(self):
+ """
+ Sets up class by registering two devices, enabling nfc on them,
+ and loading snippets.
+ """
+ self.emulator, self.reader = self.register_controller(android_device,
+ min_number=2)[:2]
+ self.reader.load_snippet('nfc_reader',
+ 'com.android.nfc.reader')
+ self.emulator.load_snippet('nfc_emulator',
+ 'com.android.nfc.emulator')
+
+ self.reader.adb.shell(['svc', 'nfc', 'enable'])
+ self.emulator.adb.shell(['svc', 'nfc', 'enable'])
+
+ self.reader.debug_tag = 'reader'
+ self.emulator.debug_tag = 'emulator'
+
+ def setup_test(self):
+ """
+ Turns emulator/reader screen on and unlocks between tests as some tests will
+ turn the screen off.
+ """
+ self.emulator.nfc_emulator.logInfo("*** TEST START: " + self.current_test_info.name + " ***")
+ self.reader.nfc_reader.logInfo("*** TEST START: " + self.current_test_info.name + " ***")
+ asserts.skip_if(
+ not self.emulator.nfc_emulator.isNfcSupported() or
+ not self.emulator.nfc_emulator.isNfcHceSupported(),
+ f"NFC is not supported on {self.emulator}",
+ )
+ asserts.skip_if(
+ not self.reader.nfc_reader.isNfcSupported(),
+ f"NFC is not supported on {self.reader}"
+ )
+
+ self.emulator.nfc_emulator.turnScreenOn()
+ self.emulator.nfc_emulator.pressMenu()
+ self.reader.nfc_reader.turnScreenOn()
+ self.reader.nfc_reader.pressMenu()
+
+ def on_fail(self, record):
+ test_name = record.test_name
+ self.emulator.take_bug_report(
+ test_name=self.emulator.debug_tag + "_" + test_name,
+ destination=self.current_test_info.output_path,
+ )
+ self.reader.take_bug_report(
+ test_name=self.reader.debug_tag + "_" + test_name,
+ destination=self.current_test_info.output_path,
+ )
+
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_single_non_payment_service(self):
+ """Tests successful APDU exchange between non-payment service and
+ reader.
+
+ Test Steps:
+ 1. Start emulator activity and set up non-payment HCE Service.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and
+ Transport Service after
+ _NFC_TIMEOUT_SEC.
+ """
+ self.emulator.nfc_emulator.startSingleNonPaymentEmulatorActivity()
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startSingleNonPaymentReaderActivity()
+ test_pass_event = test_pass_handler.waitAndGet('ApduSuccess',
+ _NFC_TIMEOUT_SEC)
+
+ asserts.assert_is_not_none(test_pass_event,
+ 'ApduSuccess event was not received.')
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"})
+ def test_single_payment_service(self):
+ """Tests successful APDU exchange between payment service and
+ reader.
+
+ Test Steps:
+ 1. Set callback handler on emulator for when the instrumentation app is
+ set to default wallet app.
+ 2. Start emulator activity and wait for the role to be set.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies emulator device sets the instrumentation emulator app to the
+ default wallet app.
+ 2. Verifies a successful APDU exchange between the emulator and
+ Transport Service after _NFC_TIMEOUT_SEC.
+ """
+ # Wait for instrumentation app to hold onto wallet role before starting
+ # reader
+ role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
+ 'RoleHeld')
+ self.emulator.nfc_emulator.startSinglePaymentEmulatorActivity()
+ role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
+ self.emulator.nfc_emulator.waitForService()
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startSinglePaymentReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"})
+ def test_dual_payment_service(self):
+ """Tests successful APDU exchange between a payment service and
+ reader when two payment services are set up on the emulator.
+
+ Test Steps:
+ 1. Set callback handler on emulator for when the instrumentation app is
+ set to default wallet app.
+ 2. Start emulator activity and wait for the role to be set.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and the
+ payment service.
+ """
+ role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
+ 'RoleHeld')
+ self.emulator.nfc_emulator.startDualPaymentEmulatorActivity()
+ role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
+ self.emulator.nfc_emulator.waitForService()
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startDualPaymentReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"})
+ def test_foreground_payment_emulator(self):
+ """Tests successful APDU exchange between non-default payment service and
+ reader when the foreground app sets a preference for the non-default
+ service.
+
+ Test Steps:
+ 1. Set callback handler on emulator for when the instrumentation app is
+ set to default wallet app.
+ 2. Start emulator activity and wait for the role to be set.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and the
+ preferred service.
+ """
+ role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
+ 'RoleHeld')
+ self.emulator.nfc_emulator.startForegroundPaymentEmulatorActivity()
+ role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
+ self.emulator.nfc_emulator.waitForService()
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startForegroundPaymentReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_dynamic_aid_emulator(self):
+ """Tests successful APDU exchange between payment service and reader
+ when the payment service has registered dynamic AIDs.
+
+ Test Steps:
+ 1. Set callback handler on emulator for when the instrumentation app is
+ set to default wallet app.
+ 2. Start emulator activity and wait for the role to be set.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and the
+ payment service with dynamic AIDs.
+ """
+ role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
+ 'RoleHeld')
+ self.emulator.nfc_emulator.startDynamicAidEmulatorActivity()
+ role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
+ self.emulator.nfc_emulator.waitForService()
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startDynamicAidReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"})
+ def test_payment_prefix_emulator(self):
+ """Tests successful APDU exchange between payment service and reader
+ when the payment service has statically registered prefix AIDs.
+
+ Test Steps:
+ 1. Set callback handler on emulator for when the instrumentation app is
+ set to default wallet app.
+ 2. Start emulator activity and wait for the role to be set.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and the
+ payment service with prefix AIDs.
+ """
+ role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
+ 'RoleHeld')
+ self.emulator.nfc_emulator.startPrefixPaymentEmulatorActivity()
+ role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
+ self.emulator.nfc_emulator.waitForService()
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startPrefixPaymentReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2", "9.1/C-0-1"})
+ def test_prefix_payment_emulator_2(self):
+ """Tests successful APDU exchange between payment service and reader
+ when the payment service has statically registered prefix AIDs.
+ Identical to the test above, except PrefixPaymentService2 is set up
+ first in the emulator activity.
+
+ Test Steps:
+ 1. Set callback handler on emulator for when the instrumentation app is
+ set to default wallet app.
+ 2. Start emulator activity and wait for the role to be set.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and the
+ payment service with prefix AIDs.
+ """
+ role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
+ 'RoleHeld')
+ self.emulator.nfc_emulator.startPrefixPaymentEmulator2Activity()
+ role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
+ self.emulator.nfc_emulator.waitForService()
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startPrefixPaymentReader2Activity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_other_prefix(self):
+ """Tests successful APDU exchange when the emulator dynamically
+ registers prefix AIDs for a non-payment service.
+
+ Test steps:
+ 1. Start emulator activity.
+ 2. Set callback handler on emulator for when ApduSuccess event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies successful APDU sequence exchange.
+
+ """
+ self.emulator.nfc_emulator.startDualNonPaymentPrefixEmulatorActivity()
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startDualNonPaymentPrefixReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_offhost_service(self):
+ """Tests successful APDU exchange between offhost service and reader.
+
+ Test Steps:
+ 1. Start emulator activity.
+ 2. Set callback handler for when reader TestPass event is received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange inside the reader.
+ We cannot verify the APDUs in the emulator since we don't have access to the secure element.
+ """
+ self.emulator.nfc_emulator.startOffHostEmulatorActivity(False)
+ test_pass_handler = self.reader.nfc_reader.asyncWaitForTestPass('ApduSuccess')
+ self.reader.nfc_reader.startOffHostReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_on_and_offhost_service(self):
+ """Tests successful APDU exchange between when reader selects both an on-host and off-host
+ service.
+
+ Test Steps:
+ 1. Start emulator activity.
+ 2. Set callback handler for when reader TestPass event is received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange inside the reader.
+ We cannot verify the APDUs in the emulator since we don't have access to the secure element.
+ """
+ self.emulator.nfc_emulator.startOnAndOffHostEmulatorActivity()
+ test_pass_handler = self.reader.nfc_reader.asyncWaitForTestPass('ApduSuccess')
+ self.reader.nfc_reader.startOnAndOffHostReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_dual_non_payment(self):
+ """Tests successful APDU exchange between transport service and reader
+ when two non-payment services are enabled.
+
+ Test Steps:
+ 1. Start emulator activity which sets up TransportService2 and
+ AccessService.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and the
+ transport service.
+ """
+ self.emulator.nfc_emulator.startDualNonPaymentEmulatorActivity()
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startDualNonPaymentReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_foreground_non_payment(self):
+ """Tests successful APDU exchange between non-payment service and
+ reader when the foreground app sets a preference for the
+ non-default service.
+
+ Test Steps:
+ 1. Start emulator activity which sets up TransportService1 and
+ TransportService2
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and non-default service.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and the
+ transport service.
+ """
+ self.emulator.nfc_emulator.startForegroundNonPaymentEmulatorActivity()
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startForegroundNonPaymentReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_throughput(self):
+ """Tests that APDU sequence exchange occurs with under 60ms per APDU.
+
+ Test Steps:
+ 1. Start emulator activity.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger APDU exchange between
+ reader and non-default service.
+
+ Verifies:
+ 1. Verifies a successful APDU exchange between the emulator and the
+ transport service with the duration averaging under 60 ms per single
+ exchange.
+ """
+ self.emulator.nfc_emulator.startThroughputEmulatorActivity()
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduUnderThreshold')
+ self.reader.nfc_reader.startThroughputReaderActivity()
+ test_pass_handler.waitAndGet('ApduUnderThreshold', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_tap_50_times(self):
+ """Tests that 50 consecutive APDU exchanges are successful.
+
+ Test Steps:
+ 1. Start emulator activity.
+ 2. Perform the following sequence 50 times:
+ a. Set callback handler on emulator for when a TestPass event is
+ received.
+ b. Start reader activity.
+ c. Wait for successful APDU exchange.
+ d. Close reader activity.
+
+ Verifies:
+ 1. Verifies 50 ApduSuccess events are received in a row.
+ """
+ self.emulator.nfc_emulator.startTapTestEmulatorActivity()
+ for i in range(50):
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess'
+ )
+ self.reader.nfc_reader.startTapTestReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+ self.reader.nfc_reader.closeActivity()
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_large_num_aids(self):
+ """Tests that a long APDU sequence (256 commands/responses) is
+ successfully exchanged.
+
+ Test Steps:
+ 1. Start emulator activity.
+ 2. Set callback handler on emulator for when a TestPass event is
+ received.
+ 3. Start reader activity.
+ 4. Wait for successful APDU exchange.
+
+ Verifies:
+ 1. Verifies successful APDU exchange.
+ """
+ # This test requires a larger timeout due to large number of AIDs
+ large_timeout = 60
+ self.emulator.nfc_emulator.startLargeNumAidsEmulatorActivity()
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess'
+ )
+ self.reader.nfc_reader.startLargeNumAidsReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', large_timeout)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_screen_off_payment(self):
+ """Tests that APDU exchange occurs when device screen is off.
+
+ Test Steps:
+ 1. Set callback handler on emulator for when the instrumentation app is
+ set to default wallet app.
+ 2. Start emulator activity and wait for the role to be set.
+ 3. Set callback handler for when screen is off.
+ 4. Turn emulator screen off and wait for event.
+ 5. Set callback handler on emulator for when a TestPass event is
+ received.
+ 6. Start reader activity, which should trigger successful APDU exchange.
+ 7. Wait for successful APDU exchange.
+
+ Verifies:
+ 1. Verifies default wallet app is set.
+ 2. Verifies screen is turned off on the emulator.
+ 3. Verifies successful APDU exchange with emulator screen off.
+ """
+ role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
+ 'RoleHeld'
+ )
+ self.emulator.nfc_emulator.startScreenOffPaymentEmulatorActivity()
+ role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
+ self.emulator.nfc_emulator.waitForService()
+
+ screen_off_handler = self.emulator.nfc_emulator.asyncWaitForScreenOff(
+ 'ScreenOff')
+ self.emulator.nfc_emulator.turnScreenOff()
+ screen_off_handler.waitAndGet('ScreenOff', _NFC_TIMEOUT_SEC)
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess'
+ )
+ self.reader.nfc_reader.startScreenOffPaymentReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_conflicting_non_payment(self):
+ """ This test registers two non-payment services with conflicting AIDs,
+ selects a service to use, and ensures the selected service exchanges
+ an APDU sequence with the reader.
+
+ Test Steps:
+ 1. Start emulator.
+ 2. Start reader.
+ 3. Select a service on the emulator device from the list of services.
+ 4. Disable polling on the reader.
+ 5. Set a callback handler on the emulator for a successful APDU
+ exchange.
+ 6. Re-enable polling on the reader, which should trigger the APDU
+ exchange with the selected service.
+
+ Verifies:
+ 1. Verifies APDU exchange is successful between the reader and the
+ selected service.
+ """
+ self.emulator.nfc_emulator.startConflictingNonPaymentEmulatorActivity()
+ self.reader.nfc_reader.startConflictingNonPaymentReaderActivity()
+ self.emulator.nfc_emulator.selectItem()
+ self.reader.nfc_reader.setPollTech(_NFC_TECH_A_POLLING_OFF)
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess'
+ )
+ self.reader.nfc_reader.setPollTech(_NFC_TECH_A_POLLING_ON)
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_conflicting_non_payment_prefix(self):
+ """ This test registers two non-payment services with conflicting
+ prefix AIDs, selects a service to use, and ensures the selected
+ service exchanges an APDU sequence with the reader.
+
+ Test Steps:
+ 1. Start emulator.
+ 2. Start reader.
+ 3. Select a service on the emulator device from the list of services.
+ 4. Disable polling on the reader.
+ 5. Set a callback handler on the emulator for a successful APDU
+ exchange.
+ 6. Re-enable polling on the reader, which should trigger the APDU
+ exchange with the selected service.
+
+ Verifies:
+ 1. Verifies APDU exchange is successful between the reader and the
+ selected service.
+ """
+ (self.emulator.nfc_emulator
+ .startConflictingNonPaymentPrefixEmulatorActivity())
+ self.reader.nfc_reader.startConflictingNonPaymentPrefixReaderActivity()
+ self.emulator.nfc_emulator.selectItem()
+ self.reader.nfc_reader.setPollTech(_NFC_TECH_A_POLLING_OFF)
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess'
+ )
+ self.reader.nfc_reader.setPollTech(_NFC_TECH_A_POLLING_ON)
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_protocol_params(self):
+ """ Tests that the Nfc-A and ISO-DEP protocol parameters are being
+ set correctly.
+
+ Test Steps:
+ 1. Start emulator.
+ 2. Start callback handler on reader for when a TestPass event is
+ received.
+ 3. Start reader.
+ 4. Wait for success event to be sent.
+
+ Verifies:
+ 1. Verifies Nfc-A and ISO-DEP protocol parameters are set correctly.
+ """
+ self.emulator.nfc_emulator.startProtocolParamsEmulatorActivity()
+ test_pass_handler = self.reader.nfc_reader.asyncWaitForTestPass(
+ 'TestPass')
+ self.reader.nfc_reader.startProtocolParamsReaderActivity()
+ test_pass_handler.waitAndGet('TestPass', _NFC_TIMEOUT_SEC)
+
+ #@CddTest(requirements = {"7.4.4/C-2-2", "7.4.4/C-1-2"})
+ def test_screen_on_only_off_host_service(self):
+ """
+ Test Steps:
+ 1. Start emulator and turn screen off.
+ 2. Start callback handler on reader for when a TestPass event is
+ received.
+ 3. Start reader activity, which should trigger callback handler.
+ 4. Ensure expected APDU is received.
+ 5. Close reader and turn screen off on the emulator.
+
+ Verifies:
+ 1. Verifies correct APDU response when screen is off.
+ 2. Verifies correct APDU response between reader and off-host service
+ when screen is on.
+ """
+ #Tests APDU exchange with screen off.
+ self.emulator.nfc_emulator.startScreenOnOnlyOffHostEmulatorActivity()
+ self.emulator.nfc_emulator.turnScreenOff()
+ screen_off_handler = self.emulator.nfc_emulator.asyncWaitForScreenOff(
+ 'ScreenOff')
+ screen_off_handler.waitAndGet('ScreenOff', _NFC_TIMEOUT_SEC)
+ test_pass_handler = (
+ self.reader.nfc_reader.asyncWaitForTestPass(
+ 'ApduSuccessScreenOff'))
+ self.reader.nfc_reader.startScreenOnOnlyOffHostReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccessScreenOff', _NFC_TIMEOUT_SEC)
+ self.reader.nfc_reader.closeActivity()
+
+ #Tests APDU exchange with screen on.
+ screen_on_handler = self.emulator.nfc_emulator.asyncWaitForScreenOn(
+ 'ScreenOn')
+ self.emulator.nfc_emulator.pressMenu()
+ screen_on_handler.waitAndGet('ScreenOn', _NFC_TIMEOUT_SEC)
+ test_pass_handler = self.reader.nfc_reader.asyncWaitForTestPass(
+ 'ApduSuccessScreenOn')
+ self.reader.nfc_reader.startScreenOnOnlyOffHostReaderActivity()
+
+ test_pass_handler.waitAndGet('ApduSuccessScreenOn', _NFC_TIMEOUT_SEC)
+
+ def test_single_payment_service_toggle_nfc_off_on(self):
+ """Tests successful APDU exchange between payment service and
+ reader.
+
+ Test Steps:
+ 1. Set callback handler on emulator for when the instrumentation app is
+ set to default wallet app.
+ 2. Start emulator activity and wait for the role to be set.
+ 3. Toggle NFC off and back on the emulator.
+ 4. Set callback handler on emulator for when a TestPass event is
+ received.
+ 5. Start reader activity, which should trigger APDU exchange between
+ reader and emulator.
+
+ Verifies:
+ 1. Verifies emulator device sets the instrumentation emulator app to the
+ default wallet app.
+ 2. Verifies a successful APDU exchange between the emulator and
+ Transport Service after _NFC_TIMEOUT_SEC after toggling NFC off and on.
+ """
+ # Wait for instrumentation app to hold onto wallet role before starting
+ # reader
+ role_held_handler = self.emulator.nfc_emulator.asyncWaitForRoleHeld(
+ 'RoleHeld')
+ self.emulator.nfc_emulator.startSinglePaymentEmulatorActivity()
+ role_held_handler.waitAndGet('RoleHeld', _NFC_TIMEOUT_SEC)
+ self.emulator.nfc_emulator.waitForService()
+
+ self.emulator.nfc_emulator.setNfcState(False)
+ self.emulator.nfc_emulator.setNfcState(True)
+
+ test_pass_handler = self.emulator.nfc_emulator.asyncWaitForTestPass(
+ 'ApduSuccess')
+ self.reader.nfc_reader.startSinglePaymentReaderActivity()
+ test_pass_handler.waitAndGet('ApduSuccess', _NFC_TIMEOUT_SEC)
+
+ def teardown_test(self):
+ self.emulator.nfc_emulator.closeActivity()
+ self.reader.nfc_reader.closeActivity()
+ utils.concurrent_exec(lambda d: d.services.create_output_excerpts_all(
+ self.current_test_info),
+ param_list=[[self.emulator], [self.reader]],
+ raise_on_exception=True)
+ self.emulator.nfc_emulator.logInfo("*** TEST END: " + self.current_test_info.name + " ***")
+ self.reader.nfc_reader.logInfo("*** TEST END: " + self.current_test_info.name + " ***")
+
+
+if __name__ == '__main__':
+ # Take test args
+ if '--' in sys.argv:
+ index = sys.argv.index('--')
+ sys.argv = sys.argv[:1] + sys.argv[index + 1:]
+ test_runner.main()
diff --git a/hostsidetests/multidevices/tools/run_all_tests.py b/hostsidetests/multidevices/tools/run_all_tests.py
new file mode 100644
index 0000000..3b496a7
--- /dev/null
+++ b/hostsidetests/multidevices/tools/run_all_tests.py
@@ -0,0 +1,173 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import logging
+import os
+import os.path
+from pathlib import Path
+import re
+import subprocess
+import tempfile
+import time
+import multi_device_utils
+import yaml
+
+
+RESULT_KEY = 'result'
+RESULT_PASS = 'PASS'
+RESULT_FAIL = 'FAIL'
+CONFIG_FILE = os.path.join(os.getcwd(), 'config.yml')
+TESTS_DIR = os.path.join(os.getcwd(), 'tests')
+CTS_VERIFIER_PACKAGE_NAME = 'com.android.cts.verifier'
+MOBLY_TEST_SUMMARY_TXT_FILE = 'test_mobly_summary.txt'
+MULTI_DEVICE_TEST_ACTIVITY = (
+ 'com.android.cts.verifier/.multidevice.MultiDeviceTestsActivity'
+)
+ACTION_HOST_TEST_RESULT = 'com.android.cts.verifier.ACTION_HOST_TEST_RESULT'
+EXTRA_VERSION = 'com.android.cts.verifier.extra.HOST_TEST_RESULT'
+ACTIVITY_START_WAIT = 2 # seconds
+
+
+def get_config_file_contents():
+ """Read the config file contents from a YML file.
+
+ Args: None
+
+ Returns:
+ config_file_contents: a dict read from config.yml
+ """
+ with open(CONFIG_FILE) as file:
+ config_file_contents = yaml.safe_load(file)
+ return config_file_contents
+
+
+def get_device_serial_number(config_file_contents):
+ """Returns the serial number of the dut devices.
+
+ Args:
+ config_file_contents: dict read from config.yml file.
+
+ Returns:
+ The serial numbers (str) or None if the device is not found.
+ """
+
+ device_serial_numbers = []
+ for _, testbed_data in config_file_contents.items():
+ for data_dict in testbed_data:
+ android_devices = data_dict.get('Controllers', {}).get(
+ 'AndroidDevice', []
+ )
+
+ for device_dict in android_devices:
+ device_serial_numbers.append(device_dict.get('serial'))
+ return device_serial_numbers
+
+
+def report_result(device_id, results):
+ """Sends a pass/fail result to the device, via an intent.
+
+ Args:
+ device_id: the serial number of the device.
+ results: a dictionary contains all multi-device test names as key and
+ result/summary of current test run.
+ """
+ adb = f'adb -s {device_id}'
+
+ # Start MultiDeviceTestsActivity to receive test results
+ cmd = (
+ f'{adb} shell am start'
+ f' {MULTI_DEVICE_TEST_ACTIVITY} --activity-brought-to-front'
+ )
+ multi_device_utils.run(cmd)
+ time.sleep(ACTIVITY_START_WAIT)
+
+ json_results = json.dumps(results)
+ cmd = (
+ f'{adb} shell am broadcast -a {ACTION_HOST_TEST_RESULT} --es'
+ f" {EXTRA_VERSION} '{json_results}'"
+ )
+ if len(cmd) > 4095:
+ logging.info('Command string might be too long! len:%s', len(cmd))
+ multi_device_utils.run(cmd)
+
+
+def main():
+ """Run all Multi-device Mobly tests and collect results."""
+
+ logging.basicConfig(level=logging.INFO)
+ topdir = tempfile.mkdtemp(prefix='MultiDevice_')
+ subprocess.call(['chmod', 'g+rx', topdir]) # Add permissions
+ logging.info('Saving multi-device tests output files to: %s', topdir)
+
+ config_file_contents = get_config_file_contents()
+ device_ids = get_device_serial_number(config_file_contents)
+
+ test_results = {}
+ test_summary_file_list = []
+
+ # Run tests
+ for root, _, files in os.walk(TESTS_DIR):
+ for file in files:
+ if file.endswith('_test.py'):
+ test_name = os.path.splitext(file)[0]
+ test_file_path = os.path.join(root, file)
+ logging.info('Start running test: %s', test_name)
+ cmd = [
+ 'python3',
+ test_file_path, # Use the full path to the test file
+ '-c',
+ CONFIG_FILE,
+ '--testbed',
+ test_name,
+ ]
+ summary_file_path = os.path.join(topdir, MOBLY_TEST_SUMMARY_TXT_FILE)
+
+ test_completed = False
+ with open(summary_file_path, 'w+') as fp:
+ subprocess.run(cmd, stdout=fp, check=False)
+ fp.seek(0)
+ for line in fp:
+ if line.startswith('Test summary saved in'):
+ match = re.search(r'"(.*?)"', line) # Get file path
+ if match:
+ test_summary = Path(match.group(1))
+ if test_summary.exists():
+ test_summary_file_list.append(test_summary)
+ test_completed = True
+ break
+ if not test_completed:
+ logging.error('Failed to get test summary file path')
+ os.remove(os.path.join(topdir, MOBLY_TEST_SUMMARY_TXT_FILE))
+
+ # Parse test summary files
+ for test_summary_file in test_summary_file_list:
+ with open(test_summary_file) as file:
+ test_summary_content = yaml.safe_load_all(file)
+ for doc in test_summary_content:
+ if doc['Type'] == 'Record':
+ test_key = f"{doc['Test Class']}#{doc['Test Name']}"
+ result = (
+ RESULT_PASS if doc['Result'] in ('PASS', 'SKIP') else RESULT_FAIL
+ )
+ test_results.setdefault(test_key, {RESULT_KEY: result})
+
+ for device_id in device_ids:
+ report_result(device_id, test_results)
+
+ logging.info('Test execution completed. Results: %s', test_results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/hostsidetests/multidevices/utils/multi_device_utils.py b/hostsidetests/multidevices/utils/multi_device_utils.py
new file mode 100644
index 0000000..ee8c05e
--- /dev/null
+++ b/hostsidetests/multidevices/utils/multi_device_utils.py
@@ -0,0 +1,59 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Utility functions to interact with devices for Multidevice test."""
+
+
+import os
+import subprocess
+
+
+def run(cmd):
+ """Replacement for os.system, with hiding of stdout+stderr messages.
+
+ Args:
+ cmd: Command to be executed in string format.
+ """
+ with open(os.devnull, 'wb') as devnull:
+ subprocess.call(cmd.split(), stdout=devnull, stderr=subprocess.STDOUT)
+
+
+def install_apk(device_id, package_name):
+ """Installs an APK on a given device.
+
+ Args:
+ device_id: str; ID of the device.
+ package_name: str; name of the package to be installed.
+ """
+ run(f'adb -s {device_id} install -r -g {package_name}')
+
+
+def check_apk_installed(device_id, package_name):
+ """Verifies that an APK is installed on a given device.
+
+ Args:
+ device_id: str; ID of the device.
+ package_name: str; name of the package that should be installed.
+ """
+ verify_cts_cmd = (
+ f'adb -s {device_id} shell pm list packages | '
+ f'grep {package_name}'
+ )
+ bytes_output = subprocess.check_output(
+ verify_cts_cmd, stderr=subprocess.STDOUT, shell=True
+ )
+ output = str(bytes_output.decode('utf-8')).strip()
+ if package_name not in output:
+ raise AssertionError(
+ f'{package_name} not installed on device {device_id}!'
+ )
diff --git a/hostsidetests/network-policy/Android.bp b/hostsidetests/network-policy/Android.bp
new file mode 100644
index 0000000..c3ce0b9
--- /dev/null
+++ b/hostsidetests/network-policy/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+ name: "CtsHostsideNetworkPolicyTests",
+ defaults: ["cts_defaults"],
+ // Only compile source java files in this apk.
+ srcs: [
+ "src/**/*.java",
+ ":ArgumentConstants",
+ ],
+ libs: [
+ "cts-tradefed",
+ "tradefed",
+ ],
+ static_libs: [
+ "modules-utils-build-testing",
+ ],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ ],
+ data: [
+ ":CtsHostsideNetworkPolicyTestsApp",
+ ":CtsHostsideNetworkPolicyTestsApp2",
+ ],
+ per_testcase_directory: true,
+}
diff --git a/hostsidetests/network-policy/AndroidTest.xml b/hostsidetests/network-policy/AndroidTest.xml
new file mode 100644
index 0000000..44f77f8
--- /dev/null
+++ b/hostsidetests/network-policy/AndroidTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS network policy host 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="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
+ <target_preparer class="com.android.cts.netpolicy.NetworkPolicyTestsPreparer" />
+
+ <!-- Enabling change id ALLOW_TEST_API_ACCESS allows that package to access @TestApi methods -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="am compat enable ALLOW_TEST_API_ACCESS com.android.cts.netpolicy.hostside.app2" />
+ <option name="teardown-command" value="am compat reset ALLOW_TEST_API_ACCESS com.android.cts.netpolicy.hostside.app2" />
+ <option name="teardown-command" value="cmd power set-mode 0" />
+ <option name="teardown-command" value="cmd battery reset" />
+ <option name="teardown-command" value="cmd netpolicy stop-watching" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="set-global-setting" key="low_power_standby_enabled" value="0" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsHostsideNetworkPolicyTests.jar" />
+ <option name="runtime-hint" value="3m56s" />
+ </test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/CtsHostsideNetworkPolicyTests" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+</configuration>
diff --git a/hostsidetests/network-policy/OWNERS b/hostsidetests/network-policy/OWNERS
new file mode 100644
index 0000000..ea83e61
--- /dev/null
+++ b/hostsidetests/network-policy/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 61373
+# Inherits parent owners
+include platform/frameworks/base:/services/core/java/com/android/server/net/OWNERS
diff --git a/hostsidetests/network-policy/TEST_MAPPING b/hostsidetests/network-policy/TEST_MAPPING
new file mode 100644
index 0000000..57ac4f7
--- /dev/null
+++ b/hostsidetests/network-policy/TEST_MAPPING
@@ -0,0 +1,27 @@
+{
+ "presubmit-large": [
+ {
+ "name": "CtsHostsideNetworkPolicyTests",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ // Postsubmit on virtual devices to monitor flakiness of all tests that don't require a
+ // physical device
+ "name": "CtsHostsideNetworkPolicyTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ }
+ ]
+ }
+ ]
+}
diff --git a/hostsidetests/network-policy/aidl/Android.bp b/hostsidetests/network-policy/aidl/Android.bp
new file mode 100644
index 0000000..b182090
--- /dev/null
+++ b/hostsidetests/network-policy/aidl/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_helper_library {
+ name: "CtsHostsideNetworkPolicyTestsAidl",
+ sdk_version: "current",
+ srcs: [
+ "com/android/cts/netpolicy/hostside/*.aidl",
+ ],
+}
diff --git a/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl b/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl
new file mode 100644
index 0000000..068d9d8
--- /dev/null
+++ b/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import android.app.job.JobInfo;
+
+import com.android.cts.netpolicy.hostside.INetworkCallback;
+import com.android.cts.netpolicy.hostside.NetworkCheckResult;
+
+interface IMyService {
+ void registerBroadcastReceiver();
+ int getCounters(String receiverName, String action);
+ NetworkCheckResult checkNetworkStatus(String customUrl);
+ String getRestrictBackgroundStatus();
+ void sendNotification(int notificationId, String notificationType);
+ void registerNetworkCallback(in NetworkRequest request, in INetworkCallback cb);
+ void unregisterNetworkCallback();
+ int scheduleJob(in JobInfo jobInfo);
+}
diff --git a/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl b/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl
new file mode 100644
index 0000000..38efc7b
--- /dev/null
+++ b/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.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/network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl b/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl
new file mode 100644
index 0000000..c6b7a1c
--- /dev/null
+++ b/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import android.net.NetworkInfo;
+
+import com.android.cts.netpolicy.hostside.NetworkCheckResult;
+
+interface INetworkStateObserver {
+ void onNetworkStateChecked(int resultCode, in NetworkCheckResult networkCheckResult);
+
+ const int RESULT_SUCCESS_NETWORK_STATE_CHECKED = 0;
+ const int RESULT_ERROR_UNEXPECTED_PROC_STATE = 1;
+ const int RESULT_ERROR_UNEXPECTED_CAPABILITIES = 2;
+ const int RESULT_ERROR_OTHER = 3;
+}
\ No newline at end of file
diff --git a/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl b/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl
new file mode 100644
index 0000000..7aac2ab
--- /dev/null
+++ b/hostsidetests/network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import android.net.NetworkInfo;
+
+@JavaDerive(toString=true)
+parcelable NetworkCheckResult {
+ boolean connected;
+ String details;
+ NetworkInfo networkInfo;
+}
\ No newline at end of file
diff --git a/hostsidetests/network-policy/app/Android.bp b/hostsidetests/network-policy/app/Android.bp
new file mode 100644
index 0000000..7916440
--- /dev/null
+++ b/hostsidetests/network-policy/app/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_defaults {
+ name: "CtsHostsideNetworkPolicyTestsAppDefaults",
+ platform_apis: true,
+ static_libs: [
+ "CtsHostsideNetworkPolicyTestsAidl",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "compatibility-device-util-axt",
+ "cts-net-utils",
+ "ctstestrunner-axt",
+ "modules-utils-build",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ":ArgumentConstants",
+ ],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "general-tests",
+ "sts",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkPolicyTestsApp",
+ defaults: [
+ "cts_support_defaults",
+ "CtsHostsideNetworkPolicyTestsAppDefaults",
+ ],
+}
diff --git a/hostsidetests/network-policy/app/AndroidManifest.xml b/hostsidetests/network-policy/app/AndroidManifest.xml
new file mode 100644
index 0000000..f19e35f
--- /dev/null
+++ b/hostsidetests/network-policy/app/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.netpolicy.hostside">
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <application android:requestLegacyExternalStorage="true">
+ <uses-library android:name="android.test.runner"/>
+ <service android:name=".MyNotificationListenerService"
+ android:label="MyNotificationListenerService"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService"/>
+ </intent-filter>
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.netpolicy.hostside"/>
+
+</manifest>
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java
new file mode 100644
index 0000000..19e4364
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.SystemClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Base class for metered and non-metered tests on idle apps.
+ */
+@RequiredProperties({APP_STANDBY_MODE})
+abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+
+ // Set initial state.
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ setAppIdle(false);
+ turnBatteryOn();
+
+ registerBroadcastReceiver();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+
+ resetBatteryState();
+ setAppIdle(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_enabled() throws Exception {
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure foreground app doesn't lose access upon enabling it.
+ setAppIdle(true);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ finishActivity();
+ assertAppIdle(false); // verify - not idle anymore, since activity was launched...
+ assertBackgroundNetworkAccess(true);
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ // Same for foreground service.
+ setAppIdle(true);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ stopForegroundService();
+ assertAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ // Set Idle after foreground service start.
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ setAppIdle(true);
+ addPowerSaveModeWhitelist(TEST_PKG);
+ removePowerSaveModeWhitelist(TEST_PKG);
+ assertForegroundServiceNetworkAccess();
+ stopForegroundService();
+ assertAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertAppIdle(false); // verify - not idle anymore, since whitelisted
+ assertBackgroundNetworkAccess(true);
+
+ setAppIdleNoAssert(true);
+ assertAppIdle(false); // app is still whitelisted
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertAppIdle(true); // verify - idle again, once whitelisted was removed
+ assertBackgroundNetworkAccess(false);
+
+ setAppIdle(true);
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertAppIdle(false); // verify - not idle anymore, since whitelisted
+ assertBackgroundNetworkAccess(true);
+
+ setAppIdleNoAssert(true);
+ assertAppIdle(false); // app is still whitelisted
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertAppIdle(true); // verify - idle again, once whitelisted was removed
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+
+ // verify - no whitelist, no access!
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_disabled() throws Exception {
+ assertBackgroundNetworkAccess(true);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ }
+
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ @Test
+ public void testAppIdleNetworkAccess_whenCharging() throws Exception {
+ // Check that app is paroled when charging
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+ turnBatteryOff();
+ assertBackgroundNetworkAccess(true);
+ turnBatteryOn();
+ assertBackgroundNetworkAccess(false);
+
+ // Check that app is restricted when not idle but power-save is on
+ setAppIdle(false);
+ assertBackgroundNetworkAccess(true);
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ // Use setBatterySaverMode API to leave power-save mode instead of plugging in charger
+ setBatterySaverMode(false);
+ turnBatteryOff();
+ assertBackgroundNetworkAccess(true);
+
+ // And when no longer charging, it still has network access, since it's not idle
+ turnBatteryOn();
+ assertBackgroundNetworkAccess(true);
+ }
+
+ @Test
+ public void testAppIdleNetworkAccess_idleWhitelisted() throws Exception {
+ setAppIdle(true);
+ assertAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ addAppIdleWhitelist(mUid);
+ assertBackgroundNetworkAccess(true);
+
+ removeAppIdleWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure whitelisting a random app doesn't affect the tested app.
+ addAppIdleWhitelist(mUid + 1);
+ assertBackgroundNetworkAccess(false);
+ removeAppIdleWhitelist(mUid + 1);
+ }
+
+ @Test
+ public void testAppIdle_toast() throws Exception {
+ setAppIdle(true);
+ assertAppIdle(true);
+ assertEquals("Shown", showToast());
+ assertAppIdle(true);
+ // Wait for a couple of seconds for the toast to actually be shown
+ SystemClock.sleep(2000);
+ assertAppIdle(true);
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java
new file mode 100644
index 0000000..ae226e2
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Base class for metered and non-metered Battery Saver Mode tests.
+ */
+@RequiredProperties({BATTERY_SAVER_MODE})
+abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+
+ // Set initial state.
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ setBatterySaverMode(false);
+
+ registerBroadcastReceiver();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+
+ setBatterySaverMode(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_enabled() throws Exception {
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure foreground app doesn't lose access upon Battery Saver.
+ setBatterySaverMode(false);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ setBatterySaverMode(true);
+ assertTopNetworkAccess(true);
+
+ // Although it should not have access while the screen is off.
+ turnScreenOff();
+ assertBackgroundNetworkAccess(false);
+ turnScreenOn();
+ assertTopNetworkAccess(true);
+
+ // Goes back to background state.
+ finishActivity();
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure foreground service doesn't lose access upon enabling Battery Saver.
+ setBatterySaverMode(false);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ setBatterySaverMode(true);
+ assertForegroundServiceNetworkAccess();
+ stopForegroundService();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_disabled() throws Exception {
+ assertBackgroundNetworkAccess(true);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
new file mode 100644
index 0000000..00f67f4
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.os.SystemClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Base class for default, always-on network restrictions.
+ */
+abstract class AbstractDefaultRestrictionsTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+
+ registerBroadcastReceiver();
+ assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+
+ stopApp();
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ }
+
+ @Test
+ public void testFgsNetworkAccess() throws Exception {
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
+ assertNetworkAccess(false, null);
+
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ }
+
+ @Test
+ public void testActivityNetworkAccess() throws Exception {
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
+ assertNetworkAccess(false, null);
+
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_inFullAllowlist() throws Exception {
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
+ assertNetworkAccess(false, null);
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ assertNetworkAccess(true, null);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_inExceptIdleAllowlist() throws Exception {
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
+ assertNetworkAccess(false, null);
+
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ assertNetworkAccess(true, null);
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java
new file mode 100644
index 0000000..0c8cb706
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+
+import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
+import static com.android.cts.netpolicy.hostside.Property.NOT_LOW_RAM_DEVICE;
+
+import android.os.SystemClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Base class for metered and non-metered Doze Mode tests.
+ */
+@RequiredProperties({DOZE_MODE})
+abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+
+ // Set initial state.
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ setDozeMode(false);
+
+ registerBroadcastReceiver();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+
+ setDozeMode(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_enabled() throws Exception {
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure foreground service doesn't lose network access upon enabling doze.
+ setDozeMode(false);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ setDozeMode(true);
+ assertForegroundServiceNetworkAccess();
+ stopForegroundService();
+ assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ assertBackgroundNetworkAccess(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ @Test
+ public void testBackgroundNetworkAccess_disabled() throws Exception {
+ assertBackgroundNetworkAccess(true);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ }
+
+ @RequiredProperties({NOT_LOW_RAM_DEVICE})
+ @Test
+ public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
+ throws Exception {
+ setPendingIntentAllowlistDuration(NETWORK_TIMEOUT_MS);
+ try {
+ registerNotificationListenerService();
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ testNotification(4, NOTIFICATION_TYPE_CONTENT);
+ testNotification(8, NOTIFICATION_TYPE_DELETE);
+ testNotification(15, NOTIFICATION_TYPE_FULL_SCREEN);
+ testNotification(16, NOTIFICATION_TYPE_BUNDLE);
+ testNotification(23, NOTIFICATION_TYPE_ACTION);
+ testNotification(42, NOTIFICATION_TYPE_ACTION_BUNDLE);
+ testNotification(108, NOTIFICATION_TYPE_ACTION_REMOTE_INPUT);
+ } finally {
+ resetDeviceIdleSettings();
+ }
+ }
+
+ private void testNotification(int id, String type) throws Exception {
+ sendNotification(id, type);
+ assertBackgroundNetworkAccess(true);
+ if (type.equals(NOTIFICATION_TYPE_ACTION)) {
+ // Make sure access is disabled after it expires. Since this check considerably slows
+ // downs the CTS tests, do it just once.
+ SystemClock.sleep(NETWORK_TIMEOUT_MS);
+ assertBackgroundNetworkAccess(false);
+ }
+ }
+
+ // Must override so it only tests foreground service - once an app goes to foreground, device
+ // leaves Doze Mode.
+ @Override
+ protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception {
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ stopForegroundService();
+ assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java
new file mode 100644
index 0000000..5435920
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AbstractExpeditedJobTest extends AbstractRestrictBackgroundNetworkTestCase {
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+ resetDeviceState();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+ resetDeviceState();
+ }
+
+ private void resetDeviceState() throws Exception {
+ resetBatteryState();
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+
+ @Test
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ public void testNetworkAccess_batterySaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dataSaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({APP_STANDBY_MODE})
+ public void testNetworkAccess_appIdleState() throws Exception {
+ turnBatteryOn();
+ setAppIdle(false);
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE})
+ public void testNetworkAccess_dozeMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dataAndBatterySaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE, DATA_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dozeAndDataSaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK, DOZE_MODE,
+ APP_STANDBY_MODE})
+ public void testNetworkAccess_allRestrictionsEnabled() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+ setAppIdle(true);
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
new file mode 100644
index 0000000..0f5f58c
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -0,0 +1,1165 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.job.JobScheduler.RESULT_SUCCESS;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static android.os.BatteryManager.BATTERY_PLUGGED_ANY;
+
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.executeShellCommand;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.forceRunJob;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getConnectivityManager;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getContext;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getInstrumentation;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackgroundInternal;
+
+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;
+
+import android.annotation.NonNull;
+import android.app.Instrumentation;
+import android.app.NotificationManager;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
+import android.net.NetworkRequest;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.RemoteCallback;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.service.notification.NotificationListenerService;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AmUtils;
+import com.android.compatibility.common.util.BatteryUtils;
+import com.android.compatibility.common.util.DeviceConfigStateHelper;
+import com.android.compatibility.common.util.ThrowingRunnable;
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
+
+/**
+ * Superclass for tests related to background network restrictions.
+ */
+@RunWith(NetworkPolicyTestRunner.class)
+public abstract class AbstractRestrictBackgroundNetworkTestCase {
+ public static final String TAG = "RestrictBackgroundNetworkTests";
+
+ protected static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
+ protected static final String TEST_APP2_PKG = "com.android.cts.netpolicy.hostside.app2";
+ // TODO(b/321797685): Configure it via device-config once it is available.
+ protected final long mProcessStateTransitionLongDelayMs =
+ useDifferentDelaysForBackgroundChain() ? TimeUnit.SECONDS.toMillis(20)
+ : TimeUnit.SECONDS.toMillis(5);
+ protected final long mProcessStateTransitionShortDelayMs =
+ useDifferentDelaysForBackgroundChain() ? TimeUnit.SECONDS.toMillis(2)
+ : TimeUnit.SECONDS.toMillis(5);
+
+ private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity";
+ private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService";
+ private static final String TEST_APP2_JOB_SERVICE_CLASS = TEST_APP2_PKG + ".MyJobService";
+
+ private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(
+ TEST_APP2_PKG, TEST_APP2_JOB_SERVICE_CLASS);
+ private static final int TEST_JOB_ID = 7357437;
+
+ private static final int SLEEP_TIME_SEC = 1;
+
+ // Constants below must match values defined on app2's Common.java
+ private static final String MANIFEST_RECEIVER = "ManifestReceiver";
+ private static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+ private static final String ACTION_FINISH_ACTIVITY =
+ "com.android.cts.netpolicy.hostside.app2.action.FINISH_ACTIVITY";
+ private static final String ACTION_FINISH_JOB =
+ "com.android.cts.netpolicy.hostside.app2.action.FINISH_JOB";
+ // Copied from com.android.server.net.NetworkPolicyManagerService class
+ private static final String ACTION_SNOOZE_WARNING =
+ "com.android.server.net.action.SNOOZE_WARNING";
+
+ private static final String ACTION_RECEIVER_READY =
+ "com.android.cts.netpolicy.hostside.app2.action.RECEIVER_READY";
+ static final String ACTION_SHOW_TOAST =
+ "com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST";
+
+ protected static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
+ protected static final String NOTIFICATION_TYPE_DELETE = "DELETE";
+ protected static final String NOTIFICATION_TYPE_FULL_SCREEN = "FULL_SCREEN";
+ protected static final String NOTIFICATION_TYPE_BUNDLE = "BUNDLE";
+ protected static final String NOTIFICATION_TYPE_ACTION = "ACTION";
+ protected static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
+ protected static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
+
+ private static final String NETWORK_STATUS_SEPARATOR = "\\|";
+ private static final int SECOND_IN_MS = 1000;
+ static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
+
+ private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+ private static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
+ private static final String KEY_CUSTOM_URL = TEST_PKG + ".custom_url";
+
+ private static final String EMPTY_STRING = "";
+
+ protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
+ protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
+ protected static final int TYPE_EXPEDITED_JOB = 2;
+
+ private static final int BATTERY_STATE_TIMEOUT_MS = 5000;
+ private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 500;
+
+ private static final int ACTIVITY_NETWORK_STATE_TIMEOUT_MS = 10_000;
+ private static final int JOB_NETWORK_STATE_TIMEOUT_MS = 10_000;
+ private static final int LAUNCH_ACTIVITY_TIMEOUT_MS = 10_000;
+
+ // Must be higher than NETWORK_TIMEOUT_MS
+ private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4;
+
+ private static final IntentFilter BATTERY_CHANGED_FILTER =
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+
+ protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 20_000; // 20 sec
+
+ private static final long BROADCAST_TIMEOUT_MS = 5_000;
+
+ protected Context mContext;
+ protected Instrumentation mInstrumentation;
+ protected ConnectivityManager mCm;
+ protected int mUid;
+ private int mMyUid;
+ private @Nullable String mCustomUrl;
+ private MyServiceClient mServiceClient;
+ private DeviceConfigStateHelper mDeviceIdleDeviceConfigStateHelper;
+ private PowerManager mPowerManager;
+ private PowerManager.WakeLock mLock;
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain.outerRule(new RequiredPropertiesRule())
+ .around(new MeterednessConfigurationRule());
+
+ protected void setUp() throws Exception {
+ mInstrumentation = getInstrumentation();
+ mContext = getContext();
+ mCm = getConnectivityManager();
+ mDeviceIdleDeviceConfigStateHelper =
+ new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_DEVICE_IDLE);
+ mUid = getUid(TEST_APP2_PKG);
+ mMyUid = getUid(mContext.getPackageName());
+ mServiceClient = new MyServiceClient(mContext);
+
+ final Bundle args = InstrumentationRegistry.getArguments();
+ mCustomUrl = args.getString(ARG_CONNECTION_CHECK_CUSTOM_URL);
+ if (mCustomUrl != null) {
+ Log.d(TAG, "Using custom URL " + mCustomUrl + " for network checks");
+ }
+
+ final int bindPriorityFlags;
+ if (Boolean.valueOf(args.getString(ARG_WAIVE_BIND_PRIORITY, "false"))) {
+ bindPriorityFlags = Context.BIND_WAIVE_PRIORITY;
+ } else {
+ bindPriorityFlags = Context.BIND_NOT_FOREGROUND;
+ }
+ mServiceClient.bind(bindPriorityFlags);
+
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ executeShellCommand("cmd netpolicy start-watching " + mUid);
+ // Some of the test cases assume that Data saver mode is initially disabled, which might not
+ // always be the case. Therefore, explicitly disable it before running the tests.
+ // Invoke setRestrictBackgroundInternal() directly instead of going through
+ // setRestrictBackground(), as some devices do not fully support the Data saver mode but
+ // still have certain parts of it enabled by default.
+ setRestrictBackgroundInternal(false);
+ setAppIdle(false);
+ mLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ Log.i(TAG, "Apps status:\n"
+ + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
+ + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
+ }
+
+ protected void tearDown() throws Exception {
+ executeShellCommand("cmd netpolicy stop-watching");
+ mServiceClient.unbind();
+ final PowerManager.WakeLock lock = mLock;
+ if (null != lock && lock.isHeld()) lock.release();
+ }
+
+ /**
+ * Check if the feature blocking network for top_sleeping and lower priority proc-states is
+ * enabled. This is a manual check because the feature flag infrastructure may not be available
+ * in all the branches that will get this code.
+ * TODO: b/322115994 - Use @RequiresFlagsEnabled with
+ * Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE once the tests are moved to cts.
+ */
+ protected boolean isNetworkBlockedForTopSleepingAndAbove() {
+ if (!SdkLevel.isAtLeastV()) {
+ return false;
+ }
+ final String output = executeShellCommand("device_config get backstage_power"
+ + " com.android.server.net.network_blocked_for_top_sleeping_and_above");
+ return Boolean.parseBoolean(output);
+ }
+
+ /**
+ * Check if the flag to use different delays for sensitive proc-states is enabled.
+ * This is a manual check because the feature flag infrastructure may not be available
+ * in all the branches that will get this code.
+ * TODO: b/322115994 - Use @RequiresFlagsEnabled with
+ * Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN once the tests are moved to cts.
+ */
+ private boolean useDifferentDelaysForBackgroundChain() {
+ if (!SdkLevel.isAtLeastV()) {
+ return false;
+ }
+ final String output = executeShellCommand("device_config get backstage_power"
+ + " com.android.server.net.use_different_delays_for_background_chain");
+ return Boolean.parseBoolean(output);
+ }
+
+ protected int getUid(String packageName) throws Exception {
+ return mContext.getPackageManager().getPackageUid(packageName, 0);
+ }
+
+ protected void assertRestrictBackgroundChangedReceived(int expectedCount) throws Exception {
+ assertRestrictBackgroundChangedReceived(DYNAMIC_RECEIVER, expectedCount);
+ assertRestrictBackgroundChangedReceived(MANIFEST_RECEIVER, 0);
+ }
+
+ protected void assertRestrictBackgroundChangedReceived(String receiverName, int expectedCount)
+ throws Exception {
+ int attempts = 0;
+ int count = 0;
+ final int maxAttempts = 5;
+ do {
+ attempts++;
+ count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED);
+ 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 "
+ + attempts + " attempts; sleeping "
+ + SLEEP_TIME_SEC + " seconds before trying again");
+ // No sleep after the last turn
+ if (attempts <= maxAttempts) {
+ SystemClock.sleep(SLEEP_TIME_SEC * SECOND_IN_MS);
+ }
+ } while (attempts <= maxAttempts);
+ assertEquals("Number of expected broadcasts for " + receiverName + " not reached after "
+ + maxAttempts * SLEEP_TIME_SEC + " seconds", expectedCount, count);
+ }
+
+ protected void assertSnoozeWarningNotReceived() throws Exception {
+ // Wait for a while to take broadcast queue delays into account
+ SystemClock.sleep(BROADCAST_TIMEOUT_MS);
+ assertEquals(0, getNumberBroadcastsReceived(DYNAMIC_RECEIVER, ACTION_SNOOZE_WARNING));
+ }
+
+ protected String sendOrderedBroadcast(Intent intent) throws Exception {
+ return sendOrderedBroadcast(intent, ORDERED_BROADCAST_TIMEOUT_MS);
+ }
+
+ protected String sendOrderedBroadcast(Intent intent, int timeoutMs) throws Exception {
+ final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
+ Log.d(TAG, "Sending ordered broadcast: " + intent);
+ mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String resultData = getResultData();
+ if (resultData == null) {
+ Log.e(TAG, "Received null data from ordered intent");
+ // Offer an empty string so that the code waiting for the result can return.
+ result.offer(EMPTY_STRING);
+ return;
+ }
+ result.offer(resultData);
+ }
+ }, null, 0, null, null);
+
+ final String resultData = result.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ Log.d(TAG, "Ordered broadcast response after " + timeoutMs + "ms: " + resultData );
+ return resultData;
+ }
+
+ protected int getNumberBroadcastsReceived(String receiverName, String action) throws Exception {
+ return mServiceClient.getCounters(receiverName, action);
+ }
+
+ protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception {
+ final String status = mServiceClient.getRestrictBackgroundStatus();
+ assertNotNull("didn't get API status from app2", status);
+ assertEquals(restrictBackgroundValueToString(expectedStatus),
+ restrictBackgroundValueToString(Integer.parseInt(status)));
+ }
+
+ /**
+ * @deprecated The definition of "background" can be ambiguous. Use separate calls to
+ * {@link #assertProcessStateBelow(int)} with
+ * {@link #assertNetworkAccess(boolean, boolean, String)} to be explicit, instead.
+ */
+ @Deprecated
+ protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
+ assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ assertNetworkAccess(expectAllowed, false, null);
+ }
+
+ protected void assertTopNetworkAccess(boolean expectAllowed) throws Exception {
+ assertTopState();
+ assertNetworkAccess(expectAllowed, true /* needScreenOn */);
+ }
+
+ protected void assertForegroundServiceNetworkAccess() throws Exception {
+ assertForegroundServiceState();
+ assertNetworkAccess(true /* expectAvailable */, false /* needScreenOn */);
+ }
+
+ /**
+ * Asserts that an app always have access while on foreground or running a foreground service.
+ *
+ * <p>This method will launch an activity, a foreground service to make
+ * the assertion, but will finish the activity / stop the service afterwards.
+ */
+ protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{
+ // Checks foreground first.
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ finishActivity();
+
+ // Then foreground service
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ stopForegroundService();
+ }
+
+ protected void assertExpeditedJobHasNetworkAccess() throws Exception {
+ launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB);
+ finishExpeditedJob();
+ }
+
+ protected void assertExpeditedJobHasNoNetworkAccess() throws Exception {
+ launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB, false);
+ finishExpeditedJob();
+ }
+
+ /**
+ * Asserts that the process state of the test app is below, in priority, to the given
+ * {@link android.app.ActivityManager.ProcessState}.
+ */
+ protected final void assertProcessStateBelow(int processState) throws Exception {
+ assertProcessState(ps -> ps.state > processState, null);
+ }
+
+ protected final void assertTopState() throws Exception {
+ assertProcessState(ps -> ps.state == PROCESS_STATE_TOP, () -> turnScreenOn());
+ }
+
+ protected final void assertForegroundServiceState() throws Exception {
+ assertProcessState(ps -> ps.state == PROCESS_STATE_FOREGROUND_SERVICE, null);
+ }
+
+ private void assertProcessState(Predicate<ProcessState> statePredicate,
+ ThrowingRunnable onRetry) throws Exception {
+ final int maxTries = 30;
+ ProcessState state = null;
+ for (int i = 1; i <= maxTries; i++) {
+ if (onRetry != null) {
+ onRetry.run();
+ }
+ state = getProcessStateByUid(mUid);
+ Log.v(TAG, "assertProcessState(): status for app2 (" + mUid + ") on attempt #" + i
+ + ": " + state);
+ if (statePredicate.test(state)) {
+ return;
+ }
+ Log.i(TAG, "App not in desired process state on attempt #" + i
+ + "; sleeping 1s before trying again");
+ if (i < maxTries) {
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ }
+ fail("App2 (" + mUid + ") is not in the desired process state after " + maxTries
+ + " attempts: " + state);
+ }
+
+ /**
+ * Asserts whether the active network is available or not. If the network is unavailable, also
+ * checks whether it is blocked by the expected error.
+ *
+ * @param expectAllowed expect background network access to be allowed or not.
+ * @param expectedUnavailableError the expected error when {@code expectAllowed} is false. It's
+ * meaningful only when the {@code expectAllowed} is 'false'.
+ * Throws an IllegalArgumentException when {@code expectAllowed}
+ * is true and this parameter is not null. When the
+ * {@code expectAllowed} is 'false' and this parameter is null,
+ * this function does not compare error type of the networking
+ * access failure.
+ */
+ protected void assertNetworkAccess(boolean expectAllowed, String expectedUnavailableError)
+ throws Exception {
+ if (expectAllowed && expectedUnavailableError != null) {
+ throw new IllegalArgumentException("expectedUnavailableError is not null");
+ }
+ assertNetworkAccess(expectAllowed, false, expectedUnavailableError);
+ }
+
+ /**
+ * Asserts whether the active network is available or not.
+ */
+ private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn)
+ throws Exception {
+ assertNetworkAccess(expectAvailable, needScreenOn, null);
+ }
+
+ private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn,
+ @Nullable final String expectedUnavailableError) throws Exception {
+ final int maxTries = 5;
+ String error = null;
+ int timeoutMs = 500;
+
+ for (int i = 1; i <= maxTries; i++) {
+ error = checkNetworkAccess(expectAvailable, expectedUnavailableError);
+
+ if (error == null) return;
+
+ // TODO: ideally, it should retry only when it cannot connect to an external site,
+ // or no retry at all! But, currently, the initial change fails almost always on
+ // battery saver tests because the netd changes are made asynchronously.
+ // Once b/27803922 is fixed, this retry mechanism should be revisited.
+
+ Log.w(TAG, "Network status didn't match for expectAvailable=" + expectAvailable
+ + " on attempt #" + i + ": " + error + "\n"
+ + "Sleeping " + timeoutMs + "ms before trying again");
+ if (needScreenOn) {
+ turnScreenOn();
+ }
+ // No sleep after the last turn
+ if (i < maxTries) {
+ SystemClock.sleep(timeoutMs);
+ }
+ // Exponential back-off.
+ timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS);
+ }
+ fail("Invalid state for " + mUid + "; expectAvailable=" + expectAvailable + " after "
+ + maxTries + " attempts.\nLast error: " + error);
+ }
+
+ /**
+ * Asserts whether the network is blocked by accessing bpf maps if command-line tool supports.
+ */
+ void assertNetworkAccessBlockedByBpf(boolean expectBlocked, int uid, boolean metered) {
+ final String result;
+ try {
+ result = executeShellCommand(
+ "cmd network_stack is-uid-networking-blocked " + uid + " " + metered);
+ } catch (AssertionError e) {
+ // If NetworkStack is too old to support this command, ignore and continue
+ // this test to verify other parts.
+ if (e.getMessage().contains("No shell command implementation.")) {
+ return;
+ }
+ throw e;
+ }
+
+ // Tethering module is too old.
+ if (result.contains("API is unsupported")) {
+ return;
+ }
+
+ assertEquals(expectBlocked, parseBooleanOrThrow(result.trim()));
+ }
+
+ /**
+ * Similar to {@link Boolean#parseBoolean} but throws when the input
+ * is unexpected instead of returning false.
+ */
+ private static boolean parseBooleanOrThrow(@NonNull String s) {
+ // Don't use Boolean.parseBoolean
+ if ("true".equalsIgnoreCase(s)) return true;
+ if ("false".equalsIgnoreCase(s)) return false;
+ throw new IllegalArgumentException("Unexpected: " + s);
+ }
+
+ /**
+ * Checks whether the network is available as expected.
+ *
+ * @return error message with the mismatch (or empty if assertion passed).
+ */
+ private String checkNetworkAccess(boolean expectAvailable,
+ @Nullable final String expectedUnavailableError) throws Exception {
+ final NetworkCheckResult checkResult = mServiceClient.checkNetworkStatus(mCustomUrl);
+ return checkForAvailabilityInNetworkCheckResult(checkResult, expectAvailable,
+ expectedUnavailableError);
+ }
+
+ private String checkForAvailabilityInNetworkCheckResult(NetworkCheckResult networkCheckResult,
+ boolean expectAvailable, @Nullable final String expectedUnavailableError) {
+ assertNotNull("NetworkCheckResult from app2 is null", networkCheckResult);
+
+ final NetworkInfo networkInfo = networkCheckResult.networkInfo;
+ assertNotNull("NetworkInfo from app2 is null", networkInfo);
+
+ final State state = networkInfo.getState();
+ final DetailedState detailedState = networkInfo.getDetailedState();
+
+ final boolean connected = networkCheckResult.connected;
+ final String connectionCheckDetails = networkCheckResult.details;
+
+ final StringBuilder errors = new StringBuilder();
+ final State expectedState;
+ final DetailedState expectedDetailedState;
+ if (expectAvailable) {
+ expectedState = State.CONNECTED;
+ expectedDetailedState = DetailedState.CONNECTED;
+ } else {
+ expectedState = State.DISCONNECTED;
+ expectedDetailedState = DetailedState.BLOCKED;
+ }
+
+ if (expectAvailable != connected) {
+ errors.append(String.format("External site connection failed: expected %s, got %s\n",
+ expectAvailable, connected));
+ }
+ if (expectedState != state || expectedDetailedState != detailedState) {
+ errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n",
+ expectedState, expectedDetailedState, state, detailedState));
+ } else if (!expectAvailable && (expectedUnavailableError != null)
+ && !connectionCheckDetails.contains(expectedUnavailableError)) {
+ errors.append("Connection unavailable reason mismatch: expected "
+ + expectedUnavailableError + "\n");
+ }
+
+ if (errors.length() > 0) {
+ errors.append("\tnetworkInfo: " + networkInfo + "\n");
+ errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n");
+ }
+ return errors.length() == 0 ? null : errors.toString();
+ }
+
+ /**
+ * Runs a Shell command which is not expected to generate output.
+ */
+ protected void executeSilentShellCommand(String command) {
+ final String result = executeShellCommand(command);
+ assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty());
+ }
+
+ /**
+ * Asserts the result of a command, wait and re-running it a couple times if necessary.
+ */
+ protected void assertDelayedShellCommand(String command, final String expectedResult)
+ throws Exception {
+ assertDelayedShellCommand(command, 5, 1, expectedResult);
+ }
+
+ protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
+ final String expectedResult) throws Exception {
+ assertDelayedShellCommand(command, maxTries, napTimeSeconds, new ExpectResultChecker() {
+
+ @Override
+ public boolean isExpected(String result) {
+ return expectedResult.equals(result);
+ }
+
+ @Override
+ public String getExpected() {
+ return expectedResult;
+ }
+ });
+ }
+
+ protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
+ ExpectResultChecker checker) throws Exception {
+ String result = "";
+ for (int i = 1; i <= maxTries; i++) {
+ result = executeShellCommand(command).trim();
+ if (checker.isExpected(result)) return;
+ Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
+ + checker.getExpected() + "' on attempt #" + i
+ + "; sleeping " + napTimeSeconds + "s before trying again");
+ // No sleep after the last turn
+ if (i < maxTries) {
+ SystemClock.sleep(napTimeSeconds * SECOND_IN_MS);
+ }
+ }
+ fail("Command '" + command + "' did not return '" + checker.getExpected() + "' after "
+ + maxTries
+ + " attempts. Last result: '" + result + "'");
+ }
+
+ protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
+ assertRestrictBackgroundWhitelist(uid, true);
+ // UID policies live by the Highlander rule: "There can be only one".
+ // Hence, if app is whitelisted, it should not be blacklisted.
+ assertRestrictBackgroundBlacklist(uid, false);
+ }
+
+ protected void removeRestrictBackgroundWhitelist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy remove restrict-background-whitelist " + uid);
+ assertRestrictBackgroundWhitelist(uid, false);
+ }
+
+ protected void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
+ assertRestrictBackground("restrict-background-whitelist", uid, expected);
+ }
+
+ protected void addRestrictBackgroundBlacklist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy add restrict-background-blacklist " + uid);
+ assertRestrictBackgroundBlacklist(uid, true);
+ // UID policies live by the Highlander rule: "There can be only one".
+ // Hence, if app is blacklisted, it should not be whitelisted.
+ assertRestrictBackgroundWhitelist(uid, false);
+ }
+
+ protected void removeRestrictBackgroundBlacklist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy remove restrict-background-blacklist " + uid);
+ assertRestrictBackgroundBlacklist(uid, false);
+ }
+
+ protected void assertRestrictBackgroundBlacklist(int uid, boolean expected) throws Exception {
+ assertRestrictBackground("restrict-background-blacklist", uid, expected);
+ }
+
+ protected void addAppIdleWhitelist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy add app-idle-whitelist " + uid);
+ assertAppIdleWhitelist(uid, true);
+ }
+
+ protected void removeAppIdleWhitelist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy remove app-idle-whitelist " + uid);
+ assertAppIdleWhitelist(uid, false);
+ }
+
+ protected void assertAppIdleWhitelist(int uid, boolean expected) throws Exception {
+ assertRestrictBackground("app-idle-whitelist", uid, expected);
+ }
+
+ private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception {
+ final int maxTries = 5;
+ boolean actual = false;
+ final String expectedUid = Integer.toString(uid);
+ String uids = "";
+ for (int i = 1; i <= maxTries; i++) {
+ final String output =
+ executeShellCommand("cmd netpolicy list " + list);
+ uids = output.split(":")[1];
+ for (String candidate : uids.split(" ")) {
+ actual = candidate.trim().equals(expectedUid);
+ if (expected == actual) {
+ return;
+ }
+ }
+ Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected "
+ + expected + ", got " + actual + "); sleeping 1s before polling again");
+ // No sleep after the last turn
+ if (i < maxTries) {
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ }
+ fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual
+ + ". Full list: " + uids);
+ }
+
+ protected void addTempPowerSaveModeWhitelist(String packageName, long duration)
+ throws Exception {
+ Log.i(TAG, "Adding pkg " + packageName + " to temp-power-save-mode whitelist");
+ executeShellCommand("dumpsys deviceidle tempwhitelist -d " + duration + " " + packageName);
+ }
+
+ protected void assertPowerSaveModeWhitelist(String packageName, boolean expected)
+ throws Exception {
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ assertDelayedShellCommand("dumpsys deviceidle whitelist =" + packageName,
+ Boolean.toString(expected));
+ }
+
+ protected void addPowerSaveModeWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist");
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ executeShellCommand("dumpsys deviceidle whitelist +" + packageName);
+ assertPowerSaveModeWhitelist(packageName, true);
+ }
+
+ protected void removePowerSaveModeWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Removing package " + packageName + " from power-save-mode whitelist");
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ executeShellCommand("dumpsys deviceidle whitelist -" + packageName);
+ assertPowerSaveModeWhitelist(packageName, false);
+ }
+
+ protected void assertPowerSaveModeExceptIdleWhitelist(String packageName, boolean expected)
+ throws Exception {
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ assertDelayedShellCommand("dumpsys deviceidle except-idle-whitelist =" + packageName,
+ Boolean.toString(expected));
+ }
+
+ protected void addPowerSaveModeExceptIdleWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Adding package " + packageName + " to power-save-mode-except-idle whitelist");
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ executeShellCommand("dumpsys deviceidle except-idle-whitelist +" + packageName);
+ assertPowerSaveModeExceptIdleWhitelist(packageName, true);
+ }
+
+ protected void removePowerSaveModeExceptIdleWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Removing package " + packageName
+ + " from power-save-mode-except-idle whitelist");
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ executeShellCommand("dumpsys deviceidle except-idle-whitelist reset");
+ assertPowerSaveModeExceptIdleWhitelist(packageName, false);
+ }
+
+ protected void turnBatteryOn() throws Exception {
+ executeSilentShellCommand("cmd battery unplug");
+ executeSilentShellCommand("cmd battery set status "
+ + BatteryManager.BATTERY_STATUS_DISCHARGING);
+ assertBatteryState(false);
+ }
+
+ protected void turnBatteryOff() throws Exception {
+ executeSilentShellCommand("cmd battery set ac " + BATTERY_PLUGGED_ANY);
+ executeSilentShellCommand("cmd battery set level 100");
+ executeSilentShellCommand("cmd battery set status "
+ + BatteryManager.BATTERY_STATUS_CHARGING);
+ assertBatteryState(true);
+ }
+
+ protected void resetBatteryState() {
+ BatteryUtils.runDumpsysBatteryReset();
+ }
+
+ private void assertBatteryState(boolean pluggedIn) throws Exception {
+ final long endTime = SystemClock.elapsedRealtime() + BATTERY_STATE_TIMEOUT_MS;
+ while (isDevicePluggedIn() != pluggedIn && SystemClock.elapsedRealtime() <= endTime) {
+ Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS);
+ }
+ if (isDevicePluggedIn() != pluggedIn) {
+ fail("Timed out waiting for the plugged-in state to change,"
+ + " expected pluggedIn: " + pluggedIn);
+ }
+ }
+
+ private boolean isDevicePluggedIn() {
+ final Intent batteryIntent = mContext.registerReceiver(null, BATTERY_CHANGED_FILTER);
+ return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0;
+ }
+
+ protected void turnScreenOff() throws Exception {
+ if (!mLock.isHeld()) mLock.acquire();
+ executeSilentShellCommand("input keyevent KEYCODE_SLEEP");
+ }
+
+ protected void turnScreenOn() throws Exception {
+ executeSilentShellCommand("input keyevent KEYCODE_WAKEUP");
+ if (mLock.isHeld()) mLock.release();
+ executeSilentShellCommand("wm dismiss-keyguard");
+ }
+
+ protected void setBatterySaverMode(boolean enabled) throws Exception {
+ if (!isBatterySaverSupported()) {
+ return;
+ }
+ Log.i(TAG, "Setting Battery Saver Mode to " + enabled);
+ if (enabled) {
+ turnBatteryOn();
+ AmUtils.waitForBroadcastBarrier();
+ executeSilentShellCommand("cmd power set-mode 1");
+ } else {
+ executeSilentShellCommand("cmd power set-mode 0");
+ turnBatteryOff();
+ AmUtils.waitForBroadcastBarrier();
+ }
+ }
+
+ protected void setDozeMode(boolean enabled) throws Exception {
+ if (!isDozeModeSupported()) {
+ return;
+ }
+
+ Log.i(TAG, "Setting Doze Mode to " + enabled);
+ if (enabled) {
+ turnBatteryOn();
+ turnScreenOff();
+ executeShellCommand("dumpsys deviceidle force-idle deep");
+ } else {
+ turnScreenOn();
+ turnBatteryOff();
+ executeShellCommand("dumpsys deviceidle unforce");
+ }
+ assertDozeMode(enabled);
+ }
+
+ protected void assertDozeMode(boolean enabled) throws Exception {
+ assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
+ }
+
+ protected void stopApp() {
+ executeSilentShellCommand("am stop-app " + TEST_APP2_PKG);
+ }
+
+ protected void setAppIdle(boolean isIdle) throws Exception {
+ setAppIdleNoAssert(isIdle);
+ assertAppIdle(isIdle);
+ }
+
+ protected void setAppIdleNoAssert(boolean isIdle) throws Exception {
+ if (!isAppStandbySupported()) {
+ return;
+ }
+ Log.i(TAG, "Setting app idle to " + isIdle);
+ final String bucketName = isIdle ? "rare" : "active";
+ executeSilentShellCommand("am set-standby-bucket " + TEST_APP2_PKG + " " + bucketName);
+ }
+
+ protected void assertAppIdle(boolean isIdle) throws Exception {
+ try {
+ assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG,
+ 30 /* maxTries */, 1 /* napTimeSeconds */, "Idle=" + isIdle);
+ } catch (Throwable e) {
+ throw e;
+ }
+ }
+
+ /**
+ * Starts a service that will register a broadcast receiver to receive
+ * {@code RESTRICT_BACKGROUND_CHANGE} intents.
+ * <p>
+ * The service must run in a separate app because otherwise it would be killed every time
+ * {@link #runDeviceTests(String, String)} is executed.
+ */
+ protected void registerBroadcastReceiver() throws Exception {
+ mServiceClient.registerBroadcastReceiver();
+
+ final Intent intent = new Intent(ACTION_RECEIVER_READY)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ // Wait until receiver is ready.
+ final int maxTries = 10;
+ for (int i = 1; i <= maxTries; i++) {
+ final String message = sendOrderedBroadcast(intent, SECOND_IN_MS * 4);
+ Log.d(TAG, "app2 receiver acked: " + message);
+ if (message != null) {
+ return;
+ }
+ Log.v(TAG, "app2 receiver is not ready yet; sleeping 1s before polling again");
+ // No sleep after the last turn
+ if (i < maxTries) {
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ }
+ fail("app2 receiver is not ready in " + mUid);
+ }
+
+ protected void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb)
+ throws Exception {
+ Log.i(TAG, "Registering network callback for request: " + request);
+ mServiceClient.registerNetworkCallback(request, 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.
+ */
+ protected void registerNotificationListenerService() throws Exception {
+ executeShellCommand("cmd notification allow_listener "
+ + MyNotificationListenerService.getId());
+ final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
+ final ComponentName listenerComponent = MyNotificationListenerService.getComponentName();
+ assertTrue(listenerComponent + " has not been granted access",
+ nm.isNotificationListenerAccessGranted(listenerComponent));
+ }
+
+ protected void setPendingIntentAllowlistDuration(long durationMs) {
+ mDeviceIdleDeviceConfigStateHelper.set("notification_allowlist_duration_ms",
+ String.valueOf(durationMs));
+ }
+
+ protected void resetDeviceIdleSettings() {
+ mDeviceIdleDeviceConfigStateHelper.restoreOriginalValues();
+ }
+
+ protected void launchActivity() throws Exception {
+ turnScreenOn();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_ACTIVTIY);
+ final RemoteCallback callback = new RemoteCallback(result -> latch.countDown());
+ launchIntent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
+ mContext.startActivity(launchIntent);
+ // There might be a race when app2 is launched but ACTION_FINISH_ACTIVITY has not registered
+ // before test calls finishActivity(). When the issue is happened, there is no way to fix
+ // it, so have a callback design to make sure that the app is launched completely and
+ // ACTION_FINISH_ACTIVITY will be registered before leaving this method.
+ if (!latch.await(LAUNCH_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for launching activity");
+ }
+ }
+
+ protected void launchComponentAndAssertNetworkAccess(int type) throws Exception {
+ launchComponentAndAssertNetworkAccess(type, true);
+ }
+
+ protected void launchComponentAndAssertNetworkAccess(int type, boolean expectAvailable)
+ throws Exception {
+ if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
+ startForegroundService();
+ assertForegroundServiceNetworkAccess();
+ } else if (type == TYPE_COMPONENT_ACTIVTIY) {
+ turnScreenOn();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final Intent launchIntent = getIntentForComponent(type);
+ final Bundle extras = new Bundle();
+ final AtomicReference<Pair<Integer, NetworkCheckResult>> result =
+ new AtomicReference<>();
+ extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
+ extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
+ extras.putString(KEY_CUSTOM_URL, mCustomUrl);
+ launchIntent.putExtras(extras);
+ mContext.startActivity(launchIntent);
+ if (latch.await(ACTIVITY_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ final int resultCode = result.get().first;
+ final NetworkCheckResult networkCheckResult = result.get().second;
+ if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
+ final String error = checkForAvailabilityInNetworkCheckResult(
+ networkCheckResult, expectAvailable,
+ null /* expectedUnavailableError */);
+ if (error != null) {
+ fail("Network is not available for activity in app2 (" + mUid + "): "
+ + error);
+ }
+ } else if (resultCode == INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE) {
+ Log.d(TAG, networkCheckResult.details);
+ // App didn't come to foreground when the activity is started, so try again.
+ assertTopNetworkAccess(true);
+ } else {
+ fail("Unexpected resultCode=" + resultCode
+ + "; networkCheckResult=[" + networkCheckResult + "]");
+ }
+ } else {
+ fail("Timed out waiting for network availability status from app2's activity ("
+ + mUid + ")");
+ }
+ } else if (type == TYPE_EXPEDITED_JOB) {
+ final Bundle extras = new Bundle();
+ final AtomicReference<Pair<Integer, NetworkCheckResult>> result =
+ new AtomicReference<>();
+ final CountDownLatch latch = new CountDownLatch(1);
+ extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
+ extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
+ extras.putString(KEY_CUSTOM_URL, mCustomUrl);
+ final JobInfo jobInfo = new JobInfo.Builder(TEST_JOB_ID, TEST_JOB_COMPONENT)
+ .setExpedited(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .setTransientExtras(extras)
+ .build();
+ assertEquals("Error scheduling " + jobInfo,
+ RESULT_SUCCESS, mServiceClient.scheduleJob(jobInfo));
+ forceRunJob(TEST_APP2_PKG, TEST_JOB_ID);
+ if (latch.await(JOB_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ final int resultCode = result.get().first;
+ final NetworkCheckResult networkCheckResult = result.get().second;
+ if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
+ final String error = checkForAvailabilityInNetworkCheckResult(
+ networkCheckResult, expectAvailable,
+ null /* expectedUnavailableError */);
+ if (error != null) {
+ Log.d(TAG, "Network state is unexpected, checking again. " + error);
+ // Right now we could end up in an unexpected state if expedited job
+ // doesn't have network access immediately after starting, so check again.
+ assertNetworkAccess(expectAvailable, false /* needScreenOn */);
+ }
+ } else {
+ fail("Unexpected resultCode=" + resultCode
+ + "; networkCheckResult=[" + networkCheckResult + "]");
+ }
+ } else {
+ fail("Timed out waiting for network availability status from app2's expedited job ("
+ + mUid + ")");
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ }
+
+ protected void startActivity() throws Exception {
+ final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_ACTIVTIY);
+ mContext.startActivity(launchIntent);
+ }
+
+ private void startForegroundService() throws Exception {
+ final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ mContext.startForegroundService(launchIntent);
+ assertForegroundServiceState();
+ }
+
+ private Intent getIntentForComponent(int type) {
+ final Intent intent = new Intent();
+ if (type == TYPE_COMPONENT_ACTIVTIY) {
+ intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
+ intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS))
+ .setFlags(1);
+ } else {
+ fail("Unknown type: " + type);
+ }
+ return intent;
+ }
+
+ protected void stopForegroundService() throws Exception {
+ executeShellCommand(String.format("am startservice -f 2 %s/%s",
+ TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS));
+ // NOTE: cannot assert state because it depends on whether activity was on top before.
+ }
+
+ private Binder getNewNetworkStateObserver(final CountDownLatch latch,
+ final AtomicReference<Pair<Integer, NetworkCheckResult>> result) {
+ return new INetworkStateObserver.Stub() {
+ @Override
+ public void onNetworkStateChecked(int resultCode,
+ NetworkCheckResult networkCheckResult) {
+ result.set(Pair.create(resultCode, networkCheckResult));
+ latch.countDown();
+ }
+ };
+ }
+
+ /**
+ * Finishes an activity on app2 so its process is demoted from foreground status.
+ */
+ protected void finishActivity() throws Exception {
+ final Intent intent = new Intent(ACTION_FINISH_ACTIVITY)
+ .setPackage(TEST_APP2_PKG)
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ sendOrderedBroadcast(intent);
+ }
+
+ /**
+ * Finishes the expedited job on app2 so its process is demoted from foreground status.
+ */
+ private void finishExpeditedJob() throws Exception {
+ final Intent intent = new Intent(ACTION_FINISH_JOB)
+ .setPackage(TEST_APP2_PKG)
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ sendOrderedBroadcast(intent);
+ }
+
+ protected void sendNotification(int notificationId, String notificationType) throws Exception {
+ Log.d(TAG, "Sending notification broadcast (id=" + notificationId
+ + ", type=" + notificationType);
+ mServiceClient.sendNotification(notificationId, notificationType);
+ }
+
+ protected String showToast() {
+ final Intent intent = new Intent(ACTION_SHOW_TOAST);
+ intent.setPackage(TEST_APP2_PKG);
+ Log.d(TAG, "Sending request to show toast");
+ try {
+ return sendOrderedBroadcast(intent, 3 * SECOND_IN_MS);
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ private ProcessState getProcessStateByUid(int uid) throws Exception {
+ return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid));
+ }
+
+ private static class ProcessState {
+ private final String fullState;
+ final int state;
+
+ ProcessState(String fullState) {
+ this.fullState = fullState;
+ try {
+ this.state = Integer.parseInt(fullState.split(" ")[0]);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Could not parse " + fullState);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return fullState;
+ }
+ }
+
+ /**
+ * Helper class used to assert the result of a Shell command.
+ */
+ protected static interface ExpectResultChecker {
+ /**
+ * Checkes whether the result of the command matched the expectation.
+ */
+ boolean isExpected(String result);
+ /**
+ * Gets the expected result so it's displayed on log and failure messages.
+ */
+ String getExpected();
+ }
+
+ protected void setRestrictedNetworkingMode(boolean enabled) throws Exception {
+ executeSilentShellCommand(
+ "settings put global restricted_networking_mode " + (enabled ? 1 : 0));
+ assertRestrictedNetworkingModeState(enabled);
+ }
+
+ protected void assertRestrictedNetworkingModeState(boolean enabled) throws Exception {
+ assertDelayedShellCommand("cmd netpolicy get restricted-mode",
+ "Restricted mode status: " + (enabled ? "enabled" : "disabled"));
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java
new file mode 100644
index 0000000..6b802f6
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
+public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java
new file mode 100644
index 0000000..2e725ae
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java
new file mode 100644
index 0000000..2e421f6
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
+public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java
new file mode 100644
index 0000000..0be5644
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
new file mode 100644
index 0000000..bfccce9
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getUiDevice;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class ConnOnActivityStartTest extends AbstractRestrictBackgroundNetworkTestCase {
+ private static final int TEST_ITERATION_COUNT = 5;
+
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+ resetDeviceState();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+ stopApp();
+ resetDeviceState();
+ }
+
+ private void resetDeviceState() throws Exception {
+ resetBatteryState();
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+
+
+ @Test
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ public void testStartActivity_batterySaver() throws Exception {
+ setBatterySaverMode(true);
+ assertNetworkAccess(false, null);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver", null);
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+ public void testStartActivity_dataSaver() throws Exception {
+ setRestrictBackground(true);
+ assertNetworkAccess(false, null);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver", null);
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE})
+ public void testStartActivity_doze() throws Exception {
+ setDozeMode(true);
+ assertNetworkAccess(false, null);
+ // TODO (235284115): We need to turn on Doze every time before starting
+ // the activity.
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_doze", null);
+ }
+
+ @Test
+ @RequiredProperties({APP_STANDBY_MODE})
+ public void testStartActivity_appStandby() throws Exception {
+ turnBatteryOn();
+ setAppIdle(true);
+ assertNetworkAccess(false, null);
+ // TODO (235284115): We need to put the app into app standby mode every
+ // time before starting the activity.
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby", null);
+ }
+
+ @Test
+ public void testStartActivity_default() throws Exception {
+ assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_default", () -> {
+ assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+ SystemClock.sleep(mProcessStateTransitionLongDelayMs);
+ assertNetworkAccess(false, null);
+ });
+ }
+
+ private void assertLaunchedActivityHasNetworkAccess(String testName,
+ ThrowingRunnable onBeginIteration) throws Exception {
+ for (int i = 0; i < TEST_ITERATION_COUNT; ++i) {
+ if (onBeginIteration != null) {
+ onBeginIteration.run();
+ }
+ Log.i(TAG, testName + " start #" + i);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ getUiDevice().pressHome();
+ assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ Log.i(TAG, testName + " end #" + i);
+ }
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java
new file mode 100644
index 0000000..66e0d00
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+
+import static com.android.compatibility.common.util.FeatureUtil.isTV;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NO_DATA_SAVER_MODE;
+
+import static org.junit.Assert.fail;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.compatibility.common.util.CddTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+@RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+@LargeTest
+public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+ private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
+ "com.android.providers.downloads"
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Set initial state.
+ setRestrictBackground(false);
+ removeRestrictBackgroundWhitelist(mUid);
+ removeRestrictBackgroundBlacklist(mUid);
+
+ registerBroadcastReceiver();
+ assertRestrictBackgroundChangedReceived(0);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ setRestrictBackground(false);
+ }
+
+ @Test
+ public void testGetRestrictBackgroundStatus_disabled() throws Exception {
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+ // Verify status is always disabled, never whitelisted
+ addRestrictBackgroundWhitelist(mUid);
+ assertRestrictBackgroundChangedReceived(0);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ }
+
+ @Test
+ public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(1);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ addRestrictBackgroundWhitelist(mUid);
+ assertRestrictBackgroundChangedReceived(2);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+
+ removeRestrictBackgroundWhitelist(mUid);
+ assertRestrictBackgroundChangedReceived(3);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ }
+
+ @Test
+ public void testGetRestrictBackgroundStatus_enabled() throws Exception {
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(1);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ // Make sure foreground app doesn't lose access upon enabling Data Saver.
+ setRestrictBackground(false);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ setRestrictBackground(true);
+ assertTopNetworkAccess(true);
+
+ // Although it should not have access while the screen is off.
+ turnScreenOff();
+ assertBackgroundNetworkAccess(false);
+ turnScreenOn();
+ // On some TVs, it is possible that the activity on top may change after the screen is
+ // turned off and on again, so relaunch the activity in the test app again.
+ if (isTV()) {
+ startActivity();
+ }
+ assertTopNetworkAccess(true);
+
+ // Goes back to background state.
+ finishActivity();
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure foreground service doesn't lose access upon enabling Data Saver.
+ setRestrictBackground(false);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ setRestrictBackground(true);
+ assertForegroundServiceNetworkAccess();
+ stopForegroundService();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ @Test
+ public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
+ addRestrictBackgroundBlacklist(mUid);
+ assertRestrictBackgroundChangedReceived(1);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertRestrictBackgroundChangedReceived(1);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ // UID policies live by the Highlander rule: "There can be only one".
+ // Hence, if app is whitelisted, it should not be blacklisted anymore.
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(2);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ addRestrictBackgroundWhitelist(mUid);
+ assertRestrictBackgroundChangedReceived(3);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+
+ // Check status after removing blacklist.
+ // ...re-enables first
+ addRestrictBackgroundBlacklist(mUid);
+ assertRestrictBackgroundChangedReceived(4);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ assertsForegroundAlwaysHasNetworkAccess();
+ // ... remove blacklist - access's still rejected because Data Saver is on
+ removeRestrictBackgroundBlacklist(mUid);
+ assertRestrictBackgroundChangedReceived(4);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ assertsForegroundAlwaysHasNetworkAccess();
+ // ... finally, disable Data Saver
+ setRestrictBackground(false);
+ assertRestrictBackgroundChangedReceived(5);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ assertsForegroundAlwaysHasNetworkAccess();
+ }
+
+ @Test
+ public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
+ final StringBuilder error = new StringBuilder();
+ for (String packageName : REQUIRED_WHITELISTED_PACKAGES) {
+ int uid = -1;
+ try {
+ uid = getUid(packageName);
+ assertRestrictBackgroundWhitelist(uid, true);
+ } catch (Throwable t) {
+ error.append("\nFailed for '").append(packageName).append("'");
+ if (uid > 0) {
+ error.append(" (uid ").append(uid).append(")");
+ }
+ error.append(": ").append(t).append("\n");
+ }
+ }
+ if (error.length() > 0) {
+ fail(error.toString());
+ }
+ }
+
+ @RequiredProperties({NO_DATA_SAVER_MODE})
+ @CddTest(requirement="7.4.7/C-2-2")
+ @Test
+ public void testBroadcastNotSentOnUnsupportedDevices() throws Exception {
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(0);
+
+ setRestrictBackground(false);
+ assertRestrictBackgroundChangedReceived(0);
+
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(0);
+ }
+
+ private void assertDataSaverStatusOnBackground(int expectedStatus) throws Exception {
+ assertRestrictBackgroundStatus(expectedStatus);
+ assertBackgroundNetworkAccess(expectedStatus != RESTRICT_BACKGROUND_STATUS_ENABLED);
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java
new file mode 100644
index 0000000..69ca206
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.clearSnoozeTimestamps;
+
+import android.content.pm.PackageManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.UiAutomatorUtils2;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Period;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+public class DataWarningReceiverTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ clearSnoozeTimestamps();
+ registerBroadcastReceiver();
+ turnScreenOn();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testSnoozeWarningNotReceived() throws Exception {
+ Assume.assumeTrue("Feature not supported: " + PackageManager.FEATURE_TELEPHONY,
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+ final int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ Assume.assumeTrue("Valid subId not found",
+ subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ setSubPlanOwner(subId, TEST_PKG);
+ final List<SubscriptionPlan> originalPlans = sm.getSubscriptionPlans(subId);
+ try {
+ // In NetworkPolicyManagerService class, we set the data warning bytes to 90% of
+ // data limit bytes. So, create the subscription plan in such a way this data warning
+ // threshold is already reached.
+ final SubscriptionPlan plan = SubscriptionPlan.Builder
+ .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
+ Period.ofMonths(1))
+ .setTitle("CTS")
+ .setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
+ .setDataUsage(999_000_000, System.currentTimeMillis())
+ .build();
+ sm.setSubscriptionPlans(subId, Arrays.asList(plan));
+ final UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
+ uiDevice.openNotification();
+ try {
+ final UiObject2 uiObject = UiAutomatorUtils2.waitFindObject(
+ By.text("Data warning"));
+ Assume.assumeNotNull(uiObject);
+ uiObject.wait(Until.clickable(true), 10_000L);
+ uiObject.getParent().swipe(Direction.RIGHT, 1.0f);
+ } catch (Throwable t) {
+ Assume.assumeNoException(
+ "Error occurred while finding and swiping the notification", t);
+ }
+ assertSnoozeWarningNotReceived();
+ uiDevice.pressHome();
+ } finally {
+ sm.setSubscriptionPlans(subId, originalPlans);
+ setSubPlanOwner(subId, "");
+ }
+ }
+
+ private static void setSubPlanOwner(int subId, String packageName) throws Exception {
+ SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+ "cmd netpolicy set sub-plan-owner " + subId + " " + packageName);
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java
new file mode 100644
index 0000000..810fd19
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
+public class DefaultRestrictionsMeteredTest extends AbstractDefaultRestrictionsTest {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java
new file mode 100644
index 0000000..fef546c
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class DefaultRestrictionsNonMeteredTest extends AbstractDefaultRestrictionsTest {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java
new file mode 100644
index 0000000..741dd7e
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
+public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java
new file mode 100644
index 0000000..f343df5
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java
new file mode 100644
index 0000000..2dc6cc4
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_APP2_PKG;
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
+
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.compatibility.common.util.OnFailureRule;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class DumpOnFailureRule extends OnFailureRule {
+ private File mDumpDir = new File(Environment.getExternalStorageDirectory(),
+ "CtsHostsideNetworkPolicyTests");
+
+ @Override
+ public void onTestFailure(Statement base, Description description, Throwable throwable) {
+ if (throwable instanceof AssumptionViolatedException) {
+ final String testName = description.getClassName() + "_" + description.getMethodName();
+ Log.d(TAG, "Skipping test " + testName + ": " + throwable);
+ return;
+ }
+
+ prepareDumpRootDir();
+ final String shortenedTestName = getShortenedTestName(description);
+ final File dumpFile = new File(mDumpDir, "dump-" + shortenedTestName);
+ Log.i(TAG, "Dumping debug info for " + description + ": " + dumpFile.getPath());
+ try (FileOutputStream out = new FileOutputStream(dumpFile)) {
+ for (String cmd : new String[] {
+ "dumpsys netpolicy",
+ "dumpsys network_management",
+ "dumpsys usagestats " + TEST_PKG + " " + TEST_APP2_PKG,
+ "dumpsys usagestats appstandby",
+ "dumpsys connectivity trafficcontroller",
+ "dumpsys netd trafficcontroller",
+ "dumpsys platform_compat", // TODO (b/279829773): Remove this dump
+ "dumpsys jobscheduler " + TEST_APP2_PKG, // TODO (b/288220398): Remove this dump
+ }) {
+ dumpCommandOutput(out, cmd);
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Error opening file: " + dumpFile, e);
+ } catch (IOException e) {
+ Log.e(TAG, "Error closing file: " + dumpFile, e);
+ }
+ final UiDevice uiDevice = UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation());
+ final File screenshotFile = new File(mDumpDir, "sc-" + shortenedTestName + ".png");
+ uiDevice.takeScreenshot(screenshotFile);
+ final File windowHierarchyFile = new File(mDumpDir, "wh-" + shortenedTestName + ".xml");
+ try {
+ uiDevice.dumpWindowHierarchy(windowHierarchyFile);
+ } catch (IOException e) {
+ Log.e(TAG, "Error dumping window hierarchy", e);
+ }
+ }
+
+ private String getShortenedTestName(Description description) {
+ final String qualifiedClassName = description.getClassName();
+ final String className = qualifiedClassName.substring(
+ qualifiedClassName.lastIndexOf(".") + 1);
+ final String shortenedClassName = className.chars()
+ .filter(Character::isUpperCase)
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString();
+ return shortenedClassName + "_" + description.getMethodName();
+ }
+
+ void dumpCommandOutput(FileOutputStream out, String cmd) {
+ final ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().executeShellCommand(cmd);
+ try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ out.write(("Output of '" + cmd + "':\n").getBytes(StandardCharsets.UTF_8));
+ FileUtils.copy(in, out);
+ out.write("\n\n=================================================================\n\n"
+ .getBytes(StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ Log.e(TAG, "Error dumping '" + cmd + "'", e);
+ }
+ }
+
+ void prepareDumpRootDir() {
+ if (!mDumpDir.exists() && !mDumpDir.mkdir()) {
+ Log.e(TAG, "Error creating " + mDumpDir);
+ }
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java
new file mode 100644
index 0000000..d56a50b
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
+public class ExpeditedJobMeteredTest extends AbstractExpeditedJobTest {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java
new file mode 100644
index 0000000..0a776ee
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class ExpeditedJobNonMeteredTest extends AbstractExpeditedJobTest {
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java
new file mode 100644
index 0000000..4f4e68e
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setupActiveNetworkMeteredness;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
+
+import android.util.ArraySet;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class MeterednessConfigurationRule extends BeforeAfterRule {
+ private ThrowingRunnable mMeterednessResetter;
+
+ @Override
+ public void onBefore(Statement base, Description description) throws Throwable {
+ final ArraySet<Property> requiredProperties
+ = RequiredPropertiesRule.getRequiredProperties();
+ if (requiredProperties.contains(METERED_NETWORK)) {
+ configureNetworkMeteredness(true);
+ } else if (requiredProperties.contains(NON_METERED_NETWORK)) {
+ configureNetworkMeteredness(false);
+ }
+ }
+
+ @Override
+ public void onAfter(Statement base, Description description) throws Throwable {
+ resetNetworkMeteredness();
+ }
+
+ public void configureNetworkMeteredness(boolean metered) throws Exception {
+ mMeterednessResetter = setupActiveNetworkMeteredness(metered);
+ }
+
+ public void resetNetworkMeteredness() throws Exception {
+ if (mMeterednessResetter != null) {
+ mMeterednessResetter.run();
+ mMeterednessResetter = null;
+ }
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java
new file mode 100644
index 0000000..b0fa106
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test cases for the more complex scenarios where multiple restrictions (like Battery Saver Mode
+ * and Data Saver Mode) are applied simultaneously.
+ * <p>
+ * <strong>NOTE: </strong>it might sound like the test methods on this class are testing too much,
+ * which would make it harder to diagnose individual failures, but the assumption is that such
+ * failure most likely will happen when the restriction is tested individually as well.
+ */
+public class MixedModesTest extends AbstractRestrictBackgroundNetworkTestCase {
+ private static final String TAG = "MixedModesTest";
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Set initial state.
+ removeRestrictBackgroundWhitelist(mUid);
+ removeRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+
+ registerBroadcastReceiver();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ try {
+ setRestrictBackground(false);
+ } finally {
+ setBatterySaverMode(false);
+ }
+ }
+
+ /**
+ * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on metered networks.
+ */
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
+ @Test
+ public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(true);
+ try {
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+
+ Log.v(TAG, "Not whitelisted for any.");
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+ addRestrictBackgroundWhitelist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ removeRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ Log.v(TAG, "Whitelisted for both.");
+ addRestrictBackgroundWhitelist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundBlacklist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
+ }
+ }
+
+ /**
+ * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on non-metered
+ * networks.
+ */
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, NON_METERED_NETWORK})
+ @Test
+ public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(false);
+ try {
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+
+ Log.v(TAG, "Not whitelisted for any.");
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+ addRestrictBackgroundWhitelist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ removeRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ Log.v(TAG, "Whitelisted for both.");
+ addRestrictBackgroundWhitelist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundBlacklist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removeRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
+ }
+ }
+
+ /**
+ * Tests that powersave whitelists works as expected when doze and battery saver modes
+ * are enabled.
+ */
+ @RequiredProperties({DOZE_MODE, BATTERY_SAVER_MODE})
+ @Test
+ public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
+ setBatterySaverMode(true);
+ setDozeMode(true);
+
+ try {
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setBatterySaverMode(false);
+ setDozeMode(false);
+ }
+ }
+
+ /**
+ * Tests that powersave whitelists works as expected when doze and appIdle modes
+ * are enabled.
+ */
+ @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+ @Test
+ public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
+ setDozeMode(true);
+ setAppIdle(true);
+
+ try {
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+ }
+
+ @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+ @Test
+ public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
+ setDozeMode(true);
+ setAppIdle(true);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+ }
+
+ @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+ @Test
+ public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
+ setBatterySaverMode(true);
+ setAppIdle(true);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setBatterySaverMode(false);
+ }
+ }
+
+ /**
+ * Tests that the app idle whitelist works as expected when doze and appIdle mode are enabled.
+ */
+ @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+ @Test
+ public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
+ setDozeMode(true);
+ setAppIdle(true);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ // UID still shouldn't have access because of Doze.
+ addAppIdleWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+
+ removeAppIdleWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+ }
+
+ @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+ @Test
+ public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+ setDozeMode(true);
+ setAppIdle(true);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ addAppIdleWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setDozeMode(false);
+ removeAppIdleWhitelist(mUid);
+ }
+ }
+
+ @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+ @Test
+ public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+ setBatterySaverMode(true);
+ setAppIdle(true);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ addAppIdleWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setBatterySaverMode(false);
+ removeAppIdleWhitelist(mUid);
+ }
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java
new file mode 100644
index 0000000..6dc9921
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteInput;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+/**
+ * NotificationListenerService implementation that executes the notification actions once they're
+ * created.
+ */
+public class MyNotificationListenerService extends NotificationListenerService {
+ private static final String TAG = "MyNotificationListenerService";
+
+ @Override
+ public void onListenerConnected() {
+ Log.d(TAG, "onListenerConnected()");
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ Log.d(TAG, "onNotificationPosted(): " + sbn);
+ if (!sbn.getPackageName().startsWith(getPackageName())) {
+ Log.v(TAG, "ignoring notification from a different package");
+ return;
+ }
+ final PendingIntentSender sender = new PendingIntentSender();
+ final Notification notification = sbn.getNotification();
+ if (notification.contentIntent != null) {
+ sender.send("content", notification.contentIntent);
+ }
+ if (notification.deleteIntent != null) {
+ sender.send("delete", notification.deleteIntent);
+ }
+ if (notification.fullScreenIntent != null) {
+ sender.send("full screen", notification.fullScreenIntent);
+ }
+ if (notification.actions != null) {
+ for (Notification.Action action : notification.actions) {
+ sender.send("action", action.actionIntent);
+ sender.send("action extras", action.getExtras());
+ final RemoteInput[] remoteInputs = action.getRemoteInputs();
+ if (remoteInputs != null && remoteInputs.length > 0) {
+ for (RemoteInput remoteInput : remoteInputs) {
+ sender.send("remote input extras", remoteInput.getExtras());
+ }
+ }
+ }
+ }
+ sender.send("notification extras", notification.extras);
+ }
+
+ static String getId() {
+ return String.format("%s/%s", MyNotificationListenerService.class.getPackage().getName(),
+ MyNotificationListenerService.class.getName());
+ }
+
+ static ComponentName getComponentName() {
+ return new ComponentName(MyNotificationListenerService.class.getPackage().getName(),
+ MyNotificationListenerService.class.getName());
+ }
+
+ private static final class PendingIntentSender {
+ private PendingIntent mSentIntent = null;
+ private String mReason = null;
+
+ private void send(String reason, PendingIntent pendingIntent) {
+ if (pendingIntent == null) {
+ // Could happen on action that only has extras
+ Log.v(TAG, "Not sending null pending intent for " + reason);
+ return;
+ }
+ if (mSentIntent != null || mReason != null) {
+ // Sanity check: make sure test case set up just one pending intent in the
+ // notification, otherwise it could pass because another pending intent caused the
+ // whitelisting.
+ throw new IllegalStateException("Already sent a PendingIntent (" + mSentIntent
+ + ") for reason '" + mReason + "' when requested another for '" + reason
+ + "' (" + pendingIntent + ")");
+ }
+ Log.i(TAG, "Sending pending intent for " + reason + ":" + pendingIntent);
+ try {
+ pendingIntent.send();
+ mSentIntent = pendingIntent;
+ mReason = reason;
+ } catch (CanceledException e) {
+ Log.w(TAG, "Pending intent " + pendingIntent + " canceled");
+ }
+ }
+
+ private void send(String reason, Bundle extras) {
+ if (extras != null) {
+ for (String key : extras.keySet()) {
+ Object value = extras.get(key);
+ if (value instanceof PendingIntent) {
+ send(reason + " with key '" + key + "'", (PendingIntent) value);
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java
new file mode 100644
index 0000000..71b28f6
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.NetworkRequest;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+public class MyServiceClient {
+ private static final int TIMEOUT_MS = 20_000;
+ private static final String PACKAGE = MyServiceClient.class.getPackage().getName();
+ private static final String APP2_PACKAGE = PACKAGE + ".app2";
+ private static final String SERVICE_NAME = APP2_PACKAGE + ".MyService";
+
+ private Context mContext;
+ private ServiceConnection mServiceConnection;
+ private volatile IMyService mService;
+ private final ConditionVariable mServiceCondition = new ConditionVariable();
+
+ public MyServiceClient(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Binds to a service in the test app to communicate state.
+ * @param bindPriorityFlags Flags to influence the process-state of the bound app.
+ */
+ public void bind(int bindPriorityFlags) {
+ if (mService != null) {
+ throw new IllegalStateException("Already bound");
+ }
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = IMyService.Stub.asInterface(service);
+ mServiceCondition.open();
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mServiceCondition.close();
+ mService = null;
+ }
+ };
+
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName(APP2_PACKAGE, SERVICE_NAME));
+ // Needs to use BIND_NOT_FOREGROUND so app2 does not run in
+ // the same process state as app
+ mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE
+ | bindPriorityFlags);
+ ensureServiceConnection();
+ }
+
+ public void unbind() {
+ if (mService != null) {
+ mContext.unbindService(mServiceConnection);
+ }
+ }
+
+ private void ensureServiceConnection() {
+ if (mService != null) {
+ return;
+ }
+ mServiceCondition.block(TIMEOUT_MS);
+ if (mService == null) {
+ throw new IllegalStateException(
+ "Could not bind to MyService service after " + TIMEOUT_MS + "ms");
+ }
+ }
+
+ public void registerBroadcastReceiver() throws RemoteException {
+ ensureServiceConnection();
+ mService.registerBroadcastReceiver();
+ }
+
+ public int getCounters(String receiverName, String action) throws RemoteException {
+ ensureServiceConnection();
+ return mService.getCounters(receiverName, action);
+ }
+
+ /** Retrieves the network state as observed from the bound test app */
+ public NetworkCheckResult checkNetworkStatus(String address) throws RemoteException {
+ ensureServiceConnection();
+ return mService.checkNetworkStatus(address);
+ }
+
+ public String getRestrictBackgroundStatus() throws RemoteException {
+ ensureServiceConnection();
+ return mService.getRestrictBackgroundStatus();
+ }
+
+ public void sendNotification(int notificationId, String notificationType)
+ throws RemoteException {
+ ensureServiceConnection();
+ mService.sendNotification(notificationId, notificationType);
+ }
+
+ public void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb)
+ throws RemoteException {
+ ensureServiceConnection();
+ mService.registerNetworkCallback(request, cb);
+ }
+
+ public void unregisterNetworkCallback() throws RemoteException {
+ ensureServiceConnection();
+ mService.unregisterNetworkCallback();
+ }
+
+ public int scheduleJob(JobInfo jobInfo) throws RemoteException {
+ ensureServiceConnection();
+ return mService.scheduleJob(jobInfo);
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
new file mode 100644
index 0000000..0b5e164
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getActiveNetworkCapabilities;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.cts.util.CtsNetUtils;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
+ private Network mNetwork;
+ private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
+ private CtsNetUtils mCtsNetUtils;
+ private static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google";
+
+ @Rule
+ public final MeterednessConfigurationRule mMeterednessConfiguration
+ = new MeterednessConfigurationRule();
+
+ enum CallbackState {
+ NONE,
+ AVAILABLE,
+ LOST,
+ BLOCKED_STATUS,
+ CAPABILITIES
+ }
+
+ private static class CallbackInfo {
+ public final CallbackState state;
+ public final Network network;
+ public final Object arg;
+
+ CallbackInfo(CallbackState s, Network n, Object o) {
+ state = s; network = n; arg = o;
+ }
+
+ public String toString() {
+ return String.format("%s (%s) (%s)", state, network, arg);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof CallbackInfo)) return false;
+ // Ignore timeMs, since it's unpredictable.
+ final CallbackInfo other = (CallbackInfo) o;
+ return (state == other.state) && Objects.equals(network, other.network)
+ && Objects.equals(arg, other.arg);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(state, network, arg);
+ }
+ }
+
+ private class TestNetworkCallback extends INetworkCallback.Stub {
+ private static final int TEST_CONNECT_TIMEOUT_MS = 30_000;
+ private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000;
+
+ private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
+
+ protected void setLastCallback(CallbackState state, Network network, Object o) {
+ mCallbacks.offer(new CallbackInfo(state, network, o));
+ }
+
+ CallbackInfo nextCallback(int timeoutMs) {
+ CallbackInfo cb = null;
+ try {
+ cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
+ if (cb == null) {
+ fail("Did not receive callback after " + timeoutMs + "ms");
+ }
+ return cb;
+ }
+
+ CallbackInfo expectCallback(CallbackState state, Network expectedNetwork, Object o) {
+ final CallbackInfo expected = new CallbackInfo(state, expectedNetwork, o);
+ final CallbackInfo actual = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+ assertEquals("Unexpected callback:", expected, actual);
+ return actual;
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ setLastCallback(CallbackState.AVAILABLE, network, null);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ setLastCallback(CallbackState.LOST, network, null);
+ }
+
+ @Override
+ public void onBlockedStatusChanged(Network network, boolean blocked) {
+ setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+ setLastCallback(CallbackState.CAPABILITIES, network, cap);
+ }
+
+ public Network expectAvailableCallbackAndGetNetwork() {
+ final CallbackInfo cb = nextCallback(TEST_CONNECT_TIMEOUT_MS);
+ if (cb.state != CallbackState.AVAILABLE) {
+ fail("Network is not available. Instead obtained the following callback :" + cb);
+ }
+ return cb.network;
+ }
+
+ public void drainAndWaitForIdle() {
+ try {
+ do {
+ mCallbacks.drainTo(new ArrayList<>());
+ } while (mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS) != null);
+ } catch (InterruptedException ie) {
+ Log.e(TAG, "Interrupted while draining callback queue", ie);
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
+ expectCallback(CallbackState.BLOCKED_STATUS, expectedNetwork, expectBlocked);
+ }
+
+ public void expectBlockedStatusCallbackEventually(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
+ && cb.network.equals(expectedNetwork)) {
+ assertEquals(expectBlocked, cb.arg);
+ return;
+ }
+ } while (System.currentTimeMillis() <= deadline);
+ fail("Didn't receive onBlockedStatusChanged()");
+ }
+
+ public void expectCapabilitiesCallbackEventually(Network expectedNetwork, boolean hasCap,
+ int cap) {
+ final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS;
+ do {
+ final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis()));
+ if (cb.state != CallbackState.CAPABILITIES
+ || !expectedNetwork.equals(cb.network)
+ || (hasCap != ((NetworkCapabilities) cb.arg).hasCapability(cap))) {
+ Log.i("NetworkCallbackTest#expectCapabilitiesCallback",
+ "Ignoring non-matching callback : " + cb);
+ continue;
+ }
+ // Found a match, return
+ return;
+ } while (System.currentTimeMillis() <= deadline);
+ fail("Didn't receive the expected callback to onCapabilitiesChanged(). Check the "
+ + "log for a list of received callbacks, if any.");
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ assumeTrue(canChangeActiveNetworkMeteredness());
+
+ registerBroadcastReceiver();
+
+ removeRestrictBackgroundWhitelist(mUid);
+ removeRestrictBackgroundBlacklist(mUid);
+ assertRestrictBackgroundChangedReceived(0);
+
+ // Initial state
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setAppIdle(false);
+
+ // Get transports of the active network, this has to be done before changing meteredness,
+ // since wifi will be disconnected when changing from non-metered to metered.
+ final NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder()
+ .clearCapabilities();
+ final NetworkCapabilities networkCapabilities = getActiveNetworkCapabilities();
+ for (final int capability : networkCapabilities.getCapabilities()) {
+ networkRequestBuilder.addCapability(capability);
+ }
+ for (final int transportType : networkCapabilities.getTransportTypes()) {
+ networkRequestBuilder.addTransportType(transportType);
+ }
+ networkRequestBuilder.setNetworkSpecifier(networkCapabilities.getNetworkSpecifier());
+ networkRequestBuilder.removeCapability(NET_CAPABILITY_NOT_METERED);
+
+ // Mark network as metered.
+ mMeterednessConfiguration.configureNetworkMeteredness(true);
+
+ // Register callback, copy the capabilities from the active network to expect the "original"
+ // network before disconnecting, but null out some fields to prevent over-specified.
+ registerNetworkCallback(networkRequestBuilder.build(), mTestNetworkCallback);
+ // Wait for onAvailable() callback to ensure network is available before the test
+ // and store the default network.
+ mNetwork = mTestNetworkCallback.expectAvailableCallbackAndGetNetwork();
+ // Check that the network is metered.
+ mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
+ false /* hasCapability */, NET_CAPABILITY_NOT_METERED);
+ mTestNetworkCallback.drainAndWaitForIdle();
+
+ // Before Android T, DNS queries over private DNS should be but are not restricted by Power
+ // Saver or Data Saver. The issue is fixed in mainline update and apps can no longer request
+ // DNS queries when its network is restricted by Power Saver. The fix takes effect backwards
+ // starting from Android T. But for Data Saver, the fix is not backward compatible since
+ // there are some platform changes involved. It is only available on devices that a specific
+ // trunk flag is enabled.
+ //
+ // This test can not only verify that the network traffic from apps is blocked at the right
+ // time, but also verify whether it is correctly blocked at the DNS stage, or at a later
+ // socket connection stage.
+ if (SdkLevel.isAtLeastT()) {
+ // Enable private DNS
+ mCtsNetUtils = new CtsNetUtils(mContext);
+ mCtsNetUtils.storePrivateDnsSetting();
+ mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
+ mCtsNetUtils.awaitPrivateDnsSetting(
+ "NetworkCallbackTest wait private DNS setting timeout", mNetwork,
+ GOOGLE_PRIVATE_DNS_SERVER, true);
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ setRestrictBackground(false);
+ setBatterySaverMode(false);
+ unregisterNetworkCallback();
+ stopApp();
+
+ if (SdkLevel.isAtLeastT() && (mCtsNetUtils != null)) {
+ mCtsNetUtils.restorePrivateDnsSetting();
+ }
+ }
+
+ @RequiredProperties({DATA_SAVER_MODE})
+ @Test
+ public void testOnBlockedStatusChanged_dataSaver() throws Exception {
+ try {
+ // Enable restrict background
+ setRestrictBackground(true);
+ // TODO: Verify expectedUnavailableError when aconfig support mainline.
+ // (see go/aconfig-in-mainline-problems)
+ assertBackgroundNetworkAccess(false);
+ assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
+
+ // Add to whitelist
+ addRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(true);
+ assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
+
+ // Remove from whitelist
+ removeRestrictBackgroundWhitelist(mUid);
+ // TODO: Verify expectedUnavailableError when aconfig support mainline.
+ assertBackgroundNetworkAccess(false);
+ assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
+ } finally {
+ mMeterednessConfiguration.resetNetworkMeteredness();
+ }
+
+ // Set to non-metered network
+ mMeterednessConfiguration.configureNetworkMeteredness(false);
+ mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
+ true /* hasCapability */, NET_CAPABILITY_NOT_METERED);
+ try {
+ assertBackgroundNetworkAccess(true);
+ assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
+
+ // Disable restrict background, should not trigger callback
+ setRestrictBackground(false);
+ assertBackgroundNetworkAccess(true);
+ assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
+ } finally {
+ mMeterednessConfiguration.resetNetworkMeteredness();
+ }
+ }
+
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ @Test
+ public void testOnBlockedStatusChanged_powerSaver() throws Exception {
+ try {
+ // Enable Power Saver
+ setBatterySaverMode(true);
+ if (SdkLevel.isAtLeastT()) {
+ assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ assertNetworkAccess(false, "java.net.UnknownHostException");
+ } else {
+ assertBackgroundNetworkAccess(false);
+ }
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
+ assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
+
+ // Disable Power Saver
+ setBatterySaverMode(false);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
+ assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */);
+ } finally {
+ mMeterednessConfiguration.resetNetworkMeteredness();
+ }
+
+ // Set to non-metered network
+ mMeterednessConfiguration.configureNetworkMeteredness(false);
+ mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
+ true /* hasCapability */, NET_CAPABILITY_NOT_METERED);
+ try {
+ // Enable Power Saver
+ setBatterySaverMode(true);
+ if (SdkLevel.isAtLeastT()) {
+ assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ assertNetworkAccess(false, "java.net.UnknownHostException");
+ } else {
+ assertBackgroundNetworkAccess(false);
+ }
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
+ assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
+
+ // Disable Power Saver
+ setBatterySaverMode(false);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
+ assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
+ } finally {
+ mMeterednessConfiguration.resetNetworkMeteredness();
+ }
+ }
+
+ @Test
+ public void testOnBlockedStatusChanged_default() throws Exception {
+ assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
+
+ try {
+ assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+ assertNetworkAccess(false, null);
+ assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
+
+ launchActivity();
+ assertTopState();
+ assertNetworkAccess(true, null);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
+ assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */);
+
+ finishActivity();
+ assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+ SystemClock.sleep(mProcessStateTransitionLongDelayMs);
+ assertNetworkAccess(false, null);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
+ assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
+
+ } finally {
+ mMeterednessConfiguration.resetNetworkMeteredness();
+ }
+
+ // Set to non-metered network
+ mMeterednessConfiguration.configureNetworkMeteredness(false);
+ mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
+ true /* hasCapability */, NET_CAPABILITY_NOT_METERED);
+ try {
+ assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+ assertNetworkAccess(false, null);
+ assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
+
+ launchActivity();
+ assertTopState();
+ assertNetworkAccess(true, null);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
+ assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
+
+ finishActivity();
+ assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+ SystemClock.sleep(mProcessStateTransitionLongDelayMs);
+ assertNetworkAccess(false, null);
+ mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
+ assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
+ } finally {
+ mMeterednessConfiguration.resetNetworkMeteredness();
+ }
+ }
+
+ // TODO: 1. test against VPN lockdown.
+ // 2. test against multiple networks.
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
new file mode 100644
index 0000000..6c5f2ff
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.assertIsUidRestrictedOnMeteredNetworks;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.assertNetworkingBlockedStatusForUid;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isUidNetworkingBlocked;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.os.SystemClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NetworkPolicyManagerTest extends AbstractRestrictBackgroundNetworkTestCase {
+ private static final boolean METERED = true;
+ private static final boolean NON_METERED = false;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ registerBroadcastReceiver();
+
+ removeRestrictBackgroundWhitelist(mUid);
+ removeRestrictBackgroundBlacklist(mUid);
+ assertRestrictBackgroundChangedReceived(0);
+
+ // Initial state
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setRestrictedNetworkingMode(false);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setRestrictedNetworkingMode(false);
+ unregisterNetworkCallback();
+ stopApp();
+ }
+
+ @Test
+ public void testIsUidNetworkingBlocked_withUidNotBlocked() throws Exception {
+ // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
+ // test the cases of non-metered network and uid not matched by any rule.
+ // If mUid is not blocked by data saver mode or power saver mode, no matter the network is
+ // metered or non-metered, mUid shouldn't be blocked.
+ assertFalse(isUidNetworkingBlocked(mUid, METERED)); // Match NTWK_ALLOWED_DEFAULT
+ assertFalse(isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
+ }
+
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE})
+ @Test
+ public void testIsUidNetworkingBlocked_withSystemUid() throws Exception {
+ // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
+ // test the case of uid is system uid.
+ // SYSTEM_UID will never be blocked.
+ assertFalse(isUidNetworkingBlocked(SYSTEM_UID, METERED)); // Match NTWK_ALLOWED_SYSTEM
+ assertFalse(isUidNetworkingBlocked(SYSTEM_UID, NON_METERED)); // Match NTWK_ALLOWED_SYSTEM
+ try {
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+ setRestrictedNetworkingMode(true);
+ assertNetworkingBlockedStatusForUid(SYSTEM_UID, METERED,
+ false /* expectedResult */); // Match NTWK_ALLOWED_SYSTEM
+ assertFalse(
+ isUidNetworkingBlocked(SYSTEM_UID, NON_METERED)); // Match NTWK_ALLOWED_SYSTEM
+ } finally {
+ setRestrictBackground(false);
+ setBatterySaverMode(false);
+ setRestrictedNetworkingMode(false);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
+ }
+ }
+
+ @RequiredProperties({DATA_SAVER_MODE})
+ @Test
+ public void testIsUidNetworkingBlocked_withDataSaverMode() throws Exception {
+ // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
+ // test the cases of non-metered network, uid is matched by restrict background blacklist,
+ // uid is matched by restrict background whitelist, app is in the foreground with restrict
+ // background enabled and the app is in the background with restrict background enabled.
+ try {
+ // Enable restrict background and mUid will be blocked because it's not in the
+ // foreground.
+ setRestrictBackground(true);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ true /* expectedResult */); // Match NTWK_BLOCKED_BG_RESTRICT
+
+ // Although restrict background is enabled and mUid is in the background, but mUid will
+ // not be blocked if network is non-metered.
+ assertFalse(
+ isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
+
+ // Add mUid into the restrict background blacklist.
+ addRestrictBackgroundBlacklist(mUid);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ true /* expectedResult */); // Match NTWK_BLOCKED_DENYLIST
+
+ // Although mUid is in the restrict background blacklist, but mUid won't be blocked if
+ // the network is non-metered.
+ assertFalse(
+ isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
+ removeRestrictBackgroundBlacklist(mUid);
+
+ // Add mUid into the restrict background whitelist.
+ addRestrictBackgroundWhitelist(mUid);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ false /* expectedResult */); // Match NTWK_ALLOWED_ALLOWLIST
+ assertFalse(
+ isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
+ removeRestrictBackgroundWhitelist(mUid);
+
+ // Make TEST_APP2_PKG go to foreground and mUid will be allowed temporarily.
+ launchActivity();
+ assertTopState();
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ false /* expectedResult */); // Match NTWK_ALLOWED_TMP_ALLOWLIST
+
+ // Back to background.
+ finishActivity();
+ assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ true /* expectedResult */); // Match NTWK_BLOCKED_BG_RESTRICT
+ } finally {
+ setRestrictBackground(false);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
+ }
+ }
+
+ @Test
+ public void testIsUidNetworkingBlocked_withRestrictedNetworkingMode() throws Exception {
+ // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
+ // test the cases of restricted networking mode enabled.
+ try {
+ // All apps should be blocked if restricted networking mode is enabled except for those
+ // apps who have CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
+ // This test won't test if an app who has CONNECTIVITY_USE_RESTRICTED_NETWORKS will not
+ // be blocked because CONNECTIVITY_USE_RESTRICTED_NETWORKS is a signature/privileged
+ // permission that CTS cannot acquire. Also it's not good for this test to use those
+ // privileged apps which have CONNECTIVITY_USE_RESTRICTED_NETWORKS to test because there
+ // is no guarantee that those apps won't remove this permission someday, and if it
+ // happens, then this test will fail.
+ setRestrictedNetworkingMode(true);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ true /* expectedResult */); // Match NTWK_BLOCKED_RESTRICTED_MODE
+ assertTrue(isUidNetworkingBlocked(mUid,
+ NON_METERED)); // Match NTWK_BLOCKED_RESTRICTED_MODE
+ } finally {
+ setRestrictedNetworkingMode(false);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
+ }
+ }
+
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ @Test
+ public void testIsUidNetworkingBlocked_withPowerSaverMode() throws Exception {
+ // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
+ // test the cases of power saver mode enabled, uid in the power saver mode whitelist and
+ // uid in the power saver mode whitelist with non-metered network.
+ try {
+ // mUid should be blocked if power saver mode is enabled.
+ setBatterySaverMode(true);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ true /* expectedResult */); // Match NTWK_BLOCKED_POWER
+ assertTrue(isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_BLOCKED_POWER
+
+ // Add TEST_APP2_PKG into power saver mode whitelist, its uid rule is RULE_ALLOW_ALL and
+ // it shouldn't be blocked.
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
+ assertFalse(
+ isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ } finally {
+ setBatterySaverMode(false);
+ assertNetworkingBlockedStatusForUid(mUid, METERED,
+ false /* expectedResult */); // Match NTWK_ALLOWED_DEFAULT
+ }
+ }
+
+ @RequiredProperties({DATA_SAVER_MODE})
+ @Test
+ public void testIsUidRestrictedOnMeteredNetworks() throws Exception {
+ try {
+ // isUidRestrictedOnMeteredNetworks() will only return true when restrict background is
+ // enabled and mUid is not in the restrict background whitelist and TEST_APP2_PKG is not
+ // in the foreground. For other cases, it will return false.
+ setRestrictBackground(true);
+ assertIsUidRestrictedOnMeteredNetworks(mUid, true /* expectedResult */);
+
+ // Make TEST_APP2_PKG go to foreground and isUidRestrictedOnMeteredNetworks() will
+ // return false.
+ launchActivity();
+ assertTopState();
+ assertIsUidRestrictedOnMeteredNetworks(mUid, false /* expectedResult */);
+ // Back to background.
+ finishActivity();
+ assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+
+ // Add mUid into restrict background whitelist and isUidRestrictedOnMeteredNetworks()
+ // will return false.
+ addRestrictBackgroundWhitelist(mUid);
+ assertIsUidRestrictedOnMeteredNetworks(mUid, false /* expectedResult */);
+ removeRestrictBackgroundWhitelist(mUid);
+ } finally {
+ // Restrict background is disabled and isUidRestrictedOnMeteredNetworks() will return
+ // false.
+ setRestrictBackground(false);
+ assertIsUidRestrictedOnMeteredNetworks(mUid, false /* expectedResult */);
+ }
+ }
+
+ @Test
+ public void testIsUidNetworkingBlocked_whenInBackground() throws Exception {
+ assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
+
+ try {
+ assertProcessStateBelow(PROCESS_STATE_LAST_ACTIVITY);
+ SystemClock.sleep(mProcessStateTransitionShortDelayMs);
+ assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */);
+ assertTrue(isUidNetworkingBlocked(mUid, NON_METERED));
+
+ launchActivity();
+ assertTopState();
+ assertNetworkingBlockedStatusForUid(mUid, METERED, false /* expectedResult */);
+ assertFalse(isUidNetworkingBlocked(mUid, NON_METERED));
+
+ finishActivity();
+ assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+ SystemClock.sleep(mProcessStateTransitionLongDelayMs);
+ assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */);
+ assertTrue(isUidNetworkingBlocked(mUid, NON_METERED));
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertNetworkingBlockedStatusForUid(mUid, METERED, false /* expectedResult */);
+ assertFalse(isUidNetworkingBlocked(mUid, NON_METERED));
+ } finally {
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ }
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java
new file mode 100644
index 0000000..0207b00
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java
@@ -0,0 +1,44 @@
+/*
+ * 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.cts.netpolicy.hostside;
+
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import org.junit.rules.RunRules;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+
+import java.util.List;
+
+/**
+ * Custom runner to allow dumping logs after a test failure before the @After methods get to run.
+ */
+public class NetworkPolicyTestRunner extends AndroidJUnit4ClassRunner {
+ private TestRule mDumpOnFailureRule = new DumpOnFailureRule();
+
+ public NetworkPolicyTestRunner(Class<?> klass) throws InitializationError {
+ super(klass);
+ }
+
+ @Override
+ public Statement methodInvoker(FrameworkMethod method, Object test) {
+ return new RunRules(super.methodInvoker(method, test), List.of(mDumpOnFailureRule),
+ describeChild(method));
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java
new file mode 100644
index 0000000..26a88f2
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED;
+import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkPolicyManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.ActionListener;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.data.ApnSetting;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.BatteryUtils;
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class NetworkPolicyTestUtils {
+
+ // android.telephony.CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS
+ // TODO: Expose it as a @TestApi instead of copying the constant
+ private static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS =
+ "carrier_metered_apn_types_strings";
+
+ private static final int TIMEOUT_CHANGE_METEREDNESS_MS = 10_000;
+
+ private static ConnectivityManager mCm;
+ private static WifiManager mWm;
+ private static CarrierConfigManager mCarrierConfigManager;
+ private static NetworkPolicyManager sNpm;
+
+ private static Boolean mBatterySaverSupported;
+ private static Boolean mDataSaverSupported;
+ private static Boolean mDozeModeSupported;
+ private static Boolean mAppStandbySupported;
+
+ private NetworkPolicyTestUtils() {}
+
+ public static boolean isBatterySaverSupported() {
+ if (mBatterySaverSupported == null) {
+ mBatterySaverSupported = BatteryUtils.isBatterySaverSupported();
+ }
+ return mBatterySaverSupported;
+ }
+
+ private static boolean isWear() {
+ return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
+ /**
+ * As per CDD requirements, if the device doesn't support data saver mode then
+ * ConnectivityManager.getRestrictBackgroundStatus() will always return
+ * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
+ * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
+ * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
+ */
+ public static boolean isDataSaverSupported() {
+ if (isWear()) {
+ return false;
+ }
+ if (mDataSaverSupported == null) {
+ setRestrictBackgroundInternal(false);
+ assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ try {
+ setRestrictBackgroundInternal(true);
+ mDataSaverSupported = !isMyRestrictBackgroundStatus(
+ RESTRICT_BACKGROUND_STATUS_DISABLED);
+ } finally {
+ setRestrictBackgroundInternal(false);
+ }
+ }
+ return mDataSaverSupported;
+ }
+
+ public static boolean isDozeModeSupported() {
+ if (mDozeModeSupported == null) {
+ final String result = executeShellCommand("cmd deviceidle enabled deep");
+ mDozeModeSupported = result.equals("1");
+ }
+ return mDozeModeSupported;
+ }
+
+ public static boolean isAppStandbySupported() {
+ if (mAppStandbySupported == null) {
+ mAppStandbySupported = AppStandbyUtils.isAppStandbyEnabled();
+ }
+ return mAppStandbySupported;
+ }
+
+ public static boolean isLowRamDevice() {
+ final ActivityManager am = (ActivityManager) getContext().getSystemService(
+ Context.ACTIVITY_SERVICE);
+ return am.isLowRamDevice();
+ }
+
+ /** Forces JobScheduler to run the job if constraints are met. */
+ public static void forceRunJob(String pkg, int jobId) {
+ executeShellCommand("cmd jobscheduler run -f -u " + UserHandle.myUserId()
+ + " " + pkg + " " + jobId);
+ }
+
+ public static boolean isLocationEnabled() {
+ final LocationManager lm = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ return lm.isLocationEnabled();
+ }
+
+ public static void setLocationEnabled(boolean enabled) {
+ final LocationManager lm = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ lm.setLocationEnabledForUser(enabled, Process.myUserHandle());
+ assertEquals("Couldn't change location enabled state", lm.isLocationEnabled(), enabled);
+ Log.d(TAG, "Changed location enabled state to " + enabled);
+ }
+
+ public static boolean isActiveNetworkMetered(boolean metered) {
+ return getConnectivityManager().isActiveNetworkMetered() == metered;
+ }
+
+ public static boolean canChangeActiveNetworkMeteredness() {
+ final NetworkCapabilities networkCapabilities = getActiveNetworkCapabilities();
+ return networkCapabilities.hasTransport(TRANSPORT_WIFI)
+ || networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
+ }
+
+ /**
+ * Updates the meteredness of the active network. Right now we can only change meteredness
+ * of either Wifi or cellular network, so if the active network is not either of these, this
+ * will throw an exception.
+ *
+ * @return a {@link ThrowingRunnable} object that can used to reset the meteredness change
+ * made by this method.
+ */
+ public static ThrowingRunnable setupActiveNetworkMeteredness(boolean metered) throws Exception {
+ if (isActiveNetworkMetered(metered)) {
+ return null;
+ }
+ final NetworkCapabilities networkCapabilities = getActiveNetworkCapabilities();
+ if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+ final String ssid = getWifiSsid();
+ setWifiMeteredStatus(ssid, metered);
+ return () -> setWifiMeteredStatus(ssid, !metered);
+ } else if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ final int subId = SubscriptionManager.getActiveDataSubscriptionId();
+ setCellularMeteredStatus(subId, metered);
+ return () -> setCellularMeteredStatus(subId, !metered);
+ } else {
+ // Right now, we don't have a way to change meteredness of networks other
+ // than Wi-Fi or Cellular, so just throw an exception.
+ throw new IllegalStateException("Can't change meteredness of current active network");
+ }
+ }
+
+ private static String getWifiSsid() {
+ final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ final String ssid = getWifiManager().getConnectionInfo().getSSID();
+ assertNotEquals(WifiManager.UNKNOWN_SSID, ssid);
+ return ssid;
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ static NetworkCapabilities getActiveNetworkCapabilities() {
+ final Network activeNetwork = getConnectivityManager().getActiveNetwork();
+ assertNotNull("No active network available", activeNetwork);
+ return getConnectivityManager().getNetworkCapabilities(activeNetwork);
+ }
+
+ private static void setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
+ final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ final WifiConfiguration currentConfig = getWifiConfiguration(ssid);
+ currentConfig.meteredOverride = metered
+ ? METERED_OVERRIDE_METERED : METERED_OVERRIDE_NONE;
+ BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();
+ getWifiManager().save(currentConfig, createActionListener(
+ blockingQueue, Integer.MAX_VALUE));
+ Integer resultCode = blockingQueue.poll(TIMEOUT_CHANGE_METEREDNESS_MS,
+ TimeUnit.MILLISECONDS);
+ if (resultCode == null) {
+ fail("Timed out waiting for meteredness to change; ssid=" + ssid
+ + ", metered=" + metered);
+ } else if (resultCode != Integer.MAX_VALUE) {
+ fail("Error overriding the meteredness; ssid=" + ssid
+ + ", metered=" + metered + ", error=" + resultCode);
+ }
+ final boolean success = assertActiveNetworkMetered(metered, false /* throwOnFailure */);
+ if (!success) {
+ Log.i(TAG, "Retry connecting to wifi; ssid=" + ssid);
+ blockingQueue = new LinkedBlockingQueue<>();
+ getWifiManager().connect(currentConfig, createActionListener(
+ blockingQueue, Integer.MAX_VALUE));
+ resultCode = blockingQueue.poll(TIMEOUT_CHANGE_METEREDNESS_MS,
+ TimeUnit.MILLISECONDS);
+ if (resultCode == null) {
+ fail("Timed out waiting for wifi to connect; ssid=" + ssid);
+ } else if (resultCode != Integer.MAX_VALUE) {
+ fail("Error connecting to wifi; ssid=" + ssid
+ + ", error=" + resultCode);
+ }
+ assertActiveNetworkMetered(metered, true /* throwOnFailure */);
+ }
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private static WifiConfiguration getWifiConfiguration(String ssid) {
+ final List<String> ssids = new ArrayList<>();
+ for (WifiConfiguration config : getWifiManager().getConfiguredNetworks()) {
+ if (config.SSID.equals(ssid)) {
+ return config;
+ }
+ ssids.add(config.SSID);
+ }
+ fail("Couldn't find the wifi config; ssid=" + ssid
+ + ", all=" + Arrays.toString(ssids.toArray()));
+ return null;
+ }
+
+ private static ActionListener createActionListener(BlockingQueue<Integer> blockingQueue,
+ int successCode) {
+ return new ActionListener() {
+ @Override
+ public void onSuccess() {
+ blockingQueue.offer(successCode);
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ blockingQueue.offer(reason);
+ }
+ };
+ }
+
+ private static void setCellularMeteredStatus(int subId, boolean metered) throws Exception {
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[] {ApnSetting.TYPE_MMS_STRING});
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(getCarrierConfigManager(),
+ (cm) -> cm.overrideConfig(subId, metered ? null : bundle));
+ assertActiveNetworkMetered(metered, true /* throwOnFailure */);
+ }
+
+ private static boolean assertActiveNetworkMetered(boolean expectedMeteredStatus,
+ boolean throwOnFailure) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final NetworkCallback networkCallback = new NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+ final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ if (metered == expectedMeteredStatus) {
+ latch.countDown();
+ }
+ }
+ };
+ // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+ // with the current setting. Therefore, if the setting has already been changed,
+ // this method will return right away, and if not it will wait for the setting to change.
+ getConnectivityManager().registerDefaultNetworkCallback(networkCallback);
+ try {
+ if (!latch.await(TIMEOUT_CHANGE_METEREDNESS_MS, TimeUnit.MILLISECONDS)) {
+ final String errorMsg = "Timed out waiting for active network metered status "
+ + "to change to " + expectedMeteredStatus + "; network = "
+ + getConnectivityManager().getActiveNetwork();
+ if (throwOnFailure) {
+ fail(errorMsg);
+ }
+ Log.w(TAG, errorMsg);
+ return false;
+ }
+ return true;
+ } finally {
+ getConnectivityManager().unregisterNetworkCallback(networkCallback);
+ }
+ }
+
+ public static void setRestrictBackground(boolean enabled) {
+ if (!isDataSaverSupported()) {
+ return;
+ }
+ setRestrictBackgroundInternal(enabled);
+ }
+
+ static void setRestrictBackgroundInternal(boolean enabled) {
+ executeShellCommand("cmd netpolicy set restrict-background " + enabled);
+ final String output = executeShellCommand("cmd netpolicy get restrict-background");
+ final String expectedSuffix = enabled ? "enabled" : "disabled";
+ assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+ output.endsWith(expectedSuffix));
+ }
+
+ public static boolean isMyRestrictBackgroundStatus(int expectedStatus) {
+ final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+ if (expectedStatus != actualStatus) {
+ Log.d(TAG, "MyRestrictBackgroundStatus: "
+ + "Expected: " + restrictBackgroundValueToString(expectedStatus)
+ + "; Actual: " + restrictBackgroundValueToString(actualStatus));
+ return false;
+ }
+ return true;
+ }
+
+ // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+ private static String unquoteSSID(String ssid) {
+ // SSID is returned surrounded by quotes if it can be decoded as UTF-8.
+ // Otherwise it's guaranteed not to start with a quote.
+ if (ssid.charAt(0) == '"') {
+ return ssid.substring(1, ssid.length() - 1);
+ } else {
+ return ssid;
+ }
+ }
+
+ public static String restrictBackgroundValueToString(int status) {
+ switch (status) {
+ case RESTRICT_BACKGROUND_STATUS_DISABLED:
+ return "DISABLED";
+ case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+ return "WHITELISTED";
+ case RESTRICT_BACKGROUND_STATUS_ENABLED:
+ return "ENABLED";
+ default:
+ return "UNKNOWN_STATUS_" + status;
+ }
+ }
+
+ public static void clearSnoozeTimestamps() {
+ executeShellCommand("dumpsys netpolicy --unsnooze");
+ }
+
+ public static String executeShellCommand(String command) {
+ final String result = runShellCommandOrThrow(command).trim();
+ Log.d(TAG, "Output of '" + command + "': '" + result + "'");
+ return result;
+ }
+
+ public static void assertMyRestrictBackgroundStatus(int expectedStatus) {
+ final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+ assertEquals(restrictBackgroundValueToString(expectedStatus),
+ restrictBackgroundValueToString(actualStatus));
+ }
+
+ public static ConnectivityManager getConnectivityManager() {
+ if (mCm == null) {
+ mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+ return mCm;
+ }
+
+ public static WifiManager getWifiManager() {
+ if (mWm == null) {
+ mWm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+ }
+ return mWm;
+ }
+
+ public static CarrierConfigManager getCarrierConfigManager() {
+ if (mCarrierConfigManager == null) {
+ mCarrierConfigManager = (CarrierConfigManager) getContext().getSystemService(
+ Context.CARRIER_CONFIG_SERVICE);
+ }
+ return mCarrierConfigManager;
+ }
+
+ public static NetworkPolicyManager getNetworkPolicyManager() {
+ if (sNpm == null) {
+ sNpm = getContext().getSystemService(NetworkPolicyManager.class);
+ }
+ return sNpm;
+ }
+
+ public static Context getContext() {
+ return getInstrumentation().getContext();
+ }
+
+ public static Instrumentation getInstrumentation() {
+ return InstrumentationRegistry.getInstrumentation();
+ }
+
+ public static UiDevice getUiDevice() {
+ return UiDevice.getInstance(getInstrumentation());
+ }
+
+ // When power saver mode or restrict background enabled or adding any white/black list into
+ // those modes, NetworkPolicy may need to take some time to update the rules of uids. So having
+ // this function and using PollingCheck to try to make sure the uid has updated and reduce the
+ // flaky rate.
+ public static void assertNetworkingBlockedStatusForUid(int uid, boolean metered,
+ boolean expectedResult) {
+ final String errMsg = String.format("Unexpected result from isUidNetworkingBlocked; "
+ + "uid= " + uid + ", metered=" + metered + ", expected=" + expectedResult);
+ PollingCheck.waitFor(() -> (expectedResult == isUidNetworkingBlocked(uid, metered)),
+ errMsg);
+ }
+
+ public static void assertIsUidRestrictedOnMeteredNetworks(int uid, boolean expectedResult) {
+ final String errMsg = String.format(
+ "Unexpected result from isUidRestrictedOnMeteredNetworks; "
+ + "uid= " + uid + ", expected=" + expectedResult);
+ PollingCheck.waitFor(() -> (expectedResult == isUidRestrictedOnMeteredNetworks(uid)),
+ errMsg);
+ }
+
+ public static boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork) {
+ final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ return getNetworkPolicyManager().isUidNetworkingBlocked(uid, meteredNetwork);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ public static boolean isUidRestrictedOnMeteredNetworks(int uid) {
+ final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ return getNetworkPolicyManager().isUidRestrictedOnMeteredNetworks(uid);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java
new file mode 100644
index 0000000..a03833f
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isActiveNetworkMetered;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDataSaverSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isLowRamDevice;
+
+public enum Property {
+ BATTERY_SAVER_MODE(1 << 0) {
+ public boolean isSupported() { return isBatterySaverSupported(); }
+ },
+
+ DATA_SAVER_MODE(1 << 1) {
+ public boolean isSupported() { return isDataSaverSupported(); }
+ },
+
+ NO_DATA_SAVER_MODE(~DATA_SAVER_MODE.getValue()) {
+ public boolean isSupported() { return !isDataSaverSupported(); }
+ },
+
+ DOZE_MODE(1 << 2) {
+ public boolean isSupported() { return isDozeModeSupported(); }
+ },
+
+ APP_STANDBY_MODE(1 << 3) {
+ public boolean isSupported() { return isAppStandbySupported(); }
+ },
+
+ NOT_LOW_RAM_DEVICE(1 << 4) {
+ public boolean isSupported() { return !isLowRamDevice(); }
+ },
+
+ METERED_NETWORK(1 << 5) {
+ public boolean isSupported() {
+ return isActiveNetworkMetered(true) || canChangeActiveNetworkMeteredness();
+ }
+ },
+
+ NON_METERED_NETWORK(~METERED_NETWORK.getValue()) {
+ public boolean isSupported() {
+ return isActiveNetworkMetered(false) || canChangeActiveNetworkMeteredness();
+ }
+ };
+
+ private int mValue;
+
+ Property(int value) { mValue = value; }
+
+ public int getValue() { return mValue; }
+
+ abstract boolean isSupported();
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java
new file mode 100644
index 0000000..799a513
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target({METHOD, TYPE})
+@Inherited
+public @interface RequiredProperties {
+ Property[] value();
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java
new file mode 100644
index 0000000..5dea67c
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside;
+
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.Assume;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class RequiredPropertiesRule extends BeforeAfterRule {
+
+ private static ArraySet<Property> mRequiredProperties;
+
+ @Override
+ public void onBefore(Statement base, Description description) {
+ mRequiredProperties = getAllRequiredProperties(description);
+
+ final String testName = description.getClassName() + "#" + description.getMethodName();
+ assertTestIsValid(testName, mRequiredProperties);
+ Log.i(TAG, "Running test " + testName + " with required properties: "
+ + propertiesToString(mRequiredProperties));
+ }
+
+ private ArraySet<Property> getAllRequiredProperties(Description description) {
+ final ArraySet<Property> allRequiredProperties = new ArraySet<>();
+ RequiredProperties requiredProperties = description.getAnnotation(RequiredProperties.class);
+ if (requiredProperties != null) {
+ Collections.addAll(allRequiredProperties, requiredProperties.value());
+ }
+
+ for (Class<?> clazz = description.getTestClass();
+ clazz != null; clazz = clazz.getSuperclass()) {
+ requiredProperties = clazz.getDeclaredAnnotation(RequiredProperties.class);
+ if (requiredProperties == null) {
+ continue;
+ }
+ for (Property requiredProperty : requiredProperties.value()) {
+ for (Property p : Property.values()) {
+ if (p.getValue() == ~requiredProperty.getValue()
+ && allRequiredProperties.contains(p)) {
+ continue;
+ }
+ }
+ allRequiredProperties.add(requiredProperty);
+ }
+ }
+ return allRequiredProperties;
+ }
+
+ private void assertTestIsValid(String testName, ArraySet<Property> requiredProperies) {
+ if (requiredProperies == null) {
+ return;
+ }
+ final ArrayList<Property> unsupportedProperties = new ArrayList<>();
+ for (Property property : requiredProperies) {
+ if (!property.isSupported()) {
+ unsupportedProperties.add(property);
+ }
+ }
+ Assume.assumeTrue("Unsupported properties: "
+ + propertiesToString(unsupportedProperties), unsupportedProperties.isEmpty());
+ }
+
+ public static ArraySet<Property> getRequiredProperties() {
+ return mRequiredProperties;
+ }
+
+ private static String propertiesToString(Iterable<Property> properties) {
+ return "[" + TextUtils.join(",", properties) + "]";
+ }
+}
diff --git a/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java
new file mode 100644
index 0000000..f183f4e
--- /dev/null
+++ b/hostsidetests/network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public final class RestrictedModeTest extends AbstractRestrictBackgroundNetworkTestCase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ setRestrictedNetworkingMode(false);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ setRestrictedNetworkingMode(false);
+ super.tearDown();
+ }
+
+ @Test
+ public void testNetworkAccess() throws Exception {
+ // go to foreground state and enable restricted mode
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ setRestrictedNetworkingMode(true);
+ assertTopNetworkAccess(false);
+
+ // go to background state
+ finishActivity();
+ assertBackgroundNetworkAccess(false);
+
+ // disable restricted mode and assert network access in foreground and background states
+ setRestrictedNetworkingMode(false);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ assertTopNetworkAccess(true);
+
+ // go to background state
+ finishActivity();
+ assertBackgroundNetworkAccess(true);
+ }
+
+ @Test
+ public void testNetworkAccess_withBatterySaver() throws Exception {
+ setBatterySaverMode(true);
+ try {
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ setRestrictedNetworkingMode(true);
+ // App would be denied network access since Restricted mode is on.
+ assertBackgroundNetworkAccess(false);
+ setRestrictedNetworkingMode(false);
+ // Given that Restricted mode is turned off, app should be able to access network again.
+ assertBackgroundNetworkAccess(true);
+ } finally {
+ setBatterySaverMode(false);
+ }
+ }
+}
diff --git a/hostsidetests/network-policy/app2/Android.bp b/hostsidetests/network-policy/app2/Android.bp
new file mode 100644
index 0000000..6ef0b06
--- /dev/null
+++ b/hostsidetests/network-policy/app2/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkPolicyTestsApp2",
+ defaults: ["cts_support_defaults"],
+ platform_apis: true,
+ static_libs: [
+ "androidx.annotation_annotation",
+ "CtsHostsideNetworkPolicyTestsAidl",
+ "modules-utils-build",
+ ],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ ],
+ certificate: ":cts-netpolicy-app",
+}
diff --git a/hostsidetests/network-policy/app2/AndroidManifest.xml b/hostsidetests/network-policy/app2/AndroidManifest.xml
new file mode 100644
index 0000000..668f2da
--- /dev/null
+++ b/hostsidetests/network-policy/app2/AndroidManifest.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.netpolicy.hostside.app2">
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
+ <!--
+ This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
+ them in a shared preferences which is then read by the test app. These broadcasts are
+ handled by 2 listeners, one defined the manifest and another dynamically registered by
+ a service.
+
+ The manifest-defined listener also handles ordered broadcasts used to share data with the
+ test app.
+
+ This application also provides a service, RemoteSocketFactoryService, that the test app can
+ use to open sockets to remote hosts as a different user ID.
+ -->
+ <application android:usesCleartextTraffic="true"
+ android:testOnly="true"
+ android:debuggable="true">
+
+ <activity android:name=".MyActivity"
+ android:exported="true"/>
+ <service android:name=".MyService"
+ android:exported="true"/>
+ <service android:name=".MyForegroundService"
+ android:foregroundServiceType="specialUse"
+ android:exported="true">
+ <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
+ android:value="Connectivity" />
+ </service>
+ <receiver android:name=".MyBroadcastReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.GET_COUNTERS"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.CHECK_NETWORK"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.SEND_NOTIFICATION"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST"/>
+ </intent-filter>
+ </receiver>
+ <service android:name=".MyJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
+ </application>
+
+ <!--
+ Adding this to make sure that receiving the broadcast is not restricted by
+ package visibility restrictions.
+ -->
+ <queries>
+ <package android:name="android" />
+ </queries>
+
+</manifest>
diff --git a/hostsidetests/network-policy/app2/res/drawable/ic_notification.png b/hostsidetests/network-policy/app2/res/drawable/ic_notification.png
new file mode 100644
index 0000000..6ae570b
--- /dev/null
+++ b/hostsidetests/network-policy/app2/res/drawable/ic_notification.png
Binary files differ
diff --git a/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java
new file mode 100644
index 0000000..1719f9b
--- /dev/null
+++ b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside.app2;
+
+import static com.android.cts.netpolicy.hostside.INetworkStateObserver.RESULT_ERROR_OTHER;
+import static com.android.cts.netpolicy.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES;
+import static com.android.cts.netpolicy.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.cts.netpolicy.hostside.INetworkStateObserver;
+import com.android.cts.netpolicy.hostside.NetworkCheckResult;
+
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.concurrent.TimeUnit;
+
+public final class Common {
+
+ static final String TAG = "CtsNetApp2";
+
+ // Constants below must match values defined on app's
+ // AbstractRestrictBackgroundNetworkTestCase.java
+ static final String MANIFEST_RECEIVER = "ManifestReceiver";
+ static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+
+ static final String ACTION_RECEIVER_READY =
+ "com.android.cts.netpolicy.hostside.app2.action.RECEIVER_READY";
+ static final String ACTION_FINISH_ACTIVITY =
+ "com.android.cts.netpolicy.hostside.app2.action.FINISH_ACTIVITY";
+ static final String ACTION_FINISH_JOB =
+ "com.android.cts.netpolicy.hostside.app2.action.FINISH_JOB";
+ static final String ACTION_SHOW_TOAST =
+ "com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST";
+ // Copied from com.android.server.net.NetworkPolicyManagerService class
+ static final String ACTION_SNOOZE_WARNING =
+ "com.android.server.net.action.SNOOZE_WARNING";
+
+ private static final String DEFAULT_TEST_URL =
+ "https://connectivitycheck.android.com/generate_204";
+
+ static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
+ static final String NOTIFICATION_TYPE_DELETE = "DELETE";
+ static final String NOTIFICATION_TYPE_FULL_SCREEN = "FULL_SCREEN";
+ static final String NOTIFICATION_TYPE_BUNDLE = "BUNDLE";
+ static final String NOTIFICATION_TYPE_ACTION = "ACTION";
+ static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
+ static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
+
+ static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
+ static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+ static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
+ static final String KEY_CUSTOM_URL = TEST_PKG + ".custom_url";
+
+ static final int TYPE_COMPONENT_ACTIVTY = 0;
+ static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
+ static final int TYPE_COMPONENT_EXPEDITED_JOB = 2;
+ private static final int NETWORK_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(10);
+
+ static int getUid(Context context) {
+ final String packageName = context.getPackageName();
+ try {
+ return context.getPackageManager().getPackageUid(packageName, 0);
+ } catch (NameNotFoundException e) {
+ throw new IllegalStateException("Could not get UID for " + packageName, e);
+ }
+ }
+
+ private static NetworkCheckResult createNetworkCheckResult(boolean connected, String details,
+ NetworkInfo networkInfo) {
+ final NetworkCheckResult checkResult = new NetworkCheckResult();
+ checkResult.connected = connected;
+ checkResult.details = details;
+ checkResult.networkInfo = networkInfo;
+ return checkResult;
+ }
+
+ private static boolean validateComponentState(Context context, int componentType,
+ INetworkStateObserver observer) throws RemoteException {
+ final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+ switch (componentType) {
+ case TYPE_COMPONENT_ACTIVTY: {
+ final int procState = activityManager.getUidProcessState(Process.myUid());
+ if (procState != ActivityManager.PROCESS_STATE_TOP) {
+ observer.onNetworkStateChecked(RESULT_ERROR_UNEXPECTED_PROC_STATE,
+ createNetworkCheckResult(false, "Unexpected procstate: " + procState,
+ null));
+ return false;
+ }
+ return true;
+ }
+ case TYPE_COMPONENT_FOREGROUND_SERVICE: {
+ final int procState = activityManager.getUidProcessState(Process.myUid());
+ if (procState != ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ observer.onNetworkStateChecked(RESULT_ERROR_UNEXPECTED_PROC_STATE,
+ createNetworkCheckResult(false, "Unexpected procstate: " + procState,
+ null));
+ return false;
+ }
+ return true;
+ }
+ case TYPE_COMPONENT_EXPEDITED_JOB: {
+ final int capabilities = activityManager.getUidProcessCapabilities(Process.myUid());
+ if ((capabilities
+ & ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) == 0) {
+ observer.onNetworkStateChecked(RESULT_ERROR_UNEXPECTED_CAPABILITIES,
+ createNetworkCheckResult(false,
+ "Unexpected capabilities: " + capabilities, null));
+ return false;
+ }
+ return true;
+ }
+ default: {
+ observer.onNetworkStateChecked(RESULT_ERROR_OTHER,
+ createNetworkCheckResult(false, "Unknown component type: " + componentType,
+ null));
+ return false;
+ }
+ }
+ }
+
+ static void notifyNetworkStateObserver(Context context, Intent intent, int componentType) {
+ if (intent == null) {
+ return;
+ }
+ final Bundle extras = intent.getExtras();
+ notifyNetworkStateObserver(context, extras, componentType);
+ }
+
+ static void notifyNetworkStateObserver(Context context, Bundle extras, int componentType) {
+ if (extras == null) {
+ return;
+ }
+ final INetworkStateObserver observer = INetworkStateObserver.Stub.asInterface(
+ extras.getBinder(KEY_NETWORK_STATE_OBSERVER));
+ if (observer != null) {
+ final String customUrl = extras.getString(KEY_CUSTOM_URL);
+ try {
+ final boolean skipValidation = extras.getBoolean(KEY_SKIP_VALIDATION_CHECKS);
+ if (!skipValidation && !validateComponentState(context, componentType, observer)) {
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error occurred while informing the validation result: " + e);
+ }
+ AsyncTask.execute(() -> {
+ try {
+ observer.onNetworkStateChecked(
+ INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED,
+ checkNetworkStatus(context, customUrl));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error occurred while notifying the observer: " + e);
+ }
+ });
+ }
+ }
+
+ /**
+ * Checks whether the network is available by attempting a connection to the given address
+ * and returns a {@link NetworkCheckResult} object containing all the relevant details for
+ * debugging. Uses a default address if the given address is {@code null}.
+ *
+ * <p>
+ * The returned object has the following fields:
+ *
+ * <ul>
+ * <li>{@code connected}: whether or not the connection was successful.
+ * <li>{@code networkInfo}: the {@link NetworkInfo} describing the current active network as
+ * visible to this app.
+ * <li>{@code details}: A human readable string giving useful information about the success or
+ * failure.
+ * </ul>
+ */
+ static NetworkCheckResult checkNetworkStatus(Context context, String customUrl) {
+ final String address = (customUrl == null) ? DEFAULT_TEST_URL : customUrl;
+
+ // The current Android DNS resolver returns an UnknownHostException whenever network access
+ // is blocked. This can get cached in the current process-local InetAddress cache. Clearing
+ // the cache before attempting a connection ensures we never report a failure due to a
+ // negative cache entry.
+ InetAddress.clearDnsCache();
+
+ final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+
+ final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+ Log.d(TAG, "Running checkNetworkStatus() on thread "
+ + Thread.currentThread().getName() + " for UID " + getUid(context)
+ + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
+ boolean checkStatus = false;
+ String checkDetails = "N/A";
+ try {
+ final URL url = new URL(address);
+ final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(NETWORK_TIMEOUT_MS);
+ conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2);
+ conn.setRequestMethod("GET");
+ conn.connect();
+ final int response = conn.getResponseCode();
+ checkStatus = true;
+ checkDetails = "HTTP response for " + address + ": " + response;
+ } catch (Exception e) {
+ checkStatus = false;
+ checkDetails = "Exception getting " + address + ": " + e;
+ }
+ final NetworkCheckResult result = createNetworkCheckResult(checkStatus, checkDetails,
+ networkInfo);
+ Log.d(TAG, "Offering: " + result);
+ return result;
+ }
+}
diff --git a/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java
new file mode 100644
index 0000000..d274c509
--- /dev/null
+++ b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside.app2;
+
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
+import static com.android.cts.netpolicy.hostside.app2.Common.TYPE_COMPONENT_ACTIVTY;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.Log;
+import android.view.WindowManager;
+
+import androidx.annotation.GuardedBy;
+
+/**
+ * Activity used to bring process to foreground.
+ */
+public class MyActivity extends Activity {
+
+ @GuardedBy("this")
+ private BroadcastReceiver finishCommandReceiver = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(TAG, "MyActivity.onCreate()");
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ @Override
+ public void finish() {
+ synchronized (this) {
+ if (finishCommandReceiver != null) {
+ unregisterReceiver(finishCommandReceiver);
+ finishCommandReceiver = null;
+ }
+ }
+ super.finish();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "MyActivity.onStart()");
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Log.d(TAG, "MyActivity.onNewIntent()");
+ setIntent(intent);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.d(TAG, "MyActivity.onResume(): " + getIntent());
+ Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
+ synchronized (this) {
+ finishCommandReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Finishing MyActivity");
+ MyActivity.this.finish();
+ }
+ };
+ registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY),
+ Context.RECEIVER_EXPORTED);
+ }
+ final RemoteCallback callback = getIntent().getParcelableExtra(
+ Intent.EXTRA_REMOTE_CALLBACK);
+ if (callback != null) {
+ callback.sendResult(null);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "MyActivity.onDestroy()");
+ super.onDestroy();
+ }
+}
diff --git a/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java
new file mode 100644
index 0000000..27aec8c
--- /dev/null
+++ b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.hostside.app2;
+
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SHOW_TOAST;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SNOOZE_WARNING;
+import static com.android.cts.netpolicy.hostside.app2.Common.MANIFEST_RECEIVER;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_BUNDLE;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_REMOTE_INPUT;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_BUNDLE;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_CONTENT;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_DELETE;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_FULL_SCREEN;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Receiver used to:
+ * <ol>
+ * <li>Count number of {@code RESTRICT_BACKGROUND_CHANGED} broadcasts received.
+ * <li>Show a toast.
+ * </ol>
+ */
+public class MyBroadcastReceiver extends BroadcastReceiver {
+
+ private final String mName;
+
+ public MyBroadcastReceiver() {
+ this(MANIFEST_RECEIVER);
+ }
+
+ MyBroadcastReceiver(String name) {
+ Log.d(TAG, "Constructing MyBroadcastReceiver named " + name);
+ mName = name;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive() for " + mName + ": " + intent);
+ final String action = intent.getAction();
+ switch (action) {
+ case ACTION_SNOOZE_WARNING:
+ increaseCounter(context, action);
+ break;
+ case ACTION_RESTRICT_BACKGROUND_CHANGED:
+ increaseCounter(context, action);
+ break;
+ case ACTION_RECEIVER_READY:
+ final String message = mName + " is ready to rumble";
+ Log.d(TAG, message);
+ setResultData(message);
+ break;
+ case ACTION_SHOW_TOAST:
+ showToast(context);
+ break;
+ default:
+ Log.e(TAG, "received unexpected action: " + action);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "[MyBroadcastReceiver: mName=" + mName + "]";
+ }
+
+ private void increaseCounter(Context context, String action) {
+ final SharedPreferences prefs = context.getApplicationContext()
+ .getSharedPreferences(mName, Context.MODE_PRIVATE);
+ final int value = prefs.getInt(action, 0) + 1;
+ Log.d(TAG, "increaseCounter('" + action + "'): setting '" + mName + "' to " + value);
+ prefs.edit().putInt(action, value).apply();
+ }
+
+ static int getCounter(Context context, String action, String receiverName) {
+ final SharedPreferences prefs = context.getSharedPreferences(receiverName,
+ Context.MODE_PRIVATE);
+ final int value = prefs.getInt(action, 0);
+ Log.d(TAG, "getCounter('" + action + "', '" + receiverName + "'): " + value);
+ return value;
+ }
+
+ static String getRestrictBackgroundStatus(Context context) {
+ final ConnectivityManager cm = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ final int apiStatus = cm.getRestrictBackgroundStatus();
+ Log.d(TAG, "getRestrictBackgroundStatus: returning " + apiStatus);
+ return String.valueOf(apiStatus);
+ }
+
+ /**
+ * Sends a system notification containing actions with pending intents to launch the app's
+ * main activitiy or service.
+ */
+ static void sendNotification(Context context, String channelId, int notificationId,
+ String notificationType ) {
+ Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType);
+ final Intent serviceIntent = new Intent(context, MyService.class);
+ final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent,
+ PendingIntent.FLAG_MUTABLE);
+ final Bundle bundle = new Bundle();
+ bundle.putCharSequence("parcelable", "I am not");
+
+ final Notification.Builder builder = new Notification.Builder(context, channelId)
+ .setSmallIcon(R.drawable.ic_notification);
+
+ Action action = null;
+ switch (notificationType) {
+ case NOTIFICATION_TYPE_CONTENT:
+ builder
+ .setContentTitle("Light, Cameras...")
+ .setContentIntent(pendingIntent);
+ break;
+ case NOTIFICATION_TYPE_DELETE:
+ builder.setDeleteIntent(pendingIntent);
+ break;
+ case NOTIFICATION_TYPE_FULL_SCREEN:
+ builder.setFullScreenIntent(pendingIntent, true);
+ break;
+ case NOTIFICATION_TYPE_BUNDLE:
+ bundle.putParcelable("Magnum P.I. (Pending Intent)", pendingIntent);
+ builder.setExtras(bundle);
+ break;
+ case NOTIFICATION_TYPE_ACTION:
+ action = new Action.Builder(
+ R.drawable.ic_notification, "ACTION", pendingIntent)
+ .build();
+ builder.addAction(action);
+ break;
+ case NOTIFICATION_TYPE_ACTION_BUNDLE:
+ bundle.putParcelable("Magnum A.P.I. (Action Pending Intent)", pendingIntent);
+ action = new Action.Builder(
+ R.drawable.ic_notification, "ACTION WITH BUNDLE", null)
+ .addExtras(bundle)
+ .build();
+ builder.addAction(action);
+ break;
+ case NOTIFICATION_TYPE_ACTION_REMOTE_INPUT:
+ bundle.putParcelable("Magnum R.I. (Remote Input)", null);
+ final RemoteInput remoteInput = new RemoteInput.Builder("RI")
+ .addExtras(bundle)
+ .build();
+ action = new Action.Builder(
+ R.drawable.ic_notification, "ACTION WITH REMOTE INPUT", pendingIntent)
+ .addRemoteInput(remoteInput)
+ .build();
+ builder.addAction(action);
+ break;
+ default:
+ Log.e(TAG, "Unknown notification type: " + notificationType);
+ return;
+ }
+
+ final Notification notification = builder.build();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .notify(notificationId, notification);
+ }
+
+ private void showToast(Context context) {
+ Toast.makeText(context, "Toast from CTS test", Toast.LENGTH_SHORT).show();
+ setResultData("Shown");
+ }
+}
diff --git a/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java
new file mode 100644
index 0000000..54cee3c
--- /dev/null
+++ b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside.app2;
+
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
+import static com.android.cts.netpolicy.hostside.app2.Common.TEST_PKG;
+import static com.android.cts.netpolicy.hostside.app2.Common.TYPE_COMPONENT_FOREGROUND_SERVICE;
+
+import android.R;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.cts.netpolicy.hostside.INetworkStateObserver;
+
+/**
+ * Service used to change app state to FOREGROUND_SERVICE.
+ */
+public class MyForegroundService extends Service {
+ private static final String NOTIFICATION_CHANNEL_ID = "cts/MyForegroundService";
+ private static final int FLAG_START_FOREGROUND = 1;
+ private static final int FLAG_STOP_FOREGROUND = 2;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.v(TAG, "MyForegroundService.onStartCommand(): " + intent);
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(new NotificationChannel(
+ NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
+ NotificationManager.IMPORTANCE_DEFAULT));
+ switch (intent.getFlags()) {
+ case FLAG_START_FOREGROUND:
+ Log.d(TAG, "Starting foreground");
+ startForeground(42, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
+ .build());
+ Common.notifyNetworkStateObserver(this, intent, TYPE_COMPONENT_FOREGROUND_SERVICE);
+ break;
+ case FLAG_STOP_FOREGROUND:
+ Log.d(TAG, "Stopping foreground");
+ stopForeground(true);
+ break;
+ default:
+ Log.wtf(TAG, "Invalid flag on intent " + intent);
+ }
+ return START_STICKY;
+ }
+}
diff --git a/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java
new file mode 100644
index 0000000..eba55ed
--- /dev/null
+++ b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside.app2;
+
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_FINISH_JOB;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
+import static com.android.cts.netpolicy.hostside.app2.Common.TYPE_COMPONENT_EXPEDITED_JOB;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+public class MyJobService extends JobService {
+
+ private BroadcastReceiver mFinishCommandReceiver = null;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.v(TAG, "MyJobService.onCreate()");
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Log.v(TAG, "MyJobService.onStartJob()");
+ Common.notifyNetworkStateObserver(this, params.getTransientExtras(),
+ TYPE_COMPONENT_EXPEDITED_JOB);
+ mFinishCommandReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.v(TAG, "Finishing MyJobService");
+ try {
+ jobFinished(params, /*wantsReschedule=*/ false);
+ } finally {
+ if (mFinishCommandReceiver != null) {
+ unregisterReceiver(mFinishCommandReceiver);
+ mFinishCommandReceiver = null;
+ }
+ }
+ }
+ };
+ registerReceiver(mFinishCommandReceiver, new IntentFilter(ACTION_FINISH_JOB),
+ Context.RECEIVER_EXPORTED);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ // If this job is stopped before it had a chance to send network status via
+ // INetworkStateObserver, the test will fail. It could happen either due to test timing out
+ // or this app moving to a lower proc_state and losing network access.
+ Log.v(TAG, "MyJobService.onStopJob()");
+ if (mFinishCommandReceiver != null) {
+ unregisterReceiver(mFinishCommandReceiver);
+ mFinishCommandReceiver = null;
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.v(TAG, "MyJobService.onDestroy()");
+ }
+}
diff --git a/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java
new file mode 100644
index 0000000..71bcead
--- /dev/null
+++ b/hostsidetests/network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy.hostside.app2;
+
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SNOOZE_WARNING;
+import static com.android.cts.netpolicy.hostside.app2.Common.DYNAMIC_RECEIVER;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.cts.netpolicy.hostside.IMyService;
+import com.android.cts.netpolicy.hostside.INetworkCallback;
+import com.android.cts.netpolicy.hostside.NetworkCheckResult;
+import com.android.modules.utils.build.SdkLevel;
+
+/**
+ * Service used to dynamically register a broadcast receiver.
+ */
+public class MyService extends Service {
+ private static final String NOTIFICATION_CHANNEL_ID = "MyService";
+
+ ConnectivityManager mCm;
+
+ private MyBroadcastReceiver mReceiver;
+ private ConnectivityManager.NetworkCallback mNetworkCallback;
+
+ // TODO: move MyBroadcast static functions here - they were kept there to make git diff easier.
+
+ private IMyService.Stub mBinder = new IMyService.Stub() {
+ @Override
+ public void registerBroadcastReceiver() {
+ if (mReceiver != null) {
+ Log.d(TAG, "receiver already registered: " + mReceiver);
+ return;
+ }
+ final Context context = getApplicationContext();
+ final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0;
+ mReceiver = new MyBroadcastReceiver(DYNAMIC_RECEIVER);
+ context.registerReceiver(mReceiver,
+ new IntentFilter(ACTION_RECEIVER_READY), flags);
+ context.registerReceiver(mReceiver,
+ new IntentFilter(ACTION_RESTRICT_BACKGROUND_CHANGED), flags);
+ context.registerReceiver(mReceiver,
+ new IntentFilter(ACTION_SNOOZE_WARNING), flags);
+ Log.d(TAG, "receiver registered");
+ }
+
+ @Override
+ public int getCounters(String receiverName, String action) {
+ return MyBroadcastReceiver.getCounter(getApplicationContext(), action, receiverName);
+ }
+
+ @Override
+ public NetworkCheckResult checkNetworkStatus(String customUrl) {
+ return Common.checkNetworkStatus(getApplicationContext(), customUrl);
+ }
+
+ @Override
+ public String getRestrictBackgroundStatus() {
+ return MyBroadcastReceiver.getRestrictBackgroundStatus(getApplicationContext());
+ }
+
+ @Override
+ public void sendNotification(int notificationId, String notificationType) {
+ MyBroadcastReceiver.sendNotification(getApplicationContext(), NOTIFICATION_CHANNEL_ID,
+ notificationId, notificationType);
+ }
+
+ @Override
+ public void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb) {
+ if (mNetworkCallback != null) {
+ Log.d(TAG, "unregister previous network callback: " + mNetworkCallback);
+ unregisterNetworkCallback();
+ }
+ Log.d(TAG, "registering network callback for " + request);
+
+ mNetworkCallback = new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onBlockedStatusChanged(Network network, boolean blocked) {
+ try {
+ cb.onBlockedStatusChanged(network, blocked);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Cannot send onBlockedStatusChanged: " + e);
+ unregisterNetworkCallback();
+ }
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ try {
+ cb.onAvailable(network);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Cannot send onAvailable: " + e);
+ unregisterNetworkCallback();
+ }
+ }
+
+ @Override
+ public void onLost(Network network) {
+ try {
+ cb.onLost(network);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Cannot send onLost: " + e);
+ 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(request, mNetworkCallback);
+ try {
+ cb.asBinder().linkToDeath(() -> unregisterNetworkCallback(), 0);
+ } catch (RemoteException e) {
+ unregisterNetworkCallback();
+ }
+ }
+
+ @Override
+ public void unregisterNetworkCallback() {
+ Log.d(TAG, "unregistering network callback");
+ if (mNetworkCallback != null) {
+ mCm.unregisterNetworkCallback(mNetworkCallback);
+ mNetworkCallback = null;
+ }
+ }
+
+ @Override
+ public int scheduleJob(JobInfo jobInfo) {
+ final JobScheduler jobScheduler = getApplicationContext()
+ .getSystemService(JobScheduler.class);
+ return jobScheduler.schedule(jobInfo);
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public void onCreate() {
+ final Context context = getApplicationContext();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT));
+ mCm = (ConnectivityManager) getApplicationContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ @Override
+ public void onDestroy() {
+ final Context context = getApplicationContext();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
+ if (mReceiver != null) {
+ Log.d(TAG, "onDestroy(): unregistering " + mReceiver);
+ getApplicationContext().unregisterReceiver(mReceiver);
+ }
+
+ super.onDestroy();
+ }
+}
diff --git a/hostsidetests/network-policy/certs/Android.bp b/hostsidetests/network-policy/certs/Android.bp
new file mode 100644
index 0000000..bfbc341
--- /dev/null
+++ b/hostsidetests/network-policy/certs/Android.bp
@@ -0,0 +1,9 @@
+package {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app_certificate {
+ name: "cts-netpolicy-app",
+ certificate: "cts-net-app",
+}
diff --git a/hostsidetests/network-policy/certs/README b/hostsidetests/network-policy/certs/README
new file mode 100644
index 0000000..b660a82
--- /dev/null
+++ b/hostsidetests/network-policy/certs/README
@@ -0,0 +1,2 @@
+# Generated with:
+development/tools/make_key cts-net-app '/CN=cts-net-app'
diff --git a/hostsidetests/network-policy/certs/cts-net-app.pk8 b/hostsidetests/network-policy/certs/cts-net-app.pk8
new file mode 100644
index 0000000..1703e4e
--- /dev/null
+++ b/hostsidetests/network-policy/certs/cts-net-app.pk8
Binary files differ
diff --git a/hostsidetests/network-policy/certs/cts-net-app.x509.pem b/hostsidetests/network-policy/certs/cts-net-app.x509.pem
new file mode 100644
index 0000000..a15ff48
--- /dev/null
+++ b/hostsidetests/network-policy/certs/cts-net-app.x509.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAeqgAwIBAgIJAMhWwIIqr1r6MA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV
+BAMMC2N0cy1uZXQtYXBwMB4XDTE4MDYyMDAyMjAwN1oXDTQ1MTEwNTAyMjAwN1ow
+FjEUMBIGA1UEAwwLY3RzLW5ldC1hcHAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDefOayWQss1E+FQIONK6IhlXhe0BEyHshIrnPOOmuCPa/Svfbnmziy
+hr1KTjaQ3ET/mGShwlt6AUti7nKx9aB71IJp5mSBuwW62A8jvN3yNOo45YV8+n1o
+TrEoMWMf7hQmoOSqaSJ+VFuVms/kPSEh99okDgHCej6rsEkEcDoh6pJajQyUYDwR
+SNAF8SrqCDhqFbZW/LWedvuikCUlNtzuv7/GrcLcsiWEfHv7UOBKpMjLo9BhD1XF
+IefnxImcBQrQGMnE9TLixBiEeX5yauLgbZuxBqD/zsI2TH1FjxTeuJan83kLbqqH
+FgyvPaUjwckAdQPyom7ZUYFnBc0LQ9xzAgMBAAGjUzBRMB0GA1UdDgQWBBRZrBEw
+tAB2WNXj8dQ7ZOuJ34kY5DAfBgNVHSMEGDAWgBRZrBEwtAB2WNXj8dQ7ZOuJ34kY
+5DAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQDeI9AnLW6l/39y
+z96w/ldxZVFPzBRiFIsJsPHVyXlD5vUHZv/ju2jFn8TZSZR5TK0bzCEoVLp34Sho
+bbS0magP82yIvCRibyoyD+TDNnZkNJwjYnikE+/oyshTSQtpkn/rDA+0Y09BUC1E
+N2I6bV9pTXLFg7oah2FmqPRPzhgeYUKENgOQkrrjUCn6y0i/k374n7aftzdniSIz
+2kCRVEeN9gws6CnoMPx0vr32v/JVuPV6zfdJYadgj/eFRyTNE4msd9kE82Wc46eU
+YiI+LuXZ3ZMUNWGY7MK2pOUUS52JsBQ3K235dA5WaU4x8OBlY/WkNYX/eLbNs5jj
+FzLmhZZ1
+-----END CERTIFICATE-----
diff --git a/hostsidetests/network-policy/instrumentation_arguments/Android.bp b/hostsidetests/network-policy/instrumentation_arguments/Android.bp
new file mode 100644
index 0000000..cdede36
--- /dev/null
+++ b/hostsidetests/network-policy/instrumentation_arguments/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "ArgumentConstants",
+ srcs: ["src/**/*.java"],
+}
diff --git a/hostsidetests/network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java b/hostsidetests/network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java
new file mode 100644
index 0000000..0fe98e9
--- /dev/null
+++ b/hostsidetests/network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy.arguments;
+
+public interface InstrumentationArguments {
+ String ARG_WAIVE_BIND_PRIORITY = "waive_bind_priority";
+ String ARG_CONNECTION_CHECK_CUSTOM_URL = "connection_check_custom_url";
+}
diff --git a/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java
new file mode 100644
index 0000000..422231d
--- /dev/null
+++ b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy;
+
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+
+import android.platform.test.annotations.FlakyTest;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+
+import org.junit.Test;
+
+import java.util.Map;
+
+@FlakyTest(bugId = 288324467)
+public class HostsideConnOnActivityStartTest extends HostsideNetworkPolicyTestCase {
+ private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest";
+
+ @BeforeClassWithInfo
+ public static void setUpOnce(TestInformation testInfo) throws Exception {
+ uninstallPackage(testInfo, TEST_APP2_PKG, false);
+ installPackage(testInfo, TEST_APP2_APK);
+ }
+
+ @AfterClassWithInfo
+ public static void tearDownOnce(TestInformation testInfo) throws DeviceNotAvailableException {
+ uninstallPackage(testInfo, TEST_APP2_PKG, true);
+ }
+
+ @Test
+ public void testStartActivity_batterySaver() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_batterySaver");
+ }
+
+ @Test
+ public void testStartActivity_dataSaver() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_dataSaver");
+ }
+
+ @FlakyTest(bugId = 231440256)
+ @Test
+ public void testStartActivity_doze() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_doze");
+ }
+
+ @Test
+ public void testStartActivity_appStandby() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_appStandby");
+ }
+
+ // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
+ @Test
+ public void testStartActivity_default() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_CLASS, "testStartActivity_default",
+ Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+ }
+}
diff --git a/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java
new file mode 100644
index 0000000..62952bb
--- /dev/null
+++ b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy;
+
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+
+import android.platform.test.annotations.FlakyTest;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+// TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side tests.
+@FlakyTest(bugId = 288324467)
+public class HostsideDefaultNetworkRestrictionsTests extends HostsideNetworkPolicyTestCase {
+ private static final String METERED_TEST_CLASS = TEST_PKG + ".DefaultRestrictionsMeteredTest";
+ private static final String NON_METERED_TEST_CLASS =
+ TEST_PKG + ".DefaultRestrictionsNonMeteredTest";
+
+ @Before
+ public void setUp() throws Exception {
+ uninstallPackage(TEST_APP2_PKG, false);
+ installPackage(TEST_APP2_APK);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uninstallPackage(TEST_APP2_PKG, true);
+ }
+
+ private void runMeteredTest(String methodName) throws DeviceNotAvailableException {
+ runDeviceTestsWithCustomOptions(TEST_PKG, METERED_TEST_CLASS, methodName,
+ Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+ }
+
+ private void runNonMeteredTest(String methodName) throws DeviceNotAvailableException {
+ runDeviceTestsWithCustomOptions(TEST_PKG, NON_METERED_TEST_CLASS, methodName,
+ Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+ }
+
+ @Test
+ public void testMeteredNetworkAccess_defaultRestrictions_testActivityNetworkAccess()
+ throws Exception {
+ runMeteredTest("testActivityNetworkAccess");
+ }
+
+ @Test
+ public void testMeteredNetworkAccess_defaultRestrictions_testFgsNetworkAccess()
+ throws Exception {
+ runMeteredTest("testFgsNetworkAccess");
+ }
+
+ @Test
+ public void testMeteredNetworkAccess_defaultRestrictions_inFullAllowlist() throws Exception {
+ runMeteredTest("testBackgroundNetworkAccess_inFullAllowlist");
+ }
+
+ @Test
+ public void testMeteredNetworkAccess_defaultRestrictions_inExceptIdleAllowlist()
+ throws Exception {
+ runMeteredTest("testBackgroundNetworkAccess_inExceptIdleAllowlist");
+ }
+
+ @Test
+ public void testNonMeteredNetworkAccess_defaultRestrictions_testActivityNetworkAccess()
+ throws Exception {
+ runNonMeteredTest("testActivityNetworkAccess");
+ }
+
+ @Test
+ public void testNonMeteredNetworkAccess_defaultRestrictions_testFgsNetworkAccess()
+ throws Exception {
+ runNonMeteredTest("testFgsNetworkAccess");
+ }
+
+ @Test
+ public void testNonMeteredNetworkAccess_defaultRestrictions_inFullAllowlist() throws Exception {
+ runNonMeteredTest("testBackgroundNetworkAccess_inFullAllowlist");
+ }
+
+ @Test
+ public void testNonMeteredNetworkAccess_defaultRestrictions_inExceptIdleAllowlist()
+ throws Exception {
+ runNonMeteredTest("testBackgroundNetworkAccess_inExceptIdleAllowlist");
+ }
+}
diff --git a/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java
new file mode 100644
index 0000000..2c2b118
--- /dev/null
+++ b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.netpolicy;
+
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+
+import android.platform.test.annotations.FlakyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+@FlakyTest(bugId = 288324467)
+public class HostsideNetworkCallbackTests extends HostsideNetworkPolicyTestCase {
+
+ @Before
+ public void setUp() throws Exception {
+ uninstallPackage(TEST_APP2_PKG, false);
+ installPackage(TEST_APP2_APK);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uninstallPackage(TEST_APP2_PKG, true);
+ }
+
+ @Test
+ public void testOnBlockedStatusChanged_dataSaver() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG,
+ TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_dataSaver");
+ }
+
+ @Test
+ public void testOnBlockedStatusChanged_powerSaver() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG,
+ TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_powerSaver");
+ }
+
+ // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
+ @Test
+ public void testOnBlockedStatusChanged_default() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".NetworkCallbackTest",
+ "testOnBlockedStatusChanged_default", Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+ }
+}
+
diff --git a/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java
new file mode 100644
index 0000000..8ffe360
--- /dev/null
+++ b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy;
+
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+public class HostsideNetworkPolicyManagerTests extends HostsideNetworkPolicyTestCase {
+ @Before
+ public void setUp() throws Exception {
+ uninstallPackage(TEST_APP2_PKG, false);
+ installPackage(TEST_APP2_APK);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uninstallPackage(TEST_APP2_PKG, true);
+ }
+
+ @Test
+ public void testIsUidNetworkingBlocked_withUidNotBlocked() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG,
+ TEST_PKG + ".NetworkPolicyManagerTest",
+ "testIsUidNetworkingBlocked_withUidNotBlocked");
+ }
+
+ @Test
+ public void testIsUidNetworkingBlocked_withSystemUid() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG,
+ TEST_PKG + ".NetworkPolicyManagerTest", "testIsUidNetworkingBlocked_withSystemUid");
+ }
+
+ @Test
+ public void testIsUidNetworkingBlocked_withDataSaverMode() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG,
+ TEST_PKG + ".NetworkPolicyManagerTest",
+ "testIsUidNetworkingBlocked_withDataSaverMode");
+ }
+
+ @Test
+ public void testIsUidNetworkingBlocked_withRestrictedNetworkingMode() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG,
+ TEST_PKG + ".NetworkPolicyManagerTest",
+ "testIsUidNetworkingBlocked_withRestrictedNetworkingMode");
+ }
+
+ @Test
+ public void testIsUidNetworkingBlocked_withPowerSaverMode() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG,
+ TEST_PKG + ".NetworkPolicyManagerTest",
+ "testIsUidNetworkingBlocked_withPowerSaverMode");
+ }
+
+ @Test
+ public void testIsUidRestrictedOnMeteredNetworks() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG,
+ TEST_PKG + ".NetworkPolicyManagerTest", "testIsUidRestrictedOnMeteredNetworks");
+ }
+
+ // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
+ @Test
+ public void testIsUidNetworkingBlocked_whenInBackground() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".NetworkPolicyManagerTest",
+ "testIsUidNetworkingBlocked_whenInBackground",
+ Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+ }
+}
diff --git a/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java
new file mode 100644
index 0000000..6de6b17
--- /dev/null
+++ b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy;
+
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.targetprep.BuildError;
+import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.targetprep.suite.SuiteApkInstaller;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import com.android.tradefed.util.RunUtil;
+
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+abstract class HostsideNetworkPolicyTestCase extends BaseHostJUnit4Test {
+ protected static final boolean DEBUG = false;
+ protected static final String TAG = "HostsideNetworkPolicyTests";
+ protected static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
+ protected static final String TEST_APK = "CtsHostsideNetworkPolicyTestsApp.apk";
+ protected static final String TEST_APP2_PKG = "com.android.cts.netpolicy.hostside.app2";
+ protected static final String TEST_APP2_APK = "CtsHostsideNetworkPolicyTestsApp2.apk";
+
+ @Option(name = "custom-url", importance = Option.Importance.IF_UNSET,
+ description = "A custom url to use for testing network connections")
+ protected String mCustomUrl;
+
+ @BeforeClassWithInfo
+ public static void setUpOnceBase(TestInformation testInfo) throws Exception {
+ uninstallPackage(testInfo, TEST_PKG, false);
+ installPackage(testInfo, TEST_APK);
+ }
+
+ @AfterClassWithInfo
+ public static void tearDownOnceBase(TestInformation testInfo)
+ throws DeviceNotAvailableException {
+ uninstallPackage(testInfo, TEST_PKG, true);
+ }
+
+ // Custom static method to install the specified package, this is used to bypass auto-cleanup
+ // per test in BaseHostJUnit4.
+ protected static void installPackage(TestInformation testInfo, String apk)
+ throws DeviceNotAvailableException, TargetSetupError {
+ assertNotNull(testInfo);
+ final int userId = testInfo.getDevice().getCurrentUser();
+ final SuiteApkInstaller installer = new SuiteApkInstaller();
+ // Force the apk clean up
+ installer.setCleanApk(true);
+ installer.addTestFileName(apk);
+ installer.setUserId(userId);
+ installer.setShouldGrantPermission(true);
+ installer.addInstallArg("-t");
+ try {
+ installer.setUp(testInfo);
+ } catch (BuildError e) {
+ throw new TargetSetupError(
+ e.getMessage(), e, testInfo.getDevice().getDeviceDescriptor(), e.getErrorId());
+ }
+ }
+
+ protected void installPackage(String apk) throws DeviceNotAvailableException, TargetSetupError {
+ installPackage(getTestInformation(), apk);
+ }
+
+ protected static void uninstallPackage(TestInformation testInfo, String packageName,
+ boolean shouldSucceed)
+ throws DeviceNotAvailableException {
+ assertNotNull(testInfo);
+ final String result = testInfo.getDevice().uninstallPackage(packageName);
+ if (shouldSucceed) {
+ assertNull("uninstallPackage(" + packageName + ") failed: " + result, result);
+ }
+ }
+
+ protected void uninstallPackage(String packageName,
+ boolean shouldSucceed)
+ throws DeviceNotAvailableException {
+ uninstallPackage(getTestInformation(), packageName, shouldSucceed);
+ }
+
+ protected void assertPackageUninstalled(String packageName) throws DeviceNotAvailableException {
+ final String command = "cmd package list packages " + packageName;
+ final int max_tries = 5;
+ for (int i = 1; i <= max_tries; i++) {
+ final String result = runCommand(command);
+ if (result.trim().isEmpty()) {
+ return;
+ }
+ // 'list packages' filters by substring, so we need to iterate with the results
+ // and check one by one, otherwise 'com.android.cts.netpolicy.hostside' could return
+ // 'com.android.cts.netpolicy.hostside.app2'
+ boolean found = false;
+ for (String line : result.split("[\\r\\n]+")) {
+ if (line.endsWith(packageName)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+ Log.v(TAG, "Package " + packageName + " not uninstalled yet (" + result
+ + "); sleeping 1s before polling again");
+ RunUtil.getDefault().sleep(1000);
+ }
+ fail("Package '" + packageName + "' not uinstalled after " + max_tries + " seconds");
+ }
+
+ protected int getUid(String packageName) throws DeviceNotAvailableException {
+ final int currentUser = getDevice().getCurrentUser();
+ final String uidLines = runCommand(
+ "cmd package list packages -U --user " + currentUser + " " + packageName);
+ for (String uidLine : uidLines.split("\n")) {
+ if (uidLine.startsWith("package:" + packageName + " uid:")) {
+ final String[] uidLineParts = uidLine.split(":");
+ // 3rd entry is package uid
+ return Integer.parseInt(uidLineParts[2].trim());
+ }
+ }
+ throw new IllegalStateException("Failed to find the test app on the device; pkg="
+ + packageName + ", u=" + currentUser);
+ }
+
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className)
+ throws DeviceNotAvailableException {
+ return runDeviceTestsWithCustomOptions(packageName, className, null);
+ }
+
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className,
+ String methodName) throws DeviceNotAvailableException {
+ return runDeviceTestsWithCustomOptions(packageName, className, methodName, null);
+ }
+
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className,
+ String methodName, Map<String, String> testArgs) throws DeviceNotAvailableException {
+ final DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(packageName)
+ .setTestClassName(className)
+ .setTestMethodName(methodName);
+
+ // Currently there is only one custom option that the test exposes.
+ if (mCustomUrl != null) {
+ deviceTestRunOptions.addInstrumentationArg(ARG_CONNECTION_CHECK_CUSTOM_URL, mCustomUrl);
+ }
+ // Pass over any test specific arguments.
+ if (testArgs != null) {
+ for (Map.Entry<String, String> arg : testArgs.entrySet()) {
+ deviceTestRunOptions.addInstrumentationArg(arg.getKey(), arg.getValue());
+ }
+ }
+ return runDeviceTests(deviceTestRunOptions);
+ }
+
+ protected String runCommand(String command) throws DeviceNotAvailableException {
+ Log.d(TAG, "Command: '" + command + "'");
+ final String output = getDevice().executeShellCommand(command);
+ if (DEBUG) Log.v(TAG, "Output: " + output.trim());
+ return output;
+ }
+}
diff --git a/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java
new file mode 100644
index 0000000..0261c7d
--- /dev/null
+++ b/hostsidetests/network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.netpolicy;
+
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.SecurityTest;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.util.RunUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+@FlakyTest(bugId = 288324467)
+public class HostsideRestrictBackgroundNetworkTests extends HostsideNetworkPolicyTestCase {
+
+ @Before
+ public void setUp() throws Exception {
+ uninstallPackage(TEST_APP2_PKG, false);
+ installPackage(TEST_APP2_APK);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uninstallPackage(TEST_APP2_PKG, true);
+ }
+
+ @SecurityTest
+ @Test
+ public void testDataWarningReceiver() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataWarningReceiverTest",
+ "testSnoozeWarningNotReceived");
+ }
+
+ /**************************
+ * Data Saver Mode tests. *
+ **************************/
+
+ @Test
+ public void testDataSaverMode_disabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_disabled");
+ }
+
+ @Test
+ public void testDataSaverMode_whitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_whitelisted");
+ }
+
+ @Test
+ public void testDataSaverMode_enabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_enabled");
+ }
+
+ @Test
+ public void testDataSaverMode_blacklisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_blacklisted");
+ }
+
+ @Test
+ public void testDataSaverMode_reinstall() throws Exception {
+ final int oldUid = getUid(TEST_APP2_PKG);
+
+ // Make sure whitelist is revoked when package is removed
+ addRestrictBackgroundWhitelist(oldUid);
+
+ uninstallPackage(TEST_APP2_PKG, true);
+ assertPackageUninstalled(TEST_APP2_PKG);
+ assertRestrictBackgroundWhitelist(oldUid, false);
+
+ installPackage(TEST_APP2_APK);
+ final int newUid = getUid(TEST_APP2_PKG);
+ assertRestrictBackgroundWhitelist(oldUid, false);
+ assertRestrictBackgroundWhitelist(newUid, false);
+ }
+
+ @Test
+ public void testDataSaverMode_requiredWhitelistedPackages() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_requiredWhitelistedPackages");
+ }
+
+ @Test
+ public void testDataSaverMode_broadcastNotSentOnUnsupportedDevices() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testBroadcastNotSentOnUnsupportedDevices");
+ }
+
+ /*****************************
+ * Battery Saver Mode tests. *
+ *****************************/
+
+ @Test
+ public void testBatterySaverModeMetered_disabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ @Test
+ public void testBatterySaverModeMetered_whitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ @Test
+ public void testBatterySaverModeMetered_enabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ @Test
+ public void testBatterySaverMode_reinstall() throws Exception {
+ if (!isDozeModeEnabled()) {
+ Log.w(TAG, "testBatterySaverMode_reinstall() skipped because device does not support "
+ + "Doze Mode");
+ return;
+ }
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ uninstallPackage(TEST_APP2_PKG, true);
+ assertPackageUninstalled(TEST_APP2_PKG);
+ assertPowerSaveModeWhitelist(TEST_APP2_PKG, false);
+
+ installPackage(TEST_APP2_APK);
+ assertPowerSaveModeWhitelist(TEST_APP2_PKG, false);
+ }
+
+ @Test
+ public void testBatterySaverModeNonMetered_disabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ @Test
+ public void testBatterySaverModeNonMetered_whitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ @Test
+ public void testBatterySaverModeNonMetered_enabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ /*******************
+ * App idle tests. *
+ *******************/
+
+ @Test
+ public void testAppIdleMetered_disabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ @Test
+ public void testAppIdleMetered_whitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ @Test
+ public void testAppIdleMetered_tempWhitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_tempWhitelisted");
+ }
+
+ @Test
+ public void testAppIdleMetered_enabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ @Test
+ public void testAppIdleMetered_idleWhitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testAppIdleNetworkAccess_idleWhitelisted");
+ }
+
+ // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
+ // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
+ // public void testAppIdle_reinstall() throws Exception {
+ // }
+
+ @Test
+ public void testAppIdleNonMetered_disabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+
+ @Test
+ public void testAppIdleNonMetered_whitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ @Test
+ public void testAppIdleNonMetered_tempWhitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_tempWhitelisted");
+ }
+
+ @Test
+ public void testAppIdleNonMetered_enabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ @Test
+ public void testAppIdleNonMetered_idleWhitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testAppIdleNetworkAccess_idleWhitelisted");
+ }
+
+ @Test
+ public void testAppIdleNonMetered_whenCharging() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testAppIdleNetworkAccess_whenCharging");
+ }
+
+ @Test
+ public void testAppIdleMetered_whenCharging() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testAppIdleNetworkAccess_whenCharging");
+ }
+
+ @Test
+ public void testAppIdle_toast() throws Exception {
+ // Check that showing a toast doesn't bring an app out of standby
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testAppIdle_toast");
+ }
+
+ /********************
+ * Doze Mode tests. *
+ ********************/
+
+ @Test
+ public void testDozeModeMetered_disabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ @Test
+ public void testDozeModeMetered_whitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ @Test
+ public void testDozeModeMetered_enabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ @Test
+ public void testDozeModeMetered_enabledButWhitelistedOnNotificationAction() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ "testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
+ }
+
+ // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
+ // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
+ // public void testDozeMode_reinstall() throws Exception {
+ // }
+
+ @Test
+ public void testDozeModeNonMetered_disabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ @Test
+ public void testDozeModeNonMetered_whitelisted() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ @Test
+ public void testDozeModeNonMetered_enabled() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ @Test
+ public void testDozeModeNonMetered_enabledButWhitelistedOnNotificationAction()
+ throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ "testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
+ }
+
+ /**********************
+ * Mixed modes tests. *
+ **********************/
+
+ @Test
+ public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDataAndBatterySaverModes_meteredNetwork");
+ }
+
+ @Test
+ public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDataAndBatterySaverModes_nonMeteredNetwork");
+ }
+
+ @Test
+ public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDozeAndBatterySaverMode_powerSaveWhitelists");
+ }
+
+ @Test
+ public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDozeAndAppIdle_powerSaveWhitelists");
+ }
+
+ @Test
+ public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndDoze_tempPowerSaveWhitelists");
+ }
+
+ @Test
+ public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndBatterySaver_tempPowerSaveWhitelists");
+ }
+
+ @Test
+ public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDozeAndAppIdle_appIdleWhitelist");
+ }
+
+ @Test
+ public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists");
+ }
+
+ @Test
+ public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists");
+ }
+
+ /**************************
+ * Restricted mode tests. *
+ **************************/
+
+ @Test
+ public void testNetworkAccess_restrictedMode() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
+ "testNetworkAccess");
+ }
+
+ @Test
+ public void testNetworkAccess_restrictedMode_withBatterySaver() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
+ "testNetworkAccess_withBatterySaver");
+ }
+
+ /************************
+ * Expedited job tests. *
+ ************************/
+
+ @Test
+ public void testMeteredNetworkAccess_expeditedJob() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".ExpeditedJobMeteredTest");
+ }
+
+ @Test
+ public void testNonMeteredNetworkAccess_expeditedJob() throws Exception {
+ runDeviceTestsWithCustomOptions(TEST_PKG, TEST_PKG + ".ExpeditedJobNonMeteredTest");
+ }
+
+ /*******************
+ * Helper methods. *
+ *******************/
+
+ private void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
+ final int max_tries = 5;
+ boolean actual = false;
+ for (int i = 1; i <= max_tries; i++) {
+ final String output = runCommand("cmd netpolicy list restrict-background-whitelist ");
+ actual = output.contains(Integer.toString(uid));
+ if (expected == actual) {
+ return;
+ }
+ Log.v(TAG, "whitelist check for uid " + uid + " doesn't match yet (expected "
+ + expected + ", got " + actual + "); sleeping 1s before polling again");
+ RunUtil.getDefault().sleep(1000);
+ }
+ fail("whitelist check for uid " + uid + " failed: expected "
+ + expected + ", got " + actual);
+ }
+
+ private void assertPowerSaveModeWhitelist(String packageName, boolean expected)
+ throws Exception {
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ assertDelayedCommand("dumpsys deviceidle whitelist =" + packageName,
+ Boolean.toString(expected));
+ }
+
+ /**
+ * Asserts the result of a command, wait and re-running it a couple times if necessary.
+ */
+ private void assertDelayedCommand(String command, String expectedResult)
+ throws InterruptedException, DeviceNotAvailableException {
+ final int maxTries = 5;
+ for (int i = 1; i <= maxTries; i++) {
+ final String result = runCommand(command).trim();
+ if (result.equals(expectedResult)) return;
+ Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
+ + expectedResult + "' on attempt #; sleeping 1s before polling again");
+ RunUtil.getDefault().sleep(1000);
+ }
+ fail("Command '" + command + "' did not return '" + expectedResult + "' after " + maxTries
+ + " attempts");
+ }
+
+ protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
+ runCommand("cmd netpolicy add restrict-background-whitelist " + uid);
+ assertRestrictBackgroundWhitelist(uid, true);
+ }
+
+ private void addPowerSaveModeWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist");
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ runCommand("dumpsys deviceidle whitelist +" + packageName);
+ assertPowerSaveModeWhitelist(packageName, true);
+ }
+
+ protected boolean isDozeModeEnabled() throws Exception {
+ final String result = runCommand("cmd deviceidle enabled deep").trim();
+ return result.equals("1");
+ }
+}
diff --git a/hostsidetests/network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java b/hostsidetests/network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java
new file mode 100644
index 0000000..cbf2f4d
--- /dev/null
+++ b/hostsidetests/network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.cts.netpolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.targetprep.ITargetPreparer;
+
+public class NetworkPolicyTestsPreparer implements ITargetPreparer {
+ private ITestDevice mDevice;
+ private boolean mOriginalAirplaneModeEnabled;
+ private String mOriginalAppStandbyEnabled;
+ private String mOriginalBatteryStatsConstants;
+ private final static String KEY_STABLE_CHARGING_DELAY_MS = "battery_charged_delay_ms";
+ private final static int DESIRED_STABLE_CHARGING_DELAY_MS = 0;
+
+ @Override
+ public void setUp(TestInformation testInformation) throws DeviceNotAvailableException {
+ mDevice = testInformation.getDevice();
+ mOriginalAppStandbyEnabled = getAppStandbyEnabled();
+ setAppStandbyEnabled("1");
+ LogUtil.CLog.d("Original app_standby_enabled: " + mOriginalAppStandbyEnabled);
+
+ mOriginalBatteryStatsConstants = getBatteryStatsConstants();
+ setBatteryStatsConstants(
+ KEY_STABLE_CHARGING_DELAY_MS + "=" + DESIRED_STABLE_CHARGING_DELAY_MS);
+ LogUtil.CLog.d("Original battery_saver_constants: " + mOriginalBatteryStatsConstants);
+
+ mOriginalAirplaneModeEnabled = getAirplaneModeEnabled();
+ // Turn off airplane mode in case another test left the device in that state.
+ setAirplaneModeEnabled(false);
+ LogUtil.CLog.d("Original airplane mode state: " + mOriginalAirplaneModeEnabled);
+ }
+
+ @Override
+ public void tearDown(TestInformation testInformation, Throwable e)
+ throws DeviceNotAvailableException {
+ setAirplaneModeEnabled(mOriginalAirplaneModeEnabled);
+ setAppStandbyEnabled(mOriginalAppStandbyEnabled);
+ setBatteryStatsConstants(mOriginalBatteryStatsConstants);
+ }
+
+ private void setAirplaneModeEnabled(boolean enable) throws DeviceNotAvailableException {
+ executeCmd("cmd connectivity airplane-mode " + (enable ? "enable" : "disable"));
+ }
+
+ private boolean getAirplaneModeEnabled() throws DeviceNotAvailableException {
+ return "enabled".equals(executeCmd("cmd connectivity airplane-mode").trim());
+ }
+
+ private void setAppStandbyEnabled(String appStandbyEnabled) throws DeviceNotAvailableException {
+ if ("null".equals(appStandbyEnabled)) {
+ executeCmd("settings delete global app_standby_enabled");
+ } else {
+ executeCmd("settings put global app_standby_enabled " + appStandbyEnabled);
+ }
+ }
+
+ private String getAppStandbyEnabled() throws DeviceNotAvailableException {
+ return executeCmd("settings get global app_standby_enabled").trim();
+ }
+
+ private void setBatteryStatsConstants(String batteryStatsConstants)
+ throws DeviceNotAvailableException {
+ executeCmd("settings put global battery_stats_constants \"" + batteryStatsConstants + "\"");
+ }
+
+ private String getBatteryStatsConstants() throws DeviceNotAvailableException {
+ return executeCmd("settings get global battery_stats_constants");
+ }
+
+ private String executeCmd(String cmd) throws DeviceNotAvailableException {
+ final String output = mDevice.executeShellCommand(cmd).trim();
+ LogUtil.CLog.d("Output for '%s': %s", cmd, output);
+ return output;
+ }
+}
diff --git a/hostsidetests/os/OWNERS b/hostsidetests/os/OWNERS
index 867f59d..cda6a22 100644
--- a/hostsidetests/os/OWNERS
+++ b/hostsidetests/os/OWNERS
@@ -4,3 +4,4 @@
per-file InattentiveSleepTests.java=qingxun@google.com
per-file QuiescentBootTests.java=qingxun@google.com
per-file StaticSharedLibsHostTests.java=patb@google.com
+per-file MemcgV2HostTests.java=tjmercier@google.com
diff --git a/hostsidetests/os/src/android/os/cts/MemcgV2HostTests.java b/hostsidetests/os/src/android/os/cts/MemcgV2HostTests.java
new file mode 100644
index 0000000..81bf31e
--- /dev/null
+++ b/hostsidetests/os/src/android/os/cts/MemcgV2HostTests.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+
+/*
+ * These tests exercise kernel UAPIs for memory control groups (memcg) version 2, which Android may
+ * use if configured to do so.
+ *
+ * No Android interfaces are tested here.
+ *
+ * If the device does not have memcg v2 enabled the tests will be skipped, but they will fail if
+ * we cannot determine whether memcg v2 is enabled or not.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MemcgV2HostTests extends BaseHostJUnit4Test {
+
+ // For the remote possibility that it has been mounted somewhere other than the default
+ private String getCgroupV2MountPoint() {
+ try {
+ CommandResult commandResult = getDevice().executeShellV2Command("mount | grep cgroup2");
+ if (commandResult.getExitCode() == 0) {
+ String[] tokens = commandResult.getStdout().split("\\s+");
+ return tokens[2];
+ }
+ } catch (Exception ignore) { }
+
+ // Hope the default is correct
+ return "/sys/fs/cgroup";
+ }
+
+ private Boolean isMemcgV2Enabled() {
+ try {
+ CommandResult commandResult = getDevice().executeShellV2Command(
+ "grep memory /proc/cgroups");
+
+ if (commandResult.getExitCode() == 0) {
+ String[] tokens = commandResult.getStdout().split("\\s+");
+ boolean memcg_enabled = tokens[3].equals("1");
+ if (!memcg_enabled) return false;
+ return tokens[1].equals("0"); // 0 == default hierarchy == v2
+ } else if (commandResult.getExitCode() == 1) { // "memory" not found by grep
+ // We know for sure it's not enabled, either because it is mounted as v1
+ // (cgroups.json override), or because it was intentionally disabled via kernel
+ // command line (cgroup_disable=memory), or because it's not built in to the
+ // kernel (CONFIG_MEMCG is not set).
+ return false;
+ } else { // Problems accessing /proc/cgroups
+ CLog.w("Could not read /proc/cgroups: " + commandResult.getStderr());
+ // We don't really know if it's enabled or not. Try checking the root
+ // cgroup.controllers file directly.
+ String cg_controllers_path = mCgroupV2Root + "/cgroup.controllers";
+
+ commandResult = getDevice().executeShellV2Command(
+ "grep memory " + cg_controllers_path);
+ if (commandResult.getExitCode() == 0) return true;
+ if (commandResult.getExitCode() == 1) return false;
+
+ CLog.e("Could not determine if memcg v2 is enabled: " + commandResult.getStderr());
+ }
+ } catch (Exception e) {
+ CLog.e(e.toString());
+ }
+ return null;
+ }
+
+ // Record the original subtree state for memcg so we can restore it after the test if necessary
+ private Boolean checkRootSubtreeState() {
+ try {
+ CommandResult commandResult = getDevice().executeShellV2Command(
+ "grep memory " + mCgroupV2Root + "/cgroup.subtree_control");
+ if (commandResult.getExitCode() == 0) return true;
+ if (commandResult.getExitCode() == 1) return false;
+
+ CLog.e("Could not determine if root memcg subtree control is enabled: "
+ + commandResult.getStderr());
+ } catch (Exception e) {
+ CLog.e(e.toString());
+ }
+ return null;
+ }
+
+ private String mCgroupV2Root;
+ private Boolean mMemcgV2Enabled;
+ private Boolean mRootSubtreeStateWasEnabled;
+ private String mChildCgroup;
+
+ @Before
+ public void initialize() {
+ mCgroupV2Root = getCgroupV2MountPoint();
+ mMemcgV2Enabled = isMemcgV2Enabled();
+ mRootSubtreeStateWasEnabled = checkRootSubtreeState();
+
+ assertNotNull(mMemcgV2Enabled);
+ assertNotNull(mRootSubtreeStateWasEnabled);
+ }
+
+ @After
+ public void cleanup() {
+ try {
+ if (mChildCgroup != null) getDevice().executeShellV2Command("rmdir " + mChildCgroup);
+
+ if (mRootSubtreeStateWasEnabled != null && !mRootSubtreeStateWasEnabled) {
+ getDevice().executeShellV2Command(
+ "echo -memory > " + mCgroupV2Root + "/cgroup.subtree_control");
+ }
+ } catch (Exception ignore) { }
+ }
+
+ @Test
+ public void testCanActivateMemcgV2Cgroup() throws Exception {
+ assumeTrue(mMemcgV2Enabled);
+
+ // The root has to have memcg in the subtree control to activate it in children
+ if (!mRootSubtreeStateWasEnabled) {
+ CommandResult commandResult = getDevice().executeShellV2Command(
+ "echo +memory > " + mCgroupV2Root + "/cgroup.subtree_control");
+ assertTrue("Could not activate memcg under root", commandResult.getExitCode() == 0);
+ }
+
+ // Make a new, temporary, randomly-named v2 cgroup in which we will attempt to activate
+ // memcg
+ CommandResult commandResult = getDevice().executeShellV2Command(
+ "mktemp -d -p " + mCgroupV2Root + " " + this.getClass().getSimpleName()
+ + ".XXXXXXXXXX");
+ assertTrue("Could not make child cgroup", commandResult.getExitCode() == 0);
+
+ mChildCgroup = commandResult.getStdout();
+
+
+ commandResult = getDevice().executeShellV2Command(
+ "grep memory " + mChildCgroup + "/cgroup.controllers");
+ assertTrue("Memcg was not activated in child cgroup", commandResult.getExitCode() == 0);
+
+
+ commandResult = getDevice().executeShellV2Command(
+ "echo +memory > " + mChildCgroup + "/cgroup.subtree_control");
+ assertTrue("Could not activate memcg for child cgroup subtree",
+ commandResult.getExitCode() == 0);
+ }
+
+
+ // Test for fix: mm: memcg: use larger batches for proactive reclaim
+ // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=287d5fedb377ddc232b216b882723305b27ae31a
+ @Test(timeout = 20000)
+ public void testProactiveReclaimDoesntTakeForever() throws Exception {
+ // Not all kernels have memory.reclaim
+ CommandResult commandResult = getDevice().executeShellV2Command(
+ "test -f " + mCgroupV2Root + "/memory.reclaim");
+ assumeTrue(commandResult.getExitCode() == 0);
+
+ getDevice().executeShellV2Command(
+ "echo \"\" > " + mCgroupV2Root + "/memory.reclaim");
+ // This is a test for completion within the timeout. The command is likely to "fail" with
+ // exit code 1 since we are asking to reclaim more memory than probably exists.
+ }
+
+}
diff --git a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
index 7b9d25a..064ce89 100644
--- a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
@@ -332,8 +332,10 @@
}
}
} else if (CpuFeatures.isX86(mDevice)) {
- assertTrue("Linux kernel must have KPTI enabled: CONFIG_PAGE_TABLE_ISOLATION=y",
- configSet.contains("CONFIG_PAGE_TABLE_ISOLATION=y"));
+ assertTrue("Linux kernel must have KPTI enabled: "
+ + "CONFIG_PAGE_TABLE_ISOLATION=y or CONFIG_MITIGATION_PAGE_TABLE_ISOLATION=y",
+ configSet.contains("CONFIG_PAGE_TABLE_ISOLATION=y")
+ || configSet.contains("CONFIG_MITIGATION_PAGE_TABLE_ISOLATION=y"));
}
}
diff --git a/hostsidetests/theme/OWNERS b/hostsidetests/theme/OWNERS
index 32162ef..aad77be 100644
--- a/hostsidetests/theme/OWNERS
+++ b/hostsidetests/theme/OWNERS
@@ -1 +1,3 @@
# Bug component: 25700
+siyamed@google.com
+adamp@google.com
diff --git a/tests/inputmethod/mockime/Android.bp b/tests/inputmethod/mockime/Android.bp
index a0ef52d..b26fca5 100644
--- a/tests/inputmethod/mockime/Android.bp
+++ b/tests/inputmethod/mockime/Android.bp
@@ -37,6 +37,8 @@
additional_manifests: [
"AndroidManifest_template.xml",
],
+ // Disable use_resource_processor so that an aapt.srcjar is generated for CtsMockInputMethodLib.
+ use_resource_processor: false,
}
java_test_helper_library {
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderDetachedSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderDetachedSurfaceTest.java
index 3dd7f9e..0fef676 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderDetachedSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderDetachedSurfaceTest.java
@@ -19,8 +19,8 @@
import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.graphics.ImageFormat;
@@ -78,6 +78,8 @@
public class CodecDecoderDetachedSurfaceTest extends CodecDecoderTestBase {
private static final String LOG_TAG = CodecDecoderDetachedSurfaceTest.class.getSimpleName();
private static final String MEDIA_DIR = WorkDir.getMediaDirString();
+ // 1 - 2 frames can be dropped during surface change
+ private static final int FRAMES_DROPPED_PER_SWITCH = 2;
private static final int MAX_ACTIVE_SURFACES = 4;
private static final int IMAGE_SURFACE_QUEUE_SIZE = 3;
private static final long WAIT_FOR_IMAGE_TIMEOUT_MS = 5;
@@ -92,6 +94,7 @@
private final int[] mFramesRendered = new int[MAX_ACTIVE_SURFACES];
// total frames rendered on to output surface
private final int[] mFramesRenderedExpected = new int[MAX_ACTIVE_SURFACES];
+ private int mTotalSurfaceSwitch;
// exp number of frames to be rendered on output surface
private boolean mSurfaceAttached = true;
private int mAttachedSurfaceId;
@@ -183,6 +186,7 @@
protected void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
super.resetContext(isAsync, signalEOSWithLastFrame);
mOutputCountInBursts = 0;
+ mTotalSurfaceSwitch = 0;
Arrays.fill(mFramesRendered, 0);
Arrays.fill(mFramesRenderedExpected, 0);
}
@@ -282,6 +286,26 @@
}
}
+ private void validateTestRun() {
+ int totalFramesRenderedExpected = 0;
+ int totalFramesRendered = 0;
+ for (int i = 0; i < mFramesRendered.length; i++) {
+ assertTrue(String.format(Locale.getDefault(),
+ "Number of frames rendered to output surface is exceeding the total "
+ + "frames released to the surface. Exp / got : %d / %d \n",
+ mFramesRenderedExpected[i], mFramesRendered[i]) + mTestConfig
+ + mTestEnv, mFramesRendered[i] <= mFramesRenderedExpected[i]);
+ totalFramesRenderedExpected += mFramesRenderedExpected[i];
+ totalFramesRendered += mFramesRendered[i];
+ }
+ Assume.assumeTrue(String.format(Locale.getDefault(),
+ "Number of frames rendered to output surface is much lesser than the "
+ + "total frames released to the surface. Exp / got : %d / %d \n",
+ totalFramesRenderedExpected, totalFramesRendered) + mTestConfig + mTestEnv,
+ totalFramesRenderedExpected - totalFramesRendered
+ <= FRAMES_DROPPED_PER_SWITCH * mTotalSurfaceSwitch);
+ }
+
/**
* At the start of the test #MAX_ACTIVE_SURFACES number of surfaces are instantiated. The
* first surface is used for codec configuration. After decoding/rendering 'n' frames,
@@ -311,6 +335,7 @@
while (!mSawInputEOS) {
mOutputCountInBursts = 0;
mCodec.setOutputSurface(mSurfaces.get(surfaceId)); // switch surface periodically
+ mTotalSurfaceSwitch++;
mImageSurface = mImageSurfaces.get(surfaceId);
mSurface = mSurfaces.get(surfaceId);
mAttachedSurfaceId = surfaceId;
@@ -323,11 +348,7 @@
waitForAllOutputs();
endCodecSession(mCodec);
getAllImagesInRenderQueue();
- assertArrayEquals(String.format(Locale.getDefault(),
- "Number of frames rendered to output surface are not as expected."
- + " Exp / got : %s / %s \n",
- Arrays.toString(mFramesRenderedExpected), Arrays.toString(mFramesRendered))
- + mTestConfig + mTestEnv, mFramesRenderedExpected, mFramesRendered);
+ validateTestRun();
}
mCodec.release();
mExtractor.release();
@@ -373,6 +394,7 @@
mOutputCountInBursts = 0;
if (attachSurface) {
mCodec.setOutputSurface(mSurfaces.get(surfaceId));
+ mTotalSurfaceSwitch++;
mImageSurface = mImageSurfaces.get(surfaceId);
mSurface = mSurfaces.get(surfaceId);
mSurfaceAttached = true;
@@ -391,11 +413,7 @@
waitForAllOutputs();
endCodecSession(mCodec);
getAllImagesInRenderQueue();
- assertArrayEquals(String.format(Locale.getDefault(),
- "Number of frames rendered to output surface are not as expected."
- + " Exp / got : %s / %s \n",
- Arrays.toString(mFramesRenderedExpected), Arrays.toString(mFramesRendered))
- + mTestConfig + mTestEnv, mFramesRenderedExpected, mFramesRendered);
+ validateTestRun();
}
mCodec.release();
mExtractor.release();
@@ -444,6 +462,7 @@
int surfaceId = 0;
mOutputCountInBursts = 0;
mCodec.setOutputSurface(mSurfaces.get(surfaceId));
+ mTotalSurfaceSwitch++;
mImageSurface = mImageSurfaces.get(surfaceId);
mSurface = mSurfaces.get(surfaceId);
mSurfaceAttached = true;
@@ -451,7 +470,7 @@
doWork(mBurstLength); // decode
getAllImagesInRenderQueue();
- // detach surface and release it
+ // detach surface
try {
mCodec.detachOutputSurface();
} catch (IllegalStateException e) {
@@ -459,9 +478,6 @@
+ " detachOutputSurface() fails with " + e + "\n" + mTestConfig
+ mTestEnv);
}
- mImageSurfaces.get(surfaceId).release();
- mImageSurfaces.remove(surfaceId);
- mSurfaces.remove(surfaceId);
// decode few frames without attaching surface
mOutputCountInBursts = 0;
@@ -469,9 +485,15 @@
doWork(mBurstLength);
getAllImagesInRenderQueue();
+ // release surface
+ mImageSurfaces.get(surfaceId).release();
+ mImageSurfaces.remove(surfaceId);
+ mSurfaces.remove(surfaceId);
+
// attach new surface and decode few frames
mOutputCountInBursts = 0;
mCodec.setOutputSurface(mSurfaces.get(surfaceId));
+ mTotalSurfaceSwitch++;
mImageSurface = mImageSurfaces.get(surfaceId);
mSurface = mSurfaces.get(surfaceId);
mSurfaceAttached = true;
@@ -507,11 +529,7 @@
waitForAllOutputs();
endCodecSession(mCodec);
getAllImagesInRenderQueue();
- assertArrayEquals(String.format(Locale.getDefault(),
- "Number of frames rendered to output surface are not as expected."
- + " Exp / got : %s / %s \n",
- Arrays.toString(mFramesRenderedExpected), Arrays.toString(mFramesRendered))
- + mTestConfig + mTestEnv, mFramesRenderedExpected, mFramesRendered);
+ validateTestRun();
}
mCodec.release();
mExtractor.release();
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
index 59705c8..104c480 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
@@ -200,9 +200,10 @@
}
}
}
- // P010 support was added in Android T, hence limit the following tests to Android T and
- // above
- if (CodecTestBase.IS_AT_LEAST_T) {
+ // P010 support was added in Android T and on some devices with vendor
+ // partition older than T these tests are failing hence limit the
+ // following tests to vndk Android T and above
+ if (CodecTestBase.VNDK_IS_AT_LEAST_T) {
int[] colorFormatsHbd = {COLOR_FormatSurface, COLOR_FormatYUVP010};
for (Object[] arg : argsHighBitDepth) {
final String mediaType = (String) arg[0];
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java b/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
index 45c0ea2..9e0a0ab 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
@@ -59,6 +59,14 @@
this.measuredValue = measuredValue;
}
+ T getMeasuredValue() {
+ if (!measuredValueSet) {
+ throw new IllegalStateException(
+ "tried to get measured value before it was set for measurement " + id());
+ }
+ return measuredValue;
+ }
+
@AutoValue.Builder
public static abstract class Builder<T> {
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
index 2160b10..336ad2c 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
@@ -104,10 +104,16 @@
protected <T> void setMeasuredValue(String measurement, T measuredValue) {
RequiredMeasurement<T> rm =
- (RequiredMeasurement<T>)this.mRequiredMeasurements.get(measurement);
+ (RequiredMeasurement<T>) this.mRequiredMeasurements.get(measurement);
rm.setMeasuredValue(measuredValue);
}
+ protected <T> T getMeasuredValue(String measurement, Class<T> clazz) {
+ RequiredMeasurement<T> rm =
+ (RequiredMeasurement<T>) this.mRequiredMeasurements.get(measurement);
+ return clazz.cast(rm.getMeasuredValue());
+ }
+
/**
* @return whether or not the requirement meets the device's specified performance class
*/
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Requirements.java.tmpl b/tests/mediapc/common/src/android/mediapc/cts/common/Requirements.java.tmpl
index 9bc5d06..f6f5139 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Requirements.java.tmpl
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Requirements.java.tmpl
@@ -128,75 +128,81 @@
public static final class With {
private With() {}
- {{- range $t := $r.GetTestConfigs }}
- {{- if ne $t.GetId "" }}
- public static final class Config{{ SafeTestConfigID $t.GetId | UpperCamelCase}} {
- private Config{{ SafeTestConfigID $t.GetId | UpperCamelCase}}() {}
+ {{- range $t_id, $t := $r.GetTestConfigs }}
+ {{- if ne $t_id "" }}
+ public static final class Config{{ SafeTestConfigID $t_id | UpperCamelCase}} {
+ private Config{{ SafeTestConfigID $t_id | UpperCamelCase}}() {}
public {{UpperCamelCase $r.GetName}}Requirement to(PerformanceClassEvaluator pce) {
return pce.addRequirement({{UpperCamelCase $r.GetName}}Requirement.create
- {{- SafeTestConfigID $t.GetId | UpperCamelCase}}());
+ {{- SafeTestConfigID $t_id | UpperCamelCase}}());
}
{{- range $v_id, $v := $r.GetVariants }}
- public Config{{ SafeTestConfigID $t.GetId | UpperCamelCase}}AndVariant
+ {{- if HasConfigVariant $r $t_id $v_id }}
+ public Config{{ SafeTestConfigID $t_id | UpperCamelCase}}AndVariant
{{- UpperCamelCase $v_id}} withVariant{{- UpperCamelCase $v_id}}() {
- return new Config{{SafeTestConfigID $t.GetId | UpperCamelCase}}AndVariant
+ return new Config{{SafeTestConfigID $t_id | UpperCamelCase}}AndVariant
{{- UpperCamelCase $v_id}}();
}
+ {{- end }}{{/* if HasConfigVariant */}}
{{- end }}{{/* range $v_id, $v */}}
}
{{- end }}
- {{- end }}{{/* range $t */}}
+ {{- end }}{{/* range $t_id, $t */}}
{{- range $v_id, $v := $r.GetVariants }}
public static final class Variant{{UpperCamelCase $v_id}} {
private Variant{{UpperCamelCase $v_id}}() {}
- {{- range $t := $r.GetTestConfigs }}
- {{- if eq $t.GetId "" }}
+ {{- range $t_id, $t := $r.GetTestConfigs }}
+ {{- if HasConfigVariant $r $t_id $v_id }}
+ {{- if eq $t_id "" }}
public {{UpperCamelCase $r.GetName}}Requirement to(PerformanceClassEvaluator pce) {
return pce.addRequirement({{UpperCamelCase $r.GetName}}Requirement.create
{{- UpperCamelCase $v_id}}());
}
{{- else }}
- public Config{{ SafeTestConfigID $t.GetId | UpperCamelCase}}AndVariant
+ public Config{{ SafeTestConfigID $t_id | UpperCamelCase}}AndVariant
{{- UpperCamelCase $v_id}} withConfig
- {{- SafeTestConfigID $t.GetId | UpperCamelCase}}() {
- return new Config{{SafeTestConfigID $t.GetId | UpperCamelCase}}AndVariant
+ {{- SafeTestConfigID $t_id | UpperCamelCase}}() {
+ return new Config{{SafeTestConfigID $t_id | UpperCamelCase}}AndVariant
{{- UpperCamelCase $v_id}}();
}
{{- end }}
- {{- end }}{{/* range $t */}}
+ {{- end }}{{/* if HasConfigVariant */}}
+ {{- end }}{{/* range $t_id, $t */}}
}
{{- end }}{{/* range $v_id, $v */}}
- {{- range $t := $r.GetTestConfigs }}
- {{- if ne $t.GetId "" }}
+ {{- range $t_id, $t := $r.GetTestConfigs }}
+ {{- if ne $t_id "" }}
{{- range $v_id, $v := $r.GetVariants }}
- public static final class Config{{SafeTestConfigID $t.GetId | UpperCamelCase}}AndVariant
+ {{- if HasConfigVariant $r $t_id $v_id }}
+ public static final class Config{{SafeTestConfigID $t_id | UpperCamelCase}}AndVariant
{{- UpperCamelCase $v_id}} {
- private Config{{ SafeTestConfigID $t.GetId | UpperCamelCase}}AndVariant
+ private Config{{ SafeTestConfigID $t_id | UpperCamelCase}}AndVariant
{{- UpperCamelCase $v_id}}() {}
public {{UpperCamelCase $r.GetName}}Requirement to(PerformanceClassEvaluator pce) {
return pce.addRequirement({{UpperCamelCase $r.GetName}}Requirement.create
- {{- SafeTestConfigID $t.GetId | UpperCamelCase}}
+ {{- SafeTestConfigID $t_id | UpperCamelCase}}
{{- UpperCamelCase $v_id}}());
}
}
+ {{- end }}{{/* if HasConfigVariant */}}
{{- end }}{{/* range $v_id, $v */}}
{{- end }}
- {{- end }}{{/* range $t */}}
+ {{- end }}{{/* range $t_id, $t */}}
- {{- range $t := $r.GetTestConfigs }}
- {{- if eq $t.GetId "" }}
+ {{- range $t_id, $t := $r.GetTestConfigs }}
+ {{- if eq $t_id "" }}
public {{UpperCamelCase $r.GetName}}Requirement to(PerformanceClassEvaluator pce) {
return pce.addRequirement({{UpperCamelCase $r.GetName}}Requirement.create());
}
{{- else }}
- public Config{{ SafeTestConfigID $t.GetId | UpperCamelCase}} withConfig
- {{- SafeTestConfigID $t.GetId | UpperCamelCase}}() {
- return new Config{{ SafeTestConfigID $t.GetId | UpperCamelCase}}();
+ public Config{{ SafeTestConfigID $t_id | UpperCamelCase}} withConfig
+ {{- SafeTestConfigID $t_id | UpperCamelCase}}() {
+ return new Config{{ SafeTestConfigID $t_id | UpperCamelCase}}();
}
{{- end }}
- {{- end }}{{/* range $t */}}
+ {{- end }}{{/* range $t_id, $t */}}
{{- range $v_id, $v := $r.GetVariants }}
public Variant{{UpperCamelCase $v_id}} withVariant{{UpperCamelCase $v_id}}() {
@@ -206,7 +212,7 @@
}
- {{- range $t := $r.GetTestConfigs }}
+ {{- range $t_id, $t := $r.GetTestConfigs }}
/**
* {{ $r.GetId }} {{$r.GetName}}
@@ -215,7 +221,7 @@
* {{.}}{{end}}
*/
private static {{UpperCamelCase $r.GetName}}Requirement create
- {{- SafeTestConfigID $t.GetId | UpperCamelCase}}() {
+ {{- SafeTestConfigID $t_id | UpperCamelCase}}() {
{{- range $m_id, $m := $r.GetMeasurements }}
{{- $mt := $m.GetMeasurementType}}
{{- $c := $m.GetComparison}}
@@ -224,7 +230,7 @@
.setId("{{$m_id}}")
.setPredicate(RequirementConstants.{{template "CompMethod" $m}})
{{- range $mpc, $s := $r.GetSpecs }}
- {{- if eq $s.GetTestConfigId $t.GetId }}
+ {{- if eq $s.GetTestConfigId $t_id }}
{{- with index $s.GetRequiredValues $m_id}}
.addRequiredValue({{template "VersionCode" $mpc}}, {{template
"MeasurementValue" Dict "RequiredValue" . "MeasurementType" $mt}})
@@ -245,9 +251,9 @@
{{- $c := $m.GetComparison}}
{{- if eq $c.String "COMPARISON_CONFIG" }}
{{- range $s := $r.GetSpecs }}
- {{- if eq $s.GetTestConfigId $t.GetId }}
+ {{- if eq $s.GetTestConfigId $t_id }}
{{- with index $s.GetRequiredValues $m_id}}
- req.set{{UpperCamelCase $m_id}}(
+ req.setMeasuredValue("{{$m_id}}",
{{- template "MeasurementValue" Dict "RequiredValue" . "MeasurementType" $mt}});
{{- break }}
{{- end }}
@@ -257,10 +263,11 @@
{{- end }}
return req;
}
- {{- end }}{{/* range $t */}}
+ {{- end }}{{/* range $t_id, $t */}}
- {{- range $t := $r.GetTestConfigs }}
+ {{- range $t_id, $t := $r.GetTestConfigs }}
{{- range $v_id, $v := $r.GetVariants }}
+ {{- if HasConfigVariant $r $t_id $v_id }}
/**
* {{ $r.GetId }} {{$r.GetName}}{{- with $v.GetDescription}} {{.}}{{end}}
@@ -269,7 +276,7 @@
* {{.}}{{end}}
*/
private static {{UpperCamelCase $r.GetName}}Requirement create
- {{- SafeTestConfigID $t.GetId | UpperCamelCase }}{{UpperCamelCase $v_id}}() {
+ {{- SafeTestConfigID $t_id | UpperCamelCase }}{{UpperCamelCase $v_id}}() {
{{- range $m_id, $m := $r.GetMeasurements }}
{{- $mt := $m.GetMeasurementType}}
{{- $c := $m.GetComparison}}
@@ -278,7 +285,7 @@
.setId("{{$m_id}}")
.setPredicate(RequirementConstants.{{template "CompMethod" $m}})
{{- range $mpc, $s := $r.GetSpecs }}
- {{- if eq $s.GetTestConfigId $t.GetId }}
+ {{- if eq $s.GetTestConfigId $t_id }}
{{- $vs := index $s.GetVariantSpecs $v_id}}
{{- if $vs }}
{{- with index $vs.GetRequiredValues $m_id}}
@@ -290,22 +297,49 @@
{{- end}}{{/* range $mpc, $s */}}
.build();
{{- end}}{{/* range $m_id, $m */}}
- return new {{UpperCamelCase $r.GetName}}Requirement(
+ {{UpperCamelCase $r.GetName}}Requirement req =
+ new {{UpperCamelCase $r.GetName}}Requirement(
"{{SafeReqID $r.GetId }}"
{{- range $m_id, $m := $r.GetMeasurements }},
{{LowerCamelCase $m_id}}
{{- end}});
+ {{- range $m_id, $m := $r.GetMeasurements }}
+ {{- $mt := $m.GetMeasurementType}}
+ {{- $c := $m.GetComparison}}
+ {{- if eq $c.String "COMPARISON_CONFIG" }}
+ {{- range $s := $r.GetSpecs }}
+ {{- if eq $s.GetTestConfigId $t.GetId }}
+ {{- with index $s.GetRequiredValues $m_id}}
+ req.setMeasuredValue("{{$m_id}}",
+ {{- template "MeasurementValue" Dict "RequiredValue" . "MeasurementType" $mt}});
+ {{- break }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ return req;
}
+ {{- end }}{{/* if HasConfigVariant */}}
{{- end }}{{/* range $v_id, $v */}}
- {{- end }}{{/* range $t */}}
+ {{- end }}{{/* range $t_id, $t */}}
{{- range $m_id, $m := $r.GetMeasurements }}
{{- $mt := $m.GetMeasurementType}}
+ {{- $c := $m.GetComparison}}
+ {{- if eq $c.String "COMPARISON_CONFIG" }}
+
+ /** {{$m.GetDescription}} */
+ public {{template "JavaType" $mt}} get{{UpperCamelCase $m_id}}() {
+ return this.getMeasuredValue("{{$m_id}}", {{template "JavaClass" $mt}}.class);
+ }
+ {{- else }}
/** {{$m.GetDescription}} */
public void set{{UpperCamelCase $m_id}}({{template "JavaType" $mt}} v) {
this.setMeasuredValue("{{$m_id}}", v);
}
+ {{- end }}{{/* if eq */}}
{{- end }}{{/* range $m_id, $m */}}
private {{UpperCamelCase $r.GetName}}Requirement(String id, RequiredMeasurement<?>... reqs) {
diff --git a/tests/mediapc/requirements/Android.bp b/tests/mediapc/requirements/Android.bp
index 78ebcf2..fba42ba 100644
--- a/tests/mediapc/requirements/Android.bp
+++ b/tests/mediapc/requirements/Android.bp
@@ -87,4 +87,8 @@
name: "templatefns",
pkgPath: "cts/test/mediapc/requirements/templatefns",
srcs: ["templatefns.go"],
+ deps: [
+ "golang-protobuf-proto",
+ "requirements_go_proto",
+ ],
}
diff --git a/tests/mediapc/requirements/requirements.proto b/tests/mediapc/requirements/requirements.proto
index bcfdd00..b91f217 100644
--- a/tests/mediapc/requirements/requirements.proto
+++ b/tests/mediapc/requirements/requirements.proto
@@ -142,7 +142,7 @@
message TestConfig {
// The id is a field name safe string.
- optional string id = 1;
+ optional string id = 1 [deprecated = true];
optional string description = 2;
}
diff --git a/tests/mediapc/requirements/requirements.txtpb b/tests/mediapc/requirements/requirements.txtpb
index 815aadc..91bc9f6 100644
--- a/tests/mediapc/requirements/requirements.txtpb
+++ b/tests/mediapc/requirements/requirements.txtpb
@@ -56,12 +56,105 @@
}
requirements {
id: "5.1/H-1-2"
- description: "- Concurrent video codec sessions\n- AV1 hardware decoder"
+ name: "Concurrent Video Decoder Sessions"
+ description: "Concurrent video decoder sessions"
+ test_configs: {
+ key: "720p"
+ value: {
+ id: "720p"
+ description: "Tests running at 720p"
+ }
+ }
+ test_configs: {
+ key: "1080p"
+ value: {
+ id: "1080p"
+ description: "Tests running at 1080p"
+ }
+ }
+ test_configs: {
+ key: "4k"
+ value: {
+ id: "4k"
+ description: "Tests running at 4k"
+ }
+ }
+ variants: {
+ key: "VP9"
+ value: {
+ description: "When one of the codecs is VP9, variant used in 720p tests"
+ }
+ }
+ variants: {
+ key: "AV1"
+ value: {
+ description: "When one of the codecs is AV1, variant used in 720p tests"
+ }
+ }
+ measurements: {
+ key: "concurrent_fps"
+ value: {
+ description: "The number of frames per second that can be decoded concurrently"
+ measurement_type: MEASUREMENT_TYPE_DOUBLE
+ comparison: COMPARISON_GREATER_THAN_OR_EQUAL
+ }
+ }
+ measurements: {
+ key: "frame_drops_per_sec"
+ value: {
+ description: "The number of frames dropped per second"
+ measurement_type: MEASUREMENT_TYPE_DOUBLE
+ comparison: COMPARISON_LESS_THAN_OR_EQUAL
+ }
+ }
+ measurements: {
+ key: "resolution"
+ value: {
+ description: "The resolution the test was run at"
+ measurement_type: MEASUREMENT_TYPE_INT
+ comparison: COMPARISON_CONFIG
+ }
+ }
specs {
key: 30
value {
mpc: MEDIA_PERFORMANCE_CLASS_11
specification: "MUST support 6 instances of hardware video decoder sessions (AVC or HEVC) in any codec combination running concurrently at 720p resolution@30 fps."
+ test_config_id: "720p"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 171 # 6 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 720
+ }
+ }
+ variant_specs: {
+ key: "VP9"
+ value: {
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 720
+ }
+ }
+ }
+ }
+ variant_specs: {
+ key: "AV1"
+ value: {
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 720
+ }
+ }
+ }
+ }
}
}
specs {
@@ -69,6 +162,53 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_12
specification: "MUST support 6 instances of hardware video decoder sessions (AVC, HEVC, VP9* or later) in any codec combination running concurrently at 720p resolution@30 fps. *Only 2 instances are required if VP9 codec is present."
+ test_config_id: "720p"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 171 # 6 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 720
+ }
+ }
+ variant_specs: {
+ key: "VP9"
+ value: {
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 57 # 2 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 720
+ }
+ }
+ }
+ }
+ variant_specs: {
+ key: "AV1"
+ value: {
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 171 # 6 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 720
+ }
+ }
+ }
+ }
}
}
specs {
@@ -76,6 +216,19 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_13
specification: "MUST support 6 instances of hardware video decoder sessions (AVC, HEVC, VP9, AV1 or later) in any codec combination running concurrently at 1080p resolution@30 fps."
+ test_config_id: "1080p"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 171 # 6 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 1080
+ }
+ }
}
}
specs {
@@ -83,6 +236,19 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_14
specification: "MUST support 6 instances of 8-bit (SDR) hardware video decoder sessions (AVC, HEVC, VP9, AV1 or later) in any codec combination running concurrently with 3 sessions at 1080p resolution@30 fps and 3 sessions at 4k resolution@30fps, unless AV1. AV1 codecs are only required to support 1080p resolution, but are still required to support 6 instances at 1080p30fps."
+ test_config_id: "4k"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 171 # 6 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 2160
+ }
+ }
}
}
specs {
@@ -90,6 +256,25 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_15
specification: "MUST support 6 instances of 8-bit (SDR) hardware video decoder sessions (AVC, HEVC, VP9, AV1, or later) in any codec combination running concurrently with 3 sessions at 1080p resolution@30 fps and 3 sessions at 4k resolution@30fps, unless AV1. For all sessions, there MUST NOT be more than 1 frame dropped per second. AV1 codecs are only required to support 1080p resolution, but are still required to support 6 instances at 1080p30fps."
+ test_config_id: "4k"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 171 # 6 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "frame_drops_per_sec"
+ value {
+ double_value: 1
+ }
+ }
+ required_values: {
+ key: "resolution"
+ value {
+ int_value: 2160
+ }
+ }
}
}
}
@@ -510,12 +695,50 @@
}
requirements {
id: "5.1/H-1-9"
- description: "- AV1 hardware decoder\n- Secure hardware decoders"
+ name: "Secure Video Decoder Sessions"
+ description: "Secure hardware decoders"
+ test_configs: {
+ key: "1080p"
+ value: {
+ id: "1080p"
+ description: "Test config for 1080p"
+ }
+ }
+ test_configs: {
+ key: "4k"
+ value: {
+ id: "4k"
+ description: "Test config for 4k"
+ }
+ }
+ measurements: {
+ key: "concurrent_fps"
+ value: {
+ description: "Achieved FPS"
+ measurement_type: MEASUREMENT_TYPE_DOUBLE
+ comparison: COMPARISON_GREATER_THAN_OR_EQUAL
+ }
+ }
+ measurements: {
+ key: "frame_drops_per_sec"
+ value: {
+ description: "Number of frame drops per second"
+ measurement_type: MEASUREMENT_TYPE_DOUBLE
+ comparison: COMPARISON_LESS_THAN_OR_EQUAL
+ }
+ }
specs {
key: 33
value {
mpc: MEDIA_PERFORMANCE_CLASS_13
specification: "MUST support 2 instances of secure hardware video decoder sessions (AVC, HEVC, VP9, AV1 or later) in any codec combination running concurrently at 1080p resolution@30 fps."
+ test_config_id: "1080p"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 57 # 2 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
}
}
specs {
@@ -523,6 +746,13 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_14
specification: "MUST support 2 instances of secure hardware video decoder sessions (AVC, HEVC, VP9, AV1 or later) in any codec combination running concurrently at 4k resolution@30 fps (unless AV1) for both 8-bit (SDR) and 10-bit HDR content. AV1 codec sessions are only required to support 1080p resolution even when this requirement calls for 4K."
+ test_config_id: "4k"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 57 # 2 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
}
}
specs {
@@ -530,17 +760,68 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_15
specification: "MUST support 2 instances of secure hardware video decoder sessions (AVC, HEVC, VP9, AV1, or later) in any codec combination running concurrently at 4k resolution@30 fps (unless AV1) for both 8-bit (SDR) and 10-bit HDR content. For all sessions, there MUST NOT be more than 1 frame dropped per second. AV1 codec sessions are only required to support 1080p resolution even when this requirement calls for 4K."
+ test_config_id: "4k"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 57 # 2 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "frame_drops_per_sec"
+ value {
+ double_value: 1
+ }
+ }
}
}
}
requirements {
id: "5.1/H-1-10"
- description: "- AV1 hardware decoder\n- Secure hardware decoders"
+ name: "Video Decoder Sessions"
+ description: "hardware decoders, secure with non-secure hardware decoders"
+ test_configs: {
+ key: "1080p"
+ value: {
+ id: "1080p"
+ description: "Test config for 1080p"
+ }
+ }
+ test_configs: {
+ key: "4k"
+ value: {
+ id: "4k"
+ description: "Test config for 4k"
+ }
+ }
+ measurements: {
+ key: "concurrent_fps"
+ value: {
+ description: "Achieved FPS"
+ measurement_type: MEASUREMENT_TYPE_DOUBLE
+ comparison: COMPARISON_GREATER_THAN_OR_EQUAL
+ }
+ }
+ measurements: {
+ key: "frame_drops_per_sec"
+ value: {
+ description: "Number of frame drops per second"
+ measurement_type: MEASUREMENT_TYPE_DOUBLE
+ comparison: COMPARISON_LESS_THAN_OR_EQUAL
+ }
+ }
specs {
key: 33
value {
mpc: MEDIA_PERFORMANCE_CLASS_13
specification: "MUST support 3 instances of non-secure hardware video decoder sessions together with 1 instance of secure hardware video decoder session (4 instances total) (AVC, HEVC, VP9, AV1 or later) in any codec combination running concurrently at 1080p resolution@30fps."
+ test_config_id: "1080p"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 114 # 4 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
}
}
specs {
@@ -548,6 +829,13 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_14
specification: "MUST support 3 instances of non-secure hardware video decoder sessions together with 1 instance of secure hardware video decoder session (4 instances total) (AVC, HEVC, VP9, AV1 or later) in any codec combination running concurrently with 3 sessions at 4K resolution@30 fps (unless AV1) which includes one secure decoder session and 1 nn-secure session at 1080p resolution@30fps where at most 2 sessions can be in 10-bit HDR. AV1 codec sessions are only required to support 1080p resolution even when this requirement calls for 4K."
+ test_config_id: "4k"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 114 # 4 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
}
}
specs {
@@ -555,6 +843,19 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_15
specification: "MUST support 3 instances of non-secure hardware video decoder sessions together with 1 instance of secure hardware video decoder session (4 instances total) (AVC, HEVC, VP9, AV1, or later) in any codec combination running concurrently with 3 sessions at 4K resolution@30fps (unless AV1) which includes one secure decoder session and 1 nn-secure session at 1080p resolution@30fps where at most 2 sessions can be in 10-bit HDR. For all sessions, there MUST NOT be more than 1 frame dropped per second. AV1 codec sessions are only required to support 1080p resolution even when this requirement calls for 4K."
+ test_config_id: "4k"
+ required_values: {
+ key: "concurrent_fps"
+ value {
+ double_value: 114 # 4 decoders * 30 fps * 0.95 tolerance factor
+ }
+ }
+ required_values: {
+ key: "frame_drops_per_sec"
+ value {
+ double_value: 1
+ }
+ }
}
}
}
@@ -585,19 +886,74 @@
}
requirements {
id: "5.1/H-1-12"
- description: "- Decoder initialization latency"
+ name: "Video Decoder Init Latency"
+ description: "Video decoder initialization latency"
+ test_configs: {
+ key: ""
+ value: {
+ id: ""
+ }
+ }
+ variants: {
+ key: "dolby"
+ value: {
+ description: "When the codec is Dolby Vision"
+ }
+ }
+ measurements: {
+ key: "codec_initialization_latency_ms"
+ value: {
+ description: "Codec initialization latency in milliseconds"
+ measurement_type: MEASUREMENT_TYPE_LONG
+ comparison: COMPARISON_LESS_THAN_OR_EQUAL
+ }
+ }
specs {
key: 33
value {
mpc: MEDIA_PERFORMANCE_CLASS_13
- specification: "MUST have a video decoder initialization latency of 40 ms or less."
+ specification: "MUST have a codec initialization latency of 40 ms or less for a 1080p or smaller video decoding session for all hardware video decoders when under load. Load here is defined as a concurrent 1080p to 720p video-only transcoding session using hardware video codecs together with the 1080p audio-video playback initialization. For Dolby vision codec, the codec initialization latency MUST be 50 ms or less."
+ required_values: {
+ key: "codec_initialization_latency_ms"
+ value {
+ long_value: 40
+ }
+ }
+ variant_specs: {
+ key: "dolby"
+ value: {
+ required_values: {
+ key: "codec_initialization_latency_ms"
+ value {
+ long_value: 50
+ }
+ }
+ }
+ }
}
}
specs {
key: 34
value {
mpc: MEDIA_PERFORMANCE_CLASS_14
- specification: "MUST have a codec initialization latency of 40 ms or less for a 1080p or smaller video decoding session for all hardware video decoders when under load. Load here is defined as a concurrent 1080p to 720p video-only transcoding session using hardware video codecs together with the 1080p audio-video playback initialization. For Dolby vision codec, the codec initialization latency MUST be 50 ms or less."
+ specification: "same"
+ required_values: {
+ key: "codec_initialization_latency_ms"
+ value {
+ long_value: 40
+ }
+ }
+ variant_specs: {
+ key: "dolby"
+ value: {
+ required_values: {
+ key: "codec_initialization_latency_ms"
+ value {
+ long_value: 50
+ }
+ }
+ }
+ }
}
}
specs {
@@ -605,6 +961,23 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_15
specification: "same"
+ required_values: {
+ key: "codec_initialization_latency_ms"
+ value {
+ long_value: 40
+ }
+ }
+ variant_specs: {
+ key: "dolby"
+ value: {
+ required_values: {
+ key: "codec_initialization_latency_ms"
+ value {
+ long_value: 50
+ }
+ }
+ }
+ }
}
}
}
@@ -1158,12 +1531,33 @@
}
requirements {
id: "5.12/H-1-3"
- description: "- YUV texture sampling"
+ name: "Ext Yuv Target"
+ description: "YUV texture sampling"
+ test_configs: {
+ key: ""
+ value: {
+ id: ""
+ }
+ }
+ measurements: {
+ key: "ext_yuv_target_supported"
+ value: {
+ description: "Is EXT_YUV_target supported."
+ measurement_type: MEASUREMENT_TYPE_BOOL
+ comparison: COMPARISON_EQUAL
+ }
+ }
specs {
key: 34
value {
mpc: MEDIA_PERFORMANCE_CLASS_14
specification: "MUST advertise support for the EXT_YUV_target extension to sample from YUV textures in both 8 and 10-bits."
+ required_values: {
+ key: "ext_yuv_target_supported"
+ value {
+ bool_value: true
+ }
+ }
}
}
specs {
@@ -1171,6 +1565,12 @@
value {
mpc: MEDIA_PERFORMANCE_CLASS_15
specification: "same"
+ required_values: {
+ key: "ext_yuv_target_supported"
+ value {
+ bool_value: true
+ }
+ }
}
}
}
@@ -1312,18 +1712,21 @@
key: "1080p_30fps"
value: {
id: "1080p_30fps"
+ description: "Tests frame drop at 1080p resolution and 30fps"
}
}
test_configs: {
key: "1080p_60fps"
value: {
id: "1080p_60fps"
+ description: "Tests frame drop at 1080p resolution and 60fps"
}
}
test_configs: {
key: "4k_60fps"
value: {
id: "4k_60fps"
+ description: "Tests frame drop at 4k resolution and 60fps"
}
}
measurements: {
@@ -1489,18 +1892,21 @@
key: "1080p_30fps"
value: {
id: "1080p_30fps"
+ description: "Tests adaptive playback at 1080p resolution and 30fps"
}
}
test_configs: {
key: "1080p_60fps"
value: {
id: "1080p_60fps"
+ description: "Tests adaptive playback at 1080p resolution and 60fps"
}
}
test_configs: {
key: "4k_60fps"
value: {
id: "4k_60fps"
+ description: "Tests adaptive playback at 4k resolution and 60fps"
}
}
measurements: {
@@ -2672,7 +3078,7 @@
requirements {
id: "7.6.1/H-1-1"
name: "Android 11 Memory"
- description: "Minimum memory available to the kernel as reported by android.app.ActivityManager.MemoryInfo."
+ description: "Minimum memory available to the kernel as reported by ActivityManager.MemoryInfo.totalMemory"
test_configs: {
key: ""
value: {
@@ -2682,7 +3088,7 @@
measurements: {
key: "physical_memory_mb"
value: {
- description: "Physical memory in MiB as reported by android.app.ActivityManager.MemeryInfo.totalMemory."
+ description: "Physical memory in MiB as reported by ActivityManager.MemeryInfo.totalMemory."
comparison: COMPARISON_GREATER_THAN_OR_EQUAL
measurement_type: MEASUREMENT_TYPE_LONG
}
@@ -2704,7 +3110,7 @@
requirements {
id: "7.6.1/H-2-1"
name: "Memory"
- description: "Minimum memory available to the kernel as reported by android.app.ActivityManager.MemoryInfo."
+ description: "Minimum memory available to the kernel as reported by ActivityManager.MemoryInfo.totalMemory"
test_configs: {
key: ""
value: {
@@ -2714,7 +3120,7 @@
measurements: {
key: "physical_memory_mb"
value: {
- description: "Physical memory in MiB as reported by android.app.ActivityManager.MemeryInfo.totalMemory."
+ description: "Physical memory in MiB as reported by ActivityManager.MemoryInfo.totalMemory."
comparison: COMPARISON_GREATER_THAN_OR_EQUAL
measurement_type: MEASUREMENT_TYPE_LONG
}
diff --git a/tests/mediapc/requirements/requirementsdata_test.go b/tests/mediapc/requirements/requirementsdata_test.go
index 0409391..c482697 100644
--- a/tests/mediapc/requirements/requirementsdata_test.go
+++ b/tests/mediapc/requirements/requirementsdata_test.go
@@ -75,11 +75,8 @@
t.Run(req.GetName(), func(t *testing.T) {
specifiedTestConfigs := []string{}
- for id, testConfig := range req.GetTestConfigs() {
- if id != testConfig.GetId() {
- t.Errorf("Test config id [%s] does not match its key [%s]", testConfig.GetId(), id)
- }
- specifiedTestConfigs = append(specifiedTestConfigs, testConfig.GetId())
+ for id := range req.GetTestConfigs() {
+ specifiedTestConfigs = append(specifiedTestConfigs, id)
}
usedTestConfigs := []string{}
@@ -128,6 +125,45 @@
}
}
+func TestConfigVariantsValid(t *testing.T) {
+ reqList := mustUnmarshalRequirementList(t)
+
+ for _, req := range reqList.GetRequirements() {
+ if !req.HasName() {
+ continue // Do not check requirements that are not implemented yet
+ }
+
+ t.Run(req.GetName(), func(t *testing.T) {
+ for configID := range req.GetTestConfigs() {
+
+ // Check that all test configs have the same variants
+ t.Run(configID, func(t *testing.T) {
+ specToVariants := make(map[int64][]string)
+ for mpc, spec := range req.GetSpecs() {
+ if spec.GetTestConfigId() == configID {
+ specToVariants[mpc] = []string{}
+ for variantID := range spec.GetVariantSpecs() {
+ specToVariants[mpc] = append(specToVariants[mpc], variantID)
+ }
+ }
+ }
+
+ prev := []string{}
+ for _, variants := range specToVariants {
+ if len(prev) > 0 {
+ if diff := cmp.Diff(prev, variants, cmpopts.SortSlices(
+ func(a, b string) bool { return a < b })); diff != "" {
+ t.Errorf("Test config [%s] missing variants (-want +got):\n%s", configID, diff)
+ }
+ }
+ prev = variants
+ }
+ })
+ }
+ })
+ }
+}
+
func mustUnmarshalRequirementList(t *testing.T) *pb.RequirementList {
t.Helper()
reqList, err := requirements.UnmarshalRequirementList(reqBinary)
diff --git a/tests/mediapc/requirements/templatefns.go b/tests/mediapc/requirements/templatefns.go
index cc65822..2d4b74a 100644
--- a/tests/mediapc/requirements/templatefns.go
+++ b/tests/mediapc/requirements/templatefns.go
@@ -20,6 +20,8 @@
"strings"
"text/template"
"unicode"
+
+ pb "cts/test/mediapc/requirements/requirements_go_proto"
)
// Funcs returns a mapping from names of template helper functions to the
@@ -29,6 +31,7 @@
return template.FuncMap{
// go/keep-sorted start
"Dict": dict,
+ "HasConfigVariant": HasConfigVariant,
"KebabCase": kebabCase,
"LowerCamelCase": lowerCamelCase,
"LowerCase": strings.ToLower,
@@ -148,6 +151,19 @@
return dict
}
+// HasConfigVariant checks if a requirement has a spec for a given test config and variant.
+func HasConfigVariant(r *pb.Requirement, configID string, variantID string) bool {
+ for _, spec := range r.GetSpecs() {
+ if configID == spec.GetTestConfigId() {
+ _, ok := spec.GetVariantSpecs()[variantID]
+ if ok {
+ return true
+ }
+ }
+ }
+ return false
+}
+
// toString converts a value to a string.
func toString(v any) string {
switch v := v.(type) {
diff --git a/tests/mediapc/requirements/templatefns_test.go b/tests/mediapc/requirements/templatefns_test.go
index ba5192a..face4fd 100644
--- a/tests/mediapc/requirements/templatefns_test.go
+++ b/tests/mediapc/requirements/templatefns_test.go
@@ -17,6 +17,10 @@
import (
"errors"
"testing"
+
+ pb "cts/test/mediapc/requirements/requirements_go_proto"
+
+ "google.golang.org/protobuf/proto"
)
var caseTests = []struct {
@@ -186,3 +190,47 @@
}
}
}
+
+func TestHasConfigVariant(t *testing.T) {
+ testReq := pb.Requirement_builder{
+ Specs: map[int64]*pb.RequirementSpec{
+ 30: pb.RequirementSpec_builder{
+ TestConfigId: proto.String("720p"),
+ VariantSpecs: map[string]*pb.VariantSpec{
+ "VP9": pb.VariantSpec_builder{}.Build(),
+ },
+ }.Build(),
+ 34: pb.RequirementSpec_builder{
+ TestConfigId: proto.String("4k"),
+ VariantSpecs: make(map[string]*pb.VariantSpec),
+ }.Build(),
+ },
+ }.Build()
+
+ tests := []struct {
+ r *pb.Requirement
+ configID string
+ variantID string
+ want bool
+ }{
+ {
+ r: testReq,
+ configID: "720p",
+ variantID: "VP9",
+ want: true,
+ },
+ {
+ r: testReq,
+ configID: "4k",
+ variantID: "VP9",
+ want: false,
+ },
+ }
+
+ for _, tc := range tests {
+ got := HasConfigVariant(tc.r, tc.configID, tc.variantID)
+ if got != tc.want {
+ t.Errorf("HasConfigVariant(%v, %q, %q) = %v, want: %v", tc.r, tc.configID, tc.variantID, got, tc.want)
+ }
+ }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java
index 3e53330..a4341ee 100644
--- a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java
@@ -29,6 +29,7 @@
import static android.mediapc.cts.CodecTestBase.getMediaTypesOfAvailableCodecs;
import static android.mediapc.cts.CodecTestBase.selectCodecs;
import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+import static android.mediav2.common.cts.CodecTestBase.isDefaultCodec;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -300,7 +301,7 @@
}
/**
- * MUST support the Feature_HlgEditing feature for all hardware AV1 and HEVC
+ * MUST support the Feature_HlgEditing feature for default hardware AV1 and HEVC
* encoders present on the device at 4K resolution or the largest Camera-supported
* resolution, whichever is less.
*/
@@ -308,7 +309,7 @@
@RequiresFlagsEnabled(Flags.FLAG_HLG_EDITING)
@Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_SMALL_TEST_MS)
@CddTest(requirement = "5.1/H-1-20")
- public void testHlgEditingSupport() throws CameraAccessException {
+ public void testHlgEditingSupport() throws CameraAccessException, IOException {
final String[] mediaTypes =
{MediaFormat.MIMETYPE_VIDEO_HEVC, MIMETYPE_VIDEO_AV1};
@@ -327,6 +328,9 @@
for (String mediaType : mediaTypes) {
ArrayList<String> hwEncoders = selectHardwareCodecs(mediaType, null, null, true);
for (String encoder : hwEncoders) {
+ if (!isDefaultCodec(encoder, mediaType, true)) {
+ continue;
+ }
MediaFormat format =
MediaFormat.createVideoFormat(mediaType, maxRecordingSize.getWidth(),
maxRecordingSize.getHeight());
diff --git a/tests/signature/api-check/Android.bp b/tests/signature/api-check/Android.bp
index be01ded..c95f3b2 100644
--- a/tests/signature/api-check/Android.bp
+++ b/tests/signature/api-check/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_java_core_libraries",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/android-test-base-29-api/Android.bp b/tests/signature/api-check/android-test-base-29-api/Android.bp
index b3b8a06..dfd7acd 100644
--- a/tests/signature/api-check/android-test-base-29-api/Android.bp
+++ b/tests/signature/api-check/android-test-base-29-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/android-test-base-current-api/Android.bp b/tests/signature/api-check/android-test-base-current-api/Android.bp
index 68d5345..84bec90 100644
--- a/tests/signature/api-check/android-test-base-current-api/Android.bp
+++ b/tests/signature/api-check/android-test-base-current-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/android-test-base-uses-library-api/Android.bp b/tests/signature/api-check/android-test-base-uses-library-api/Android.bp
index 1ba42ae..acc73c2 100644
--- a/tests/signature/api-check/android-test-base-uses-library-api/Android.bp
+++ b/tests/signature/api-check/android-test-base-uses-library-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/android-test-mock-current-api/Android.bp b/tests/signature/api-check/android-test-mock-current-api/Android.bp
index 40f0eb5..40068ed 100644
--- a/tests/signature/api-check/android-test-mock-current-api/Android.bp
+++ b/tests/signature/api-check/android-test-mock-current-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/android-test-runner-current-api/Android.bp b/tests/signature/api-check/android-test-runner-current-api/Android.bp
index 0a4570e..266836d 100644
--- a/tests/signature/api-check/android-test-runner-current-api/Android.bp
+++ b/tests/signature/api-check/android-test-runner-current-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/apache-http-legacy-27-api/Android.bp b/tests/signature/api-check/apache-http-legacy-27-api/Android.bp
index 37bc240..e298eb5 100644
--- a/tests/signature/api-check/apache-http-legacy-27-api/Android.bp
+++ b/tests/signature/api-check/apache-http-legacy-27-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/apache-http-legacy-current-api/Android.bp b/tests/signature/api-check/apache-http-legacy-current-api/Android.bp
index b92a1d1..10d2c0a 100644
--- a/tests/signature/api-check/apache-http-legacy-current-api/Android.bp
+++ b/tests/signature/api-check/apache-http-legacy-current-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/apache-http-legacy-uses-library-api/Android.bp b/tests/signature/api-check/apache-http-legacy-uses-library-api/Android.bp
index d866d3d..3e6411c 100644
--- a/tests/signature/api-check/apache-http-legacy-uses-library-api/Android.bp
+++ b/tests/signature/api-check/apache-http-legacy-uses-library-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/current-api/Android.bp b/tests/signature/api-check/current-api/Android.bp
index e66deb3..1f5a0e1 100644
--- a/tests/signature/api-check/current-api/Android.bp
+++ b/tests/signature/api-check/current-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_java_core_libraries",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/shared-libs-api/Android.bp b/tests/signature/api-check/shared-libs-api/Android.bp
index 0c9b32f..148cb87 100644
--- a/tests/signature/api-check/shared-libs-api/Android.bp
+++ b/tests/signature/api-check/shared-libs-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_mainline_modularization",
+ default_team: "trendy_team_updatable_sdk_apis",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/system-annotation/Android.bp b/tests/signature/api-check/system-annotation/Android.bp
index 5044a8a..ded6011 100644
--- a/tests/signature/api-check/system-annotation/Android.bp
+++ b/tests/signature/api-check/system-annotation/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_java_core_libraries",
+ default_team: "trendy_team_updatable_sdk_apis",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/api-check/system-api/Android.bp b/tests/signature/api-check/system-api/Android.bp
index 0ce7285..68b22e5 100644
--- a/tests/signature/api-check/system-api/Android.bp
+++ b/tests/signature/api-check/system-api/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
package {
- default_team: "trendy_team_java_core_libraries",
+ default_team: "trendy_team_updatable_sdk_apis",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/signature/lib/common/src/android/signature/cts/InterfaceChecker.java b/tests/signature/lib/common/src/android/signature/cts/InterfaceChecker.java
index 71a1ec1..00be7c1 100644
--- a/tests/signature/lib/common/src/android/signature/cts/InterfaceChecker.java
+++ b/tests/signature/lib/common/src/android/signature/cts/InterfaceChecker.java
@@ -66,6 +66,7 @@
HIDDEN_INTERFACE_METHOD_ALLOW_LIST.add("public abstract void android.os.IBinder.shellCommand(java.io.FileDescriptor,java.io.FileDescriptor,java.io.FileDescriptor,java.lang.String[],android.os.ShellCallback,android.os.ResultReceiver) throws android.os.RemoteException");
HIDDEN_INTERFACE_METHOD_ALLOW_LIST.add("public abstract int android.text.ParcelableSpan.getSpanTypeIdInternal()");
HIDDEN_INTERFACE_METHOD_ALLOW_LIST.add("public abstract void android.text.ParcelableSpan.writeToParcelInternal(android.os.Parcel,int)");
+ HIDDEN_INTERFACE_METHOD_ALLOW_LIST.add("public abstract android.view.KeyboardShortcutGroup android.view.WindowManager.getApplicationLaunchKeyboardShortcuts(int)");
HIDDEN_INTERFACE_METHOD_ALLOW_LIST.add("public abstract void android.view.WindowManager.requestAppKeyboardShortcuts(android.view.WindowManager$KeyboardShortcutsReceiver,int)");
HIDDEN_INTERFACE_METHOD_ALLOW_LIST.add("public abstract boolean javax.microedition.khronos.egl.EGL10.eglReleaseThread()");
HIDDEN_INTERFACE_METHOD_ALLOW_LIST.add("public abstract void org.w3c.dom.ls.LSSerializer.setFilter(org.w3c.dom.ls.LSSerializerFilter)");
diff --git a/tests/tests/icu/Android.bp b/tests/tests/icu/Android.bp
index 17a8f3d..c515a5c 100644
--- a/tests/tests/icu/Android.bp
+++ b/tests/tests/icu/Android.bp
@@ -37,6 +37,18 @@
sdk_version: "test_current",
}
+android_ravenwood_test {
+ name: "CtsIcuTestCasesRavenwood",
+ java_resource_dirs: ["resources"],
+ static_libs: [
+ // Note: on Ravenwood, we only run the "core" part of CtsIcuTestCases for now.
+ // See external/icu/README-ravenwood.md for details.
+ "android-icu4j-tests-core-only",
+ ],
+ platform_apis: true,
+ auto_gen_config: true,
+}
+
java_test_host {
name: "CtsIcu4cTestCases",
defaults: ["cts_support_defaults"],
diff --git a/tests/tests/keystore/Android.bp b/tests/tests/keystore/Android.bp
index 3225377..9b14bed 100644
--- a/tests/tests/keystore/Android.bp
+++ b/tests/tests/keystore/Android.bp
@@ -77,7 +77,6 @@
"cts-wm-util",
"ctstestrunner-axt",
"flag-junit",
- "keystore2_flags_java",
"guava",
"hamcrest-library",
"junit",
diff --git a/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java b/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
index f947797..9eec567 100644
--- a/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
@@ -69,8 +69,8 @@
import androidx.test.runner.AndroidJUnit4;
import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.DEREncodableVector;
-import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
@@ -473,8 +473,8 @@
int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired)
throws Exception {
// Build description
- DEREncodableVector descriptionItems = new DEREncodableVector();
- descriptionItems.add(new DERInteger(keyFormat));
+ ASN1EncodableVector descriptionItems = new ASN1EncodableVector();
+ descriptionItems.add(new ASN1Integer(keyFormat));
descriptionItems.add(authorizationList);
DERSequence wrappedKeyDescription = new DERSequence(descriptionItems);
@@ -515,9 +515,9 @@
// Remove GCM tag from end of output
encryptedSecureKey = Arrays.copyOfRange(encryptedSecureKey, 0, len - tagSize);
- // Build ASN.1 DER encoded sequence WrappedKeyWrapper
- DEREncodableVector items = new DEREncodableVector();
- items.add(new DERInteger(WRAPPED_FORMAT_VERSION));
+ // Build ASN.1 encoded sequence WrappedKeyWrapper
+ ASN1EncodableVector items = new ASN1EncodableVector();
+ items.add(new ASN1Integer(WRAPPED_FORMAT_VERSION));
items.add(new DEROctetString(encryptedEphemeralKeys));
items.add(new DEROctetString(iv));
items.add(wrappedKeyDescription);
@@ -527,27 +527,27 @@
}
private DERSequence makeSymKeyAuthList(int size, int algo) {
- DEREncodableVector allPurposes = new DEREncodableVector();
- allPurposes.add(new DERInteger(KM_PURPOSE_ENCRYPT));
- allPurposes.add(new DERInteger(KM_PURPOSE_DECRYPT));
+ ASN1EncodableVector allPurposes = new ASN1EncodableVector();
+ allPurposes.add(new ASN1Integer(KM_PURPOSE_ENCRYPT));
+ allPurposes.add(new ASN1Integer(KM_PURPOSE_DECRYPT));
DERSet purposeSet = new DERSet(allPurposes);
DERTaggedObject purpose =
new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
DERTaggedObject algorithm =
- new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), new DERInteger(algo));
+ new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), new ASN1Integer(algo));
DERTaggedObject keySize =
- new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size));
+ new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size));
- DEREncodableVector allBlockModes = new DEREncodableVector();
- allBlockModes.add(new DERInteger(KM_MODE_ECB));
- allBlockModes.add(new DERInteger(KM_MODE_CBC));
+ ASN1EncodableVector allBlockModes = new ASN1EncodableVector();
+ allBlockModes.add(new ASN1Integer(KM_MODE_ECB));
+ allBlockModes.add(new ASN1Integer(KM_MODE_CBC));
DERSet blockModeSet = new DERSet(allBlockModes);
DERTaggedObject blockMode =
new DERTaggedObject(true, removeTagType(KM_TAG_BLOCK_MODE), blockModeSet);
- DEREncodableVector allPaddings = new DEREncodableVector();
- allPaddings.add(new DERInteger(KM_PAD_PKCS7));
- allPaddings.add(new DERInteger(KM_PAD_NONE));
+ ASN1EncodableVector allPaddings = new ASN1EncodableVector();
+ allPaddings.add(new ASN1Integer(KM_PAD_PKCS7));
+ allPaddings.add(new ASN1Integer(KM_PAD_NONE));
DERSet paddingSet = new DERSet(allPaddings);
DERTaggedObject padding =
new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet);
@@ -556,7 +556,7 @@
new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
// Build sequence
- DEREncodableVector allItems = new DEREncodableVector();
+ ASN1EncodableVector allItems = new ASN1EncodableVector();
allItems.add(purpose);
allItems.add(algorithm);
allItems.add(keySize);
@@ -576,40 +576,40 @@
}
private DERSequence makeRsaAuthList(int size) {
- DEREncodableVector allPurposes = new DEREncodableVector();
- allPurposes.add(new DERInteger(KM_PURPOSE_ENCRYPT));
- allPurposes.add(new DERInteger(KM_PURPOSE_DECRYPT));
- allPurposes.add(new DERInteger(KM_PURPOSE_SIGN));
- allPurposes.add(new DERInteger(KM_PURPOSE_VERIFY));
+ ASN1EncodableVector allPurposes = new ASN1EncodableVector();
+ allPurposes.add(new ASN1Integer(KM_PURPOSE_ENCRYPT));
+ allPurposes.add(new ASN1Integer(KM_PURPOSE_DECRYPT));
+ allPurposes.add(new ASN1Integer(KM_PURPOSE_SIGN));
+ allPurposes.add(new ASN1Integer(KM_PURPOSE_VERIFY));
DERSet purposeSet = new DERSet(allPurposes);
DERTaggedObject purpose =
new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
DERTaggedObject algorithm =
new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM),
- new DERInteger(KM_ALGORITHM_RSA));
+ new ASN1Integer(KM_ALGORITHM_RSA));
DERTaggedObject keySize =
- new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size));
+ new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size));
- DEREncodableVector allDigests = new DEREncodableVector();
- allDigests.add(new DERInteger(KM_DIGEST_NONE));
- allDigests.add(new DERInteger(KM_DIGEST_MD5));
- allDigests.add(new DERInteger(KM_DIGEST_SHA1));
- allDigests.add(new DERInteger(KM_DIGEST_SHA_2_224));
- allDigests.add(new DERInteger(KM_DIGEST_SHA_2_256));
- allDigests.add(new DERInteger(KM_DIGEST_SHA_2_384));
- allDigests.add(new DERInteger(KM_DIGEST_SHA_2_512));
+ ASN1EncodableVector allDigests = new ASN1EncodableVector();
+ allDigests.add(new ASN1Integer(KM_DIGEST_NONE));
+ allDigests.add(new ASN1Integer(KM_DIGEST_MD5));
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA1));
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_224));
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_256));
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_384));
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_512));
DERSet digestSet = new DERSet(allDigests);
DERTaggedObject digest =
new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet);
- DEREncodableVector allPaddings = new DEREncodableVector();
- allPaddings.add(new DERInteger(KM_PAD_PKCS7));
- allPaddings.add(new DERInteger(KM_PAD_NONE));
- allPaddings.add(new DERInteger(KM_PAD_RSA_OAEP));
- allPaddings.add(new DERInteger(KM_PAD_RSA_PSS));
- allPaddings.add(new DERInteger(KM_PAD_RSA_PKCS1_1_5_ENCRYPT));
- allPaddings.add(new DERInteger(KM_PAD_RSA_PKCS1_1_5_SIGN));
+ ASN1EncodableVector allPaddings = new ASN1EncodableVector();
+ allPaddings.add(new ASN1Integer(KM_PAD_PKCS7));
+ allPaddings.add(new ASN1Integer(KM_PAD_NONE));
+ allPaddings.add(new ASN1Integer(KM_PAD_RSA_OAEP));
+ allPaddings.add(new ASN1Integer(KM_PAD_RSA_PSS));
+ allPaddings.add(new ASN1Integer(KM_PAD_RSA_PKCS1_1_5_ENCRYPT));
+ allPaddings.add(new ASN1Integer(KM_PAD_RSA_PKCS1_1_5_SIGN));
DERSet paddingSet = new DERSet(allPaddings);
DERTaggedObject padding =
new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet);
@@ -618,7 +618,7 @@
new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
// Build sequence
- DEREncodableVector allItems = new DEREncodableVector();
+ ASN1EncodableVector allItems = new ASN1EncodableVector();
allItems.add(purpose);
allItems.add(algorithm);
allItems.add(keySize);
@@ -630,24 +630,24 @@
}
private DERSequence makeEcAuthList(int size) {
- DEREncodableVector allPurposes = new DEREncodableVector();
- allPurposes.add(new DERInteger(KM_PURPOSE_SIGN));
- allPurposes.add(new DERInteger(KM_PURPOSE_VERIFY));
+ ASN1EncodableVector allPurposes = new ASN1EncodableVector();
+ allPurposes.add(new ASN1Integer(KM_PURPOSE_SIGN));
+ allPurposes.add(new ASN1Integer(KM_PURPOSE_VERIFY));
DERSet purposeSet = new DERSet(allPurposes);
DERTaggedObject purpose =
new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);
DERTaggedObject algorithm =
new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM),
- new DERInteger(KM_ALGORITHM_EC));
+ new ASN1Integer(KM_ALGORITHM_EC));
DERTaggedObject keySize =
- new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size));
+ new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new ASN1Integer(size));
- DEREncodableVector allDigests = new DEREncodableVector();
- allDigests.add(new DERInteger(KM_DIGEST_SHA_2_224));
- allDigests.add(new DERInteger(KM_DIGEST_SHA_2_256));
- allDigests.add(new DERInteger(KM_DIGEST_SHA_2_384));
- allDigests.add(new DERInteger(KM_DIGEST_SHA_2_512));
+ ASN1EncodableVector allDigests = new ASN1EncodableVector();
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_224));
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_256));
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_384));
+ allDigests.add(new ASN1Integer(KM_DIGEST_SHA_2_512));
DERSet digestSet = new DERSet(allDigests);
DERTaggedObject digest =
new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet);
@@ -656,7 +656,7 @@
new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);
// Build sequence
- DEREncodableVector allItems = new DEREncodableVector();
+ ASN1EncodableVector allItems = new ASN1EncodableVector();
allItems.add(purpose);
allItems.add(algorithm);
allItems.add(keySize);
diff --git a/tests/tests/keystore/src/android/keystore/cts/LegacyKeystoreTest.java b/tests/tests/keystore/src/android/keystore/cts/LegacyKeystoreTest.java
deleted file mode 100644
index 26236e5..0000000
--- a/tests/tests/keystore/src/android/keystore/cts/LegacyKeystoreTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.keystore.cts;
-
-import static org.junit.Assert.assertThrows;
-
-import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.security.keystore2.Flags;
-import android.security.legacykeystore.ILegacyKeystore;
-
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Tests for the Legacy Keystore service, focusing on its behavior under different flag settings.
- */
-public class LegacyKeystoreTest {
- private static final String LEGACY_KEYSTORE_SERVICE_NAME = "android.security.legacykeystore";
- private static final String TEST_DATABASE_NAME = "test_database";
-
- private static final byte[] TEST_DATA = new byte[512];
-
- @ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
- @Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
-
-
- /** Obtains an interface to the Legacy Keystore service. */
- public static ILegacyKeystore getLegacyKeystore() {
- return ILegacyKeystore.Stub.asInterface(
- ServiceManager.checkService(LEGACY_KEYSTORE_SERVICE_NAME));
- }
-
- @Test
- @DisableFlags(Flags.FLAG_DISABLE_LEGACY_KEYSTORE_PUT_V2)
- public void testPutWithPutFlagDisabled() throws Exception {
- ILegacyKeystore legacyKeystore = getLegacyKeystore();
- // put operation should not throw an exception when the flag is disabled.
- legacyKeystore.put(TEST_DATABASE_NAME, ILegacyKeystore.UID_SELF, TEST_DATA);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_DISABLE_LEGACY_KEYSTORE_GET)
- public void testGetWithGetFlagDisabled() throws Exception {
- ILegacyKeystore legacyKeystore = getLegacyKeystore();
- // get operation should not throw an exception when the flag is disabled.
- legacyKeystore.get(TEST_DATABASE_NAME, ILegacyKeystore.UID_SELF);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_DISABLE_LEGACY_KEYSTORE_GET)
- public void testGetWithGetFlagEnabled_ThrowsException() {
- ILegacyKeystore legacyKeystore = getLegacyKeystore();
- assertThrows(
- ServiceSpecificException.class,
- () -> legacyKeystore.get(TEST_DATABASE_NAME, ILegacyKeystore.UID_SELF));
- }
-}
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/DirectAudioProfilesForAttributesTest.kt b/tests/tests/media/audio/src/android/media/audio/cts/DirectAudioProfilesForAttributesTest.kt
index 50ecc03..d223318 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/DirectAudioProfilesForAttributesTest.kt
+++ b/tests/tests/media/audio/src/android/media/audio/cts/DirectAudioProfilesForAttributesTest.kt
@@ -112,7 +112,9 @@
// Utils
private fun AudioProfile.getAllAudioFormats() =
- sampleRates.map { sampleRate ->
+ sampleRates.filter {
+ it <= AudioFormat.SAMPLE_RATE_HZ_MAX
+ }.map { sampleRate ->
channelMasks.map { channelMask ->
AudioFormat.Builder()
.setEncoding(format)
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
index 372f31f..7b457df 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
@@ -2009,14 +2009,14 @@
return; // skip
}
- // Decode to Surface.
- Surface s = getActivity().getSurfaceHolder().getSurface();
- int frames1 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, s);
+ // Decode to buffer.
+ int frames1 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, null);
assertEquals("wrong number of frames decoded", frameNum, frames1);
- // Decode to buffer.
- int frames2 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, null);
- assertEquals("different number of frames when using Surface", frames1, frames2);
+ // Decode to Surface.
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ int frames2 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, s);
+ assertEquals("different number of frames when using buffer", frames1, frames2);
}
@Test
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerCodecActivity.java b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerCodecActivity.java
index 7077122..9e1e9f3 100644
--- a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerCodecActivity.java
+++ b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerCodecActivity.java
@@ -17,6 +17,7 @@
package android.media.misc.cts;
import android.app.Activity;
+import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
@@ -31,7 +32,6 @@
public class ResourceManagerCodecActivity extends Activity {
private static final String TAG = "ResourceManagerCodecActivity";
- private static final int MAX_INSTANCES = 32;
private static final int FRAME_RATE = 30;
private static final int IFRAME_INTERVAL = 10;
private boolean mHighResolution = false;
@@ -57,7 +57,10 @@
mMime = extras.getString("mime", mMime);
}
- if (allocateCodecs(MAX_INSTANCES) == MAX_INSTANCES) {
+ Context context = getApplicationContext();
+ int maxInstances = ResourceManagerStubActivity.getMaxCodecInstances(context);
+
+ if (allocateCodecs(maxInstances) == maxInstances) {
// As we haven't reached the limit with MAX_INSTANCES,
// no need to wait for reclaim exception.
Log.d(TAG, "We may not get reclaim event");
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerStubActivity.java b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerStubActivity.java
index d16a48d..7dd6922 100644
--- a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerStubActivity.java
+++ b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerStubActivity.java
@@ -18,6 +18,7 @@
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -34,6 +35,12 @@
// Test case was skipped as the device doesn't have any camera available for recording.
public static final int RESULT_CODE_NO_CAMERA = Activity.RESULT_FIRST_USER + 3;
+ // The max concurrent codec instances.
+ private static final int MAX_INSTANCES = 32;
+ // Reduce the max concurrent codec instances on Low Ram Devices
+ // to 8 to avoid getting into low memory issues.
+ private static final int LOW_RAM_DEVICE_MAX_INSTANCES = 8;
+
private static final String TAG = "ResourceManagerStubActivity";
private final Object mFinishEvent = new Object();
private int[] mRequestCodes = {0, 1};
@@ -310,4 +317,19 @@
mResults[1] = RESULT_OK;
processActivityResults();
}
+
+ /**
+ * The max concurrent codec instances allowed to created
+ * by the test activities.
+ * Though we set this to 32 (or 8 on low ram devices), it could be
+ * lesser than that, based on how many concurrent codec instances can be supported
+ * by the oem implementation.
+ */
+ public static int getMaxCodecInstances(Context context) {
+ boolean isLowRamDevice = context.getSystemService(ActivityManager.class).isLowRamDevice();
+ if (isLowRamDevice) {
+ return LOW_RAM_DEVICE_MAX_INSTANCES;
+ }
+ return MAX_INSTANCES;
+ }
}
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTest.java b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTest.java
index ec281e9..32b6135 100644
--- a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTest.java
+++ b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTest.java
@@ -39,6 +39,36 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Verification of ResourceManagerService functionality.
+ *
+ * This tests codec reclaim by launching 2 activities:
+ * - Activity1: A background activity (that runs in its own process) that creates,
+ * configures and starts an allowable number of codecs.
+ * - Activity2: A foreground activity (that runs in its own process) that creates,
+ * configures and starts codecs, which would reclaim codecs from the Activity1.
+ *
+ * The Activity2 is started 5 seconds after Activity1 to give enough time for
+ * Activity1 to use all the codecs available.
+ *
+ * Once started, Activity1 waits for (at most) 15 seconds for a possible exception.
+ * If the expected exception (MediaCodec.CodecException#ERROR_RECLAIMED) was caught,
+ * it will finish/complete the Activity with SUCCESS or FAILED otherwise.
+ *
+ * Once started, Activity2 is expected to successfully create, configure and start the
+ * codecs (possibly by reclaiming a codec from Activity1). This activity ends with SUCCESS
+ * upon successful codec operation or FAILED otherwise.
+ *
+ * The test waits on both the Activities to complete with SUCCESS for the test to PASS.
+ *
+ * Since Activity1 starts vendor supported maximum concurrent codecs, on some devices
+ * this may cause the device to run out of memory before codec reclaimation is signaled.
+ * When the device runs out of memory, lmkd kills the applications (including the test Activities).
+ * Upon lmkd killing test Activities, the test FAILs.
+ * To avoid that, the test reduces the maximum (concurrent) codec instances on low ram devices
+ * to 8 (from 32).
+ * This may result in test not verifying reclaimation.
+ */
@RequiresDevice
@AppModeFull(reason = "TODO: evaluate and port to instant")
@FrameworkSpecificTest
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivity1.java b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivity1.java
index 921bd81..60a36459 100644
--- a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivity1.java
+++ b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivity1.java
@@ -16,6 +16,7 @@
package android.media.misc.cts;
+import android.content.Context;
import android.os.Bundle;
import android.util.Log;
@@ -33,7 +34,10 @@
mWaitForReclaim = extras.getBoolean("wait-for-reclaim", mWaitForReclaim);
}
- if (allocateCodecs(MAX_INSTANCES) == MAX_INSTANCES) {
+ Context context = getApplicationContext();
+ int maxCodecInstances = ResourceManagerStubActivity.getMaxCodecInstances(context);
+
+ if (allocateCodecs(maxCodecInstances) == maxCodecInstances) {
// haven't reached the limit with MAX_INSTANCES, no need to wait for reclaim exception.
mWaitForReclaim = false;
}
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivity2.java b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivity2.java
index 4498a26..a731389 100644
--- a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivity2.java
+++ b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivity2.java
@@ -16,6 +16,7 @@
package android.media.misc.cts;
+import android.content.Context;
import android.util.Log;
public class ResourceManagerTestActivity2 extends ResourceManagerTestActivityBase {
@@ -29,7 +30,9 @@
// Try to create as many as MAX_INSTANCES codecs from this foreground activity
// so that we run into Resource conflict (INSUFFICIENT_RESOURCE) situation
// and eventually reclaim a codec from the background activity.
- int codecCount = allocateCodecs(MAX_INSTANCES);
+ Context context = getApplicationContext();
+ int maxCodecInstances = ResourceManagerStubActivity.getMaxCodecInstances(context);
+ int codecCount = allocateCodecs(maxCodecInstances);
int result = RESULT_OK;
// See if we have failed to create at least one codec.
if (codecCount == 0) {
@@ -38,7 +41,7 @@
// If we have set codec-importance, then we expect reclaim error, provided,
// the activity has already created MAX_INSTANCES of codecs.
// So wait for the codecs to be used and reclaim error to be thrown.
- if (mChangingCodecImportance && result == RESULT_OK && codecCount < MAX_INSTANCES) {
+ if (mChangingCodecImportance && result == RESULT_OK && codecCount < maxCodecInstances) {
useCodecs();
} else {
finishWithResult(result);
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivityBase.java b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivityBase.java
index 02ff2eb..90a9a4f 100644
--- a/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivityBase.java
+++ b/tests/tests/media/misc/src/android/media/misc/cts/ResourceManagerTestActivityBase.java
@@ -36,7 +36,6 @@
private static final int FRAME_RATE = 10;
// 10 seconds between I-frames
private static final int IFRAME_INTERVAL = 10;
- protected static final int MAX_INSTANCES = 32;
// Less important codec of value 100.
private static final int CODEC_IMPORTANCE_100 = 100;
private static final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/CodecTest.java b/tests/tests/mediastress/src/android/mediastress/cts/CodecTest.java
index 3cd4afb..085bce5 100644
--- a/tests/tests/mediastress/src/android/mediastress/cts/CodecTest.java
+++ b/tests/tests/mediastress/src/android/mediastress/cts/CodecTest.java
@@ -16,18 +16,11 @@
package android.mediastress.cts;
-import android.content.res.AssetFileDescriptor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
-import android.media.MediaRecorder;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -37,19 +30,10 @@
public class CodecTest {
private static String TAG = "CodecTest";
private static MediaPlayer mMediaPlayer;
- private MediaPlayer.OnPreparedListener mOnPreparedListener;
private static int WAIT_FOR_COMMAND_TO_COMPLETE = 60000; //1 min max.
- private static boolean mInitialized = false;
- private static boolean mPrepareReset = false;
private static Looper mLooper = null;
private static final Object mLock = new Object();
- private static final Object mPrepareDone = new Object();
- private static final Object mVideoSizeChanged = new Object();
- private static boolean mOnPrepareSuccess = false;
- private static final long PAUSE_WAIT_TIME = 3000;
- private static final long WAIT_TIME = 2000;
- private static final int SEEK_TIME = 10000;
private static final int PLAYBACK_SETTLE_TIME_MS = 5000;
private static final int SETUP_SETTLE_TIME_MS = 5000;
@@ -64,584 +48,6 @@
public static int mMediaInfoNotSeekableCount = 0;
public static int mMediaInfoMetdataUpdateCount = 0;
- public static String printCpuInfo() {
- String cm = "dumpsys cpuinfo";
- String cpuinfo = null;
- int ch;
- try {
- Process p = Runtime.getRuntime().exec(cm);
- InputStream in = p.getInputStream();
- StringBuffer sb = new StringBuffer(512);
- while ( ( ch = in.read() ) != -1 ) {
- sb.append((char) ch);
- }
- cpuinfo = sb.toString();
- } catch (IOException e) {
- Log.v(TAG, e.toString());
- }
- return cpuinfo;
- }
-
-
- public static int getDuration(String filePath) {
- Log.v(TAG, "getDuration - " + filePath);
- MediaPlayer mp = new MediaPlayer();
- try {
- mp.setDataSource(filePath);
- mp.prepare();
- } catch (Exception e) {
- Log.v(TAG, e.toString());
- }
- int duration = mp.getDuration();
- Log.v(TAG, "Duration " + duration);
- mp.release();
- Log.v(TAG, "release");
- return duration;
- }
-
- public static boolean getCurrentPosition(String filePath) {
- Log.v(TAG, "GetCurrentPosition - " + filePath);
- int currentPosition = 0;
- long t1=0;
- long t2 =0;
- MediaPlayer mp = new MediaPlayer();
- try {
- mp.setDataSource(filePath);
- Log.v(TAG, "start playback");
- mp.prepare();
- mp.start();
- t1=SystemClock.uptimeMillis();
- Thread.sleep(10000);
- mp.pause();
- Thread.sleep(PAUSE_WAIT_TIME);
- t2=SystemClock.uptimeMillis();
- } catch (Exception e) {
- Log.v(TAG, e.toString());
- }
- currentPosition = mp.getCurrentPosition();
- mp.stop();
- mp.release();
- Log.v(TAG, "mp currentPositon = " + currentPosition + " play duration = " + (t2-t1));
-
- if ((currentPosition < ((t2-t1) *1.2)) && (currentPosition > 0))
- return true;
- else
- return false;
- }
-
- public static boolean seekTo(String filePath) {
- Log.v(TAG, "seekTo " + filePath);
- int currentPosition = 0;
- MediaPlayer mp = new MediaPlayer();
- try {
- mp.setDataSource(filePath);
- mp.prepare();
- mp.start();
- mp.seekTo(SEEK_TIME);
- Thread.sleep(WAIT_TIME);
- currentPosition = mp.getCurrentPosition();
- } catch (Exception e) {
- Log.v(TAG, e.getMessage());
- }
- mp.stop();
- mp.release();
- Log.v(TAG, "CurrentPosition = " + currentPosition);
- //The currentposition should be at least greater than the 80% of seek time
- if ((currentPosition > SEEK_TIME *0.8))
- return true;
- else
- return false;
- }
-
- public static boolean setLooping(String filePath) {
- int currentPosition = 0;
- int duration = 0;
- long t1 =0;
- long t2 =0;
- Log.v (TAG, "SetLooping - " + filePath);
- MediaPlayer mp = new MediaPlayer();
- try {
- mp.setDataSource(filePath);
- mp.prepare();
- duration = mp.getDuration();
- Log.v(TAG, "setLooping duration " + duration);
- mp.setLooping(true);
- mp.start();
- Thread.sleep(5000);
- mp.seekTo(duration - 5000);
- t1=SystemClock.uptimeMillis();
- Thread.sleep(20000);
- t2=SystemClock.uptimeMillis();
- Log.v(TAG, "pause");
- //Bug# 1106852 - IllegalStateException will be thrown if pause is called
- //in here
- //mp.pause();
- currentPosition = mp.getCurrentPosition();
- Log.v(TAG, "looping position " + currentPosition + "duration = " + (t2-t1));
- } catch (Exception e) {
- Log.v(TAG, "Exception : " + e.toString());
- }
- mp.stop();
- mp.release();
- //The current position should be within 20% of the sleep time
- //and should be greater than zero.
- if ((currentPosition < ((t2-t1-5000)*1.2)) && currentPosition > 0)
- return true;
- else
- return false;
- }
-
- public static boolean pause(String filePath) throws Exception {
- Log.v(TAG, "pause - " + filePath);
- boolean misPlaying = true;
- boolean pauseResult = false;
- long t1=0;
- long t2=0;
- MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(filePath);
- mp.prepare();
- int duration = mp.getDuration();
- mp.start();
- t1=SystemClock.uptimeMillis();
- Thread.sleep(5000);
- mp.pause();
- Thread.sleep(PAUSE_WAIT_TIME);
- t2=SystemClock.uptimeMillis();
- misPlaying = mp.isPlaying();
- int curPosition = mp.getCurrentPosition();
- Log.v(TAG, filePath + " pause currentPositon " + curPosition);
- Log.v(TAG, "isPlaying "+ misPlaying + " wait time " + (t2 - t1) );
- String cpuinfo = printCpuInfo();
- Log.v(TAG, cpuinfo);
- if ((curPosition>0) && (curPosition < ((t2-t1) * 1.3)) && (misPlaying == false))
- pauseResult = true;
- mp.stop();
- mp.release();
- return pauseResult;
- }
-
- public static void prepareStopRelease(String filePath) throws Exception {
- Log.v(TAG, "prepareStopRelease" + filePath);
- MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(filePath);
- mp.prepare();
- mp.stop();
- mp.release();
- }
-
- public static void preparePauseRelease(String filePath) throws Exception {
- Log.v(TAG, "preparePauseRelease" + filePath);
- MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(filePath);
- mp.prepare();
- mp.pause();
- mp.release();
- }
-
- static MediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener =
- new MediaPlayer.OnVideoSizeChangedListener() {
- public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
- synchronized (mVideoSizeChanged) {
- Log.v(TAG, "sizechanged notification received ...");
- mVideoSizeChanged.notify();
- }
- }
- };
-
- //Register the videoSizeChanged listener
- public static int videoHeight(String filePath) throws Exception {
- Log.v(TAG, "videoHeight - " + filePath);
- int videoHeight = 0;
- synchronized (mLock) {
- initializeMessageLooper();
- try {
- mLock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch(Exception e) {
- Log.v(TAG, "looper was interrupted.");
- return 0;
- }
- }
- try {
- mMediaPlayer.setDataSource(filePath);
- mMediaPlayer.setDisplay(MediaFrameworkTest.getSurfaceView().getHolder());
- mMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
- synchronized (mVideoSizeChanged) {
- try {
- mMediaPlayer.prepare();
- mMediaPlayer.start();
- mVideoSizeChanged.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch (Exception e) {
- Log.v(TAG, "wait was interrupted");
- }
- }
- videoHeight = mMediaPlayer.getVideoHeight();
- terminateMessageLooper();
- } catch (Exception e) {
- Log.e(TAG, e.getMessage());
- }
-
- return videoHeight;
- }
-
- //Register the videoSizeChanged listener
- public static int videoWidth(String filePath) throws Exception {
- Log.v(TAG, "videoWidth - " + filePath);
- int videoWidth = 0;
-
- synchronized (mLock) {
- initializeMessageLooper();
- try {
- mLock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch(Exception e) {
- Log.v(TAG, "looper was interrupted.");
- return 0;
- }
- }
- try {
- mMediaPlayer.setDataSource(filePath);
- mMediaPlayer.setDisplay(MediaFrameworkTest.getSurfaceView().getHolder());
- mMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
- synchronized (mVideoSizeChanged) {
- try {
- mMediaPlayer.prepare();
- mMediaPlayer.start();
- mVideoSizeChanged.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch (Exception e) {
- Log.v(TAG, "wait was interrupted");
- }
- }
- videoWidth = mMediaPlayer.getVideoWidth();
- terminateMessageLooper();
- } catch (Exception e) {
- Log.e(TAG, e.getMessage());
- }
- return videoWidth;
- }
-
- //This also test the streaming video which may take a long
- //time to start the playback.
- public static boolean videoSeekTo(String filePath) throws Exception {
- Log.v(TAG, "videoSeekTo - " + filePath);
- int currentPosition = 0;
- int duration = 0;
- boolean videoResult = false;
- MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(filePath);
- mp.setDisplay(MediaFrameworkTest.getSurfaceView().getHolder());
- mp.prepare();
- mp.start();
-
- Thread.sleep(5000);
- duration = mp.getDuration();
- Log.v(TAG, "video duration " + duration);
- mp.pause();
- Thread.sleep(PAUSE_WAIT_TIME);
- mp.seekTo(duration - 20000 );
- mp.start();
- Thread.sleep(1000);
- mp.pause();
- Thread.sleep(PAUSE_WAIT_TIME);
- mp.seekTo(duration/2);
- mp.start();
- Thread.sleep(10000);
- currentPosition = mp.getCurrentPosition();
- Log.v(TAG, "video currentPosition " + currentPosition);
- mp.release();
- if (currentPosition > (duration /2 )*0.9)
- return true;
- else
- return false;
-
- }
-
- public static boolean seekToEnd(String filePath) {
- Log.v(TAG, "seekToEnd - " + filePath);
- int duration = 0;
- int currentPosition = 0;
- boolean isPlaying = false;
- MediaPlayer mp = new MediaPlayer();
- try {
- mp.setDataSource(filePath);
- Log.v(TAG, "start playback");
- mp.prepare();
- duration = mp.getDuration();
- mp.seekTo(duration - 3000);
- mp.start();
- Thread.sleep(6000);
- } catch (Exception e) {}
- isPlaying = mp.isPlaying();
- currentPosition = mp.getCurrentPosition();
- Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
- mp.stop();
- mp.release();
- Log.v(TAG, "duration = " + duration);
- if (currentPosition < 0.9 * duration || isPlaying)
- return false;
- else
- return true;
- }
-
- public static boolean shortMediaStop(String filePath) {
- Log.v(TAG, "shortMediaStop - " + filePath);
- //This test is only for the short media file
- int duration = 0;
- int currentPosition = 0;
- boolean isPlaying = false;
- MediaPlayer mp = new MediaPlayer();
- try {
- mp.setDataSource(filePath);
- Log.v(TAG, "start playback");
- mp.prepare();
- duration = mp.getDuration();
- mp.start();
- Thread.sleep(10000);
- } catch (Exception e) {}
- isPlaying = mp.isPlaying();
- currentPosition = mp.getCurrentPosition();
- Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
- mp.stop();
- mp.release();
- Log.v(TAG, "duration = " + duration);
- if (currentPosition > duration || isPlaying)
- return false;
- else
- return true;
- }
-
- public static boolean playToEnd(String filePath) {
- Log.v(TAG, "shortMediaStop - " + filePath);
- //This test is only for the short media file
- int duration = 200000;
- int updateDuration = 0;
- int currentPosition = 0;
- boolean isPlaying = false;
- MediaPlayer mp = new MediaPlayer();
- try {
- Thread.sleep(5000);
- mp.setDataSource(filePath);
- Log.v(TAG, "start playback");
- mp.prepare();
- //duration = mp.getDuration();
- mp.start();
- Thread.sleep(50000);
- } catch (Exception e){}
- isPlaying = mp.isPlaying();
- currentPosition = mp.getCurrentPosition();
- //updateDuration = mp.getDuration();
- Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
- mp.stop();
- mp.release();
- //Log.v(TAG, "duration = " + duration);
- //Log.v(TAG, "Update duration = " + updateDuration);
- if (currentPosition > duration || isPlaying)
- return false;
- else
- return true;
- }
-
- public static boolean seektoBeforeStart(String filePath){
- Log.v(TAG, "seektoBeforeStart - " + filePath);
- //This test is only for the short media file
- int duration = 0;
- int currentPosition = 0;
-
- MediaPlayer mp = new MediaPlayer();
- try {
- mp.setDataSource(filePath);
- mp.prepare();
- duration = mp.getDuration();
- mp.seekTo(duration - 10000);
- mp.start();
- currentPosition=mp.getCurrentPosition();
- mp.stop();
- mp.release();
- } catch (Exception e) {}
- if (currentPosition < duration/2)
- return false;
- else
- return true;
- }
-
- public static boolean mediaRecorderRecord(String filePath){
- Log.v(TAG, "SoundRecording - " + filePath);
- //This test is only for the short media file
- int duration = 0;
- try {
- MediaRecorder mRecorder = new MediaRecorder();
- mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
- mRecorder.setOutputFile(filePath);
- mRecorder.prepare();
- mRecorder.start();
- Thread.sleep(500);
- mRecorder.stop();
- Log.v(TAG, "sound recorded");
- mRecorder.release();
- } catch (Exception e) {
- Log.v(TAG, e.toString());
- }
-
- //Verify the recorded file
- MediaPlayer mp = new MediaPlayer();
- try {
- mp.setDataSource(filePath);
- mp.prepare();
- duration = mp.getDuration();
- Log.v(TAG,"Duration " + duration);
- mp.release();
- } catch (Exception e) {}
- //Check the record media file length is greate than zero
- if (duration > 0)
- return true;
- else
- return false;
-
- }
-
- //Test for mediaMeta Data Thumbnail
- public static boolean getThumbnail(String filePath, String goldenPath) {
- Log.v(TAG, "getThumbnail - " + filePath);
-
- int goldenHeight = 0;
- int goldenWidth = 0;
- int outputWidth = 0;
- int outputHeight = 0;
-
- //This test is only for the short media file
- try {
- BitmapFactory mBitmapFactory = new BitmapFactory();
-
- Bitmap outThumbnail;
- try (MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever()) {
- try {
- mediaMetadataRetriever.setDataSource(filePath);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- outThumbnail = mediaMetadataRetriever.getFrameAtTime(-1);
- }
-
- //Verify the thumbnail
- Bitmap goldenBitmap = mBitmapFactory.decodeFile(goldenPath);
- outputWidth = outThumbnail.getWidth();
- outputHeight = outThumbnail.getHeight();
- goldenHeight = goldenBitmap.getHeight();
- goldenWidth = goldenBitmap.getWidth();
-
- //check the image dimension
- if ((outputWidth != goldenWidth) || (outputHeight != goldenHeight))
- return false;
-
- // Check half line of pixel
- int x = goldenHeight / 2;
- for (int j = 1; j < goldenWidth / 2; j++) {
- if (goldenBitmap.getPixel(x, j) != outThumbnail.getPixel(x, j)) {
- Log.v(TAG, "pixel = " + goldenBitmap.getPixel(x, j));
- return false;
- }
- }
- } catch (Exception e) {
- Log.v(TAG, e.toString());
- return false;
- }
- return true;
- }
-
- //Load midi file from resources
- public static boolean resourcesPlayback(AssetFileDescriptor afd, int expectedDuration) {
- int duration = 0;
- try {
- MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(), afd.getLength());
- mp.prepare();
- mp.start();
- duration = mp.getDuration();
- Thread.sleep(5000);
- mp.release();
- } catch (Exception e) {
- Log.v(TAG,e.getMessage());
- }
- if (duration > expectedDuration)
- return true;
- else
- return false;
- }
-
- public static boolean prepareAsyncReset(String filePath) {
- //preparesAsync
- try {
- MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(filePath);
- mp.prepareAsync();
- mp.reset();
- mp.release();
- } catch (Exception e) {
- Log.v(TAG,e.getMessage());
- return false;
- }
- return true;
- }
-
-
- public static boolean isLooping(String filePath) {
- MediaPlayer mp = null;
-
- try {
- mp = new MediaPlayer();
- if (mp.isLooping()) {
- Log.v(TAG, "MediaPlayer.isLooping() returned true after ctor");
- return false;
- }
- mp.setDataSource(filePath);
- mp.prepare();
-
- mp.setLooping(true);
- if (!mp.isLooping()) {
- Log.v(TAG, "MediaPlayer.isLooping() returned false after setLooping(true)");
- return false;
- }
-
- mp.setLooping(false);
- if (mp.isLooping()) {
- Log.v(TAG, "MediaPlayer.isLooping() returned true after setLooping(false)");
- return false;
- }
- } catch (Exception e) {
- Log.v(TAG, "Exception : " + e.toString());
- return false;
- } finally {
- if (mp != null)
- mp.release();
- }
-
- return true;
- }
-
- public static boolean isLoopingAfterReset(String filePath) {
- MediaPlayer mp = null;
- try {
- mp = new MediaPlayer();
- mp.setDataSource(filePath);
- mp.prepare();
-
- mp.setLooping(true);
- mp.reset();
- if (mp.isLooping()) {
- Log.v(TAG, "MediaPlayer.isLooping() returned true after reset()");
- return false;
- }
- } catch (Exception e){
- Log.v(TAG, "Exception : " + e.toString());
- return false;
- } finally {
- if (mp != null)
- mp.release();
- }
-
- return true;
- }
/*
* Initializes the message looper so that the mediaPlayer object can
@@ -660,7 +66,6 @@
mLooper = Looper.myLooper();
mMediaPlayer = new MediaPlayer();
synchronized (mLock) {
- mInitialized = true;
mLock.notify();
}
Looper.loop(); // Blocks forever until Looper.quit() is called.
@@ -681,75 +86,28 @@
}
}
- static MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
- public void onPrepared(MediaPlayer mp) {
- synchronized (mPrepareDone) {
- if(mPrepareReset) {
- Log.v(TAG, "call Reset");
- mMediaPlayer.reset();
+ private static MediaPlayer.OnCompletionListener mCompletionListener =
+ new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer mp) {
+ Log.v(TAG, "notify the completion callback");
+ mOnCompleteSuccess = true;
+ mCompletionLatch.countDown();
}
- Log.v(TAG, "notify the prepare callback");
- mPrepareDone.notify();
- mOnPrepareSuccess = true;
- }
- }
- };
+ };
- public static boolean prepareAsyncCallback(String filePath, boolean reset) throws Exception {
- //Added the PrepareReset flag which allow us to switch to different
- //test case.
- if (reset) {
- mPrepareReset = true;
- }
-
- synchronized (mLock) {
- initializeMessageLooper();
- try {
- mLock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch(Exception e) {
- Log.v(TAG, "looper was interrupted.");
- return false;
- }
- }
- try{
- mMediaPlayer.setOnPreparedListener(mPreparedListener);
- mMediaPlayer.setDataSource(filePath);
- mMediaPlayer.setDisplay(MediaFrameworkTest.getSurfaceView().getHolder());
- mMediaPlayer.prepareAsync();
- synchronized (mPrepareDone) {
- try {
- mPrepareDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
- } catch (Exception e) {
- Log.v(TAG, "wait was interrupted.");
+ private static MediaPlayer.OnErrorListener mOnErrorListener =
+ new MediaPlayer.OnErrorListener() {
+ public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
+ Log.v(TAG, "playback error");
+ mPlaybackError = true;
+ mp.reset();
+ mOnCompleteSuccess = false;
+ mCompletionLatch.countDown();
+ return true;
}
- }
- terminateMessageLooper();
- }catch (Exception e) {
- Log.v(TAG,e.getMessage());
- }
- return mOnPrepareSuccess;
- }
+ };
- static MediaPlayer.OnCompletionListener mCompletionListener = new MediaPlayer.OnCompletionListener() {
- public void onCompletion(MediaPlayer mp) {
- Log.v(TAG, "notify the completion callback");
- mOnCompleteSuccess = true;
- mCompletionLatch.countDown();
- }
- };
-
- static MediaPlayer.OnErrorListener mOnErrorListener = new MediaPlayer.OnErrorListener() {
- public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
- Log.v(TAG, "playback error");
- mPlaybackError = true;
- mp.reset();
- mOnCompleteSuccess = false;
- mCompletionLatch.countDown();
- return true;
- }
- };
-
- static MediaPlayer.OnInfoListener mInfoListener = new MediaPlayer.OnInfoListener() {
+ private static MediaPlayer.OnInfoListener mInfoListener = new MediaPlayer.OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_INFO_UNKNOWN:
@@ -793,8 +151,6 @@
// null == success, !null == reason why it failed
public static String playMediaSample(String fileName) throws Exception {
int duration = 0;
- int curPosition = 0;
- int nextPosition = 0;
setupPlaybackMarkers();
diff --git a/tests/tests/nfc/Android.bp b/tests/tests/nfc/Android.bp
index 21fffe0..55eec18 100644
--- a/tests/tests/nfc/Android.bp
+++ b/tests/tests/nfc/Android.bp
@@ -23,11 +23,13 @@
platform_apis: true,
static_libs: [
"android.nfc.flags-aconfig-java",
+ "nfc_aconfig_flags_lib",
"android.permission.flags-aconfig-java",
"ctstestrunner-axt",
"compatibility-device-util-axt",
"flag-junit",
"platform-test-annotations",
+ "testables",
"testng",
"androidx.appcompat_appcompat",
"CtsAppTestStubsShared",
diff --git a/tests/tests/nfc/AndroidManifest.xml b/tests/tests/nfc/AndroidManifest.xml
index 9461ba9..99ad49a 100644
--- a/tests/tests/nfc/AndroidManifest.xml
+++ b/tests/tests/nfc/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.NFC_PREFERRED_PAYMENT_INFO" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<queries>
<package android:name="com.android.test.foregroundnfc" />
@@ -55,6 +56,12 @@
<meta-data android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/background_aid_list"/>
</service>
+ <service android:name=".CtsMyOffHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE">
+ <intent-filter>
+ <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
+ </intent-filter>
+ <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service" android:resource="@xml/offhost_apdu_service"/>
+ </service>
<activity android:name="android.nfc.cts.NfcFCardEmulationActivity"
android:exported="false">
</activity>
diff --git a/tests/tests/nfc/NonPaymentNfcApp/AndroidManifest.xml b/tests/tests/nfc/NonPaymentNfcApp/AndroidManifest.xml
index 7682c76..c1a8e89 100644
--- a/tests/tests/nfc/NonPaymentNfcApp/AndroidManifest.xml
+++ b/tests/tests/nfc/NonPaymentNfcApp/AndroidManifest.xml
@@ -29,6 +29,22 @@
<meta-data android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/non_payment_aid_list"/>
</service>
+
+ <service
+ android:name=".NonPaymentQuickAccessWalletService"
+ android:label="Non Payment QAW"
+ android:exported="true"
+ android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.quickaccesswallet.QuickAccessWalletService" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <meta-data android:name="android.quickaccesswallet"
+ android:resource="@xml/quickaccesswallet_configuration" />;
+ </service>
+
+ <activity android:name="QuickAccessWalletActivity">
+ </activity>
</application>
</manifest>
diff --git a/tests/tests/nfc/NonPaymentNfcApp/res/xml/quickaccesswallet_configuration.xml b/tests/tests/nfc/NonPaymentNfcApp/res/xml/quickaccesswallet_configuration.xml
new file mode 100644
index 0000000..90ef6d4
--- /dev/null
+++ b/tests/tests/nfc/NonPaymentNfcApp/res/xml/quickaccesswallet_configuration.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ 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.
+ -->
+
+<quickaccesswallet-service
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:targetActivity="com.android.test.nonpaymentnfc.QuickAccessWalletActivity"/>
\ No newline at end of file
diff --git a/tests/tests/nfc/NonPaymentNfcApp/src/QuickAccessWalletActivity.java b/tests/tests/nfc/NonPaymentNfcApp/src/QuickAccessWalletActivity.java
new file mode 100644
index 0000000..b59ecba
--- /dev/null
+++ b/tests/tests/nfc/NonPaymentNfcApp/src/QuickAccessWalletActivity.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.app.Activity;
+
+public class QuickAccessWalletActivity extends Activity {
+}
diff --git a/tests/tests/nfc/NonPaymentNfcApp/src/com/android/test/nonpaymentnfc/NonPaymentQuickAccessWalletService.java b/tests/tests/nfc/NonPaymentNfcApp/src/com/android/test/nonpaymentnfc/NonPaymentQuickAccessWalletService.java
new file mode 100644
index 0000000..96879ab
--- /dev/null
+++ b/tests/tests/nfc/NonPaymentNfcApp/src/com/android/test/nonpaymentnfc/NonPaymentQuickAccessWalletService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.nonpaymentnfc;
+
+import android.service.quickaccesswallet.GetWalletCardsCallback;
+import android.service.quickaccesswallet.GetWalletCardsRequest;
+import android.service.quickaccesswallet.QuickAccessWalletService;
+import android.service.quickaccesswallet.SelectWalletCardRequest;
+
+public class NonPaymentQuickAccessWalletService extends QuickAccessWalletService {
+ @Override
+ public void onWalletCardsRequested(GetWalletCardsRequest request,
+ GetWalletCardsCallback callback) {
+
+ }
+
+ @Override
+ public void onWalletCardSelected(SelectWalletCardRequest request) {
+
+ }
+
+ @Override
+ public void onWalletDismissed() {
+
+ }
+}
diff --git a/tests/tests/nfc/WalletRoleHolderApp/AndroidManifest.xml b/tests/tests/nfc/WalletRoleHolderApp/AndroidManifest.xml
index 45de880..c7d72cb 100644
--- a/tests/tests/nfc/WalletRoleHolderApp/AndroidManifest.xml
+++ b/tests/tests/nfc/WalletRoleHolderApp/AndroidManifest.xml
@@ -36,7 +36,7 @@
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
</intent-filter>
<meta-data android:name="android.nfc.cardemulation.host_apdu_service"
- android:resource="@xml/payment_aid_list"/>
+ android:resource="@xml/xpayment_aid_list"/>
</service>
<activity android:name="com.android.test.walletroleholder.WalletRoleHolderForegroundActivity"
android:exported="true">
diff --git a/tests/tests/nfc/res/xml/custom_aid_list.xml b/tests/tests/nfc/res/xml/custom_aid_list.xml
index b73da02..accc93f 100644
--- a/tests/tests/nfc/res/xml/custom_aid_list.xml
+++ b/tests/tests/nfc/res/xml/custom_aid_list.xml
@@ -9,4 +9,5 @@
<polling-loop-filter android:name="261c0050"/>
<polling-loop-filter android:name="7f71156b" android:autoTransact="true"/>"
<polling-loop-filter android:name="b0343a5e" android:autoTransact="true"/>
+ <polling-loop-pattern-filter android:name="ae24db68.*"/>
</host-apdu-service>
diff --git a/tests/tests/nfc/res/xml/offhost_apdu_service.xml b/tests/tests/nfc/res/xml/offhost_apdu_service.xml
new file mode 100644
index 0000000..e809eea
--- /dev/null
+++ b/tests/tests/nfc/res/xml/offhost_apdu_service.xml
@@ -0,0 +1,6 @@
+<offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/CtsPaymentService">
+ <aid-group android:description="@string/CtsPaymentService" android:category="payment">
+ <aid-filter android:name="A000000004101014"/>
+ </aid-group>
+ </offhost-apdu-service>
\ No newline at end of file
diff --git a/tests/tests/nfc/src/android/nfc/cts/CardEmulationTest.java b/tests/tests/nfc/src/android/nfc/cts/CardEmulationTest.java
index 28fa030..ee49e73 100644
--- a/tests/tests/nfc/src/android/nfc/cts/CardEmulationTest.java
+++ b/tests/tests/nfc/src/android/nfc/cts/CardEmulationTest.java
@@ -9,6 +9,7 @@
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyInt;
@@ -36,11 +37,13 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
+import android.testing.PollingCheck;
import android.view.KeyEvent;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +65,7 @@
import org.mockito.internal.util.reflection.FieldSetter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HexFormat;
import java.util.List;
@@ -408,6 +412,43 @@
}
@Test
+ @RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
+ android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED})
+ public void testCustomFrameToCustomInTwoFullLoops() {
+ WalletRoleTestUtils.runWithRole(mContext, WalletRoleTestUtils.WALLET_HOLDER_PACKAGE_NAME,
+ () -> {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ adapter.notifyHceDeactivated();
+ CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ ComponentName customServiceName = new ComponentName(mContext,
+ CustomHostApduService.class);
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
+ Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(
+ customServiceName,
+ annotationStringHex, false));
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(6);
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_ON));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_A));
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex)));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_OFF));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_ON));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_A));
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex)));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_OFF));
+ ensurePreferredService(WalletRoleTestUtils.WALLET_HOLDER_SERVICE_DESC);
+ // Only the frames matching the filter should be delivered.
+ notifyPollingLoopAndWait(new ArrayList<PollingFrame>(
+ Arrays.asList(frames.get(2), frames.get(6))),
+ CustomHostApduService.class.getName());
+ adapter.notifyHceDeactivated();
+ });
+ }
+
+ @Test
@RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
public void testTypeAPollingLoopToForeground() {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
@@ -435,6 +476,102 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_NFC_OBSERVE_MODE)
+ public void testSetShouldDefaultToObserveModeShouldDefaultToObserveMode()
+ throws InterruptedException {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ assumeTrue(adapter.isObserveModeSupported());
+ adapter.notifyHceDeactivated();
+ Activity activity = createAndResumeActivity();
+ final CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ try {
+ ComponentName ctsService = new ComponentName(mContext, CtsMyHostApduService.class);
+ Assert.assertTrue(cardEmulation.setShouldDefaultToObserveModeForService(ctsService,
+ false));
+
+ Assert.assertTrue(cardEmulation.setPreferredService(activity, ctsService));
+ ensurePreferredService(CtsMyHostApduService.class);
+
+ Assert.assertFalse(adapter.isObserveModeEnabled());
+ Assert.assertTrue(cardEmulation.setShouldDefaultToObserveModeForService(ctsService,
+ true));
+ // Observe mode is set asynchronously, so just wait a bit to let it happen.
+ try {
+ CommonTestUtils.waitUntil("Observe mode hasn't been set", 1,
+ () -> adapter.isObserveModeEnabled());
+ } catch (InterruptedException ie) { }
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ } finally {
+ Assert.assertTrue(cardEmulation.unsetPreferredService(activity));
+ activity.finish();
+ adapter.notifyHceDeactivated();
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void testTypeAOneLoopPollingLoopToForeground() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ adapter.notifyHceDeactivated();
+ Activity activity = createAndResumeActivity();
+ final CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity,
+ new ComponentName(mContext,
+ CtsMyHostApduService.class)));
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(4);
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_ON));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_A));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_B));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_OFF));
+ ensurePreferredService(CtsMyHostApduService.class);
+ sCurrentPollLoopReceiver = new PollLoopReceiver(new ArrayList<PollingFrame>(0), null);
+ for (PollingFrame frame : frames) {
+ adapter.notifyPollingLoop(frame);
+ }
+ synchronized (sCurrentPollLoopReceiver) {
+ try {
+ sCurrentPollLoopReceiver.wait(5000);
+ } catch (InterruptedException ie) {
+ Assert.assertNull(ie);
+ }
+ }
+ sCurrentPollLoopReceiver.test();
+ sCurrentPollLoopReceiver = null;
+ } finally {
+ cardEmulation.unsetPreferredService(activity);
+ activity.finish();
+ adapter.notifyHceDeactivated();
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ @RequiresFlagsDisabled(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
+ public void testTypeABNoOffPollingLoopToDefault() {
+ ComponentName originalDefault = null;
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ adapter.notifyHceDeactivated();
+ try {
+ originalDefault = setDefaultPaymentService(CustomHostApduService.class);
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(7);
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_ON));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_A));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_B));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_A));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_B));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_A));
+ frames.add(createFrame(PollingFrame.POLLING_LOOP_TYPE_B));
+ ensurePreferredService(CustomHostApduService.class);
+ notifyPollingLoopAndWait(new ArrayList<PollingFrame>(frames),
+ CustomHostApduService.class.getName());
+ } finally {
+ setDefaultPaymentService(originalDefault);
+ adapter.notifyHceDeactivated();
+ }
+ }
+
+ @Test
@RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED})
public void testTypeAPollingLoopToForegroundWithWalletHolder() {
@@ -483,6 +620,126 @@
@Test
@RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void testTwoCustomPollingLoopToPreferredCustomAndBackgroundDynamic() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ assumeTrue(adapter.isObserveModeSupported());
+ adapter.notifyHceDeactivated();
+ CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ Activity activity = createAndResumeActivity();
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity,
+ new ComponentName(mContext, CustomHostApduService.class)));
+
+ ensurePreferredService(CustomHostApduService.class);
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+
+ ComponentName backgroundServiceName = new ComponentName(mContext,
+ BackgroundHostApduService.class);
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHex1 =
+ HexFormat.of().toHexDigits((testName + "background").hashCode());
+ Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(
+ backgroundServiceName, annotationStringHex1, false));
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(2);
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex1)));
+
+ ComponentName customServiceName = new ComponentName(mContext,
+ CustomHostApduService.class);
+
+ String annotationStringHex2 =
+ HexFormat.of().toHexDigits((testName + "custom").hashCode());
+ Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(
+ customServiceName, annotationStringHex2, false));
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex2)));
+
+ sCurrentPollLoopReceiver = new PollLoopReceiver(frames, null);
+ for (PollingFrame frame : frames) {
+ adapter.notifyPollingLoop(frame);
+ }
+ synchronized (sCurrentPollLoopReceiver) {
+ try {
+ sCurrentPollLoopReceiver.wait(5000);
+ } catch (InterruptedException ie) {
+ Assert.assertNull(ie);
+ }
+ }
+ Assert.assertEquals(frames.size(), sCurrentPollLoopReceiver.mReceivedFrames.size());
+ Assert.assertEquals(2, sCurrentPollLoopReceiver.mReceivedServiceNames.size());
+ sCurrentPollLoopReceiver = null;
+ } finally {
+ cardEmulation.unsetPreferredService(activity);
+ activity.finish();
+ sCurrentPollLoopReceiver = null;
+ adapter.notifyHceDeactivated();
+ }
+ }
+ @Test
+ @RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void testTwoCustomPollingLoopToCustomAndBackgroundDynamic() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ assumeTrue(adapter.isObserveModeSupported());
+ adapter.notifyHceDeactivated();
+ CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ Activity activity = createAndResumeActivity();
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity,
+ new ComponentName(mContext, CtsMyHostApduService.class)));
+
+ ensurePreferredService(CtsMyHostApduService.class);
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+
+ ComponentName backgroundServiceName = new ComponentName(mContext,
+ BackgroundHostApduService.class);
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHex1 =
+ HexFormat.of().toHexDigits((testName + "background").hashCode());
+ Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(
+ backgroundServiceName, annotationStringHex1, false));
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(2);
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex1)));
+
+ ComponentName customServiceName = new ComponentName(mContext,
+ CustomHostApduService.class);
+
+ String annotationStringHex2 =
+ HexFormat.of().toHexDigits((testName + "custom").hashCode());
+ Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(
+ customServiceName, annotationStringHex2, false));
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex2)));
+
+ sCurrentPollLoopReceiver = new PollLoopReceiver(frames, null);
+ for (PollingFrame frame : frames) {
+ adapter.notifyPollingLoop(frame);
+ }
+ synchronized (sCurrentPollLoopReceiver) {
+ try {
+ sCurrentPollLoopReceiver.wait(5000);
+ } catch (InterruptedException ie) {
+ Assert.assertNull(ie);
+ }
+ }
+ Assert.assertEquals(frames.size(),
+ sCurrentPollLoopReceiver.mReceivedFrames.size());
+ android.util.Log.i("PLF-test", String.join(", ",
+ sCurrentPollLoopReceiver.mReceivedServiceNames));
+ Assert.assertEquals(2, sCurrentPollLoopReceiver.mReceivedServiceNames.size());
+ sCurrentPollLoopReceiver = null;
+ } finally {
+ cardEmulation.unsetPreferredService(activity);
+ activity.finish();
+ sCurrentPollLoopReceiver = null;
+ adapter.notifyHceDeactivated();
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
public void testCustomPollingLoopToCustomDynamic() {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
adapter.notifyHceDeactivated();
@@ -502,6 +759,85 @@
@Test
@RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void testCustomPollingLoopToCustomDynamicAndRemove() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ adapter.notifyHceDeactivated();
+ CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ Activity activity = createAndResumeActivity();
+ ComponentName ctsServiceName = new ComponentName(mContext,
+ CtsMyHostApduService.class);
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity, ctsServiceName));
+ ensurePreferredService(CtsMyHostApduService.class);
+ ComponentName customServiceName =
+ new ComponentName(mContext, CustomHostApduService.class);
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
+ Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(customServiceName,
+ annotationStringHex, false));
+
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex)));
+ notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
+ adapter.notifyHceDeactivated();
+
+ Assert.assertTrue(cardEmulation.removePollingLoopFilterForService(customServiceName,
+ annotationStringHex));
+
+ notifyPollingLoopAndWait(frames, CtsMyHostApduService.class.getName());
+
+ } finally {
+ Assert.assertTrue(cardEmulation.unsetPreferredService(activity));
+ activity.finish();
+ adapter.notifyHceDeactivated();
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void testCustomPollingLoopToCustomWithPrefixDynamic() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ adapter.notifyHceDeactivated();
+ CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ ComponentName customServiceName = new ComponentName(mContext, CustomHostApduService.class);
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHexPrefix = HexFormat.of().toHexDigits(testName.hashCode());
+ String annotationStringHex = annotationStringHexPrefix + "123456789ABCDF";
+ String annotationStringHexPattern = annotationStringHexPrefix + ".*";
+ Assert.assertTrue(cardEmulation.registerPollingLoopPatternFilterForService(
+ customServiceName, annotationStringHexPattern, false));
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex)));
+ notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
+ Assert.assertTrue(cardEmulation.removePollingLoopPatternFilterForService(customServiceName,
+ annotationStringHexPrefix));
+ adapter.notifyHceDeactivated();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void testCustomPollingLoopToCustomWithPrefix() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ adapter.notifyHceDeactivated();
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHexPrefix = HexFormat.of().toHexDigits(testName.hashCode());
+ String annotationStringHex = annotationStringHexPrefix + "123456789ABCDF";
+
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex)));
+ notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
+
+ adapter.notifyHceDeactivated();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
@RequiresFlagsDisabled(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
public void testThreeWayConflictPollingLoopToForegroundDynamic() {
ComponentName originalDefault = null;
@@ -736,7 +1072,6 @@
String testName = new Object() {
}.getClass().getEnclosingMethod().getName();
String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
- android.util.Log.i("PLF", annotationStringHex);
ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
HexFormat.of().parseHex(annotationStringHex)));
@@ -746,39 +1081,170 @@
});
}
+ @Test
+ @RequiresFlagsEnabled({com.android.nfc.flags.Flags.FLAG_AUTO_DISABLE_OBSERVE_MODE,
+ android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
+ Flags.FLAG_NFC_OBSERVE_MODE,
+ android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED})
+ public void testAutoDisableObserveMode() throws Exception {
+ runWithRole(mContext, CTS_PACKAGE_NAME, () -> {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
+ assumeTrue(adapter.isObserveModeSupported());
+ adapter.notifyHceDeactivated();
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex)));
+ final CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ try {
+ ensurePreferredService(CtsMyHostApduService.class);
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ List<PollingFrame> receivedFrames =
+ notifyPollingLoopAndWait(frames, CtsMyHostApduService.class.getName());
+ Assert.assertFalse(receivedFrames.get(0).getTriggeredAutoTransact());
+ PollingCheck.check("Observe mode not disabled", 4000,
+ () -> !adapter.isObserveModeEnabled());
+ adapter.notifyHceDeactivated();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ } finally {
+ adapter.setObserveModeEnabled(false);
+ }
+ });
+ }
@Test
- @RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
- Flags.FLAG_NFC_OBSERVE_MODE})
- @RequiresFlagsDisabled(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
- public void testAutoTransact() {
+ @RequiresFlagsEnabled({com.android.nfc.flags.Flags.FLAG_AUTO_DISABLE_OBSERVE_MODE,
+ android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
+ Flags.FLAG_NFC_OBSERVE_MODE})
+ public void testDontAutoDisableObserveModeInForeground() throws Exception {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
assumeTrue(adapter.isObserveModeSupported());
adapter.notifyHceDeactivated();
- createAndResumeActivity();
String testName = new Object() {
}.getClass().getEnclosingMethod().getName();
String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
HexFormat.of().parseHex(annotationStringHex)));
- Assert.assertTrue(adapter.setObserveModeEnabled(true));
- Assert.assertTrue(adapter.isObserveModeEnabled());
- notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
- Assert.assertFalse(adapter.isObserveModeEnabled());
+ final CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ final Activity activity = createAndResumeActivity();
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity,
+ new ComponentName(mContext, CtsMyHostApduService.class)));
+ ensurePreferredService(CtsMyHostApduService.class);
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ List<PollingFrame> receivedFrames =
+ notifyPollingLoopAndWait(frames, CtsMyHostApduService.class.getName());
+ Assert.assertFalse(receivedFrames.get(0).getTriggeredAutoTransact());
+ Thread.currentThread().sleep(4000);
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ adapter.notifyHceDeactivated();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ } finally {
+ adapter.setObserveModeEnabled(false);
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled({com.android.nfc.flags.Flags.FLAG_AUTO_DISABLE_OBSERVE_MODE,
+ android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
+ Flags.FLAG_NFC_OBSERVE_MODE})
+ public void testDontAutoDisableObserveModeInForegroundTwoServices() throws Exception {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
+ assumeTrue(adapter.isObserveModeSupported());
adapter.notifyHceDeactivated();
- Assert.assertTrue(adapter.isObserveModeEnabled());
- adapter.setObserveModeEnabled(false);
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ final CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ String annotationStringHex1 = "5cadc10f";
+ ArrayList<PollingFrame> frames1 = new ArrayList<PollingFrame>(1);
+ frames1.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex1)));
+ ComponentName walletServiceName = WalletRoleTestUtils.getWalletRoleHolderService();
+ String annotationStringHex2 = HexFormat.of().toHexDigits((testName).hashCode());
+ ComponentName ctsComponentName = new ComponentName(mContext, CtsMyHostApduService.class);
+ Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(ctsComponentName,
+ annotationStringHex2, false));
+ ArrayList<PollingFrame> frames2 = new ArrayList<PollingFrame>(1);
+ frames2.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex2)));
+ final Activity activity = createAndResumeActivity();
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity, ctsComponentName));
+ ensurePreferredService(CtsMyHostApduService.class);
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ List<PollingFrame> receivedFrames =
+ notifyPollingLoopAndWait(frames1,
+ WalletRoleTestUtils.getWalletRoleHolderService().getClassName());
+ Assert.assertFalse(receivedFrames.get(0).getTriggeredAutoTransact());
+ receivedFrames =
+ notifyPollingLoopAndWait(frames2, CtsMyHostApduService.class.getName());
+ Assert.assertFalse(receivedFrames.get(0).getTriggeredAutoTransact());
+ Thread.currentThread().sleep(5000);
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ adapter.notifyHceDeactivated();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ } finally {
+ adapter.setObserveModeEnabled(false);
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
+ Flags.FLAG_NFC_OBSERVE_MODE})
+ public void testAutoTransact() throws Exception {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
+ assumeTrue(adapter.isObserveModeSupported());
+ adapter.notifyHceDeactivated();
+ final Activity activity = createAndResumeActivity();
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
+ ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
+ frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex)));
+ final CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity,
+ new ComponentName(mContext, CtsMyHostApduService.class)));
+ ensurePreferredService(CtsMyHostApduService.class);
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ List<PollingFrame> receivedFrames =
+ notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
+ Assert.assertTrue(receivedFrames.get(0).getTriggeredAutoTransact());
+ PollingCheck.check("Observe mode not disabled", 200,
+ () -> !adapter.isObserveModeEnabled());
+ adapter.notifyHceDeactivated();
+ PollingCheck.check("Observe mode not enabled", 3000, adapter::isObserveModeEnabled);
+ } finally {
+ adapter.setObserveModeEnabled(false);
+ cardEmulation.unsetPreferredService(activity);
+ activity.finish();
+ }
}
@Test
@RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
Flags.FLAG_NFC_OBSERVE_MODE,
android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED})
- public void testAutoTransact_walletRoleEnabled() throws NoSuchFieldException {
+ public void testAutoTransact_walletRoleEnabled() throws Exception {
restoreOriginalService();
runWithRole(mContext, CTS_PACKAGE_NAME, () -> {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
assumeTrue(adapter.isObserveModeSupported());
adapter.notifyHceDeactivated();
createAndResumeActivity();
@@ -790,25 +1256,32 @@
HexFormat.of().parseHex(annotationStringHex)));
Assert.assertTrue(adapter.setObserveModeEnabled(true));
Assert.assertTrue(adapter.isObserveModeEnabled());
- notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
- Assert.assertFalse(adapter.isObserveModeEnabled());
- adapter.notifyHceDeactivated();
- Assert.assertTrue(adapter.isObserveModeEnabled());
- adapter.setObserveModeEnabled(false);
+ List<PollingFrame> receivedFrames =
+ notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
+ Assert.assertTrue(receivedFrames.get(0).getTriggeredAutoTransact());
+ try {
+ PollingCheck.check("Observe mode not disabled", 200,
+ () -> !adapter.isObserveModeEnabled());
+ adapter.notifyHceDeactivated();
+ PollingCheck.check("Observe mode not enabled", 3000, adapter::isObserveModeEnabled);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ adapter.setObserveModeEnabled(false);
+ }
});
setMockService();
}
-
@Test
@RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
Flags.FLAG_NFC_OBSERVE_MODE})
- @RequiresFlagsDisabled(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
- public void testAutoTransactDynamic() {
+ public void testAutoTransactDynamic() throws Exception {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
assumeTrue(adapter.isObserveModeSupported());
adapter.notifyHceDeactivated();
- createAndResumeActivity();
+ final Activity activity = createAndResumeActivity();
String testName = new Object() {
}.getClass().getEnclosingMethod().getName();
String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
@@ -819,24 +1292,93 @@
ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
frames.add(createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
HexFormat.of().parseHex(annotationStringHex)));
- Assert.assertTrue(adapter.setObserveModeEnabled(true));
- Assert.assertTrue(adapter.isObserveModeEnabled());
- notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
- Assert.assertFalse(adapter.isObserveModeEnabled());
+ ComponentName ctsComponentName = new ComponentName(mContext, CtsMyHostApduService.class);
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity, ctsComponentName));
+ ensurePreferredService(CtsMyHostApduService.class);
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ List<PollingFrame> receivedFrames =
+ notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
+ Assert.assertTrue(receivedFrames.get(0).getTriggeredAutoTransact());
+ PollingCheck.check("Observe mode not disabled", 200,
+ () -> !adapter.isObserveModeEnabled());
+ adapter.notifyHceDeactivated();
+ PollingCheck.check("Observe mode not enabled", 3000, adapter::isObserveModeEnabled);
+ } finally {
+ adapter.setObserveModeEnabled(false);
+ cardEmulation.unsetPreferredService(activity);
+ activity.finish();
+ }
+ }
+
+
+ @Test
+ @RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
+ Flags.FLAG_NFC_OBSERVE_MODE})
+ public void testOffHostAutoTransactDynamic() throws Exception {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
+ assumeTrue(adapter.isObserveModeSupported());
adapter.notifyHceDeactivated();
- Assert.assertTrue(adapter.isObserveModeEnabled());
- adapter.setObserveModeEnabled(false);
+ final Activity activity = createAndResumeActivity();
+ String testName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
+ CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ ComponentName offhostServiceName = new ComponentName(mContext,
+ CtsMyOffHostApduService.class);
+ Assert.assertFalse(cardEmulation.registerPollingLoopFilterForService(offhostServiceName,
+ "1234567890", false));
+ Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(offhostServiceName,
+ annotationStringHex, true));
+ PollingFrame frame = createFrameWithData(PollingFrame.POLLING_LOOP_TYPE_UNKNOWN,
+ HexFormat.of().parseHex(annotationStringHex));
+ ComponentName ctsComponentName = new ComponentName(mContext, CtsMyHostApduService.class);
+ try {
+ Assert.assertTrue(cardEmulation.setPreferredService(activity, ctsComponentName));
+ ensurePreferredService(CtsMyHostApduService.class);
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ adapter.notifyPollingLoop(frame);
+ PollingCheck.check("Observe mode not disabled", 200,
+ () -> !adapter.isObserveModeEnabled());
+ adapter.notifyHceDeactivated();
+ PollingCheck.check("Observe mode not enabled", 3000, adapter::isObserveModeEnabled);
+ } finally {
+ adapter.setObserveModeEnabled(false);
+ cardEmulation.unsetPreferredService(activity);
+ activity.finish();
+ }
}
@Test
@RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
Flags.FLAG_NFC_OBSERVE_MODE,
android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED})
- public void testAutoTransactDynamic_walletRoleEnabled() throws NoSuchFieldException {
+ public void testDisallowNonDefaultSetObserveMode() throws NoSuchFieldException {
+ restoreOriginalService();
+ runWithRole(mContext, WalletRoleTestUtils.WALLET_HOLDER_PACKAGE_NAME, () -> {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
+ assumeTrue(adapter.isObserveModeSupported());
+ adapter.notifyHceDeactivated();
+ Assert.assertFalse(adapter.setObserveModeEnabled(true));
+ Assert.assertFalse(adapter.isObserveModeEnabled());
+ });
+ setMockService();
+ }
+
+ @Test
+ @RequiresFlagsEnabled({android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP,
+ Flags.FLAG_NFC_OBSERVE_MODE,
+ android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED})
+ public void testAutoTransactDynamic_walletRoleEnabled() throws Exception {
restoreOriginalService();
runWithRole(mContext, CTS_PACKAGE_NAME, () -> {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
assumeTrue(adapter.isObserveModeSupported());
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
adapter.notifyHceDeactivated();
createAndResumeActivity();
String testName = new Object() {
@@ -844,7 +1386,7 @@
String annotationStringHex = HexFormat.of().toHexDigits(testName.hashCode());
CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
ComponentName customServiceName = new ComponentName(mContext,
- CustomHostApduService.class);
+ CtsMyHostApduService.class);
Assert.assertTrue(cardEmulation.registerPollingLoopFilterForService(customServiceName,
annotationStringHex, true));
ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>(1);
@@ -852,11 +1394,19 @@
HexFormat.of().parseHex(annotationStringHex)));
Assert.assertTrue(adapter.setObserveModeEnabled(true));
Assert.assertTrue(adapter.isObserveModeEnabled());
- notifyPollingLoopAndWait(frames, CustomHostApduService.class.getName());
- Assert.assertFalse(adapter.isObserveModeEnabled());
- adapter.notifyHceDeactivated();
- Assert.assertTrue(adapter.isObserveModeEnabled());
- adapter.setObserveModeEnabled(false);
+ List<PollingFrame> receivedFrames =
+ notifyPollingLoopAndWait(frames, CtsMyHostApduService.class.getName());
+ Assert.assertTrue(receivedFrames.get(0).getTriggeredAutoTransact());
+ try {
+ PollingCheck.check("Observe mode not disabled", 200,
+ () -> !adapter.isObserveModeEnabled());
+ adapter.notifyHceDeactivated();
+ PollingCheck.check("Observe mode not enabled", 3000, adapter::isObserveModeEnabled);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ adapter.setObserveModeEnabled(false);
+ }
});
setMockService();
}
@@ -880,8 +1430,10 @@
}
static void ensureUnlocked() {
- final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ assumeFalse(userManager.isHeadlessSystemUserMode());
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
final PowerManager pm = context.getSystemService(PowerManager.class);
final KeyguardManager km = context.getSystemService(KeyguardManager.class);
try {
@@ -891,7 +1443,7 @@
() -> pm != null && pm.isInteractive());
}
if (km != null && km.isKeyguardLocked()) {
- CommonTestUtils.waitUntil("Device does not unlock after 3 seconds", 3,
+ CommonTestUtils.waitUntil("Device does not unlock after 30 seconds", 30,
() -> {
SystemUtil.runWithShellPermissionIdentity(
() -> instrumentation.sendKeyDownUpSync(
@@ -905,11 +1457,18 @@
}
private PollingFrame createFrame(@PollingFrameType int type) {
- return new PollingFrame(type, null, 8, 0);
+ if (type == PollingFrame.POLLING_LOOP_TYPE_ON
+ || type == PollingFrame.POLLING_LOOP_TYPE_OFF) {
+ return new PollingFrame(type,
+ new byte[] { ((type == PollingFrame.POLLING_LOOP_TYPE_ON)
+ ? (byte) 0x01 : (byte) 0x00) }, 8, 0,
+ false);
+ }
+ return new PollingFrame(type, null, 8, 0, false);
}
private PollingFrame createFrameWithData(@PollingFrameType int type, byte[] data) {
- return new PollingFrame(type, data, 8, 0);
+ return new PollingFrame(type, data, 8, (long) Integer.MAX_VALUE + 1L, false);
}
private ComponentName setDefaultPaymentService(Class serviceClass) {
@@ -941,20 +1500,25 @@
static class PollLoopReceiver {
int mFrameIndex = 0;
- List<PollingFrame> mFrames;
+ ArrayList<PollingFrame> mFrames;
String mServiceName;
- List<PollingFrame> mReceivedFrames;
+ ArrayList<PollingFrame> mReceivedFrames;
String mReceivedServiceName;
-
- PollLoopReceiver(List<PollingFrame> frames, String serviceName) {
+ ArrayList<String> mReceivedServiceNames;
+ PollLoopReceiver(ArrayList<PollingFrame> frames, String serviceName) {
mFrames = frames;
mServiceName = serviceName;
- mReceivedFrames = new ArrayList<PollingFrame>(1);
+ mReceivedFrames = new ArrayList<PollingFrame>();
+ mReceivedServiceNames = new ArrayList<String>();
}
void notifyPollingLoop(String className, List<PollingFrame> receivedFrames) {
+ if (receivedFrames == null) {
+ return;
+ }
mReceivedFrames.addAll(receivedFrames);
mReceivedServiceName = className;
+ mReceivedServiceNames.add(className);
if (mReceivedFrames.size() < mFrames.size()) {
return;
}
@@ -964,21 +1528,29 @@
}
void test() {
+ if (mReceivedFrames.size() > mFrames.size()) {
+ Assert.fail("received more frames than sent");
+ } else if (mReceivedFrames.size() < mFrames.size()) {
+ Assert.fail("received fewer frames than sent");
+ }
for (PollingFrame receivedFrame : mReceivedFrames) {
- if (mFrameIndex >= mFrames.size()) {
- Assert.fail("received more frames than sent: " + receivedFrame);
- }
Assert.assertEquals(mFrames.get(mFrameIndex).getType(), receivedFrame.getType());
- Assert.assertEquals(mFrames.get(mFrameIndex).getGain(), receivedFrame.getGain());
+ Assert.assertEquals(mFrames.get(mFrameIndex).getVendorSpecificGain(),
+ receivedFrame.getVendorSpecificGain());
+ Assert.assertEquals(mFrames.get(mFrameIndex).getTimestamp(),
+ receivedFrame.getTimestamp());
Assert.assertArrayEquals(mFrames.get(mFrameIndex).getData(),
receivedFrame.getData());
mFrameIndex++;
}
- Assert.assertEquals(mServiceName, mReceivedServiceName);
+ if (mServiceName != null) {
+ Assert.assertEquals(mServiceName, mReceivedServiceName);
+ }
}
}
- private void notifyPollingLoopAndWait(List<PollingFrame> frames, String serviceName) {
+ private List<PollingFrame> notifyPollingLoopAndWait(ArrayList<PollingFrame> frames,
+ String serviceName) {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
sCurrentPollLoopReceiver = new PollLoopReceiver(frames, serviceName);
for (PollingFrame frame : frames) {
@@ -993,7 +1565,9 @@
}
sCurrentPollLoopReceiver.test();
Assert.assertEquals(frames.size(), sCurrentPollLoopReceiver.mFrameIndex);
+ List<PollingFrame> receivedFrames = sCurrentPollLoopReceiver.mReceivedFrames;
sCurrentPollLoopReceiver = null;
+ return receivedFrames;
}
@RequiresFlagsEnabled(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
diff --git a/tests/tests/nfc/src/android/nfc/cts/DefaultPaymentProviderTestUtils.java b/tests/tests/nfc/src/android/nfc/cts/DefaultPaymentProviderTestUtils.java
index 4d6c116..864fbb5 100644
--- a/tests/tests/nfc/src/android/nfc/cts/DefaultPaymentProviderTestUtils.java
+++ b/tests/tests/nfc/src/android/nfc/cts/DefaultPaymentProviderTestUtils.java
@@ -45,12 +45,20 @@
return componentName;
}
+ static ComponentName setDefaultPaymentSetting(ComponentName serviceName, Context context) {
+ ComponentName originalValue = CardEmulation.getPreferredPaymentService(context);
+ Settings.Secure.putString(context.getContentResolver(),
+ Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT,
+ serviceName == null ? null
+ : serviceName.flattenToString());
+ return originalValue;
+ }
+
static ComponentName setDefaultPaymentService(ComponentName serviceName, Context context) {
try {
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
.getUiAutomation().adoptShellPermissionIdentity();
- ComponentName originalValue = CardEmulation.getPreferredPaymentService(context);
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context);
CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
CardEmulationTest.SettingsObserver settingsObserver =
@@ -59,10 +67,7 @@
Settings.Secure.getUriFor(
Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT),
true, settingsObserver, UserHandle.ALL);
- Settings.Secure.putString(context.getContentResolver(),
- Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT,
- serviceName == null ? null
- : serviceName.flattenToString());
+ ComponentName originalValue = setDefaultPaymentSetting(serviceName, context);
int count = 0;
while (!settingsObserver.mSeenChange
&& !cardEmulation.isDefaultServiceForCategory(serviceName,
@@ -97,12 +102,24 @@
}
static void runWithDefaultPaymentService(Context context,
- ComponentName service, String description, Runnable runnable) {
+ ComponentName service, String description,
+ Runnable runnable) {
ComponentName originalValue = setDefaultPaymentService(service, context);
- ensurePreferredService(description, context);
+ if (service != null) {
+ ensurePreferredService(description, context);
+ }
runnable.run();
if (originalValue != null) {
setDefaultPaymentService(originalValue, context);
}
}
+
+ static void runWithDefaultPaymentSetting(Context context, ComponentName service,
+ Runnable runnable) {
+ ComponentName originalValue = setDefaultPaymentSetting(service, context);
+ runnable.run();
+ if (originalValue != null) {
+ setDefaultPaymentSetting(originalValue, context);
+ }
+ }
}
diff --git a/tests/tests/nfc/src/android/nfc/cts/HostApduServiceTest.java b/tests/tests/nfc/src/android/nfc/cts/HostApduServiceTest.java
index b775b9e..ead3399 100644
--- a/tests/tests/nfc/src/android/nfc/cts/HostApduServiceTest.java
+++ b/tests/tests/nfc/src/android/nfc/cts/HostApduServiceTest.java
@@ -59,7 +59,7 @@
public void testProcessPollingFrame() {
ArrayList<PollingFrame> frames = new ArrayList<PollingFrame>();
PollingFrame frame =
- new PollingFrame(PollingFrame.POLLING_LOOP_TYPE_A, new byte[0], 0, 0);
+ new PollingFrame(PollingFrame.POLLING_LOOP_TYPE_A, new byte[0], 0, 0, false);
frames.add(frame);
service.processPollingFrames(frames);
}
diff --git a/tests/tests/nfc/src/android/nfc/cts/NfcAdapterTest.java b/tests/tests/nfc/src/android/nfc/cts/NfcAdapterTest.java
index 7be6bb1..2ecb9f7 100644
--- a/tests/tests/nfc/src/android/nfc/cts/NfcAdapterTest.java
+++ b/tests/tests/nfc/src/android/nfc/cts/NfcAdapterTest.java
@@ -1,26 +1,34 @@
package android.nfc.cts;
+import static com.android.compatibility.common.util.PropertyUtil.getVsrApiLevel;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.nfc.*;
import android.nfc.cardemulation.CardEmulation;
import android.os.Bundle;
+import android.os.Build;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -51,6 +59,7 @@
public class NfcAdapterTest {
@Mock private INfcAdapter mService;
+ @Mock private DevicePolicyManager mDevicePolicyManager;
private INfcAdapter mSavedService;
private Context mContext;
@Rule
@@ -64,12 +73,13 @@
@Before
public void setUp() throws NoSuchFieldException {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getContext();
+ mContext = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
assumeTrue(supportsHardware());
// Backup the original service. It is being overridden
// when creating a mocked adapter.
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
Assume.assumeNotNull(adapter);
+ Assume.assumeTrue(adapter.enable());
mSavedService = (INfcAdapter) (
new FieldReader(adapter, adapter.getClass().getDeclaredField("sService")).read());
}
@@ -90,70 +100,56 @@
@Test
public void testGetDefaultAdapter() {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
Assert.assertNotNull(adapter);
}
@Test
public void testAddNfcUnlockHandler() {
- try {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
adapter.addNfcUnlockHandler(new CtsNfcUnlockHandler(), new String[]{"IsoDep"});
- } catch (Exception e) {
- throw new IllegalStateException("Unexpected Exception: " + e);
- }
}
@Test
public void testDisableWithNoParams() throws NoSuchFieldException, RemoteException {
- NfcAdapter adapter = createMockedInstance();
- when(mService.disable(anyBoolean())).thenReturn(true);
+ NfcAdapter adapter = getDefaultAdapter();
boolean result = adapter.disable();
Assert.assertTrue(result);
+ result = adapter.enable();
+ Assert.assertTrue(result);
}
@Test
public void testDisableWithParam() throws NoSuchFieldException, RemoteException {
- NfcAdapter adapter = createMockedInstance();
- when(mService.disable(anyBoolean())).thenReturn(true);
+ NfcAdapter adapter = getDefaultAdapter();
boolean result = adapter.disable(true);
Assert.assertTrue(result);
}
@Test
public void testDisableForegroundDispatch() {
- try {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
Activity activity = createAndResumeActivity();
adapter.disableForegroundDispatch(activity);
- } catch (Exception e) {
- throw new IllegalStateException("Unexpected Exception: " + e);
- }
}
@Test
public void testDisableReaderMode() {
- try {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
Activity activity = createAndResumeActivity();
adapter.disableReaderMode(activity);
- } catch (Exception e) {
- throw new IllegalStateException("Unexpected Exception: " + e);
- }
}
@Test
public void testEnable() throws NoSuchFieldException, RemoteException {
- NfcAdapter adapter = createMockedInstance();
- when(mService.enable()).thenReturn(true);
+ NfcAdapter adapter = getDefaultAdapter();
boolean result = adapter.enable();
Assert.assertTrue(result);
}
@Test
- public void testEnableForegroundDispatch() {
- try {
- NfcAdapter adapter = createMockedInstance();
+ public void testEnableForegroundDispatch() throws RemoteException {
+ NfcAdapter adapter = getDefaultAdapter();
Activity activity = createAndResumeActivity();
Intent intent = new Intent(ApplicationProvider.getApplicationContext(),
NfcFCardEmulationActivity.class);
@@ -164,21 +160,14 @@
doNothing().when(mService).setForegroundDispatch(any(PendingIntent.class),
any(IntentFilter[].class), any(TechListParcel.class));
adapter.enableForegroundDispatch(activity, pendingIntent, null, techLists);
- } catch (Exception e) {
- throw new IllegalStateException("Unexpected Exception: " + e);
- }
}
@Test
public void testEnableReaderMode() {
- try {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
Activity activity = createAndResumeActivity();
adapter.enableReaderMode(activity, new CtsReaderCallback(),
NfcAdapter.FLAG_READER_NFC_A, new Bundle());
- } catch (Exception e) {
- throw new IllegalStateException("Unexpected Exception: " + e);
- }
}
@Test
@@ -187,10 +176,11 @@
when(mService.enableReaderOption(anyBoolean())).thenReturn(true);
boolean result = adapter.enableReaderOption(true);
Assert.assertTrue(result);
+ resetMockedInstance();
}
@Test
- public void testEnableSecureNfc() throws NoSuchFieldException, RemoteException {
+ public void testEnableSecureNfc() throws RemoteException {
NfcAdapter adapter = createMockedInstance();
when(mService.setNfcSecure(anyBoolean())).thenReturn(true);
boolean result = adapter.enableSecureNfc(true);
@@ -205,6 +195,7 @@
when(mService.getNfcAntennaInfo()).thenReturn(info);
NfcAntennaInfo result = adapter.getNfcAntennaInfo();
Assert.assertEquals(info, result);
+ resetMockedInstance();
}
@Test
@@ -222,6 +213,7 @@
when(mService.isControllerAlwaysOn()).thenReturn(true);
boolean result = adapter.isControllerAlwaysOn();
Assert.assertTrue(result);
+ resetMockedInstance();
}
@Test
@@ -230,6 +222,7 @@
when(mService.isControllerAlwaysOnSupported()).thenReturn(true);
boolean result = adapter.isControllerAlwaysOnSupported();
Assert.assertTrue(result);
+ resetMockedInstance();
}
@Test
@@ -238,13 +231,13 @@
when(mService.getState()).thenReturn(NfcAdapter.STATE_ON);
boolean result = adapter.isEnabled();
Assert.assertTrue(result);
+ resetMockedInstance();
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_NFC_READER_OPTION)
public void testIsReaderOptionEnabled() throws NoSuchFieldException, RemoteException {
- NfcAdapter adapter = createMockedInstance();
- when(mService.isReaderOptionEnabled()).thenReturn(true);
+ NfcAdapter adapter = getDefaultAdapter();
boolean result = adapter.isReaderOptionEnabled();
Assert.assertTrue(result);
}
@@ -256,6 +249,7 @@
when(mService.isReaderOptionSupported()).thenReturn(true);
boolean result = adapter.isReaderOptionSupported();
Assert.assertTrue(result);
+ resetMockedInstance();
}
@Test
@@ -264,6 +258,7 @@
NfcAdapter adapter = createMockedInstance();
when(mService.getState()).thenReturn(NfcAdapter.STATE_ON);
Assert.assertEquals(adapter.getAdapterState(), NfcAdapter.STATE_ON);
+ resetMockedInstance();
}
@Test
@@ -280,11 +275,12 @@
when(mService.deviceSupportsNfcSecure()).thenReturn(true);
boolean result = adapter.isSecureNfcSupported();
Assert.assertTrue(result);
+ resetMockedInstance();
}
@Test
public void testRemoveNfcUnlockHandler() {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
boolean result = adapter.removeNfcUnlockHandler(new CtsNfcUnlockHandler());
Assert.assertTrue(result);
}
@@ -292,39 +288,46 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
public void testResetDiscoveryTechnology() {
- try {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
- Activity activity = createAndResumeActivity();
- adapter.resetDiscoveryTechnology(activity);
- } catch (Exception e) {
- throw new IllegalStateException("Unexpected Exception: " + e);
- }
+ NfcAdapter adapter = getDefaultAdapter();
+ Activity activity = createAndResumeActivity();
+ adapter.setDiscoveryTechnology(activity, NfcAdapter.FLAG_READER_KEEP,
+ NfcAdapter.FLAG_LISTEN_KEEP);
+ adapter.resetDiscoveryTechnology(activity);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
public void testSetDiscoveryTechnology() {
- try {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
- Activity activity = createAndResumeActivity();
- adapter.setDiscoveryTechnology(activity,
- NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B
- | NfcAdapter.FLAG_READER_NFC_F,
- NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_A | NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_B
- | NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_F);
- adapter.resetDiscoveryTechnology(activity);
- adapter.setDiscoveryTechnology(activity, NfcAdapter.FLAG_READER_DISABLE,
- NfcAdapter.FLAG_LISTEN_KEEP);
- adapter.resetDiscoveryTechnology(activity);
- } catch (Exception e) {
- throw new IllegalStateException("Unexpected Exception: " + e);
- }
+ NfcAdapter adapter = getDefaultAdapter();
+ Activity activity = createAndResumeActivity();
+ adapter.setDiscoveryTechnology(activity,
+ NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B
+ | NfcAdapter.FLAG_READER_NFC_F,
+ NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_A | NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_B
+ | NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_F);
+ adapter.resetDiscoveryTechnology(activity);
+ adapter.setDiscoveryTechnology(activity, NfcAdapter.FLAG_READER_DISABLE,
+ NfcAdapter.FLAG_LISTEN_KEEP);
+ adapter.resetDiscoveryTechnology(activity);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH)
+ public void testSetDefaultDiscoveryTechnology() {
+ NfcAdapter adapter = getDefaultAdapter();
+ Activity activity = createAndResumeActivity();
+ adapter.setDiscoveryTechnology(activity,
+ NfcAdapter.FLAG_READER_KEEP,
+ NfcAdapter.FLAG_LISTEN_NFC_PASSIVE_B
+ | NfcAdapter.FLAG_SET_DEFAULT_TECH);
+ adapter.setDiscoveryTechnology(activity, NfcAdapter.FLAG_READER_KEEP,
+ NfcAdapter.FLAG_LISTEN_KEEP | NfcAdapter.FLAG_SET_DEFAULT_TECH | 0xff);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_NFC_MAINLINE)
public void testSetReaderMode() {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
// Verify the API does not crash or throw any exceptions.
adapter.setReaderModePollingEnabled(true);
adapter.setReaderModePollingEnabled(false);
@@ -337,7 +340,7 @@
ComponentName originalDefault = null;
try {
originalDefault = setDefaultPaymentService(CtsMyHostApduService.class);
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
assumeTrue(adapter.isObserveModeSupported());
boolean result = adapter.setObserveModeEnabled(false);
Assert.assertTrue(result);
@@ -353,7 +356,7 @@
ComponentName originalDefault = null;
try {
originalDefault = setDefaultPaymentService(CtsMyHostApduService.class);
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
assumeTrue(adapter.isObserveModeSupported());
boolean result = adapter.setObserveModeEnabled(true);
Assert.assertTrue(result);
@@ -367,7 +370,7 @@
public void testDefaultObserveModePaymentDynamic() {
ComponentName originalDefault = null;
try {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
cardEmulation.setShouldDefaultToObserveModeForService(new ComponentName(mContext,
CustomHostApduService.class), true);
@@ -385,7 +388,7 @@
@Test
@RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_OBSERVE_MODE)
public void testDefaultObserveModeForegroundDynamic() {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = createMockedInstance();
assumeTrue(adapter.isObserveModeSupported());
CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
try {
@@ -404,7 +407,42 @@
cardEmulation.setShouldDefaultToObserveModeForService(new ComponentName(mContext,
CustomHostApduService.class), false);
}
+ resetMockedInstance();
}
+
+ @Test
+ @RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_OBSERVE_MODE)
+ public void testDefaultObserveModeOnlyWithServiceChange() {
+ NfcAdapter adapter = getDefaultAdapter();
+ assumeTrue(adapter.isObserveModeSupported());
+ CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ cardEmulation.setShouldDefaultToObserveModeForService(new ComponentName(mContext,
+ CtsMyHostApduService.class), true);
+ WalletRoleTestUtils.runWithRole(mContext, WalletRoleTestUtils.CTS_PACKAGE_NAME, () -> {
+ CardEmulationTest.ensurePreferredService(CtsMyHostApduService.class, mContext);
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ Assert.assertTrue(adapter.setObserveModeEnabled(false));
+ Assert.assertFalse(adapter.isObserveModeEnabled());
+ try {
+ Activity activity = createAndResumeActivity();
+ Assert.assertTrue(cardEmulation.setPreferredService(activity,
+ new ComponentName(mContext, CtsMyHostApduService.class)));
+ CardEmulationTest.ensurePreferredService(CtsMyHostApduService.class, mContext);
+ Assert.assertFalse(adapter.isObserveModeEnabled());
+ Assert.assertTrue(adapter.setObserveModeEnabled(true));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ Assert.assertTrue(cardEmulation.setPreferredService(activity,
+ new ComponentName(mContext, CustomHostApduService.class)));
+ CardEmulationTest.ensurePreferredService(CustomHostApduService.class, mContext);
+ Assert.assertFalse(adapter.isObserveModeEnabled());
+ } finally {
+ cardEmulation.setShouldDefaultToObserveModeForService(new ComponentName(mContext,
+ CustomHostApduService.class), false);
+ }
+ });
+ resetMockedInstance();
+ }
+
@Test
@RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_OBSERVE_MODE)
@RequiresFlagsDisabled(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
@@ -413,7 +451,7 @@
try {
originalDefault = setDefaultPaymentService(BackgroundHostApduService.class);
CardEmulationTest.ensurePreferredService(BackgroundHostApduService.class, mContext);
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
Assert.assertTrue(adapter.isObserveModeEnabled());
setDefaultPaymentService(CtsMyHostApduService.class);
CardEmulationTest.ensurePreferredService(CtsMyHostApduService.class, mContext);
@@ -427,7 +465,7 @@
@RequiresFlagsEnabled(android.nfc.Flags.FLAG_NFC_OBSERVE_MODE)
public void testDefaultObserveModeForeground() {
Activity activity = createAndResumeActivity();
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = createMockedInstance();
assumeTrue(adapter.isObserveModeSupported());
CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
Assert.assertTrue(cardEmulation.setPreferredService(activity,
@@ -438,6 +476,7 @@
new ComponentName(mContext, CtsMyHostApduService.class)));
CardEmulationTest.ensurePreferredService(CtsMyHostApduService.class, mContext);
Assert.assertFalse(adapter.isObserveModeEnabled());
+ resetMockedInstance();
}
@Test
@@ -445,7 +484,7 @@
android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED})
public void testAllowTransaction_walletRoleEnabled() {
WalletRoleTestUtils.runWithRole(mContext, WalletRoleTestUtils.CTS_PACKAGE_NAME, () -> {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = getDefaultAdapter();
assumeTrue(adapter.isObserveModeSupported());
adapter.setObserveModeEnabled(false);
Assert.assertFalse(adapter.isObserveModeEnabled());
@@ -457,18 +496,18 @@
android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED})
public void testDisallowTransaction_walletRoleEnabled() {
WalletRoleTestUtils.runWithRole(mContext, WalletRoleTestUtils.CTS_PACKAGE_NAME, () -> {
- NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter adapter = createMockedInstance();
assumeTrue(adapter.isObserveModeSupported());
adapter.setObserveModeEnabled(true);
Assert.assertTrue(adapter.isObserveModeEnabled());
+ resetMockedInstance();
});
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_NFC_CHARGING)
public void testEnableNfcCharging() throws NoSuchFieldException, RemoteException {
- NfcAdapter adapter = createMockedInstance();
- when(mService.setWlcEnabled(anyBoolean())).thenReturn(true);
+ NfcAdapter adapter = getDefaultAdapter();
boolean result = adapter.setWlcEnabled(true);
Assert.assertTrue(result);
}
@@ -476,17 +515,18 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_NFC_CHARGING)
public void testIsNfcChargingEnabled() throws NoSuchFieldException, RemoteException {
- NfcAdapter adapter = createMockedInstance();
- when(mService.isWlcEnabled()).thenReturn(true);
+ NfcAdapter adapter = getDefaultAdapter();
boolean result = adapter.isWlcEnabled();
Assert.assertTrue(result);
}
@Test
- public void testSendVendorCmd() throws InterruptedException {
+ @RequiresFlagsEnabled(Flags.FLAG_NFC_VENDOR_CMD)
+ public void testSendVendorCmd() throws InterruptedException, RemoteException {
+ assumeTrue(getVsrApiLevel() > Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
CountDownLatch rspCountDownLatch = new CountDownLatch(1);
CountDownLatch ntfCountDownLatch = new CountDownLatch(1);
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter nfcAdapter = getDefaultAdapter();
Assert.assertNotNull(nfcAdapter);
NfcVendorNciCallback cb =
new NfcVendorNciCallback(rspCountDownLatch, ntfCountDownLatch);
@@ -515,7 +555,7 @@
@RequiresFlagsEnabled(Flags.FLAG_NFC_OEM_EXTENSION)
public void testOemExtension() throws InterruptedException {
CountDownLatch tagDetectedCountDownLatch = new CountDownLatch(1);
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
+ NfcAdapter nfcAdapter = getDefaultAdapter();
NfcOemExtension nfcOemExtension = nfcAdapter.getNfcOemExtension();
Assert.assertNotNull(nfcAdapter);
NfcOemExtensionCallback cb =
@@ -526,11 +566,100 @@
// TODO: Fix these tests as we add more functionality to this API surface.
nfcOemExtension.clearPreference();
+ nfcOemExtension.synchronizeScreenState();
+ nfcOemExtension.maybeTriggerFirmwareUpdate();
} finally {
nfcOemExtension.unregisterCallback(cb);
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NFC_STATE_CHANGE)
+ public void testEnableByDeviceOwner() throws NoSuchFieldException, RemoteException {
+ denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ when(mDevicePolicyManager.getDeviceOwnerUser())
+ .thenReturn(new UserHandle(UserHandle.getCallingUserId()));
+ when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(ComponentName.createRelative("com.android.nfc", ".AdapterTest"));
+ when(mContext.getSystemService(DevicePolicyManager.class))
+ .thenReturn(mDevicePolicyManager);
+ NfcAdapter adapter = getDefaultAdapter();
+ boolean result = adapter.enable();
+ Assert.assertTrue(result);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NFC_STATE_CHANGE)
+ public void testDisableByDeviceOwner() throws NoSuchFieldException, RemoteException {
+ denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ when(mDevicePolicyManager.getDeviceOwnerUser())
+ .thenReturn(new UserHandle(UserHandle.getCallingUserId()));
+ when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(ComponentName.createRelative("com.android.nfc", ".AdapterTest"));
+ when(mContext.getSystemService(DevicePolicyManager.class))
+ .thenReturn(mDevicePolicyManager);
+ NfcAdapter adapter = getDefaultAdapter();
+ boolean result = adapter.disable();
+ Assert.assertTrue(result);
+ result = adapter.enable();
+ Assert.assertTrue(result);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NFC_OBSERVE_MODE)
+ public void testShouldDefaultToObserveModeAfterNfcOffOn() throws InterruptedException {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ assumeTrue(adapter.isObserveModeSupported());
+ adapter.notifyHceDeactivated();
+ Activity activity = createAndResumeActivity();
+ final CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ ComponentName ctsService = new ComponentName(mContext, CtsMyHostApduService.class);
+
+ try {
+ Assert.assertTrue(cardEmulation.setShouldDefaultToObserveModeForService(ctsService,
+ true));
+
+ Assert.assertTrue(cardEmulation.setPreferredService(activity, ctsService));
+ CardEmulationTest.ensurePreferredService(CtsMyHostApduService.class, mContext);
+
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ Assert.assertTrue(NfcUtils.disableNfc(adapter, mContext));
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ } finally {
+ cardEmulation.setShouldDefaultToObserveModeForService(ctsService,
+ false);
+ cardEmulation.unsetPreferredService(activity);
+ adapter.notifyHceDeactivated();
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NFC_OBSERVE_MODE)
+ public void testShouldDefaultToObserveModeWithNfcOff() throws InterruptedException {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ assumeTrue(adapter.isObserveModeSupported());
+ adapter.notifyHceDeactivated();
+ Activity activity = createAndResumeActivity();
+ final CardEmulation cardEmulation = CardEmulation.getInstance(adapter);
+ ComponentName ctsService = new ComponentName(mContext, CtsMyHostApduService.class);
+ try {
+ Assert.assertTrue(NfcUtils.disableNfc(adapter, mContext));
+ Assert.assertTrue(cardEmulation.setShouldDefaultToObserveModeForService(ctsService,
+ true));
+
+ Assert.assertTrue(cardEmulation.setPreferredService(activity, ctsService));
+ CardEmulationTest.ensurePreferredService(CtsMyHostApduService.class, mContext);
+
+ Assert.assertTrue(NfcUtils.enableNfc(adapter, mContext));
+ Assert.assertTrue(adapter.isObserveModeEnabled());
+ } finally {
+ cardEmulation.setShouldDefaultToObserveModeForService(ctsService,
+ false);
+ cardEmulation.unsetPreferredService(activity);
+ adapter.notifyHceDeactivated();
+ }
+ }
private class NfcOemExtensionCallback implements NfcOemExtension.Callback {
private final CountDownLatch mTagDetectedCountDownLatch;
@@ -596,12 +725,38 @@
return activity;
}
- private NfcAdapter createMockedInstance() throws NoSuchFieldException {
+ private NfcAdapter getDefaultAdapter() {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
- FieldSetter.setField(adapter, adapter.getClass().getDeclaredField("sService"), mService);
+ try {
+ FieldSetter.setField(adapter, adapter.getClass().getDeclaredField("sService"),
+ mSavedService);
+ } catch (NoSuchFieldException nsfe) {
+ throw new RuntimeException(nsfe);
+ }
return adapter;
}
+ private NfcAdapter createMockedInstance() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ try {
+ FieldSetter.setField(adapter, adapter.getClass().getDeclaredField("sService"),
+ mService);
+ } catch (NoSuchFieldException nsfe) {
+ throw new RuntimeException(nsfe);
+ }
+ return adapter;
+ }
+
+ private void resetMockedInstance() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ try {
+ FieldSetter.setField(adapter, adapter.getClass().getDeclaredField("sService"),
+ mSavedService);
+ } catch (NoSuchFieldException nsfe) {
+ throw new RuntimeException(nsfe);
+ }
+ }
+
private ComponentName setDefaultPaymentService(Class serviceClass) {
ComponentName componentName = setDefaultPaymentService(
new ComponentName(mContext, serviceClass));
@@ -614,4 +769,11 @@
private ComponentName setDefaultPaymentService(ComponentName serviceName) {
return DefaultPaymentProviderTestUtils.setDefaultPaymentService(serviceName, mContext);
}
+
+ private void denyPermission(String permission) {
+ when(mContext.checkCallingOrSelfPermission(permission))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(permission), anyString());
+ }
}
diff --git a/tests/tests/nfc/src/android/nfc/cts/NfcPreferredPaymentTest.java b/tests/tests/nfc/src/android/nfc/cts/NfcPreferredPaymentTest.java
index b7f4611..863d141 100644
--- a/tests/tests/nfc/src/android/nfc/cts/NfcPreferredPaymentTest.java
+++ b/tests/tests/nfc/src/android/nfc/cts/NfcPreferredPaymentTest.java
@@ -61,8 +61,6 @@
private CardEmulation mCardEmulation;
private Context mContext;
- private WalletRoleTestUtils.RoleContext mRoleContext;
-
private boolean supportsHardware() {
final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
boolean existAnyReqFeature =
diff --git a/tests/tests/nfc/src/android/nfc/cts/NfcUtils.java b/tests/tests/nfc/src/android/nfc/cts/NfcUtils.java
new file mode 100644
index 0000000..b3dec24
--- /dev/null
+++ b/tests/tests/nfc/src/android/nfc/cts/NfcUtils.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cts;
+
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public final class NfcUtils {
+ private NfcUtils() {}
+
+ static boolean enableNfc(NfcAdapter nfcAdapter, Context context) {
+ try {
+ if (nfcAdapter.isEnabled()) {
+ return true;
+ }
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ AtomicInteger state = new AtomicInteger(NfcAdapter.STATE_OFF);
+ BroadcastReceiver nfcChangeListener = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int s = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
+ NfcAdapter.STATE_OFF);
+ if (s == NfcAdapter.STATE_TURNING_ON) {
+ return;
+ }
+ context.unregisterReceiver(this);
+ state.set(s);
+ countDownLatch.countDown();
+ }
+ };
+ HandlerThread handlerThread = new HandlerThread("nfc_cts_listener");
+ handlerThread.start();
+ Handler handler = new Handler(handlerThread.getLooper());
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+ context.registerReceiver(nfcChangeListener, intentFilter, null,
+ handler);
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity(WRITE_SECURE_SETTINGS);
+ if (!nfcAdapter.enable()) {
+ return false;
+ }
+ countDownLatch.await(2000, TimeUnit.MILLISECONDS);
+ return state.get() == NfcAdapter.STATE_ON;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+ static boolean disableNfc(NfcAdapter nfcAdapter, Context context) {
+ try {
+ if (!nfcAdapter.isEnabled()) {
+ return true;
+ }
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ AtomicInteger state = new AtomicInteger(NfcAdapter.STATE_ON);
+ BroadcastReceiver nfcChangeListener = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int s = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
+ NfcAdapter.STATE_ON);
+ if (s == NfcAdapter.STATE_TURNING_OFF) {
+ return;
+ }
+ context.unregisterReceiver(this);
+ state.set(s);
+ countDownLatch.countDown();
+ }
+ };
+ HandlerThread handlerThread = new HandlerThread("nfc_cts_listener");
+ handlerThread.start();
+ Handler handler = new Handler(handlerThread.getLooper());
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+ context.registerReceiver(nfcChangeListener, intentFilter, null,
+ handler);
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity(WRITE_SECURE_SETTINGS);
+ if (!nfcAdapter.disable()) {
+ return false;
+ }
+ countDownLatch.await(2000, TimeUnit.MILLISECONDS);
+ return state.get() == NfcAdapter.STATE_OFF;
+ } catch (Exception e) {
+ return false;
+ } finally {
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+}
diff --git a/tests/tests/nfc/src/android/nfc/cts/WalletRoleTest.java b/tests/tests/nfc/src/android/nfc/cts/WalletRoleTest.java
new file mode 100644
index 0000000..b285bf6
--- /dev/null
+++ b/tests/tests/nfc/src/android/nfc/cts/WalletRoleTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cts;
+
+import static android.nfc.cts.DefaultPaymentProviderTestUtils.CTS_MY_HOSTAPDU_SERVICE;
+import static android.nfc.cts.DefaultPaymentProviderTestUtils.runWithDefaultPaymentSetting;
+import static android.nfc.cts.WalletRoleTestUtils.CTS_PACKAGE_NAME;
+import static android.nfc.cts.WalletRoleTestUtils.canAssignRoleToPackage;
+import static android.nfc.cts.WalletRoleTestUtils.clearRoleHolders;
+import static android.nfc.cts.WalletRoleTestUtils.getDefaultWalletRoleHolder;
+import static android.nfc.cts.WalletRoleTestUtils.getOverLayDefaultHolder;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.test.InstrumentationRegistry;
+
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.testng.Assert;
+
+@RunWith(JUnit4.class)
+@RequiresFlagsEnabled(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED)
+public class WalletRoleTest {
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ Assume.assumeTrue(supportsHardware());
+ }
+
+ private boolean supportsHardware() {
+ final PackageManager pm = mContext.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+ }
+
+ @Test
+ public void testMigrationFromOverlay() {
+ String overlayConfig = getOverLayDefaultHolder(mContext);
+ Assume.assumeNotNull(overlayConfig);
+ Assume.assumeTrue(canAssignRoleToPackage(mContext, overlayConfig));
+ runWithDefaultPaymentSetting(mContext,
+ null,
+ () -> {
+ clearRoleHolders(mContext);
+ String currentHolder = getDefaultWalletRoleHolder(mContext);
+
+ Assert.assertEquals(currentHolder, overlayConfig);
+ });
+ }
+
+ @Test
+ public void testMigrationFromDefaultPaymentProvider() {
+ runWithDefaultPaymentSetting(mContext,
+ CTS_MY_HOSTAPDU_SERVICE,
+ () -> {
+ clearRoleHolders(mContext);
+ String currentHolder = getDefaultWalletRoleHolder(mContext);
+
+ Assert.assertEquals(currentHolder, CTS_PACKAGE_NAME);
+ });
+ }
+
+}
diff --git a/tests/tests/nfc/src/android/nfc/cts/WalletRoleTestUtils.java b/tests/tests/nfc/src/android/nfc/cts/WalletRoleTestUtils.java
index f0cf37b..1b23111 100644
--- a/tests/tests/nfc/src/android/nfc/cts/WalletRoleTestUtils.java
+++ b/tests/tests/nfc/src/android/nfc/cts/WalletRoleTestUtils.java
@@ -20,21 +20,27 @@
import static android.Manifest.permission.MANAGE_ROLE_HOLDERS;
import static android.Manifest.permission.OBSERVE_ROLE_HOLDERS;
-import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
+import android.os.UserManager;
+import android.text.TextUtils;
import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Assert;
+
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+
public final class WalletRoleTestUtils {
private WalletRoleTestUtils() {}
@@ -48,29 +54,14 @@
static final String PAYMENT_AID_2 = "A000000004101018";
static final String NON_PAYMENT_AID_1 = "F053414950454D";
+ private static final String WALLET_OVERLAY_CONFIG = "config_defaultWallet";
+ private static final String CERTIFICATE_SEPARATOR = ":";
+
static final List<String> WALLET_HOLDER_AIDS = Arrays.asList("A000000004101011",
"A000000004101012",
"A000000004101013",
"A000000004101018");
- static class RoleContext {
- String mOriginalHolder;
- Context mContext;
- RoleContext(Context context) {
- mContext = context;
- mOriginalHolder = null;
- androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
- .getUiAutomation().adoptShellPermissionIdentity(MANAGE_DEFAULT_APPLICATIONS);
- mOriginalHolder = getDefaultWalletRoleHolder(context);
- setDefaultWalletRoleHolder(context);
- }
-
- void clear() {
- androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
- .getUiAutomation().dropShellPermissionIdentity();
- }
- }
-
static ComponentName getWalletRoleHolderService() {
return new ComponentName(WALLET_HOLDER_PACKAGE_NAME,
"com.android.test.walletroleholder.WalletRoleHolderApduService");
@@ -136,11 +127,20 @@
}
static String getDefaultWalletRoleHolder(Context context) {
- RoleManager roleManager = context.getSystemService(RoleManager.class);
- return roleManager.getDefaultApplication(RoleManager.ROLE_WALLET);
+ try {
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity(MANAGE_DEFAULT_APPLICATIONS);
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ return roleManager.getDefaultApplication(RoleManager.ROLE_WALLET);
+ } finally {
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
}
static void runWithRole(Context context, String roleHolder, Runnable runnable) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ assumeFalse(userManager.isHeadlessSystemUserMode());
try {
runWithRoleNone(context, () -> {}); //Remove the role holder first to trigger callbacks
RoleManager roleManager = context.getSystemService(RoleManager.class);
@@ -163,7 +163,7 @@
onRoleHoldersChangedListener, context.getUser());
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
.getUiAutomation().adoptShellPermissionIdentity(MANAGE_DEFAULT_APPLICATIONS);
- assumeTrue(setDefaultWalletRoleHolder(context, roleHolder));
+ Assert.assertTrue(setDefaultWalletRoleHolder(context, roleHolder));
countDownLatch.await(4000, TimeUnit.MILLISECONDS);
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
.getUiAutomation().adoptShellPermissionIdentity(OBSERVE_ROLE_HOLDERS);
@@ -179,10 +179,24 @@
}
}
- static void runWithRoleNone(Context context, Runnable runnable) {
+ static boolean canAssignRoleToPackage(Context context, String packageName) {
+ String previousHolder = getDefaultWalletRoleHolder(context);
try {
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
.getUiAutomation().adoptShellPermissionIdentity(MANAGE_DEFAULT_APPLICATIONS);
+ boolean canSet = setDefaultWalletRoleHolder(context, packageName);
+ if (canSet && previousHolder != null) {
+ setDefaultWalletRoleHolder(context, previousHolder);
+ }
+ return canSet;
+ } finally {
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+ static void runWithRoleNone(Context context, Runnable runnable) {
+ try {
String currentHolder = getDefaultWalletRoleHolder(context);
RoleManager roleManager = context.getSystemService(RoleManager.class);
CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -205,8 +219,10 @@
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
.getUiAutomation().adoptShellPermissionIdentity(MANAGE_ROLE_HOLDERS);
if (currentHolder != null) {
- assumeTrue(removeRoleHolder(context, currentHolder));
+ roleManager.setRoleFallbackEnabled(RoleManager.ROLE_WALLET, false);
+ Assert.assertTrue(removeRoleHolder(context, currentHolder));
countDownLatch.await(4000, TimeUnit.MILLISECONDS);
+ roleManager.setRoleFallbackEnabled(RoleManager.ROLE_WALLET, true);
}
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
.getUiAutomation().adoptShellPermissionIdentity(OBSERVE_ROLE_HOLDERS);
@@ -220,4 +236,56 @@
.getUiAutomation().dropShellPermissionIdentity();
}
}
+
+ static void clearRoleHolders(Context context) {
+ try {
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity(MANAGE_ROLE_HOLDERS);
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ AtomicReference<Boolean> result = new AtomicReference<>(false);
+ roleManager.clearRoleHoldersAsUser(RoleManager.ROLE_WALLET, 0,
+ context.getUser(), MoreExecutors.directExecutor(), aBoolean -> {
+ try {
+ // Wait a second to make sure all other callbacks are also fired on
+ // their respective executors.
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ result.set(aBoolean);
+ countDownLatch.countDown();
+ });
+ countDownLatch.await(4000, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+ static String getOverLayDefaultHolder(Context context) {
+ Resources resources = context.getResources();
+ int resourceId = resources.getIdentifier(WALLET_OVERLAY_CONFIG,
+ "string", "android");
+ if (resourceId == 0) {
+ return null;
+ }
+ String defaultHolders;
+ try {
+ defaultHolders = resources.getString(resourceId);
+ } catch (Resources.NotFoundException e) {
+ return null;
+ }
+ if (TextUtils.isEmpty(defaultHolders)) {
+ return null;
+ }
+ int certificateSeparatorIndex = defaultHolders.indexOf(CERTIFICATE_SEPARATOR);
+ if (certificateSeparatorIndex != -1) {
+ return defaultHolders.substring(0, certificateSeparatorIndex);
+ } else {
+ return defaultHolders;
+ }
+ }
}
diff --git a/tests/tests/notification/src/android/app/notification/current/cts/NotificationManagerTest.java b/tests/tests/notification/src/android/app/notification/current/cts/NotificationManagerTest.java
index 1ec3122..5212841 100644
--- a/tests/tests/notification/src/android/app/notification/current/cts/NotificationManagerTest.java
+++ b/tests/tests/notification/src/android/app/notification/current/cts/NotificationManagerTest.java
@@ -1563,6 +1563,7 @@
mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME);
assertNotNull(mListener);
CountDownLatch rerankLatch = mListener.setRankingUpdateCountDown(5);
+ CountDownLatch postingLatch = mListener.setPostedCountDown(5);
sendNotification(801, R.drawable.black);
sendNotification(802, R.drawable.blue);
@@ -1570,6 +1571,7 @@
sendNotification(804, R.drawable.yellow);
// Wait until all the notifications, including the autogroup, are posted and grouped.
+ postingLatch.await(400, TimeUnit.MILLISECONDS);
rerankLatch.await(400, TimeUnit.MILLISECONDS);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
diff --git a/tests/tests/security/src/android/security/cts/EncryptionTest.java b/tests/tests/security/src/android/security/cts/EncryptionTest.java
index f6c5240..8de6a23 100644
--- a/tests/tests/security/src/android/security/cts/EncryptionTest.java
+++ b/tests/tests/security/src/android/security/cts/EncryptionTest.java
@@ -56,7 +56,7 @@
if (firstApiLevel >= Build.VERSION_CODES.S) {
// Assumes every test in this file asserts a requirement of CDD section 9.
assumeTrue("Skipping test: FEATURE_SECURITY_MODEL_COMPATIBLE missing.",
- !context.getPackageManager()
+ context.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_SECURITY_MODEL_COMPATIBLE));
}
}
diff --git a/tests/tests/settings/src/android/settings/cts/SettingsIntentsInWorkProfileTest.java b/tests/tests/settings/src/android/settings/cts/SettingsIntentsInWorkProfileTest.java
index d2938c5..18266e4a 100644
--- a/tests/tests/settings/src/android/settings/cts/SettingsIntentsInWorkProfileTest.java
+++ b/tests/tests/settings/src/android/settings/cts/SettingsIntentsInWorkProfileTest.java
@@ -16,7 +16,7 @@
package android.settings.cts;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.content.Intent;
@@ -42,6 +42,7 @@
*/
@RunWith(BedsteadJUnit4.class)
public class SettingsIntentsInWorkProfileTest {
+ private static final String TEST_PACKAGE = "package:com.android.settings";
@ClassRule
@Rule
@@ -58,7 +59,7 @@
@RequireRunOnWorkProfile
public void settingActivity_launchManageWriteSettingsIntent_shouldNotCrash() {
final Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
- intent.setData(Uri.parse("package:com.android.vending"));
+ intent.setData(Uri.parse(TEST_PACKAGE));
startActivity(intent);
}
@@ -66,7 +67,7 @@
@RequireRunOnWorkProfile
public void settingActivity_launchAppUsageIntent_shouldNotCrash() {
final Intent intent = new Intent(Settings.ACTION_APP_USAGE_SETTINGS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, "package:com.android.vending");
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, TEST_PACKAGE);
startActivity(intent);
}
@@ -74,7 +75,7 @@
@RequireRunOnWorkProfile
public void settingActivity_launchApplicationDetailsIntent_shouldNotCrash() {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- intent.setData(Uri.parse("package:com.android.vending"));
+ intent.setData(Uri.parse(TEST_PACKAGE));
startActivity(intent);
}
@@ -93,7 +94,7 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final ResolveInfo ri = packageManager.resolveActivity(intent,
PackageManager.MATCH_DEFAULT_ONLY);
- assertNotNull(ri);
+ assumeTrue(ri != null); // Skip the test if target package cannot handle this intent
targetContext.startActivity(intent);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
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 cb1b249..d5bb50c9 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
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;
import android.app.PendingIntent;
@@ -121,6 +122,10 @@
}
mEuiccManager = (EuiccManager) getContext().getSystemService(Context.EUICC_SERVICE);
+
+ if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+ assumeNotNull(mEuiccManager);
+ }
}
@After
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
index e461e0c..59e104c 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -182,4 +182,10 @@
<!-- b/236205588#comment26 -->
<option name="compatibility:exclude-filter" value="CtsGraphicsTestCases android.graphics.cts.ImageDecoderTest#testDecode10BitHeifWithLowRam" />
<option name="compatibility:exclude-filter" value="CtsGraphicsTestCases[instant] android.graphics.cts.ImageDecoderTest#testDecode10BitHeifWithLowRam" />
+
+ <!-- b/349776686 -->
+ <option name="compatibility:exclude-filter" value="CtsKeystoreTestCases android.keystore.cts.KeyAttestationTest#testAttestedRoTAcrossKeymints" />
+ <option name="compatibility:exclude-filter" value="CtsKeystoreTestCases android.keystore.cts.KeyAttestationTest#testEcAttestation_StrongBox" />
+ <option name="compatibility:exclude-filter" value="CtsKeystoreTestCases android.keystore.cts.KeyAttestationTest#testEcAttestation_UniqueIdWorksWithCorrectPermission_StrongBox" />
+ <option name="compatibility:exclude-filter" value="CtsKeystoreTestCases android.keystore.cts.KeyAttestationTest#testRsaAttestation_StrongBox" />
</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-system-physical.xml b/tools/cts-tradefed/res/config/cts-system-physical.xml
index 7fb97d5..f7f1369 100644
--- a/tools/cts-tradefed/res/config/cts-system-physical.xml
+++ b/tools/cts-tradefed/res/config/cts-system-physical.xml
@@ -21,7 +21,6 @@
<option name="result-attribute" key="system" value="1" />
<!--Include Filters -->
- <option name="compatibility:include-filter" value="CtsNNAPITestCases"/>
<option name="compatibility:include-filter" value="CtsTelecomTestCases"/>
<option name="compatibility:include-filter" value="CtsMediaStressTestCases"/>
<option name="compatibility:include-filter" value="CtsVideoTestCases"/>
@@ -528,4 +527,151 @@
<option name="compatibility:include-filter" value="CtsLocationGnssTestCases android.location.cts.gnss.GnssPseudorangeVerificationTest#testPseudorangeValue"/>
<option name="compatibility:include-filter" value="CtsLocationGnssTestCases android.location.cts.gnss.GnssMeasurementValuesTest#testListenForGnssMeasurements"/>
<option name="compatibility:include-filter" value="CtsPermissionPolicyTestCases android.permissionpolicy.cts.NoReceiveSmsPermissionTest#testReceiveTextMessage"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[271]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1091]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[496]"/>
+ <option name="compatibility:include-filter" value="CtsAppSecurityHostTestCases android.appsecurity.cts.ListeningPortsTest#testNoListeningLoopbackTcpPorts"/>
+ <option name="compatibility:include-filter" value="CtsAppEnumerationTestCases android.appenumeration.cts.SyncAdapterEnumerationTests#queriesPackage_getRunningServiceControlPanel_canSeeSyncAdapterTarget"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1701]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[680]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1350]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[670]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1693]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1124]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1530]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[758]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1107]"/>
+ <option name="compatibility:include-filter" value="CtsSurfaceControlTests android.view.surfacecontrol.cts.ASurfaceControlTest#testSurfaceTransaction_setFrameTimeline_notPreferredIndex"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[519]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[439]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1199]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[512]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1505]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1680]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1667]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[386]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[710]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[828]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1629]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1224]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[737]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[747]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1666]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1404]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1081]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1137]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[755]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[877]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[750]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testNotificationSeen_verifyBucket"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[359]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1092]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[632]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[457]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[425]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[782]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[864]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1425]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1605]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[735]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[620]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testGetAppStandbyBuckets"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1418]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1109]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[510]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1189]"/>
+ <option name="compatibility:include-filter" value="CtsVirtualDevicesAudioTestCases android.virtualdevice.cts.audio.TextToSpeechTest#textToSpeechWithVirtualDeviceContext_explicitSessionIdOverridesVdmSessionId"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[553]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1478]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[303]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[678]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[704]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[760]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1637]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[527]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testUserLaunchRemovesFromRestricted"/>
+ <option name="compatibility:include-filter" value="CtsOsTestCases android.os.cts.UsbDebuggingTest#testUsbDebugging"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1625]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[799]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[517]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[734]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1380]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1620]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1110]"/>
+ <option name="compatibility:include-filter" value="CtsSurfaceControlTests android.view.surfacecontrol.cts.SurfaceControlTest#testSurfaceTransaction_setFrameTimeline_preferredIndex"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1489]"/>
+ <option name="compatibility:include-filter" value="CtsOsTestCases android.os.cts.BuildVersionTest#testBuildFingerprint"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1436]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[749]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1173]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1554]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[635]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[581]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1633]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[583]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[709]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testStandbyBucketChangeLog"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxHostTest#testAllDomainsEnforcing"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1141]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[708]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testNotificationSeen_noImpact"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1474]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1470]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1038]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1119]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[452]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[487]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1162]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1128]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1108]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testInteractiveEvents"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1042]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1097]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1085]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[948]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1612]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[757]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testQueryEventsForSelf"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[761]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1444]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[426]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testIsAppInactive"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[290]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1218]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testNotificationSeen_verifyBucket_retainPreTImpact"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testUserForceIntoRestricted"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[805]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[754]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1065]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1212]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1132]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[934]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1413]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[623]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[350]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[543]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[748]"/>
+ <option name="compatibility:include-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testIsAppInactive_Charging"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1217]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1493]"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1641]"/>
+ <option name="compatibility:include-filter" value="CtsHealthFitnessDeviceTestCases android.healthconnect.cts.GetApplicationInfoTest#testGetApplicationInfo"/>
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1216]"/>
+ <option name="compatibility:include-filter" value="CtsHealthConnectControllerTestCases android.healthconnect.cts.ui.permissions"/>
+ <option name="compatibility:include-filter" value="CtsActivityRecognitionTestCases android.activityrecognition.cts.RenouncedPermissionsTest#testActivityRecognitionAttributionTagBlaming"/>
+ <option name="compatibility:include-filter" value="CtsStorageHostTestCases android.appsecurity.cts.StorageHostTest#testFullDisk"/>
+ <option name="compatibility:include-filter" value="CtsSurfaceControlTests android.view.surfacecontrol.cts.ASurfaceControlTest#testSurfaceTransaction_setFrameTimeline_preferredIndex"/>
+ <option name="compatibility:include-filter" value="CtsJobSchedulerTestCases android.jobscheduler.cts.JobThrottlingTest#testJobsInRestrictedBucket_WithRequiredNetwork"/>
+ <option name="compatibility:include-filter" value="CtsPackageManagerTestCases android.content.pm.cts.PackageManagerShellCommandMultiUserTest#testUpdateMimeGroup_changed"/>
+ <option name="compatibility:include-filter" value="CtsVirtualDevicesAppLaunchTestCases android.virtualdevice.cts.applaunch.VirtualDeviceImeTest#setDisplayImePolicy_changeAtRuntime"/>
+ <option name="compatibility:include-filter" value="CtsJobSchedulerTestCases android.jobscheduler.cts.JobThrottlingTest#testRestrictedJobAllowedWhenUidActive"/>
+ <option name="compatibility:include-filter" value="CtsJobSchedulerTestCases android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintFails_withMeteredWiFi"/>
+ <option name="compatibility:include-filter" value="CtsAppOpsTestCases android.app.appops.cts.AppOpsLoggingTest#getCellInfo"/>
+ <option name="compatibility:include-filter" value="CtsAppTestCases android.app.cts.SystemFeaturesTest#testTelephonyFeatures"/>
+ <option name="compatibility:include-filter" value="CtsDevicePolicyTestCases android.devicepolicy.cts.TelephonyTest"/>
+ <option name="compatibility:include-filter" value="CtsPermissionPolicyTestCases android.permissionpolicy.cts.NoProcessOutgoingCallPermissionTest#testProcessOutgoingCall"/>
+ <option name="compatibility:include-filter" value="CtsProviderTestCases android.provider.cts.SmsBackupRestoreTest#testSmsBackupRestore"/>
+ <option name="compatibility:include-filter" value="CtsAppTestCases android.app.cts.SystemFeaturesTest#testSensorFeatures"/>
+ <option name="compatibility:include-filter" value="CtsInputTestCases android.input.cts.AppKeyCombinationsTest#testCtrlSpace"/>
+ <option name="compatibility:include-filter" value="CtsBackupHostTestCases android.cts.backup.OtherSoundsSettingsHostSideTest"/>
</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-system-virtual.xml b/tools/cts-tradefed/res/config/cts-system-virtual.xml
index a4ac259..3549091 100644
--- a/tools/cts-tradefed/res/config/cts-system-virtual.xml
+++ b/tools/cts-tradefed/res/config/cts-system-virtual.xml
@@ -406,6 +406,7 @@
<option name="compatibility:include-filter" value="CtsAttributionSourceTestCases"/>
<option name="compatibility:include-filter" value="SdkSandboxManagerTests"/>
<option name="compatibility:include-filter" value="CtsInputTestCases"/>
+ <option name="compatibility:include-filter" value="CtsOnDeviceIntelligenceServiceTestCases"/>
<!--Exclude Filters -->
<option name="compatibility:exclude-filter" value="CtsAppExitTestCases android.app.cts.ActivityManagerAppExitInfoTest#testAnr"/>
@@ -542,4 +543,151 @@
<option name="compatibility:exclude-filter" value="CtsDevicePolicyTestCases android.devicepolicy.cts.WifiTest#createAdminSupportIntent_disallowConfigWifi_createsIntent"/>
<option name="compatibility:exclude-filter" value="CtsAtraceHostTestCases android.atrace.cts.AtraceHostTest#testTracingIsDisabled"/>
<option name="compatibility:exclude-filter" value="CtsDevicePolicyTestCases android.devicepolicy.cts.WifiMinimumSecurityTest#setWifiMinimumSecurity_validLevel_works[IncludeRunOnParentOfOrganizationOwnedProfileOwner][3]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[271]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1091]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[496]"/>
+ <option name="compatibility:exclude-filter" value="CtsAppSecurityHostTestCases android.appsecurity.cts.ListeningPortsTest#testNoListeningLoopbackTcpPorts"/>
+ <option name="compatibility:exclude-filter" value="CtsAppEnumerationTestCases android.appenumeration.cts.SyncAdapterEnumerationTests#queriesPackage_getRunningServiceControlPanel_canSeeSyncAdapterTarget"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1701]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[680]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1350]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[670]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1693]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1124]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1530]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[758]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1107]"/>
+ <option name="compatibility:exclude-filter" value="CtsSurfaceControlTests android.view.surfacecontrol.cts.ASurfaceControlTest#testSurfaceTransaction_setFrameTimeline_notPreferredIndex"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[519]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[439]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1199]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[512]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1505]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1680]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1667]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[386]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[710]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[828]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1629]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1224]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[737]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[747]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1666]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1404]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1081]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1137]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[755]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[877]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[750]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testNotificationSeen_verifyBucket"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[359]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1092]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[632]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[457]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[425]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[782]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[864]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1425]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1605]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[735]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[620]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testGetAppStandbyBuckets"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1418]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1109]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[510]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1189]"/>
+ <option name="compatibility:exclude-filter" value="CtsVirtualDevicesAudioTestCases android.virtualdevice.cts.audio.TextToSpeechTest#textToSpeechWithVirtualDeviceContext_explicitSessionIdOverridesVdmSessionId"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[553]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1478]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[303]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[678]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[704]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[760]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1637]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[527]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testUserLaunchRemovesFromRestricted"/>
+ <option name="compatibility:exclude-filter" value="CtsOsTestCases android.os.cts.UsbDebuggingTest#testUsbDebugging"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1625]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[799]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[517]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[734]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1380]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1620]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1110]"/>
+ <option name="compatibility:exclude-filter" value="CtsSurfaceControlTests android.view.surfacecontrol.cts.SurfaceControlTest#testSurfaceTransaction_setFrameTimeline_preferredIndex"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1489]"/>
+ <option name="compatibility:exclude-filter" value="CtsOsTestCases android.os.cts.BuildVersionTest#testBuildFingerprint"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1436]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[749]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1173]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1554]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[635]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[581]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1633]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[583]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[709]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testStandbyBucketChangeLog"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxHostTest#testAllDomainsEnforcing"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1141]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[708]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testNotificationSeen_noImpact"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1474]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1470]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1038]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1119]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[452]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[487]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1162]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1128]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1108]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testInteractiveEvents"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1042]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1097]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1085]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[948]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1612]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[757]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testQueryEventsForSelf"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[761]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1444]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[426]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testIsAppInactive"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[290]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1218]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testNotificationSeen_verifyBucket_retainPreTImpact"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testUserForceIntoRestricted"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[805]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[754]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1065]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1212]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1132]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[934]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1413]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[623]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[350]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[543]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[748]"/>
+ <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testIsAppInactive_Charging"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1217]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1493]"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1641]"/>
+ <option name="compatibility:exclude-filter" value="CtsHealthFitnessDeviceTestCases android.healthconnect.cts.GetApplicationInfoTest#testGetApplicationInfo"/>
+ <option name="compatibility:exclude-filter" value="CtsSecurityHostTestCases android.security.cts.SELinuxNeverallowRulesTest#testNeverallowRules[1216]"/>
+ <option name="compatibility:exclude-filter" value="CtsHealthConnectControllerTestCases android.healthconnect.cts.ui.permissions"/>
+ <option name="compatibility:exclude-filter" value="CtsActivityRecognitionTestCases android.activityrecognition.cts.RenouncedPermissionsTest#testActivityRecognitionAttributionTagBlaming"/>
+ <option name="compatibility:exclude-filter" value="CtsStorageHostTestCases android.appsecurity.cts.StorageHostTest#testFullDisk"/>
+ <option name="compatibility:exclude-filter" value="CtsSurfaceControlTests android.view.surfacecontrol.cts.ASurfaceControlTest#testSurfaceTransaction_setFrameTimeline_preferredIndex"/>
+ <option name="compatibility:exclude-filter" value="CtsJobSchedulerTestCases android.jobscheduler.cts.JobThrottlingTest#testJobsInRestrictedBucket_WithRequiredNetwork"/>
+ <option name="compatibility:exclude-filter" value="CtsPackageManagerTestCases android.content.pm.cts.PackageManagerShellCommandMultiUserTest#testUpdateMimeGroup_changed"/>
+ <option name="compatibility:exclude-filter" value="CtsVirtualDevicesAppLaunchTestCases android.virtualdevice.cts.applaunch.VirtualDeviceImeTest#setDisplayImePolicy_changeAtRuntime"/>
+ <option name="compatibility:exclude-filter" value="CtsJobSchedulerTestCases android.jobscheduler.cts.JobThrottlingTest#testRestrictedJobAllowedWhenUidActive"/>
+ <option name="compatibility:exclude-filter" value="CtsJobSchedulerTestCases android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintFails_withMeteredWiFi"/>
+ <option name="compatibility:exclude-filter" value="CtsAppOpsTestCases android.app.appops.cts.AppOpsLoggingTest#getCellInfo"/>
+ <option name="compatibility:exclude-filter" value="CtsAppTestCases android.app.cts.SystemFeaturesTest#testTelephonyFeatures"/>
+ <option name="compatibility:exclude-filter" value="CtsDevicePolicyTestCases android.devicepolicy.cts.TelephonyTest"/>
+ <option name="compatibility:exclude-filter" value="CtsPermissionPolicyTestCases android.permissionpolicy.cts.NoProcessOutgoingCallPermissionTest#testProcessOutgoingCall"/>
+ <option name="compatibility:exclude-filter" value="CtsProviderTestCases android.provider.cts.SmsBackupRestoreTest#testSmsBackupRestore"/>
+ <option name="compatibility:exclude-filter" value="CtsAppTestCases android.app.cts.SystemFeaturesTest#testSensorFeatures"/>
+ <option name="compatibility:exclude-filter" value="CtsInputTestCases android.input.cts.AppKeyCombinationsTest#testCtrlSpace"/>
+ <option name="compatibility:exclude-filter" value="CtsBackupHostTestCases android.cts.backup.OtherSoundsSettingsHostSideTest"/>
</configuration>
\ No newline at end of file
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java
index c89c6b2..b1878f9 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java
@@ -86,6 +86,7 @@
"mocking",
"networking",
"neuralnetworks",
+ "nfc",
"packagemanager",
"permissions",
"print",
@@ -154,7 +155,10 @@
if (Strings.isNullOrEmpty(suiteRoot)) {
fail(String.format("Should run within a suite context: %s doesn't exist", rootVar));
}
- File testcases = new File(suiteRoot, String.format("/android-%s/testcases/", getSuiteName().toLowerCase()));
+ File testcases =
+ new File(
+ suiteRoot,
+ String.format("/android-%s/testcases/", getSuiteName().toLowerCase()));
if (!testcases.exists()) {
fail(String.format("%s does not exists", testcases));
return;
@@ -213,9 +217,11 @@
String suiteName = getSuiteName().toLowerCase();
// Ensure each CTS module is tagged with <option name="test-suite-tag" value="cts" />
- Assert.assertTrue(String.format(
- "Module config %s does not contains "
- + "'<option name=\"test-suite-tag\" value=\"%s\" />'", config.getName(), suiteName),
+ Assert.assertTrue(
+ String.format(
+ "Module config %s does not contains "
+ + "'<option name=\"test-suite-tag\" value=\"%s\" />'",
+ config.getName(), suiteName),
cd.getSuiteTags().contains(suiteName));
// Ensure options have been set