Merge "[CTS] LauncherApps lockdown fix" into pi-dev
diff --git a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
new file mode 100644
index 0000000..920d40c
--- /dev/null
+++ b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
@@ -0,0 +1,229 @@
+# 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.
+
+import math
+import os.path
+import re
+import sys
+import cv2
+
+import its.caps
+import its.device
+import its.image
+import its.objects
+
+import numpy as np
+
+ALIGN_THRESH = 0.005
+CHART_DISTANCE_CM = 22 # cm
+NAME = os.path.basename(__file__).split('.')[0]
+ROTATE_REF_MATRIX = np.array([0, 0, 0, 1])
+TRANS_REF_MATRIX = np.array([0, 0, 0])
+
+
+def rotation_matrix(rotation):
+ """Convert the rotation parameters to 3-axis data.
+
+ Args:
+ rotation: android.lens.Rotation vector
+ Returns:
+ 3x3 matrix w/ rotation parameters
+ """
+ x = rotation[0]
+ y = rotation[1]
+ z = rotation[2]
+ w = rotation[3]
+ return np.array([[1-2*y**2-2*z**2, 2*x*y-2*z*w, 2*x*z+2*y*w],
+ [2*x*y+2*z*w, 1-2*x**2-2*z**2, 2*y*z-2*x*w],
+ [2*x*z-2*y*w, 2*y*z+2*x*w, 1-2*x**2-2*y**2]])
+
+
+def find_circle(gray, name):
+ """Find the circle in the image.
+
+ Args:
+ gray: gray scale image array [0,255]
+ name: string of file name
+ Returns:
+ center: circle center location (x, y)
+ """
+
+ cv2_version = cv2.__version__
+ try:
+ if cv2_version.startswith('2.4.'):
+ center = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT,
+ 1, 20)[0][0]
+ elif cv2_version.startswith('3.2.'):
+ center = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT,
+ 1, 20)[0][0]
+ except TypeError:
+ center = None
+ its.image.write_image(gray[..., np.newaxis]/255.0, name)
+ assert center is not None, 'No circle found!'
+ return center
+
+
+def main():
+ """Test the multi camera system parameters related to camera spacing."""
+ chart_distance = CHART_DISTANCE_CM
+ for s in sys.argv[1:]:
+ if s[:5] == 'dist=' and len(s) > 5:
+ chart_distance = float(re.sub('cm', '', s[5:]))
+ print 'Using chart distance: %.1fcm' % chart_distance
+
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ its.caps.skip_unless(its.caps.compute_target_exposure(props) and
+ its.caps.per_frame_control(props) and
+ its.caps.logical_multi_camera(props) and
+ its.caps.raw16(props) and
+ its.caps.manual_sensor(props))
+ props = cam.get_camera_properties()
+ debug = its.caps.debug_mode()
+
+ max_raw_size = its.objects.get_available_output_sizes('raw', props)[0]
+ w, h = its.objects.get_available_output_sizes(
+ 'yuv', props, match_ar_size=max_raw_size)[0]
+
+ # Do 3A and get the values
+ s, e, _, _, fd = cam.do_3a(get_results=True,
+ lock_ae=True, lock_awb=True)
+ e *= 2 # TODO: remove when RAW images bright enough
+ req = its.objects.manual_capture_request(s, e, fd, True, props)
+
+ # get physical camera properties
+ ids = its.caps.logical_multi_camera_physical_ids(props)
+ props_physical = {}
+ for i in ids:
+ props_physical[i] = cam.get_camera_properties_by_id(i)
+
+ # capture RAWs of 1st 2 cameras
+ cap_raw = {}
+ out_surfaces = [{'format': 'yuv', 'width': w, 'height': h},
+ {'format': 'raw', 'physicalCamera': ids[0]},
+ {'format': 'raw', 'physicalCamera': ids[1]}]
+ _, cap_raw[ids[0]], cap_raw[ids[1]] = cam.do_capture(req, out_surfaces)
+
+ size_raw = {}
+ k = {}
+ reference = {}
+ rotation = {}
+ trans = {}
+ circle = {}
+ point = {}
+ for i in ids:
+ print 'Starting camera %s' % i
+ # process image
+ img_raw = its.image.convert_capture_to_rgb_image(
+ cap_raw[i], props=props)
+ size_raw[i] = (cap_raw[i]['width'], cap_raw[i]['height'])
+
+ # save images if debug
+ if debug:
+ its.image.write_image(img_raw, '%s_raw_%s.jpg' % (NAME, i))
+
+ # convert to [0, 255] images
+ img_raw *= 255
+
+ # rotate images and scale to match calibration data
+ # TODO remove rotation when not needed w/ EVT2
+ img_rot = np.rot90(cv2.resize(img_raw.astype(np.uint8), None,
+ fx=2, fy=2), k=2)
+
+ # load parameters for each physical camera
+ ical = props_physical[i]['android.lens.intrinsicCalibration']
+ assert len(ical) == 5, 'android.lens.instrisicCalibration incorrect.'
+ k[i] = np.array([[ical[0], ical[4], ical[2]],
+ [0, ical[1], ical[3]],
+ [0, 0, 1]])
+ print ' k:', k[i]
+
+ rotation[i] = np.array(props_physical[i]['android.lens.poseRotation'])
+ print ' rotation:', rotation[i]
+ assert len(rotation[i]) == 4, 'poseRotation has wrong # of params.'
+ trans[i] = np.array(
+ props_physical[i]['android.lens.poseTranslation'])
+ print ' translation:', trans[i]
+ assert len(trans[i]) == 3, 'poseTranslation has wrong # of params.'
+ if ((rotation[i] == ROTATE_REF_MATRIX).all() and
+ (trans[i] == TRANS_REF_MATRIX).all()):
+ reference[i] = True
+ else:
+ reference[i] = False
+ # TODO: change below to android.lens.distortion when builds working
+ rad_dist = np.array(
+ props_physical[i]['android.lens.radialDistortion'])
+ assert len(rad_dist) == 6, 'radialDistortion has wrong # of params.'
+
+ # Apply correction to image (if available)
+ if its.caps.distortion_correction(props):
+ cv2_distort = np.array([rad_dist[1], rad_dist[2],
+ rad_dist[4], rad_dist[5],
+ rad_dist[3]])
+ img_rot = cv2.undistort(img_rot, k[i], cv2_distort)
+ its.image.write_image(img_rot/255.0, '%s_correct_%s.jpg' % (
+ NAME, i))
+
+ # Find the circles in grayscale image
+ circle[i] = find_circle(cv2.cvtColor(img_rot, cv2.COLOR_BGR2GRAY),
+ '%s_gray%s.jpg' % (NAME, i))
+
+ # Find 3D location of circle centers
+ point[i] = np.dot(np.linalg.inv(k[i]),
+ np.array([circle[i][0],
+ circle[i][1], 1])) * chart_distance * 1.0E-2
+
+ ref_index = (e for e in reference if e).next()
+ print 'reference camera id:', ref_index
+ ref_rotation = rotation[ref_index]
+ ref_rotation = ref_rotation.astype(np.float32)
+ print 'rotation reference:', ref_rotation
+ r = rotation_matrix(ref_rotation)
+ if debug:
+ print 'r:', r
+ t = -1 * trans[ref_index]
+ print 't:', t
+
+ # estimation ids[0] circle center from ids[1] & params
+ estimated_0 = cv2.projectPoints(point[ids[1]].reshape(1, 3),
+ r, t, k[ids[0]], None)[0][0][0]
+ err_0 = np.linalg.norm(estimated_0 - circle[ids[0]][:2])
+ print 'Circle centers [%s]' % ids[0]
+ print 'Measured: %.1f, %.1f' % (circle[ids[0]][1], circle[ids[0]][0])
+ print 'Calculated: %.1f, %.1f' % (estimated_0[1],
+ estimated_0[0])
+ print 'Error(pixels): %.1f' % err_0
+
+ # estimation ids[0] circle center from ids[1] & params
+ estimated_1 = cv2.projectPoints(point[ids[0]].reshape(1, 3),
+ r.T, -np.dot(r, t), k[ids[1]],
+ None)[0][0][0]
+ err_1 = np.linalg.norm(estimated_1 - circle[ids[1]][:2])
+ print 'Circle centers [%s]' % ids[1]
+ print 'Measured: %.1f, %.1f' % (circle[ids[1]][1], circle[ids[1]][0])
+ print 'Calculated: %.1f, %.1f' % (estimated_1[1], estimated_1[0])
+ print 'Error(pixels): %.1f' % err_1
+
+ err_0 /= math.sqrt(size_raw[ids[0]][0]**2 + size_raw[ids[0]][1]**2)
+ err_1 /= math.sqrt(size_raw[ids[1]][0]**2 + size_raw[ids[1]][1]**2)
+ msg = '%s -> %s center error too large! val=%.1f%%, THRESH=%.f%%' % (
+ ids[1], ids[0], err_0*100, ALIGN_THRESH*100)
+ assert err_0 < ALIGN_THRESH, msg
+ msg = '%s -> %s center error too large! val=%.1f%%, THRESH=%.f%%' % (
+ ids[0], ids[1], err_1*100, ALIGN_THRESH*100)
+ assert err_1 < ALIGN_THRESH, msg
+
+
+if __name__ == '__main__':
+ main()
diff --git a/apps/CameraITS/tools/load_scene.py b/apps/CameraITS/tools/load_scene.py
index b46a311..330b32f 100644
--- a/apps/CameraITS/tools/load_scene.py
+++ b/apps/CameraITS/tools/load_scene.py
@@ -18,6 +18,8 @@
import sys
import time
+import numpy as np
+
def main():
"""Load charts on device and display."""
@@ -47,7 +49,7 @@
remote_scene_file = '/sdcard/Download/%s.pdf' % scene
local_scene_file = os.path.join(os.environ['CAMERA_ITS_TOP'], 'tests',
scene)
- if chart_distance == 20 and camera_fov < 90:
+ if np.isclose(chart_distance, 20, rtol=0.1) and camera_fov < 90:
local_scene_file = os.path.join(local_scene_file,
scene+'_0.67_scaled.pdf')
else:
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index e4c20a8..b8230a8 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -28,6 +28,8 @@
from its.device import ItsSession
import its.image
+import numpy as np
+
CHART_DELAY = 1 # seconds
CHART_DISTANCE = 30.0 # cm
CHART_HEIGHT = 13.5 # cm
@@ -173,7 +175,6 @@
skip_scene_validation = False
chart_distance = CHART_DISTANCE
chart_height = CHART_HEIGHT
- approx_equal = lambda a, b, t: abs(a - b) < t
for s in sys.argv[1:]:
if s[:7] == "camera=" and len(s) > 7:
@@ -194,6 +195,7 @@
elif s[:5] == 'dist=' and len(s) > 5:
chart_distance = float(re.sub('cm', '', s[5:]))
+ chart_dist_arg = 'dist= ' + str(chart_distance)
auto_scene_switch = chart_host_id is not None
merge_result_switch = result_device_id is not None
@@ -319,7 +321,6 @@
if (not merge_result_switch or
(merge_result_switch and camera_ids[0] == '0')):
scene_arg = 'scene=' + scene
- chart_dist_arg = 'dist= ' + str(chart_distance)
fov_arg = 'fov=' + camera_fov
cmd = ['python',
os.path.join(os.getcwd(), 'tools/load_scene.py'),
@@ -343,8 +344,8 @@
# Extract chart from scene for scene3 once up front
chart_loc_arg = ''
if scene == 'scene3':
- if float(camera_fov) < 90 and approx_equal(chart_distance, 20,
- 1E-6):
+ if float(camera_fov) < 90 and np.isclose(chart_distance, 20,
+ rtol=0.1):
chart_height *= 0.67
chart = its.cv2image.Chart(SCENE3_FILE, chart_height,
chart_distance, CHART_SCALE_START,
@@ -384,6 +385,7 @@
if skip_code is not SKIP_RET_CODE:
cmd = ['python', os.path.join(os.getcwd(), testpath)]
cmd += sys.argv[1:] + [camera_id_arg] + [chart_loc_arg]
+ cmd += [chart_dist_arg]
with open(outpath, 'w') as fout, open(errpath, 'w') as ferr:
test_code = subprocess.call(
cmd, stderr=ferr, stdout=fout, cwd=outdir)
diff --git a/apps/CtsVerifier/res/layout/voicemail_hide_in_call_settings.xml b/apps/CtsVerifier/res/layout/voicemail_hide_in_call_settings.xml
index eb8b81a..4d7803b 100644
--- a/apps/CtsVerifier/res/layout/voicemail_hide_in_call_settings.xml
+++ b/apps/CtsVerifier/res/layout/voicemail_hide_in_call_settings.xml
@@ -46,6 +46,12 @@
</LinearLayout>
<Button
+ android:id="@+id/call_settings_check_not_applicable"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/visual_voicemail_service_remove_sim_not_applicable"/>
+
+ <Button
android:id="@+id/set_default_dialer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/voicemail_hide_ringtone_settings.xml b/apps/CtsVerifier/res/layout/voicemail_hide_ringtone_settings.xml
index ea11314..9ccd909 100644
--- a/apps/CtsVerifier/res/layout/voicemail_hide_ringtone_settings.xml
+++ b/apps/CtsVerifier/res/layout/voicemail_hide_ringtone_settings.xml
@@ -32,11 +32,18 @@
android:layout_height="wrap_content"
android:text="@string/open_voicemail_settings_explanation"
android:textSize="16dp"/>
+
<Button
- android:id="@+id/open_voicemail_settings"
+ android:id="@+id/voicemail_hide_ringtone_settings_not_applicable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/open_voicemail_settings"/>
+ android:text="@string/visual_voicemail_service_remove_sim_not_applicable"/>
+
+ <Button
+ android:id="@+id/open_voicemail_settings"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/open_voicemail_settings"/>
<LinearLayout
android:layout_width="wrap_content"
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/CallSettingsCheckActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/CallSettingsCheckActivity.java
index ac0f060..e2e4d30 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/CallSettingsCheckActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/CallSettingsCheckActivity.java
@@ -21,6 +21,8 @@
import android.telecom.TelecomManager;
import android.view.View;
import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -32,6 +34,10 @@
private DefaultDialerChanger mDefaultDialerChanger;
+ private Button mSetDefaultDialerButton;
+ private Button mNotApplicableButton;
+ private ImageView mRestoreDefaultDialerImage;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -44,6 +50,22 @@
mDefaultDialerChanger = new DefaultDialerChanger(this);
+ mSetDefaultDialerButton = findViewById(R.id.set_default_dialer);
+ mNotApplicableButton = findViewById(R.id.call_settings_check_not_applicable);
+ mRestoreDefaultDialerImage = findViewById(R.id.restore_default_dialer_image);
+
+ mNotApplicableButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ getPassButton().setEnabled(true);
+ mSetDefaultDialerButton.setEnabled(false);
+
+ mRestoreDefaultDialerImage.setImageDrawable(getDrawable(R.drawable.fs_warning));
+ }
+ }
+ );
+
findViewById(R.id.open_call_settings).setOnClickListener(
new OnClickListener() {
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/VoicemailSettingsCheckActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/VoicemailSettingsCheckActivity.java
index d4ac0db..f2a7345 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/VoicemailSettingsCheckActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/VoicemailSettingsCheckActivity.java
@@ -21,6 +21,8 @@
import android.telephony.TelephonyManager;
import android.view.View;
import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -30,6 +32,13 @@
*/
public class VoicemailSettingsCheckActivity extends PassFailButtons.Activity {
+ private Button mNotApplicableButton;
+ private Button mOpenVoiceMailSettingsButton;
+ private Button mRingtoneSettingsDoesNotExistButton;
+ private Button mRingtoneSettingsExistsButton;
+
+ private ImageView mRestoreDefaultDialerImage;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -40,7 +49,30 @@
setPassFailButtonClickListeners();
getPassButton().setEnabled(false);
- findViewById(R.id.open_voicemail_settings).setOnClickListener(
+
+ mNotApplicableButton = findViewById(R.id.voicemail_hide_ringtone_settings_not_applicable);
+ mOpenVoiceMailSettingsButton = findViewById(R.id.open_voicemail_settings);
+ mRingtoneSettingsDoesNotExistButton = findViewById(R.id.settings_hidden);
+ mRingtoneSettingsExistsButton = findViewById(R.id.settings_not_hidden);
+
+ mRestoreDefaultDialerImage = findViewById(R.id.restore_default_dialer_image);
+
+ mNotApplicableButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ getPassButton().setEnabled(true);
+
+ mOpenVoiceMailSettingsButton.setEnabled(false);
+ mRingtoneSettingsDoesNotExistButton.setEnabled(false);
+ mRingtoneSettingsExistsButton.setEnabled(false);
+
+ mRestoreDefaultDialerImage.setImageDrawable(getDrawable(R.drawable.fs_warning));
+ }
+ }
+ );
+
+ mOpenVoiceMailSettingsButton.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
@@ -50,7 +82,7 @@
}
);
- findViewById(R.id.settings_hidden).setOnClickListener(
+ mRingtoneSettingsDoesNotExistButton.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
@@ -60,7 +92,7 @@
}
);
- findViewById(R.id.settings_not_hidden).setOnClickListener(
+ mRingtoneSettingsExistsButton.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
@@ -68,6 +100,5 @@
}
}
);
-
}
}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
index fd05398..d12caa8 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
@@ -26,7 +26,7 @@
@Override
@Before
public void handleBusinessLogic() {
- super.loadBuisnessLogic();
+ super.loadBusinessLogic();
ensureAuthenticated();
super.executeBusinessLogic();
}
@@ -51,4 +51,4 @@
// Fail test since request was not authorized.
failTest(String.format("Unable to execute because %s.", message));
}
-}
\ No newline at end of file
+}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
index ec940ce..130bf69 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
@@ -17,6 +17,7 @@
package com.android.compatibility.common.util;
import android.content.Context;
+import android.util.Log;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -47,6 +48,14 @@
* {@inheritDoc}
*/
@Override
+ public void logInfo(String format, Object... args) {
+ Log.i(LOG_TAG, String.format(format, args));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
protected ResolvedMethod getResolvedMethod(Class cls, String methodName, String... args)
throws ClassNotFoundException {
List<Method> nameMatches = getMethodsWithName(cls, methodName);
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
index 45f979a..671d33b 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
@@ -50,7 +50,7 @@
@Before
public void handleBusinessLogic() {
- loadBuisnessLogic();
+ loadBusinessLogic();
executeBusinessLogic();
}
@@ -70,7 +70,7 @@
}
}
- protected void loadBuisnessLogic() {
+ protected void loadBusinessLogic() {
File businessLogicFile = new File(BusinessLogic.DEVICE_FILE);
if (businessLogicFile.canRead()) {
mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
index 4986b73..2216b2a 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProvider.java
@@ -23,7 +23,6 @@
import com.android.tradefed.build.IBuildProvider;
import com.android.tradefed.build.IDeviceBuildInfo;
import com.android.tradefed.build.IDeviceBuildProvider;
-import com.android.tradefed.build.VersionedFile;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.config.OptionClass;
@@ -37,8 +36,10 @@
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
@@ -206,12 +207,10 @@
public void cleanUp(IBuildInfo info) {
// Everything should have been copied properly to result folder, we clean up
if (info instanceof IDeviceBuildInfo) {
- for (VersionedFile f : info.getFiles()) {
- // do not delete the testsdir since it's the real CTS folder.
- if (!f.getFile().equals(((IDeviceBuildInfo) info).getTestsDir())) {
- FileUtil.recursiveDelete(f.getFile());
- }
- }
+ List<File> doNotDelete = new ArrayList<>();
+ // Clean up everything except the tests dir
+ doNotDelete.add(((IDeviceBuildInfo) info).getTestsDir());
+ info.cleanUp(doNotDelete);
} else {
info.cleanUp();
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
index 7f527db..cebac97 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
@@ -101,6 +101,10 @@
*/
public void validateBuildFingerprint(ITestDevice device) throws DeviceNotAvailableException {
String oldBuildFingerprint = new LightInvocationResult(getResult()).getBuildFingerprint();
+ if (oldBuildFingerprint == null) {
+ throw new IllegalArgumentException(
+ "Could not find the build_fingerprint field in the result xml.");
+ }
String currentBuildFingerprint = device.getProperty("ro.build.fingerprint");
if (!oldBuildFingerprint.equals(currentBuildFingerprint)) {
throw new IllegalArgumentException(String.format(
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProviderTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProviderTest.java
index f7116d7..e2eefa4 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProviderTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildProviderTest.java
@@ -52,6 +52,10 @@
String getRootDirPath() {
return mRootDir.getAbsolutePath();
}
+ @Override
+ protected String getSuiteInfoName() {
+ return "CTS";
+ }
};
}
@@ -79,6 +83,8 @@
*/
@Test
public void testBaseGetBuild_withDevice() throws Exception {
+ // Create real testcases dir
+ new File(mRootDir, "android-cts/testcases").mkdirs();
OptionSetter setter = new OptionSetter(mProvider);
setter.setOptionValue("use-device-build-info", "true");
setter.setOptionValue("branch", "build_branch");
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/suite/CertificationChecksumHelperTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/suite/CertificationChecksumHelperTest.java
index e36c898..3efb90d 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/suite/CertificationChecksumHelperTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/suite/CertificationChecksumHelperTest.java
@@ -93,9 +93,9 @@
for (int i = 0; i < testCount; i++) {
TestDescription test = new TestDescription("com.class.path", "testMethod" + i);
results.testStarted(test);
- results.testEnded(test, new HashMap<>());
+ results.testEnded(test, new HashMap<String, String>());
}
- results.testRunEnded(500L, new HashMap<>());
+ results.testRunEnded(500L, new HashMap<String, String>());
return results;
}
}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
index 1f2c544..b682cbf 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
@@ -31,6 +31,7 @@
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.invoker.InvocationContext;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.suite.checker.ISystemStatusChecker;
import com.android.tradefed.testtype.IRemoteTest;
@@ -44,9 +45,9 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
/**
* Unit tests for {@link RetryFactoryTest}.
@@ -183,7 +184,8 @@
mMockListener.testModuleStarted(EasyMock.anyObject());
mMockListener.testRunStarted("module1", 0);
- mMockListener.testRunEnded(EasyMock.anyLong(), (Map<String, String>) EasyMock.anyObject());
+ mMockListener.testRunEnded(EasyMock.anyLong(),
+ (HashMap<String, Metric>) EasyMock.anyObject());
mMockListener.testModuleEnded();
EasyMock.replay(mMockListener, mMockInfo, mMockDevice);
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/BusinessLogicHostExecutor.java b/common/host-side/util/src/com/android/compatibility/common/util/BusinessLogicHostExecutor.java
index 43bdea1..7438959 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/BusinessLogicHostExecutor.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/BusinessLogicHostExecutor.java
@@ -16,8 +16,10 @@
package com.android.compatibility.common.util;
+import com.android.ddmlib.Log;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -50,6 +52,14 @@
* {@inheritDoc}
*/
@Override
+ public void logInfo(String format, Object... args) {
+ LogUtil.printLog(Log.LogLevel.INFO, LOG_TAG, String.format(format, args));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
protected ResolvedMethod getResolvedMethod(Class cls, String methodName, String... args)
throws ClassNotFoundException {
List<Method> nameMatches = getMethodsWithName(cls, methodName);
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogic.java b/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
index b87a250..2e0be41 100644
--- a/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
@@ -16,8 +16,14 @@
package com.android.compatibility.common.util;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+
+import org.junit.AssumptionViolatedException;
/**
* Helper and constants accessible to host and device components that enable Business Logic
@@ -29,7 +35,7 @@
public static final String DEVICE_FILE = "/sdcard/bl";
/* A map from testcase name to the business logic rules for the test case */
- protected Map<String, List<BusinessLogicRule>> mRules;
+ protected Map<String, List<BusinessLogicRulesList>> mRules;
/* Feature flag determining if device specific tests are executed. */
public boolean mConditionalTestsEnabled;
private AuthenticationStatusEnum mAuthenticationStatus = AuthenticationStatusEnum.UNKNOWN;
@@ -41,8 +47,16 @@
* @return whether business logic exists for this test for this suite
*/
public boolean hasLogicFor(String testName) {
- List<BusinessLogicRule> rules = mRules.get(testName);
- return rules != null && !rules.isEmpty();
+ List<BusinessLogicRulesList> rulesLists = mRules.get(testName);
+ return rulesLists != null && !rulesLists.isEmpty();
+ }
+
+ /**
+ * Return whether multiple rule lists exist in the BusinessLogic for this test name.
+ */
+ private boolean hasLogicsFor(String testName) {
+ List<BusinessLogicRulesList> rulesLists = mRules.get(testName);
+ return rulesLists != null && rulesLists.size() > 1;
}
/**
@@ -52,16 +66,93 @@
* @param executor a {@link BusinessLogicExecutor}
*/
public void applyLogicFor(String testName, BusinessLogicExecutor executor) {
- List<BusinessLogicRule> rules = mRules.get(testName);
- if (rules == null || rules.isEmpty()) {
+ if (!hasLogicFor(testName)) {
return;
}
- for (BusinessLogicRule rule : rules) {
- // Check conditions
- if (rule.invokeConditions(executor)) {
- rule.invokeActions(executor);
+ if (hasLogicsFor(testName)) {
+ applyLogicsFor(testName, executor); // handle this special case separately
+ return;
+ }
+ // expecting exactly one rules list at this point
+ BusinessLogicRulesList rulesList = mRules.get(testName).get(0);
+ rulesList.invokeRules(executor);
+ }
+
+ /**
+ * Handle special case in which multiple rule lists exist for the test name provided.
+ * Execute each rule list in a sandbox and store an exception for each rule list that
+ * triggers failure or skipping for the test.
+ * If all rule lists trigger skipping, rethrow AssumptionViolatedException to report a 'skip'
+ * for the test as a whole.
+ * If one or more rule lists trigger failure, rethrow RuntimeException with a list containing
+ * each failure.
+ */
+ private void applyLogicsFor(String testName, BusinessLogicExecutor executor) {
+ Map<String, RuntimeException> failedMap = new HashMap<>();
+ Map<String, RuntimeException> skippedMap = new HashMap<>();
+ List<BusinessLogicRulesList> rulesLists = mRules.get(testName);
+ for (int index = 0; index < rulesLists.size(); index++) {
+ BusinessLogicRulesList rulesList = rulesLists.get(index);
+ String description = cleanDescription(rulesList.getDescription(), index);
+ try {
+ rulesList.invokeRules(executor);
+ } catch (RuntimeException re) {
+ if (AssumptionViolatedException.class.isInstance(re)) {
+ skippedMap.put(description, re);
+ executor.logInfo("Test %s (%s) skipped for reason: %s", testName, description,
+ re.getMessage());
+ } else {
+ failedMap.put(description, re);
+ }
}
}
+ if (skippedMap.size() == rulesLists.size()) {
+ throwAggregatedException(skippedMap, false);
+ } else if (failedMap.size() > 0) {
+ throwAggregatedException(failedMap, true);
+ } // else this test should be reported as a pure pass
+ }
+
+ /**
+ * Helper to aggregate the messages of many {@link RuntimeException}s, and optionally their
+ * stack traces, before throwing an exception.
+ * @param exceptions a map from description strings to exceptions. The descriptive keySet is
+ * used to differentiate which BusinessLogicRulesList caused which exception
+ * @param failed whether to trigger failure. When false, throws assumption failure instead, and
+ * excludes stack traces from the exception message.
+ */
+ private static void throwAggregatedException(Map<String, RuntimeException> exceptions,
+ boolean failed) {
+ Set<String> keySet = exceptions.keySet();
+ String[] descriptions = keySet.toArray(new String[keySet.size()]);
+ StringBuilder msg = new StringBuilder("");
+ msg.append(String.format("Test %s for cases: ", (failed) ? "failed" : "skipped"));
+ msg.append(String.join(", ", descriptions));
+ msg.append("\nReasons include:");
+ for (String description : descriptions) {
+ RuntimeException re = exceptions.get(description);
+ msg.append(String.format("\nMessage [%s]: %s", description, re.getMessage()));
+ if (failed) {
+ StringWriter sw = new StringWriter();
+ re.printStackTrace(new PrintWriter(sw));
+ msg.append(String.format("\nStack Trace: %s", sw.toString()));
+ }
+ }
+ if (failed) {
+ throw new RuntimeException(msg.toString());
+ } else {
+ throw new AssumptionViolatedException(msg.toString());
+ }
+ }
+
+ /**
+ * Helper method to generate a meaningful description in case the provided description is null
+ * or empty. In this case, returns a string representation of the index provided.
+ */
+ private String cleanDescription(String description, int index) {
+ return (description == null || description.length() == 0)
+ ? Integer.toString(index)
+ : description;
}
public void setAuthenticationStatus(String authenticationStatus) {
@@ -99,6 +190,43 @@
}
/**
+ * A list of BusinessLogicRules, wrapped with an optional description to differentiate rule
+ * lists that apply to the same test.
+ */
+ protected static class BusinessLogicRulesList {
+
+ /* Stored description and rules */
+ protected List<BusinessLogicRule> mRulesList;
+ protected String mDescription;
+
+ public BusinessLogicRulesList(List<BusinessLogicRule> rulesList) {
+ mRulesList = rulesList;
+ }
+
+ public BusinessLogicRulesList(List<BusinessLogicRule> rulesList, String description) {
+ mRulesList = rulesList;
+ mDescription = description;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public List<BusinessLogicRule> getRules() {
+ return mRulesList;
+ }
+
+ public void invokeRules(BusinessLogicExecutor executor) {
+ for (BusinessLogicRule rule : mRulesList) {
+ // Check conditions
+ if (rule.invokeConditions(executor)) {
+ rule.invokeActions(executor);
+ }
+ }
+ }
+ }
+
+ /**
* Nested class representing an Business Logic Rule. Stores a collection of conditions
* and actions for later invokation.
*/
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogicExecutor.java b/common/util/src/com/android/compatibility/common/util/BusinessLogicExecutor.java
index 5c42858..4709395 100644
--- a/common/util/src/com/android/compatibility/common/util/BusinessLogicExecutor.java
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogicExecutor.java
@@ -29,6 +29,8 @@
*/
public abstract class BusinessLogicExecutor {
+ protected static final String LOG_TAG = "BusinessLogicExecutor";
+
/** String representations of the String class and String[] class */
protected static final String STRING_CLASS = "java.lang.String";
protected static final String STRING_ARRAY_CLASS = "[Ljava.lang.String;";
@@ -115,6 +117,13 @@
}
/**
+ * Log information with whichever logging mechanism is available to the instance. This varies
+ * from host-side to device-side, so implementations are left to subclasses.
+ * See {@link String.format(String, Object...)} for parameter information.
+ */
+ public abstract void logInfo(String format, Object... args);
+
+ /**
* Get the test object. This method is left abstract, since non-abstract subclasses will set
* the test object in the constructor.
* @return the test case instance
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java b/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
index 4a0087e..a0747bc 100644
--- a/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
@@ -23,6 +23,7 @@
import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRule;
import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRuleAction;
import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRuleCondition;
+import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRulesList;
import java.io.File;
import java.io.IOException;
@@ -47,6 +48,8 @@
private static final String RULE_CONDITIONS = "ruleConditions";
// Name of rule actions array
private static final String RULE_ACTIONS = "ruleActions";
+ // Description of a rule list object
+ private static final String RULES_LIST_DESCRIPTION = "description";
// Name of method name string
private static final String METHOD_NAME = "methodName";
// Name of method args array of strings
@@ -62,12 +65,12 @@
*/
public static BusinessLogic createFromFile(File f) {
// Populate the map from testname to business rules for this new BusinessLogic instance
- Map<String, List<BusinessLogicRule>> rulesMap = new HashMap<>();
+ Map<String, List<BusinessLogicRulesList>> rulesMap = new HashMap<>();
BusinessLogic bl = new BusinessLogic();
try {
String businessLogicString = readFile(f);
JSONObject root = new JSONObject(businessLogicString);
- JSONArray rulesLists = null;
+ JSONArray jsonRulesLists = null;
if (root.has(AUTHENTICATION_STATUS)){
String authStatus = root.getString(AUTHENTICATION_STATUS);
bl.setAuthenticationStatus(authStatus);
@@ -77,34 +80,20 @@
bl.mConditionalTestsEnabled = enabled;
}
try {
- rulesLists = root.getJSONArray(BUSINESS_LOGIC_RULES_LISTS);
+ jsonRulesLists = root.getJSONArray(BUSINESS_LOGIC_RULES_LISTS);
} catch (JSONException e) {
bl.mRules = rulesMap;
return bl; // no rules defined for this suite, leave internal map empty
}
- for (int i = 0; i < rulesLists.length(); i++) {
- JSONObject rulesList = rulesLists.getJSONObject(i);
- String testName = rulesList.getString(TEST_NAME);
- List<BusinessLogicRule> rules = new ArrayList<>();
- JSONArray rulesJSONArray = null;
- try {
- rulesJSONArray = rulesList.getJSONArray(BUSINESS_LOGIC_RULES);
- } catch (JSONException e) {
- // no rules defined for this test case
- rulesMap.put(testName, rules); // add empty rule list to internal map
- continue; // advance to next test case
+ for (int i = 0; i < jsonRulesLists.length(); i++) {
+ JSONObject jsonRulesList = jsonRulesLists.getJSONObject(i);
+ String testName = jsonRulesList.getString(TEST_NAME);
+ List<BusinessLogicRulesList> testRulesLists = rulesMap.get(testName);
+ if (testRulesLists == null) {
+ testRulesLists = new ArrayList<>();
}
- for (int j = 0; j < rulesJSONArray.length(); j++) {
- JSONObject ruleJSONObject = rulesJSONArray.getJSONObject(j);
- // Build conditions list
- List<BusinessLogicRuleCondition> ruleConditions =
- extractRuleConditionList(ruleJSONObject);
- // Build actions list
- List<BusinessLogicRuleAction> ruleActions =
- extractRuleActionList(ruleJSONObject);
- rules.add(new BusinessLogicRule(ruleConditions, ruleActions));
- }
- rulesMap.put(testName, rules);
+ testRulesLists.add(extractRulesList(jsonRulesList));
+ rulesMap.put(testName, testRulesLists);
}
} catch (IOException | JSONException e) {
throw new RuntimeException("Business Logic failed", e);
@@ -114,6 +103,37 @@
return bl;
}
+ /* Extract a BusinessLogicRulesList from the representative JSON object */
+ private static BusinessLogicRulesList extractRulesList(JSONObject rulesListJSONObject)
+ throws JSONException {
+ // First, parse the description for this rule list object, if one exists
+ String description = null;
+ try {
+ description = rulesListJSONObject.getString(RULES_LIST_DESCRIPTION);
+ } catch (JSONException e) { /* no description set, leave null */}
+
+ // Next, get the list of rules
+ List<BusinessLogicRule> rules = new ArrayList<>();
+ JSONArray rulesJSONArray = null;
+ try {
+ rulesJSONArray = rulesListJSONObject.getJSONArray(BUSINESS_LOGIC_RULES);
+ } catch (JSONException e) {
+ // no rules defined for this test case, return new, rule-less BusinessLogicRulesList
+ return new BusinessLogicRulesList(rules, description);
+ }
+ for (int j = 0; j < rulesJSONArray.length(); j++) {
+ JSONObject ruleJSONObject = rulesJSONArray.getJSONObject(j);
+ // Build conditions list
+ List<BusinessLogicRuleCondition> ruleConditions =
+ extractRuleConditionList(ruleJSONObject);
+ // Build actions list
+ List<BusinessLogicRuleAction> ruleActions =
+ extractRuleActionList(ruleJSONObject);
+ rules.add(new BusinessLogicRule(ruleConditions, ruleActions));
+ }
+ return new BusinessLogicRulesList(rules, description);
+ }
+
/* Extract all BusinessLogicRuleConditions from a JSON business logic rule */
private static List<BusinessLogicRuleCondition> extractRuleConditionList(
JSONObject ruleJSONObject) throws JSONException {
diff --git a/common/util/tests/src/com/android/compatibility/common/util/BusinessLogicTest.java b/common/util/tests/src/com/android/compatibility/common/util/BusinessLogicTest.java
index 99b5239..23e9da3 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/BusinessLogicTest.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/BusinessLogicTest.java
@@ -46,6 +46,7 @@
" \"businessLogicRulesLists\": [\n" +
" {\n" +
" \"testName\": \"testCaseName1\",\n" +
+ " \"description\": \"first test\",\n" +
" \"businessLogicRules\": [\n" +
" {\n" +
" \"ruleConditions\": [\n" +
@@ -132,10 +133,11 @@
try {
BusinessLogic bl = BusinessLogicFactory.createFromFile(file);
assertEquals("Wrong number of business logic rule lists", 3, bl.mRules.size());
-
- List<BusinessLogicRule> ruleList1 = bl.mRules.get("testCaseName1");
- assertEquals("Wrong number of rules in first rule list", 1, ruleList1.size());
- BusinessLogicRule rule1 = ruleList1.get(0);
+ String description = bl.mRules.get("testCaseName1").get(0).getDescription();
+ assertEquals("Wrong or missing rule list description", "first test", description);
+ List<BusinessLogicRule> rulesList1 = bl.mRules.get("testCaseName1").get(0).getRules();
+ assertEquals("Wrong number of rules in first rule list", 1, rulesList1.size());
+ BusinessLogicRule rule1 = rulesList1.get(0);
List<BusinessLogicRuleCondition> rule1Conditions = rule1.mConditions;
assertEquals("Wrong number of conditions", 1, rule1Conditions.size());
BusinessLogicRuleCondition rule1Condition = rule1Conditions.get(0);
@@ -159,9 +161,9 @@
assertEquals("Wrong arg for business logic rule action", "arg2",
rule1Action.mMethodArgs.get(1));
- List<BusinessLogicRule> ruleList2 = bl.mRules.get("testCaseName2");
- assertEquals("Wrong number of rules in second rule list", 2, ruleList2.size());
- BusinessLogicRule rule2 = ruleList2.get(0);
+ List<BusinessLogicRule> rulesList2 = bl.mRules.get("testCaseName2").get(0).getRules();
+ assertEquals("Wrong number of rules in second rule list", 2, rulesList2.size());
+ BusinessLogicRule rule2 = rulesList2.get(0);
List<BusinessLogicRuleCondition> rule2Conditions = rule2.mConditions;
assertEquals("Wrong number of conditions", 1, rule2Conditions.size());
BusinessLogicRuleCondition rule2Condition = rule2Conditions.get(0);
@@ -184,7 +186,7 @@
rule2Action.mMethodArgs.get(0));
assertEquals("Wrong arg for business logic rule action", "arg2",
rule2Action.mMethodArgs.get(1));
- BusinessLogicRule rule3 = ruleList2.get(1);
+ BusinessLogicRule rule3 = rulesList2.get(1);
List<BusinessLogicRuleCondition> rule3Conditions = rule3.mConditions;
assertEquals("Wrong number of conditions", 2, rule3Conditions.size());
BusinessLogicRuleCondition rule3Condition1 = rule3Conditions.get(0);
@@ -222,8 +224,8 @@
assertEquals("Wrong arg string count for business logic rule action", 0,
rule3Action2.mMethodArgs.size());
- List<BusinessLogicRule> ruleList3 = bl.mRules.get("testCaseName3");
- assertEquals("Wrong number of rules in third rule list", 0, ruleList3.size());
+ List<BusinessLogicRule> rulesList3 = bl.mRules.get("testCaseName3").get(0).getRules();
+ assertEquals("Wrong number of rules in third rule list", 0, rulesList3.size());
} finally {
FileUtil.deleteFile(file);
}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
index 207e3a6..466dabf 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
@@ -335,7 +335,10 @@
if (!supportedHardwareForScopedDirectoryAccess()) return;
final StorageVolume volume = getPrimaryVolume();
- final String dir = DIRECTORY_PICTURES;
+ // TODO: ideally it should test all directories, but that would be too slow, and would
+ // require a more sophisticated way to get the toggle object (for example, it might require
+ // scrolling on devices with small screens)
+ final String dir = DIRECTORY_DOCUMENTS;
// First, triggers it.
deniesOnceForAllTest(volume, dir);
@@ -372,10 +375,13 @@
if (!supportedHardwareForScopedDirectoryAccess()) return;
final StorageVolume volume = getPrimaryVolume();
- final String dir = DIRECTORY_PICTURES;
+ // TODO: ideally it should test all directories, but that would be too slow, and would
+ // require a more sophisticated way to get the toggle object (for example, it might require
+ // scrolling on devices with small screens)
+ final String dir = DIRECTORY_DOCUMENTS;
// First, grants it
- userAcceptsTest(volume, DIRECTORY_PICTURES);
+ userAcceptsTest(volume, dir);
// Then revoke it using settings.
diff --git a/hostsidetests/seccomp/app/assets/syscalls.json b/hostsidetests/seccomp/app/assets/syscalls.json
deleted file mode 100644
index 527405f..0000000
--- a/hostsidetests/seccomp/app/assets/syscalls.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- "arm": {
- "add_key": 309,
- "inotify_init": 316,
- "keyctl": 311,
- "openat": 322,
- "swapoff": 115,
- "swapon": 87,
- "syncfs": 373
- },
- "arm64": {
- "add_key": 217,
- "keyctl": 219,
- "openat": 56,
- "swapoff": 225,
- "swapon": 224,
- "syncfs": 267
- },
- "mips": {
- "add_key": 4280,
- "inotify_init": 4284,
- "keyctl": 4282,
- "openat": 4288,
- "swapoff": 4115,
- "swapon": 4087,
- "syncfs": 4342
- },
- "mips64": {
- "add_key": 5239,
- "keyctl": 5241,
- "openat": 5247,
- "swapoff": 5163,
- "swapon": 5162,
- "syncfs": 5301
- },
- "x86": {
- "add_key": 286,
- "inotify_init": 291,
- "keyctl": 288,
- "openat": 295,
- "swapoff": 115,
- "swapon": 87,
- "syncfs": 344
- },
- "x86_64": {
- "add_key": 248,
- "keyctl": 250,
- "openat": 257,
- "swapoff": 168,
- "swapon": 167,
- "syncfs": 306
- }
-}
diff --git a/hostsidetests/seccomp/app/assets/syscalls_allowed.json b/hostsidetests/seccomp/app/assets/syscalls_allowed.json
new file mode 100644
index 0000000..03ce72b
--- /dev/null
+++ b/hostsidetests/seccomp/app/assets/syscalls_allowed.json
@@ -0,0 +1,24 @@
+# DO NOT MODIFY. CHANGE gen_blacklist.py INSTEAD.
+{
+ "arm": {
+ "inotify_init": 316,
+ "openat": 322,
+ "syncfs": 373
+ },
+ "arm64": {
+ "openat": 56,
+ "syncfs": 267
+ },
+ "mips": {
+ "openat": 4288
+ },
+ "mips64": {
+ "openat": 5247
+ },
+ "x86": {
+ "openat": 295
+ },
+ "x86_64": {
+ "openat": 257
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/seccomp/app/assets/syscalls_blocked.json b/hostsidetests/seccomp/app/assets/syscalls_blocked.json
new file mode 100644
index 0000000..441c8da
--- /dev/null
+++ b/hostsidetests/seccomp/app/assets/syscalls_blocked.json
@@ -0,0 +1,123 @@
+# DO NOT MODIFY. CHANGE gen_blacklist.py INSTEAD.
+{
+ "arm": {
+ "acct": 51,
+ "add_key": 309,
+ "adjtimex": 124,
+ "chroot": 61,
+ "clock_adjtime": 372,
+ "clock_settime": 262,
+ "delete_module": 129,
+ "init_module": 128,
+ "keyctl": 311,
+ "mount": 21,
+ "reboot": 88,
+ "setdomainname": 121,
+ "sethostname": 74,
+ "settimeofday": 79,
+ "swapoff": 115,
+ "swapon": 87,
+ "syslog": 103,
+ "umount2": 52
+ },
+ "arm64": {
+ "acct": 89,
+ "add_key": 217,
+ "adjtimex": 171,
+ "chroot": 51,
+ "clock_adjtime": 266,
+ "clock_settime": 112,
+ "delete_module": 106,
+ "init_module": 105,
+ "keyctl": 219,
+ "mount": 40,
+ "reboot": 142,
+ "setdomainname": 162,
+ "sethostname": 161,
+ "settimeofday": 170,
+ "swapoff": 225,
+ "swapon": 224,
+ "syslog": 116,
+ "umount2": 39
+ },
+ "mips": {
+ "acct": 4051,
+ "add_key": 4280,
+ "adjtimex": 4124,
+ "chroot": 4061,
+ "clock_adjtime": 4341,
+ "clock_settime": 4262,
+ "delete_module": 4129,
+ "init_module": 4128,
+ "keyctl": 4282,
+ "mount": 4021,
+ "reboot": 4088,
+ "setdomainname": 4121,
+ "sethostname": 4074,
+ "settimeofday": 4079,
+ "swapoff": 4115,
+ "swapon": 4087,
+ "syslog": 4103,
+ "umount2": 4052
+ },
+ "mips64": {
+ "acct": 5158,
+ "add_key": 5239,
+ "adjtimex": 5154,
+ "chroot": 5156,
+ "clock_adjtime": 5300,
+ "clock_settime": 5221,
+ "delete_module": 5169,
+ "init_module": 5168,
+ "keyctl": 5241,
+ "mount": 5160,
+ "reboot": 5164,
+ "setdomainname": 5166,
+ "sethostname": 5165,
+ "settimeofday": 5159,
+ "swapoff": 5163,
+ "swapon": 5162,
+ "syslog": 5101,
+ "umount2": 5161
+ },
+ "x86": {
+ "acct": 51,
+ "add_key": 286,
+ "adjtimex": 124,
+ "chroot": 61,
+ "clock_adjtime": 343,
+ "clock_settime": 264,
+ "delete_module": 129,
+ "init_module": 128,
+ "keyctl": 288,
+ "mount": 21,
+ "reboot": 88,
+ "setdomainname": 121,
+ "sethostname": 74,
+ "settimeofday": 79,
+ "swapoff": 115,
+ "swapon": 87,
+ "syslog": 103,
+ "umount2": 52
+ },
+ "x86_64": {
+ "acct": 163,
+ "add_key": 248,
+ "adjtimex": 159,
+ "chroot": 161,
+ "clock_adjtime": 305,
+ "clock_settime": 227,
+ "delete_module": 176,
+ "init_module": 175,
+ "keyctl": 250,
+ "mount": 165,
+ "reboot": 169,
+ "setdomainname": 171,
+ "sethostname": 170,
+ "settimeofday": 164,
+ "swapoff": 168,
+ "swapon": 167,
+ "syslog": 103,
+ "umount2": 166
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/seccomp/app/gen_blacklist.py b/hostsidetests/seccomp/app/gen_blacklist.py
index a104394..cf36d11 100755
--- a/hostsidetests/seccomp/app/gen_blacklist.py
+++ b/hostsidetests/seccomp/app/gen_blacklist.py
@@ -3,10 +3,15 @@
# This script generates syscall name to number mapping for supported
# architectures. To update the output, runs:
#
-# $ app/gen_blacklist.py > app/assets/syscalls.json
+# $ app/gen_blacklist.py --allowed app/assets/syscalls_allowed.json \
+# --blocked app/assets/syscalls_blocked.json
+#
+# Note that these are just syscalls that explicitly allowed and blocked in CTS
+# currently.
#
# TODO: Consider generating it in Android.mk/bp.
+import argparse
import glob
import json
import os
@@ -14,14 +19,39 @@
_SUPPORTED_ARCHS = ['arm', 'arm64', 'x86', 'x86_64', 'mips', 'mips64']
-_INTERESTED_SYSCALLS = {
- 'swapon': 'all',
- 'swapoff': 'all',
- 'add_key': 'all',
- 'keyctl': 'all',
+# Syscalls that are currently explicitly allowed in CTS
+_SYSCALLS_ALLOWED_IN_CTS = {
'openat': 'all',
- 'syncfs': 'all',
- 'inotify_init': 'arm,x86,mips',
+
+ # b/35034743 - do not remove test without reading bug.
+ 'syncfs': 'arm64',
+
+ # b/35906875 - do not remove test without reading bug
+ 'inotify_init': 'arm',
+}
+
+# Syscalls that are currently explicitly blocked in CTS
+_SYSCALLS_BLOCKED_IN_CTS = {
+ 'acct': 'all',
+ 'add_key': 'all',
+ 'adjtimex': 'all',
+ 'chroot': 'all',
+ 'clock_adjtime': 'all',
+ 'clock_settime': 'all',
+ 'delete_module': 'all',
+ 'init_module': 'all',
+ 'keyctl': 'all',
+ 'mount': 'all',
+ 'reboot': 'all',
+ 'setdomainname': 'all',
+ 'sethostname': 'all',
+ 'settimeofday': 'all',
+ 'swapoff': 'all',
+ 'swapoff': 'all',
+ 'swapon': 'all',
+ 'swapon': 'all',
+ 'syslog': 'all',
+ 'umount2': 'all',
}
def create_syscall_name_to_number_map(arch, names):
@@ -108,17 +138,38 @@
return clang_exe
raise FileNotFoundError('Cannot locate clang executable')
-def main():
- dictionary = {}
- for arch in _SUPPORTED_ARCHS:
- syscall_names = []
- for syscall in _INTERESTED_SYSCALLS.keys():
- if (arch in _INTERESTED_SYSCALLS[syscall] or
- 'all' == _INTERESTED_SYSCALLS[syscall]):
- syscall_names.append(syscall)
- dictionary[arch] = create_syscall_name_to_number_map(arch, syscall_names)
+def collect_syscall_names_for_arch(syscall_map, arch):
+ syscall_names = []
+ for syscall in syscall_map.keys():
+ if (arch in syscall_map[syscall] or
+ 'all' == syscall_map[syscall]):
+ syscall_names.append(syscall)
+ return syscall_names
- print(json.dumps(dictionary, sort_keys=True, indent=2))
+def main():
+ parser = argparse.ArgumentParser('syscall name to number generator')
+ parser.add_argument('--allowed', metavar='path/to/json', type=str)
+ parser.add_argument('--blocked', metavar='path/to/json', type=str)
+ args = parser.parse_args()
+
+ allowed = {}
+ blocked = {}
+ for arch in _SUPPORTED_ARCHS:
+ blocked[arch] = create_syscall_name_to_number_map(
+ arch,
+ collect_syscall_names_for_arch(_SYSCALLS_BLOCKED_IN_CTS, arch))
+ allowed[arch] = create_syscall_name_to_number_map(
+ arch,
+ collect_syscall_names_for_arch(_SYSCALLS_ALLOWED_IN_CTS, arch))
+
+ msg_do_not_modify = '# DO NOT MODIFY. CHANGE gen_blacklist.py INSTEAD.'
+ with open(args.allowed, 'w') as f:
+ print(msg_do_not_modify, file=f)
+ json.dump(allowed, f, sort_keys=True, indent=2)
+
+ with open(args.blocked, 'w') as f:
+ print(msg_do_not_modify, file=f)
+ json.dump(blocked, f, sort_keys=True, indent=2)
if __name__ == '__main__':
main()
diff --git a/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java b/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java
index 0f85146..724d235 100644
--- a/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java
+++ b/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.util.Iterator;
import android.content.Context;
import android.content.res.AssetManager;
@@ -43,49 +44,41 @@
System.loadLibrary("ctsseccomp_jni");
}
- private JSONObject mSyscallMap;
+ private JSONObject mAllowedSyscallMap;
+ private JSONObject mBlockedSyscallMap;
@Before
public void initializeSyscallMap() throws IOException, JSONException {
final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
AssetManager manager = context.getAssets();
- String json = null;
- try (InputStream is = manager.open("syscalls.json")) {
- json = readInputStreamFully(is);
+ try (InputStream is = manager.open("syscalls_allowed.json")) {
+ mAllowedSyscallMap = new JSONObject(readInputStreamFully(is));
}
- mSyscallMap = new JSONObject(json);
+ try (InputStream is = manager.open("syscalls_blocked.json")) {
+ mBlockedSyscallMap = new JSONObject(readInputStreamFully(is));
+ }
+ }
+
+ @Test
+ public void testCTSSyscallAllowed() throws JSONException {
+ JSONObject map = mAllowedSyscallMap.getJSONObject(getCurrentArch());
+ Iterator<String> iter = map.keys();
+ while (iter.hasNext()) {
+ String syscallName = iter.next();
+ testAllowed(map.getInt(syscallName));
+ }
}
@Test
public void testCTSSyscallBlocked() throws JSONException {
- String arch = getCurrentArch();
-
- testBlocked(getSyscallNumber(arch, "add_key"));
- testBlocked(getSyscallNumber(arch, "keyctl"));
- testAllowed(getSyscallNumber(arch, "openat"));
-
- if (CpuFeatures.isArm64Cpu()) {
- // b/35034743 - do not remove test without reading bug.
- testAllowed(getSyscallNumber(arch, "syncfs"));
- } else if (CpuFeatures.isArmCpu()) {
- // b/35906875 - do not remove test without reading bug
- testAllowed(getSyscallNumber(arch, "inotify_init"));
+ JSONObject map = mBlockedSyscallMap.getJSONObject(getCurrentArch());
+ Iterator<String> iter = map.keys();
+ while (iter.hasNext()) {
+ String syscallName = iter.next();
+ testBlocked(map.getInt(syscallName));
}
}
- @Test
- public void testCTSSwapOnOffBlocked() throws JSONException {
- String arch = getCurrentArch();
-
- testBlocked(getSyscallNumber(arch, "swapon"));
- testBlocked(getSyscallNumber(arch, "swapoff"));
- }
-
- private int getSyscallNumber(String arch, String name) throws JSONException {
- JSONObject perArchMap = mSyscallMap.getJSONObject(arch);
- return perArchMap.getInt(name);
- }
-
private static String getCurrentArch() {
if (CpuFeatures.isArm64Cpu()) {
return "arm64";
diff --git a/hostsidetests/seccomp/src/android/seccomp/cts/SeccompHostJUnit4DeviceTest.java b/hostsidetests/seccomp/src/android/seccomp/cts/SeccompHostJUnit4DeviceTest.java
index 63258e3..2a7242b 100644
--- a/hostsidetests/seccomp/src/android/seccomp/cts/SeccompHostJUnit4DeviceTest.java
+++ b/hostsidetests/seccomp/src/android/seccomp/cts/SeccompHostJUnit4DeviceTest.java
@@ -40,7 +40,7 @@
private static final String TEST_APP = "CtsSeccompDeviceApp.apk";
private static final String TEST_CTS_SYSCALL_BLOCKED = "testCTSSyscallBlocked";
- private static final String TEST_CTS_SWAP_ON_OFF_BLOCKED = "testCTSSwapOnOffBlocked";
+ private static final String TEST_CTS_SYSCALL_ALLOWED = "testCTSSyscallAllowed";
@Before
public void setUp() throws Exception {
@@ -53,8 +53,8 @@
}
@Test
- public void testCTSSwapOnOffBlocked() throws Exception {
- Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, TEST_CTS_SWAP_ON_OFF_BLOCKED));
+ public void testCTSSyscallAllowed() throws Exception {
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, TEST_CTS_SYSCALL_ALLOWED));
}
@After
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index 52c47cc..73d906d 100755
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -102,6 +102,28 @@
<!-- Bulletin 2017-11 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+ <!--__________________-->
+ <!-- Bulletin 2017-12 -->
+ <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+
+ <!--__________________-->
+ <!-- Bulletin 2018-01 -->
+ <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+
+ <!--__________________-->
+ <!-- Bulletin 2018-02 -->
+ <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+
+ <!--__________________-->
+ <!-- Bulletin 2018-03 -->
+ <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+ <option name="push" value="CVE-2017-13253->/data/local/tmp/CVE-2017-13253" />
+
+
+
+
+
+
<option name="append-bitness" value="true" />
</target_preparer>
diff --git a/hostsidetests/security/securityPatch/CVE-2017-13253/Android.mk b/hostsidetests/security/securityPatch/CVE-2017-13253/Android.mk
new file mode 100644
index 0000000..afbb0fe
--- /dev/null
+++ b/hostsidetests/security/securityPatch/CVE-2017-13253/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 := CVE-2017-13253
+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 := libmedia libutils libbinder libmediadrm
+
+LOCAL_COMPATIBILITY_SUITE := cts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS = -Wall -Werror -Wno-unused-parameter -Wno-unused-variable
+
+include $(BUILD_CTS_EXECUTABLE)
\ No newline at end of file
diff --git a/hostsidetests/security/securityPatch/CVE-2017-13253/poc.cpp b/hostsidetests/security/securityPatch/CVE-2017-13253/poc.cpp
new file mode 100644
index 0000000..4b19ae2f
--- /dev/null
+++ b/hostsidetests/security/securityPatch/CVE-2017-13253/poc.cpp
@@ -0,0 +1,84 @@
+/**
+ * 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.
+ */
+#include <binder/IMemory.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <media/ICrypto.h>
+#include <media/IMediaDrmService.h>
+#include <media/hardware/CryptoAPI.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <utils/StrongPointer.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+using namespace android;
+
+int main()
+{
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.drm"));
+ sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
+ if (service == NULL) {
+ printf("Failed to retrieve 'media.drm' service.\n");
+ return 1;
+ }
+ sp<ICrypto> crypto = service->makeCrypto();
+ if (crypto == NULL) {
+ printf("makeCrypto failed.\n");
+ return 1;
+ }
+
+ const uint8_t clearkey_uuid[16] = {
+ 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
+ 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B
+ };
+ if (crypto->createPlugin(clearkey_uuid, NULL, 0) != OK) {
+ printf("createPlugin failed.\n");
+ return 1;
+ }
+
+ sp<MemoryHeapBase> heap = new MemoryHeapBase(0x2000);
+ memset(heap->getBase(), 'A', 0x2000);
+ sp<MemoryBase> sourceMemory = new MemoryBase(heap, 0, 0x2000);
+ sp<MemoryBase> destMemory = new MemoryBase(heap, 0x1fff, 1);
+ int heapSeqNum = crypto->setHeap(heap);
+ if (heapSeqNum < 0) {
+ printf("setHeap failed.\n");
+ return 1;
+ }
+
+ CryptoPlugin::Pattern pattern = { .mEncryptBlocks = 0, .mSkipBlocks = 1 };
+ ICrypto::SourceBuffer source = { .mSharedMemory = sourceMemory,
+ .mHeapSeqNum = heapSeqNum };
+ CryptoPlugin::SubSample subSamples[1] = { { .mNumBytesOfClearData = 0x2000,
+ .mNumBytesOfEncryptedData = 0 } };
+ ICrypto::DestinationBuffer destination = {
+ .mType = ICrypto::kDestinationTypeSharedMemory, .mHandle = NULL, .mSharedMemory = destMemory
+ };
+
+ int val = crypto->decrypt(NULL, NULL,
+ CryptoPlugin::kMode_Unencrypted, pattern, source, 0, subSamples, 1,
+ destination, NULL);
+
+ if (val != BAD_VALUE) {
+ printf("OVERFLOW DETECTED\n");
+ }
+
+ return 0;
+}
diff --git a/hostsidetests/security/src/android/security/cts/Poc18_03.java b/hostsidetests/security/src/android/security/cts/Poc18_03.java
new file mode 100644
index 0000000..6398164
--- /dev/null
+++ b/hostsidetests/security/src/android/security/cts/Poc18_03.java
@@ -0,0 +1,31 @@
+/**
+ * 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.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+
+public class Poc18_03 extends SecurityTestCase {
+
+ /**
+ * b/71389378
+ */
+ @SecurityTest
+ public void testPocCVE_2017_13253() throws Exception {
+ String output = AdbUtils.runPoc("CVE-2017-13253", getDevice());
+ assertNotMatchesMultiLine(".*OVERFLOW DETECTED.*",output);
+ }
+}
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 72b1b9c..8428758 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -147,8 +147,13 @@
if (mDevice.doesFileExist("/system/etc/selinux/plat_file_contexts")) {
devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
"/system/etc/selinux/plat_file_contexts", "plat_file_contexts");
- deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
- "/vendor/etc/selinux/nonplat_file_contexts", "nonplat_file_contexts");
+ if (mDevice.doesFileExist("/vendor/etc/selinux/nonplat_file_contexts")){
+ deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
+ "/vendor/etc/selinux/nonplat_file_contexts", "nonplat_file_contexts");
+ } else {
+ deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
+ "/vendor/etc/selinux/vendor_file_contexts", "vendor_file_contexts");
+ }
} else {
devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
"/plat_file_contexts", "plat_file_contexts");
@@ -167,6 +172,9 @@
private static File getDeviceFile(ITestDevice device,
Map<ITestDevice, File> cache, String deviceFilePath,
String tmpFileName) throws Exception {
+ if (!device.doesFileExist(deviceFilePath)){
+ throw new Exception();
+ }
File file;
synchronized (cache) {
file = cache.get(device);
@@ -729,6 +737,24 @@
}
/**
+ * Tests that all types in /proc have the proc_type attribute.
+ *
+ * @throws Exception
+ */
+ public void testProcTypeViolators() throws Exception {
+ assertSepolicyTests("TestProcTypeViolations", "/sepolicy_tests");
+ }
+
+ /**
+ * Tests that all types in /sys have the sysfs_type attribute.
+ *
+ * @throws Exception
+ */
+ public void testSysfsTypeViolators() throws Exception {
+ assertSepolicyTests("TestSysfsTypeViolations", "/sepolicy_tests");
+ }
+
+ /**
* Tests that all types on /vendor have the vendor_file_type attribute.
*
* @throws Exception
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java b/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
new file mode 100644
index 0000000..38c35d0
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
@@ -0,0 +1,79 @@
+/*
+ * 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.cts.statsd.alarm;
+
+import android.cts.statsd.atom.AtomTestCase;
+
+import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.Alarm;
+import com.android.internal.os.StatsdConfigProto.IncidentdDetails;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.Subscription;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.util.List;
+
+/**
+ * Statsd Anomaly Detection tests.
+ */
+public class AlarmTests extends AtomTestCase {
+
+ private static final String TAG = "Statsd.AnomalyDetectionTests";
+
+ private static final boolean INCIDENTD_TESTS_ENABLED = true;
+
+ // Config constants
+ private static final int ALARM_ID = 11;
+ private static final int SUBSCRIPTION_ID_INCIDENTD = 41;
+ private static final int INCIDENTD_SECTION = -1;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (!INCIDENTD_TESTS_ENABLED) {
+ CLog.w(TAG, TAG + " alarm tests are disabled by a flag. Change flag to true to run");
+ }
+ }
+
+ public void testAlarm() throws Exception {
+ StatsdConfig.Builder config = getBaseConfig();
+ turnScreenOn();
+ uploadConfig(config);
+
+ String markTime = getCurrentLogcatDate();
+ Thread.sleep(9_000);
+
+ if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+ }
+
+
+ private final StatsdConfig.Builder getBaseConfig() throws Exception {
+ return StatsdConfig.newBuilder().setId(CONFIG_ID)
+ .addAlarm(Alarm.newBuilder()
+ .setId(ALARM_ID)
+ .setOffsetMillis(2)
+ .setPeriodMillis(5_000) // every 5 seconds.
+ )
+ .addSubscription(Subscription.newBuilder()
+ .setId(SUBSCRIPTION_ID_INCIDENTD)
+ .setRuleType(Subscription.RuleType.ALARM)
+ .setRuleId(ALARM_ID)
+ .setIncidentdDetails(IncidentdDetails.newBuilder()
+ .addSection(INCIDENTD_SECTION))
+ )
+ .addAllowedLogSource("AID_SYSTEM");
+ }
+}
diff --git a/hostsidetests/theme/assets/26/213dpi.zip b/hostsidetests/theme/assets/26/213dpi.zip
new file mode 100755
index 0000000..38531ba
--- /dev/null
+++ b/hostsidetests/theme/assets/26/213dpi.zip
Binary files differ
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index 75c3563..509092f 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -32,21 +32,6 @@
*/
@Presubmit
public class AccessibilityEventTest extends TestCase {
-
- /** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int NON_STATIC_FIELD_COUNT = 32;
-
- /**
- * Test that no new fields have been added without updating the
- * marshaling tests.
- */
- @SmallTest
- public void testNoNewFieldsAddedWithoutUpdadingMarshallTests() {
- // no new fields, so we are testing marshaling of all such
- AccessibilityRecordTest.assertNoNewNonStaticFieldsAdded(AccessibilityEvent.class,
- NON_STATIC_FIELD_COUNT);
- }
-
/**
* Tests whether accessibility events are correctly written and
* read from a parcel (version 1).
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 95d8f2d..224d860 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -37,21 +37,8 @@
@Presubmit
public class AccessibilityNodeInfoTest extends AndroidTestCase {
- /** The number of properties of the {@link AccessibilityNodeInfo} class that are marshalled. */
- private static final int NUM_MARSHALLED_PROPERTIES = 35;
-
- /**
- * The number of properties that are purposely not marshalled
- * mOriginalText - Used when resolving clickable spans; intentionally not parceled
- */
- private static final int NUM_NONMARSHALLED_PROPERTIES = 1;
-
@SmallTest
public void testMarshaling() throws Exception {
- // no new fields, so we are testing marshaling of all such
- AccessibilityRecordTest.assertNoNewNonStaticFieldsAdded(AccessibilityNodeInfo.class,
- NUM_MARSHALLED_PROPERTIES + NUM_NONMARSHALLED_PROPERTIES);
-
// fully populate the node info to marshal
AccessibilityNodeInfo sentInfo = AccessibilityNodeInfo.obtain(new View(getContext()));
fullyPopulateAccessibilityNodeInfo(sentInfo);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
index a12ccce..72bb4e0 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
@@ -35,21 +35,6 @@
*/
@Presubmit
public class AccessibilityRecordTest extends AndroidTestCase {
-
- /** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int NON_STATIC_FIELD_COUNT = 24;
-
- /**
- * Test that no new fields have been added without updating the
- * marshaling tests. Note that the marshaling tests are in
- * AccessibilityEventTests since it is a super class that is
- * responsible for marshaling and unmarshaling.
- */
- @SmallTest
- public void testNoNewFieldsAddedWithoutUpdadingMarshallTests() {
- assertNoNewNonStaticFieldsAdded(AccessibilityRecord.class, NON_STATIC_FIELD_COUNT);
- }
-
/**
* Tests the cloning obtain method.
*/
@@ -198,24 +183,4 @@
receivedTextIterator.next().toString());
}
}
-
- /**
- * Asserts that no new fields have been added, so we are testing marshaling
- * of all such.
- */
- static void assertNoNewNonStaticFieldsAdded(Class<?> clazz, int expectedCount) {
- int nonStaticFieldCount = 0;
-
- while (clazz != Object.class) {
- for (Field field : clazz.getDeclaredFields()) {
- if ((field.getModifiers() & Modifier.STATIC) == 0) {
- nonStaticFieldCount++;
- }
- }
- clazz = clazz.getSuperclass();
- }
-
- String message = "New fields have been added, so add code to test marshaling them.";
- assertEquals(message, expectedCount, nonStaticFieldCount);
- }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index 6c77471..c827364 100755
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -34,6 +34,9 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT;
+import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.assertThat;
+
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity;
@@ -52,9 +55,10 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityWindowInfo;
-
import android.widget.Button;
-import android.accessibilityservice.cts.R;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -63,6 +67,9 @@
import java.util.List;
import java.util.Queue;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Test cases for testing the accessibility APIs for querying of the screen content.
@@ -259,7 +266,6 @@
@MediumTest
public void testSingleAccessibilityFocusAcrossWindows() throws Exception {
- setAccessInteractiveWindowsFlag();
try {
// Add two more windows.
final View views[];
@@ -573,6 +579,27 @@
assertTrue(numPictureInPictureWindows >= 1);
}
+ public void testGetWindows_resultIsSortedByLayerDescending() throws TimeoutException {
+ addTwoAppPanelWindows();
+ List<AccessibilityWindowInfo> windows = getInstrumentation().getUiAutomation().getWindows();
+
+ AccessibilityWindowInfo windowAddedFirst = findWindow(windows, R.string.button1);
+ AccessibilityWindowInfo windowAddedSecond = findWindow(windows, R.string.button2);
+ assertThat(windowAddedFirst.getLayer(), lessThan(windowAddedSecond.getLayer()));
+
+ assertThat(windows, new IsSortedBy<>(w -> w.getLayer(), /* ascending */ false));
+ }
+
+ private AccessibilityWindowInfo findWindow(List<AccessibilityWindowInfo> windows,
+ int btnTextRes) {
+ return windows.stream()
+ .filter(w -> w.getRoot()
+ .findAccessibilityNodeInfosByText(getString(btnTextRes))
+ .size() == 1)
+ .findFirst()
+ .get();
+ }
+
private boolean isDividerWindowPresent(UiAutomation uiAutomation) {
List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
final int windowCount = windows.size();
@@ -659,50 +686,44 @@
}
private View[] addTwoAppPanelWindows() throws TimeoutException {
- final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ setAccessInteractiveWindowsFlag();
+ getInstrumentation().getUiAutomation()
+ .waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
- uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+ return new View[] {
+ addWindow(R.string.button1, params -> {
+ params.gravity = Gravity.TOP;
+ params.y = getStatusBarHeight(getActivity());
+ }),
+ addWindow(R.string.button2, params -> {
+ params.gravity = Gravity.BOTTOM;
+ })
+ };
+ }
- final View views[] = new View[2];
- // Add the first window.
- uiAutomation.executeAndWaitForEvent(() -> getInstrumentation().runOnMainSync(() -> {
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- params.gravity = Gravity.TOP;
- params.y = getStatusBarHeight(getActivity());
- params.width = WindowManager.LayoutParams.MATCH_PARENT;
- params.height = WindowManager.LayoutParams.WRAP_CONTENT;
- params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
- params.token = getActivity().getWindow().getDecorView().getWindowToken();
+ private Button addWindow(int btnTextRes, Consumer<WindowManager.LayoutParams> configure)
+ throws TimeoutException {
+ AtomicReference<Button> result = new AtomicReference<>();
+ getInstrumentation().getUiAutomation().executeAndWaitForEvent(() -> {
+ getInstrumentation().runOnMainSync(() -> {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+ params.token = getActivity().getWindow().getDecorView().getWindowToken();
+ configure.accept(params);
- final Button button = new Button(getActivity());
- button.setText(R.string.button1);
- views[0] = button;
- getActivity().getWindowManager().addView(button, params);
- }), filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), TIMEOUT_ASYNC_PROCESSING);
-
- // Add the second window.
- uiAutomation.executeAndWaitForEvent(() -> getInstrumentation().runOnMainSync(() -> {
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- params.gravity = Gravity.BOTTOM;
- params.width = WindowManager.LayoutParams.MATCH_PARENT;
- params.height = WindowManager.LayoutParams.WRAP_CONTENT;
- params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
- params.token = getActivity().getWindow().getDecorView().getWindowToken();
-
- final Button button = new Button(getActivity());
- button.setText(R.string.button2);
- views[1] = button;
- getActivity().getWindowManager().addView(button, params);
- }), filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), TIMEOUT_ASYNC_PROCESSING);
- return views;
+ final Button button = new Button(getActivity());
+ button.setText(btnTextRes);
+ result.set(button);
+ getActivity().getWindowManager().addView(button, params);
+ });
+ }, filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), TIMEOUT_ASYNC_PROCESSING);
+ return result.get();
}
private void setAccessInteractiveWindowsFlag () {
@@ -806,4 +827,33 @@
protected void scrubClass(Class<?> testCaseClass) {
/* intentionally do not scrub */
}
+
+ private static class IsSortedBy<T> extends TypeSafeMatcher<List<T>> {
+
+ private final Function<T, ? extends Comparable> mProperty;
+ private final boolean mAscending;
+
+ private IsSortedBy(Function<T, ? extends Comparable> comparator, boolean ascending) {
+ mProperty = comparator;
+ mAscending = ascending;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is sorted");
+ }
+
+ @Override
+ protected boolean matchesSafely(List<T> item) {
+ for (int i = 0; i < item.size() - 1; i++) {
+ Comparable a = mProperty.apply(item.get(i));
+ Comparable b = mProperty.apply(item.get(i));
+ int aMinusB = a.compareTo(b);
+ if (aMinusB != 0 && (aMinusB < 0) != mAscending) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
index 21d6c69..71e2ee9 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
@@ -79,7 +79,7 @@
try (final RotationSession rotationSession = new RotationSession()) {
rotationSession.set(ROTATION_0);
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
resizeActivityTask(LOG_CONFIGURATION_ACTIVITY, 0, 0, 100, 100);
ConfigurationChangeObserver c = new ConfigurationChangeObserver();
final boolean reportedSizeAfterResize = c.findConfigurationChange(
@@ -87,7 +87,7 @@
assertTrue("Expected to observe configuration change when resizing",
reportedSizeAfterResize);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
rotationSession.set(ROTATION_180);
final boolean reportedSizeAfterRotation = c.findConfigurationChange(
LOG_CONFIGURATION_ACTIVITY, logSeparator);
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 4474bb1..68be619 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -356,7 +356,7 @@
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.disableLockScreen()
.sleepDevice();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY);
mAmWmState.assertVisibility(TURN_SCREEN_ON_ATTR_ACTIVITY, true);
assertTrue("Display turns on", isDisplayOn());
@@ -371,7 +371,7 @@
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.setLockCredential()
.sleepDevice();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
launchActivityNoWait(TURN_SCREEN_ON_ATTR_ACTIVITY);
// Wait for the activity stopped because lock screen prevent showing the activity.
mAmWmState.waitForActivityState(TURN_SCREEN_ON_ATTR_ACTIVITY, STATE_STOPPED);
@@ -385,7 +385,7 @@
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
mAmWmState.waitForAllStoppedActivities();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
launchActivity(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY);
mAmWmState.assertVisibility(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY, true);
assertTrue("Display turns on", isDisplayOn());
@@ -398,14 +398,14 @@
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
mAmWmState.waitForAllStoppedActivities();
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY);
assertTrue("Display turns on", isDisplayOn());
assertSingleLaunch(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY, logSeparator);
lockScreenSession.sleepDevice();
mAmWmState.waitForAllStoppedActivities();
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY);
// Display should keep off, because setTurnScreenOn(false) has been called at
// {@link TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY}'s onStop.
@@ -419,14 +419,14 @@
public void testTurnScreenOnSingleTask() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY);
mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, true);
assertTrue("Display turns on", isDisplayOn());
assertSingleLaunch(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, logSeparator);
lockScreenSession.sleepDevice();
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY);
mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, true);
assertTrue("Display turns on", isDisplayOn());
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
index ab73fcd..60f48f5 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
@@ -84,7 +84,7 @@
private void startActivityAndVerifyResult(final ComponentName entryActivity,
final ComponentName actualActivity, boolean shouldStart) throws Exception {
// See TODO below
- // final LogSeparator logSeparator = clearLogcat();
+ // final LogSeparator logSeparator = separateLogs();
// Pass in different data only when cold starting. This is to make the intent
// different in subsequent warm/hot launches, so that the entrypoint alias
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
index eeecec6..d4422c0 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -93,12 +93,12 @@
public void testConfigurationUpdatesWhenResizedFromFullscreen() throws Exception {
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
logSeparator);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
setActivityTaskWindowingMode(RESIZEABLE_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
logSeparator);
@@ -116,12 +116,12 @@
public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
logSeparator);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
setActivityTaskWindowingMode(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
logSeparator);
@@ -139,7 +139,7 @@
try (final RotationSession rotationSession = new RotationSession()) {
rotationSession.set(ROTATION_0);
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
launchActivity(RESIZEABLE_ACTIVITY,
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
@@ -161,7 +161,7 @@
try (final RotationSession rotationSession = new RotationSession()) {
rotationSession.set(ROTATION_0);
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
// Launch our own activity to side in case Recents (or other activity to side) doesn't
// support rotation.
launchActivitiesInSplitScreen(
@@ -188,7 +188,7 @@
try (final RotationSession rotationSession = new RotationSession()) {
rotationSession.set(ROTATION_0);
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY));
@@ -203,7 +203,7 @@
throws Exception {
final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 };
for (final int rotation : rotations) {
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
final ActivityManagerState.ActivityTask task =
mAmWmState.getAmState().getTaskByActivity(RESIZEABLE_ACTIVITY);
final int displayId = mAmWmState.getAmState().getStackById(task.mStackId).mDisplayId;
@@ -256,14 +256,14 @@
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
// Launch to fullscreen stack and record size.
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName,
logSeparator);
final Rect displayRect = getDisplayRect(activityName);
// Move to docked stack.
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes dockedSizes = getActivityDisplaySize(activityName, logSeparator);
assertSizesAreSane(initialFullscreenSizes, dockedSizes);
@@ -277,7 +277,7 @@
// Resize docked stack to fullscreen size. This will trigger activity relaunch with
// non-empty override configuration corresponding to fullscreen size.
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
mAm.resizeStack(stack.mStackId, displayRect);
// Move activity back to fullscreen stack.
@@ -340,14 +340,14 @@
@Test
@FlakyTest(bugId = 71875755)
public void testFullscreenAppOrientationRequests() throws Exception {
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
ReportedSizes reportedSizes =
getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator);
assertEquals("portrait activity should be in portrait",
1 /* portrait */, reportedSizes.orientation);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
mAmWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
@@ -355,7 +355,7 @@
getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY, logSeparator);
assertEquals("landscape activity should be in landscape",
2 /* landscape */, reportedSizes.orientation);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
@@ -363,18 +363,18 @@
getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator);
assertEquals("portrait activity should be in portrait",
1 /* portrait */, reportedSizes.orientation);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
}
@Test
public void testNonfullscreenAppOrientationRequests() throws Exception {
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
final ReportedSizes initialReportedSizes =
getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator);
assertEquals("portrait activity should be in portrait",
1 /* portrait */, initialReportedSizes.orientation);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY);
assertEquals("Legacy non-fullscreen activity requested landscape orientation",
@@ -535,7 +535,7 @@
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
// Launch to docked stack and record size.
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivityInSplitScreenWithRecents(activityName);
final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName, logSeparator);
// Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
@@ -545,14 +545,14 @@
new WaitForValidActivityState.Builder(activityName).build());
// Move to fullscreen stack.
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
setActivityTaskWindowingMode(
activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName, logSeparator);
assertSizesAreSane(fullscreenSizes, initialDockedSizes);
// Move activity back to docked stack.
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName, logSeparator);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
index db81483..30ed944 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
@@ -111,7 +111,7 @@
}
for (int rotation = 0; rotation < 4; rotation += rotationStep) {
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
rotationSession.set(rotation);
mAmWmState.computeState(activityName);
assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange,
@@ -133,14 +133,14 @@
ComponentName activityName, boolean relaunch) throws Exception {
try (final FontScaleSession fontScaleSession = new FontScaleSession()) {
fontScaleSession.set(1.0f);
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(activityName);
mAmWmState.computeState(activityName);
final int densityDpi = getActivityDensityDpi(activityName, logSeparator);
for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
fontScaleSession.set(fontScale);
mAmWmState.computeState(activityName);
assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1,
@@ -161,13 +161,13 @@
*/
@Test
public void testUpdateApplicationInfo() throws Exception {
- final LogSeparator firstLogSeparator = clearLogcat();
+ final LogSeparator firstLogSeparator = separateLogs();
// Launch an activity that prints applied config.
launchActivity(TEST_ACTIVITY);
final int assetSeq = readAssetSeqNumber(TEST_ACTIVITY, firstLogSeparator);
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
// Update package info.
executeShellCommand("am update-appinfo all " + TEST_ACTIVITY.getPackageName());
mAmWmState.waitForWithAmState((amState) -> {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
index d8808ea..4a3370f 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
@@ -122,7 +122,7 @@
mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY).build(),
new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY).build());
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
resizeActivityTask(TEST_ACTIVITY,
TEST_TASK_OFFSET, TEST_TASK_OFFSET, testTaskSize2, testTaskSize1);
resizeActivityTask(NO_RELAUNCH_ACTIVITY,
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 0b15afc..23eb28c 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -106,7 +106,7 @@
final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
// Launch activity on new secondary display.
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
mAmWmState.computeState(TEST_ACTIVITY);
@@ -363,7 +363,7 @@
// Create new virtual display.
final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
// Try to launch an activity and check it security exception was triggered.
getLaunchActivityBuilder()
@@ -419,6 +419,7 @@
* primary display.
*/
@Presubmit
+ @FlakyTest(bugId = 77469851)
@Test
public void testConsequentLaunchActivity() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
@@ -587,7 +588,7 @@
"Activity launched on secondary display must be resumed",
LAUNCHING_ACTIVITY);
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
// Launch second activity from app on secondary display specifying same display id.
getLaunchActivityBuilder()
@@ -778,7 +779,7 @@
mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
// Destroy virtual display.
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
}
mAmWmState.computeState(true);
@@ -1046,7 +1047,7 @@
assertEquals("Focused stack must be on secondary display", newDisplay.mId,
focusedStack.mDisplayId);
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
// Launch other activity with different uid and check security exception is triggered.
getLaunchActivityBuilder()
@@ -1126,7 +1127,7 @@
RESIZEABLE_ACTIVITY);
// Destroy the display and check if activities are removed from system.
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
}
mAmWmState.waitForWithAmState(
@@ -1165,7 +1166,7 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch a resizeable activity on new secondary display.
- final LogSeparator initialLogSeparator = clearLogcat();
+ final LogSeparator initialLogSeparator = separateLogs();
launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Launched activity must be focused",
@@ -1176,7 +1177,7 @@
RESIZEABLE_ACTIVITY, initialLogSeparator);
// Resize the docked stack, so that activity with virtual display will also be resized.
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
virtualDisplaySession.resizeDisplay();
mAmWmState.waitForWithAmState(amState -> {
@@ -1333,7 +1334,7 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch activity on new secondary display.
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
RESIZEABLE_ACTIVITY);
@@ -1351,7 +1352,7 @@
final int initialRotation = mAmWmState.getWmState().getRotation();
rotationSession.set((initialRotation + 1) % 4);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
@@ -1369,7 +1370,7 @@
private void rotateAndCheckSameSizes(
RotationSession rotationSession, ComponentName activityName) throws Exception {
for (int rotation = 3; rotation >= 0; --rotation) {
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
rotationSession.set(rotation);
final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
logSeparator);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
index 1a0399e..b9e843b 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -845,7 +845,7 @@
// Launch a PiP activity and ensure configuration change only happened once, and that the
// configuration change happened after the picture-in-picture and multi-window callbacks
launchActivity(PIP_ACTIVITY);
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
waitForEnterPip(PIP_ACTIVITY);
assertPinnedStackExists();
@@ -854,7 +854,7 @@
// Trigger it to go back to fullscreen and ensure that only triggered one configuration
// change as well
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(PIP_ACTIVITY);
waitForValidPictureInPictureCallbacks(PIP_ACTIVITY, logSeparator);
assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
@@ -886,7 +886,7 @@
assertPinnedStackExists();
// Relaunch the PiP activity back into fullscreen
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(PIP_ACTIVITY);
// Wait until the PiP activity is reparented into the fullscreen stack (happens after
// the transition has finished)
@@ -916,7 +916,7 @@
assertPinnedStackExists();
// Dismiss it
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
waitForExitPipToFullscreen(PIP_ACTIVITY);
@@ -1032,7 +1032,7 @@
// Finish the task overlay activity while animating and ensure that the PiP activity never
// got resumed
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
executeShellCommand("am stack resize-animated " + stackId + " 20 20 500 500");
executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
mAmWmState.waitFor((amState, wmState) ->
@@ -1168,7 +1168,7 @@
public void testDisplayMetricsPinUnpin() throws Exception {
assumeTrue(supportsPip());
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(TEST_ACTIVITY);
final int defaultWindowingMode = mAmWmState.getAmState()
.getTaskByActivity(TEST_ACTIVITY).getWindowingMode();
@@ -1178,7 +1178,7 @@
assertNotNull("Must report display dimensions", initialSizes);
assertNotNull("Must report app bounds", initialAppBounds);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
// Wait for animation complete since we are comparing bounds
waitForEnterPipAnimationComplete(PIP_ACTIVITY);
@@ -1193,7 +1193,7 @@
assertNotEquals("Reported app size when pinned must be different from default",
initialAppSize, pinnedAppSize);
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(PIP_ACTIVITY, defaultWindowingMode);
final ReportedSizes finalSizes = getLastReportedSizesForActivity(PIP_ACTIVITY,
logSeparator);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
index 500f595..4edd541 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -150,7 +150,7 @@
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
// Exit split-screen mode and ensure we only get 1 multi-window mode changed callback.
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
removeStacksInWindowingModes(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ActivityLifecycleCounts lifecycleCounts = waitForOnMultiWindowModeChanged(
TEST_ACTIVITY, logSeparator);
@@ -164,7 +164,7 @@
launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
// Move to docked stack.
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
setActivityTaskWindowingMode(TEST_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
ActivityLifecycleCounts lifecycleCounts = waitForOnMultiWindowModeChanged(
TEST_ACTIVITY, logSeparator);
@@ -177,7 +177,7 @@
launchActivity(TEST_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
// Move activity back to fullscreen stack.
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
setActivityTaskWindowingMode(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
lifecycleCounts = waitForOnMultiWindowModeChanged(TEST_ACTIVITY, logSeparator);
assertEquals("mMultiWindowModeChangedCount",
@@ -583,7 +583,7 @@
final Rect initialDockBounds = mAmWmState.getWmState().getStandardStackByWindowingMode(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) .getBounds();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
Rect newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
resizeDockedStack(
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
index 3984af0..eca8940 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -128,7 +128,7 @@
public void testDismissKeyguardActivity_method() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.setLockCredential();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
lockScreenSession.gotoKeyguard();
mAmWmState.computeState(true);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -146,7 +146,7 @@
public void testDismissKeyguardActivity_method_cancelled() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.setLockCredential();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
lockScreenSession.gotoKeyguard();
mAmWmState.computeState(true);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
index ec1eee4..2a07b27 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -240,7 +240,7 @@
@Test
public void testDismissKeyguardActivity_method() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
lockScreenSession.gotoKeyguard();
mAmWmState.computeState(true);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -256,7 +256,7 @@
@Test
public void testDismissKeyguardActivity_method_notTop() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
lockScreenSession.gotoKeyguard();
mAmWmState.computeState(true);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -270,7 +270,7 @@
@Test
public void testDismissKeyguardActivity_method_turnScreenOn() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
lockScreenSession.sleepDevice();
mAmWmState.computeState(true);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -349,7 +349,7 @@
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
mAmWmState.computeState(true);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity(TURN_SCREEN_ON_ATTR_DISMISS_KEYGUARD_ACTIVITY);
@@ -381,7 +381,7 @@
@Test
public void testScreenOffWhileOccludedStopsActivity() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
lockScreenSession.gotoKeyguard();
mAmWmState.assertKeyguardShowingAndNotOccluded();
launchActivity(SHOW_WHEN_LOCKED_ATTR_ACTIVITY);
@@ -396,7 +396,7 @@
@Test
public void testScreenOffCausesSingleStop() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
launchActivity(TEST_ACTIVITY);
mAmWmState.assertVisibility(TEST_ACTIVITY, true);
lockScreenSession.sleepDevice();
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
index 5e7f309..c2156fe 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
@@ -114,7 +114,7 @@
public void testOccludeManifestAttr() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.gotoKeyguard();
- final LogSeparator logSeparator = clearLogcat();
+ final LogSeparator logSeparator = separateLogs();
launchActivity(SHOW_WHEN_LOCKED_ATTR_ACTIVITY);
mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY);
assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
@@ -127,7 +127,7 @@
public void testOccludeAttrRemove() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.gotoKeyguard();
- LogSeparator logSeparator = clearLogcat();
+ LogSeparator logSeparator = separateLogs();
launchActivity(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
@@ -135,7 +135,7 @@
assertSingleLaunch(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY, logSeparator);
lockScreenSession.gotoKeyguard();
- logSeparator = clearLogcat();
+ logSeparator = separateLogs();
launchActivity(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
assertSingleStartAndStop(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY, logSeparator);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
index 920de4c..380449c 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
@@ -121,6 +121,7 @@
}
@Test
+ @FlakyTest(bugId = 77565927)
public void testRelaunchConfigurationChangedWhileBecomingVisible() throws Exception {
final Activity becomingVisibleActivity =
mFirstActivityTestRule.launchActivity(new Intent());
@@ -434,6 +435,7 @@
/**
* The that recreate request from an activity is executed immediately.
*/
+ @FlakyTest(bugId = 77565927)
@Test
public void testLocalRecreate() throws Exception {
// Launch the activity that will recreate itself
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 4b39cfa..0a3ae97 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
@@ -768,12 +768,11 @@
}
/**
- * Tries to clear logcat and inserts log separator in case clearing didn't succeed, so we can
- * always find the starting point from where to evaluate following logs.
+ * Inserts a log separator so we can always find the starting point from where to evaluate
+ * following logs.
* @return Unique log separator.
*/
- protected LogSeparator clearLogcat() {
- executeShellCommand("logcat -c");
+ protected LogSeparator separateLogs() {
final LogSeparator logSeparator = new LogSeparator();
executeShellCommand("log -t " + LOG_SEPARATOR + " " + logSeparator);
return logSeparator;
diff --git a/tests/signature/api-check/hidden-api-killswitch/Android.mk b/tests/signature/api-check/hidden-api-killswitch/Android.mk
index 2e1ee05..33afcff 100644
--- a/tests/signature/api-check/hidden-api-killswitch/Android.mk
+++ b/tests/signature/api-check/hidden-api-killswitch/Android.mk
@@ -19,7 +19,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_MULTILIB := both
-LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker
+LOCAL_JNI_SHARED_LIBRARIES := libcts_dexchecker libclassdescriptors
LOCAL_NDK_STL_VARIANT := c++_static
# Tag this module as a cts test artifact
diff --git a/tests/signature/api-check/hidden-api-killswitch/AndroidManifest.xml b/tests/signature/api-check/hidden-api-killswitch/AndroidManifest.xml
index 8699d18..2e990a1 100644
--- a/tests/signature/api-check/hidden-api-killswitch/AndroidManifest.xml
+++ b/tests/signature/api-check/hidden-api-killswitch/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.signature.cts.api.killswitch">
- <application/>
+ <application android:debuggable="true"/>
<instrumentation android:name="repackaged.android.test.InstrumentationTestRunner"
android:targetPackage="android.signature.cts.api.killswitch"
diff --git a/tests/signature/api-check/hidden-api-killswitch/AndroidTest.xml b/tests/signature/api-check/hidden-api-killswitch/AndroidTest.xml
index 2d99a73..35bf729 100644
--- a/tests/signature/api-check/hidden-api-killswitch/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-killswitch/AndroidTest.xml
@@ -30,6 +30,6 @@
<option name="package" value="android.signature.cts.api.killswitch" />
<option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
<option name="class" value="android.signature.cts.api.KillswitchTest" />
- <option name="runtime-hint" value="30s" />
+ <option name="runtime-hint" value="60s" />
</test>
</configuration>
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 95f46df..61511c3 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
@@ -18,6 +18,9 @@
import android.os.Debug;
import android.signature.cts.ClassProvider;
+import android.signature.cts.DexField;
+import android.signature.cts.DexMethod;
+import android.signature.cts.DexMember;
import dalvik.system.BaseDexClassLoader;
import java.io.File;
@@ -34,19 +37,9 @@
@Override
public Stream<Class<?>> getAllClasses() {
- if (!sJvmtiAttached) {
- try {
- Debug.attachJvmtiAgent(copyAgentToFile("classdescriptors").getAbsolutePath(), null,
- BootClassPathClassesProvider.class.getClassLoader());
- sJvmtiAttached = true;
- initialize();
- } catch (Exception e) {
- throw new RuntimeException("Error while attaching JVMTI agent", e);
- }
- }
+ maybeAttachJvmtiAgent();
return Arrays.stream(getClassloaderDescriptors(Object.class.getClassLoader()))
.map(descriptor -> {
- System.err.println("Class name = " + descriptor);
String classname = descriptor.replace('/', '.');
// omit L and ; at the front and at the end
return classname.substring(1, classname.length() - 1);
@@ -60,6 +53,42 @@
});
}
+ @Override
+ public Stream<DexMember> getAllMembers(Class<?> klass) {
+ maybeAttachJvmtiAgent();
+
+ String[][] field_infos = getClassMemberNamesAndTypes(klass, /* fields */ true);
+ String[][] method_infos = getClassMemberNamesAndTypes(klass, /* fields */ false);
+ if (field_infos.length != 2 || field_infos[0].length != field_infos[1].length ||
+ method_infos.length != 2 || method_infos[0].length != method_infos[1].length) {
+ throw new RuntimeException("Invalid result from getClassMemberNamesAndTypes");
+ }
+
+ 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]);
+ }
+ 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]);
+ }
+ return Arrays.stream(members);
+ }
+
+ private static void maybeAttachJvmtiAgent() {
+ if (!sJvmtiAttached) {
+ try {
+ Debug.attachJvmtiAgent(copyAgentToFile("classdescriptors").getAbsolutePath(), null,
+ BootClassPathClassesProvider.class.getClassLoader());
+ sJvmtiAttached = true;
+ initialize();
+ } catch (Exception e) {
+ throw new RuntimeException("Error while attaching JVMTI agent", e);
+ }
+ }
+ }
+
private static File copyAgentToFile(String lib) throws Exception {
ClassLoader cl = BootClassPathClassesProvider.class.getClassLoader();
@@ -84,4 +113,5 @@
private static native void initialize();
private static native String[] getClassloaderDescriptors(ClassLoader loader);
+ private static native String[][] getClassMemberNamesAndTypes(Class<?> klass, boolean getFields);
}
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 9673fd2..f04f476 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
@@ -18,10 +18,10 @@
import android.os.Bundle;
import android.signature.cts.DexApiDocumentParser;
-import android.signature.cts.DexApiDocumentParser.DexField;
-import android.signature.cts.DexApiDocumentParser.DexMember;
-import android.signature.cts.DexApiDocumentParser.DexMethod;
+import android.signature.cts.DexField;
+import android.signature.cts.DexMember;
import android.signature.cts.DexMemberChecker;
+import android.signature.cts.DexMethod;
import android.signature.cts.FailureType;
import android.signature.cts.ResultObserver;
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/KillswitchTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/KillswitchTest.java
index 37ff746..3da22a9 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/KillswitchTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/KillswitchTest.java
@@ -18,7 +18,10 @@
import android.provider.Settings;
import android.signature.cts.DexApiDocumentParser;
+import android.signature.cts.DexField;
+import android.signature.cts.DexMember;
import android.signature.cts.DexMemberChecker;
+import android.signature.cts.DexMethod;
import android.signature.cts.FailureType;
public class KillswitchTest extends AbstractApiTest {
@@ -40,71 +43,74 @@
" when global setting hidden_api_blacklist_exemptions is set to *";
public void testKillswitch() {
- // for now, just verify that we can access something *not* on the light grey or white list
- DexApiDocumentParser.DexMember member = new DexApiDocumentParser.DexField(
- "Ldalvik/system/VMRuntime;", "THE_ONE", "Ldalvik/system/VMRuntime;");
- runWithTestResultObserver(resultObserver ->
- DexMemberChecker.checkSingleMember(member, new DexMemberChecker.Observer() {
- @Override
- public void classAccessible(boolean accessible,
- DexApiDocumentParser.DexMember member) {
- if (!accessible) {
- resultObserver.notifyFailure(
- FailureType.MISSING_CLASS,
- member.toString(),
- "Class from boot classpath is not accessible"
- + ERROR_MESSAGE_APPENDIX);
- }
+ runWithTestResultObserver(resultObserver -> {
+ DexMemberChecker.Observer observer = new DexMemberChecker.Observer() {
+ @Override
+ public void classAccessible(boolean accessible, DexMember member) {
+ if (!accessible) {
+ resultObserver.notifyFailure(
+ FailureType.MISSING_CLASS,
+ member.toString(),
+ "Class from boot classpath is not accessible"
+ + ERROR_MESSAGE_APPENDIX);
+ }
+ }
+
+ @Override
+ public void fieldAccessibleViaReflection(boolean accessible, DexField field) {
+ if (!accessible) {
+ resultObserver.notifyFailure(
+ FailureType.MISSING_FIELD,
+ field.toString(),
+ "Field from boot classpath is not accessible via reflection"
+ + ERROR_MESSAGE_APPENDIX);
+ }
+ }
+
+ @Override
+ public void fieldAccessibleViaJni(boolean accessible, DexField field) {
+ if (!accessible) {
+ resultObserver.notifyFailure(
+ FailureType.MISSING_FIELD,
+ field.toString(),
+ "Field from boot classpath is not accessible via JNI"
+ + ERROR_MESSAGE_APPENDIX);
+ }
+ }
+
+ @Override
+ public void methodAccessibleViaReflection(boolean accessible, DexMethod method) {
+ if (method.isStaticConstructor()) {
+ // Skip static constructors. They cannot be discovered with reflection.
+ return;
}
- @Override
- public void fieldAccessibleViaReflection(boolean accessible,
- DexApiDocumentParser.DexField field) {
- if (!accessible) {
- resultObserver.notifyFailure(
- FailureType.MISSING_FIELD,
- field.toString(),
- "Field from boot classpath is not accessible via reflection"
- + ERROR_MESSAGE_APPENDIX);
- }
+ if (!accessible) {
+ resultObserver.notifyFailure(
+ FailureType.MISSING_METHOD,
+ method.toString(),
+ "Method from boot classpath is not accessible via reflection"
+ + ERROR_MESSAGE_APPENDIX);
}
+ }
- @Override
- public void fieldAccessibleViaJni(boolean accessible,
- DexApiDocumentParser.DexField field) {
- if (!accessible) {
- resultObserver.notifyFailure(
- FailureType.MISSING_FIELD,
- field.toString(),
- "Field from boot classpath is not accessible via JNI"
- + ERROR_MESSAGE_APPENDIX);
- }
+ @Override
+ public void methodAccessibleViaJni(boolean accessible, DexMethod method) {
+ if (!accessible) {
+ resultObserver.notifyFailure(
+ FailureType.MISSING_METHOD,
+ method.toString(),
+ "Method from boot classpath is not accessible via JNI"
+ + ERROR_MESSAGE_APPENDIX);
}
+ }
- @Override
- public void methodAccessibleViaReflection(boolean accessible,
- DexApiDocumentParser.DexMethod method) {
- if (!accessible) {
- resultObserver.notifyFailure(
- FailureType.MISSING_METHOD,
- method.toString(),
- "Method from boot classpath is not accessible via reflection"
- + ERROR_MESSAGE_APPENDIX);
- }
- }
-
- @Override
- public void methodAccessibleViaJni(boolean accessible,
- DexApiDocumentParser.DexMethod method) {
- if (!accessible) {
- resultObserver.notifyFailure(
- FailureType.MISSING_METHOD,
- method.toString(),
- "Method from boot classpath is not accessible via JNI"
- + ERROR_MESSAGE_APPENDIX);
- }
- }
-
- }));
+ };
+ classProvider.getAllClasses().forEach(klass -> {
+ classProvider.getAllMembers(klass).forEach(member -> {
+ DexMemberChecker.checkSingleMember(member, observer);
+ });
+ });
+ });
}
}
diff --git a/tests/signature/api-check/src/jni/classdescriptors.cpp b/tests/signature/api-check/src/jni/classdescriptors.cpp
index 3da66f1..7af2c30 100644
--- a/tests/signature/api-check/src/jni/classdescriptors.cpp
+++ b/tests/signature/api-check/src/jni/classdescriptors.cpp
@@ -17,7 +17,12 @@
#include <jni.h>
#include <jvmti.h>
+#include <stdlib.h>
#include <string.h>
+#include <type_traits>
+
+#include <iostream>
+#include <sstream>
namespace android {
namespace signature {
@@ -51,6 +56,35 @@
Dealloc(data);
}
+template<typename T>
+class ScopedJvmtiReference {
+ static_assert(std::is_pointer<T>::value, "T must be a pointer type");
+
+ public:
+ ScopedJvmtiReference() : ref_(nullptr) {}
+
+ ~ScopedJvmtiReference() {
+ if (ref_ != nullptr) {
+ Dealloc(ref_);
+ }
+ }
+
+ // Return the pointer value.
+ T Get() { return ref_; };
+
+ // Return a pointer to the pointer value.
+ T* GetPtr() { return &ref_; };
+
+ private:
+ T ref_;
+};
+
+static void abortIfExceptionPending(JNIEnv* env) {
+ if (env->ExceptionCheck()) {
+ abort();
+ }
+}
+
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm,
__attribute__((unused)) char* options,
__attribute__((unused)) void* reserved) {
@@ -122,6 +156,72 @@
}
}
+extern "C" JNIEXPORT jobjectArray JNICALL
+Java_android_signature_cts_api_BootClassPathClassesProvider_getClassMemberNamesAndTypes(
+ JNIEnv* env, jclass, jclass klass, jboolean getFields) {
+ jvmtiError error;
+
+ jint count;
+ ScopedJvmtiReference<jfieldID*> fids;
+ ScopedJvmtiReference<jmethodID*> mids;
+
+ // Request a list of field/method IDs using JVMTI.
+ error = (getFields != JNI_FALSE) ? jvmti_env->GetClassFields(klass, &count, fids.GetPtr())
+ : jvmti_env->GetClassMethods(klass, &count, mids.GetPtr());
+ if (error != JVMTI_ERROR_NONE) {
+ std::stringstream ss;
+ ss << "Error while executing "
+ << ((getFields != JNI_FALSE) ? "GetClassFields" : "GetClassMethods")
+ << ", error code: " << static_cast<unsigned>(error);
+ std::string error = ss.str();
+ jclass rt_exception = env->FindClass("java/lang/RuntimeException");
+ env->ThrowNew(rt_exception, error.c_str());
+ return nullptr;
+ }
+
+ jobjectArray names = env->NewObjectArray(count, env->FindClass("java/lang/String"), nullptr);
+ abortIfExceptionPending(env);
+ jobjectArray types = env->NewObjectArray(count, env->FindClass("java/lang/String"), nullptr);
+ abortIfExceptionPending(env);
+
+ // Convert IDs to names and types using JVMTI.
+ for (jint i = 0; i < count; ++i) {
+ ScopedJvmtiReference<char*> name;
+ ScopedJvmtiReference<char*> type;
+
+ error = (getFields != JNI_FALSE)
+ ? jvmti_env->GetFieldName(klass, fids.Get()[i], name.GetPtr(), type.GetPtr(), nullptr)
+ : jvmti_env->GetMethodName(mids.Get()[i], name.GetPtr(), type.GetPtr(), nullptr);
+ if (error != JVMTI_ERROR_NONE) {
+ std::stringstream ss;
+ ss << "Error while executing "
+ << ((getFields != JNI_FALSE) ? "GetFieldName" : "GetMethodName")
+ << ", error code: " << static_cast<unsigned>(error);
+ std::string error = ss.str();
+ jclass rt_exception = env->FindClass("java/lang/RuntimeException");
+ env->ThrowNew(rt_exception, error.c_str());
+ return nullptr;
+ }
+
+ env->SetObjectArrayElement(names, i, env->NewStringUTF(name.Get()));
+ abortIfExceptionPending(env);
+ env->SetObjectArrayElement(types, i, env->NewStringUTF(type.Get()));
+ abortIfExceptionPending(env);
+ }
+
+ // Return as a array size 2 x count, where result[0] is an array of names and
+ // result[1] is an array of types.
+ jobjectArray result = env->NewObjectArray(
+ /* count */ 2, env->FindClass("[Ljava/lang/String;"), nullptr);
+ abortIfExceptionPending(env);
+ env->SetObjectArrayElement(result, 0, names);
+ abortIfExceptionPending(env);
+ env->SetObjectArrayElement(result, 1, types);
+ abortIfExceptionPending(env);
+
+ return result;
+}
+
} // namespace api
} // namespace cts
} // namespace signature
diff --git a/tests/signature/src/android/signature/cts/ClassProvider.java b/tests/signature/src/android/signature/cts/ClassProvider.java
index 2461f26..799b47c 100644
--- a/tests/signature/src/android/signature/cts/ClassProvider.java
+++ b/tests/signature/src/android/signature/cts/ClassProvider.java
@@ -36,4 +36,9 @@
* Gets all classes available to this provider.
*/
public abstract Stream<Class<?>> getAllClasses();
+
+ /**
+ * Gets all class members available for the given class.
+ */
+ public abstract Stream<DexMember> getAllMembers(Class<?> klass);
}
diff --git a/tests/signature/src/android/signature/cts/DexApiDocumentParser.java b/tests/signature/src/android/signature/cts/DexApiDocumentParser.java
index 7cb60fb..c199bc0 100644
--- a/tests/signature/src/android/signature/cts/DexApiDocumentParser.java
+++ b/tests/signature/src/android/signature/cts/DexApiDocumentParser.java
@@ -19,13 +19,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import java.text.ParseException;
@@ -52,7 +49,7 @@
private static final Pattern REGEX_CLASS = Pattern.compile("^L[^->]*;$");
private static final Pattern REGEX_FIELD = Pattern.compile("^(L[^->]*;)->(.*):(.*)$");
private static final Pattern REGEX_METHOD =
- Pattern.compile("^(L[^->]*;)->(.*)\\((.*)\\)(.*)$");
+ Pattern.compile("^(L[^->]*;)->(.*)(\\(.*\\).*)$");
DexApiSpliterator(BufferedReader reader) {
mReader = reader;
@@ -137,169 +134,11 @@
matchField.group(1), matchField.group(2), matchField.group(3));
} else if (matchMethod.matches()) {
return new DexMethod(
- matchMethod.group(1),matchMethod.group(2),
- parseDexTypeList(matchMethod.group(3)), matchMethod.group(4));
+ matchMethod.group(1),matchMethod.group(2), matchMethod.group(3));
} else {
throw new IllegalStateException();
}
}
}
-
- private List<String> parseDexTypeList(String typeSequence) throws ParseException {
- List<String> list = new ArrayList<String>();
- while (!typeSequence.isEmpty()) {
- String type = firstDexTypeFromList(typeSequence);
- list.add(type);
- typeSequence = typeSequence.substring(type.length());
- }
- return list;
- }
-
- /**
- * Returns the first dex type in `typeList` or throws a ParserException
- * if a dex type is not recognized. The input is not changed.
- */
- private String firstDexTypeFromList(String typeList) throws ParseException {
- String dexDimension = "";
- while (typeList.startsWith("[")) {
- dexDimension += "[";
- typeList = typeList.substring(1);
- }
-
- String type = null;
- if (typeList.startsWith("V")
- || typeList.startsWith("Z")
- || typeList.startsWith("B")
- || typeList.startsWith("C")
- || typeList.startsWith("S")
- || typeList.startsWith("I")
- || typeList.startsWith("J")
- || typeList.startsWith("F")
- || typeList.startsWith("D")) {
- type = typeList.substring(0, 1);
- } else if (typeList.startsWith("L") && typeList.indexOf(";") > 0) {
- type = typeList.substring(0, typeList.indexOf(";") + 1);
- } else {
- throw new ParseException(
- "Unexpected dex type in \"" + typeList + "\"", mLineNum);
- }
-
- return dexDimension + type;
- }
- }
-
- /**
- * Represents one class member parsed from the reader of dex signatures.
- */
- public static abstract class DexMember {
- private final String mName;
- private final String mClassDescriptor;
- private final String mType;
-
- protected DexMember(String className, String name, String type) {
- mName = name;
- mClassDescriptor = className;
- mType = type;
- }
-
- public String getName() {
- return mName;
- }
-
- public String getDexClassName() {
- return mClassDescriptor;
- }
-
- public String getJavaClassName() {
- return dexToJavaType(mClassDescriptor);
- }
-
- public String getDexType() {
- return mType;
- }
-
- public String getJavaType() {
- return dexToJavaType(mType);
- }
-
- /**
- * Converts `type` to a Java type.
- */
- protected static String dexToJavaType(String type) {
- String javaDimension = "";
- while (type.startsWith("[")) {
- javaDimension += "[]";
- type = type.substring(1);
- }
-
- String javaType = null;
- if ("V".equals(type)) {
- javaType = "void";
- } else if ("Z".equals(type)) {
- javaType = "boolean";
- } else if ("B".equals(type)) {
- javaType = "byte";
- } else if ("C".equals(type)) {
- javaType = "char";
- } else if ("S".equals(type)) {
- javaType = "short";
- } else if ("I".equals(type)) {
- javaType = "int";
- } else if ("J".equals(type)) {
- javaType = "long";
- } else if ("F".equals(type)) {
- javaType = "float";
- } else if ("D".equals(type)) {
- javaType = "double";
- } else if (type.startsWith("L") && type.endsWith(";")) {
- javaType = type.substring(1, type.length() - 1).replace('/', '.');
- } else {
- throw new IllegalStateException("Unexpected type " + type);
- }
-
- return javaType + javaDimension;
- }
- }
-
- public static class DexField extends DexMember {
- public DexField(String className, String name, String type) {
- super(className, name, type);
- }
-
- @Override
- public String toString() {
- return getJavaType() + " " + getJavaClassName() + "." + getName();
- }
- }
-
- public static class DexMethod extends DexMember {
- private final List<String> mParamTypeList;
-
- public DexMethod(String className, String name, List<String> paramTypeList,
- String dexReturnType) {
- super(className, name, dexReturnType);
- mParamTypeList = paramTypeList;
- }
-
- public String getDexSignature() {
- return "(" + String.join("", mParamTypeList) + ")" + getDexType();
- }
-
- public List<String> getJavaParameterTypes() {
- return mParamTypeList
- .stream()
- .map(DexMember::dexToJavaType)
- .collect(Collectors.toList());
- }
-
- public boolean isConstructor() {
- return "<init>".equals(getName()) && "V".equals(getDexType());
- }
-
- @Override
- public String toString() {
- return getJavaType() + " " + getJavaClassName() + "." + getName()
- + "(" + String.join(", ", getJavaParameterTypes()) + ")";
- }
}
}
diff --git a/tests/signature/src/android/signature/cts/DexField.java b/tests/signature/src/android/signature/cts/DexField.java
new file mode 100644
index 0000000..57f6196
--- /dev/null
+++ b/tests/signature/src/android/signature/cts/DexField.java
@@ -0,0 +1,27 @@
+/*
+ * 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.signature.cts;
+
+public class DexField extends DexMember {
+ public DexField(String className, String name, String type) {
+ super(className, name, type);
+ }
+
+ @Override
+ public String toString() {
+ return getJavaType() + " " + getJavaClassName() + "." + getName();
+ }
+}
diff --git a/tests/signature/src/android/signature/cts/DexMember.java b/tests/signature/src/android/signature/cts/DexMember.java
new file mode 100644
index 0000000..930f92b
--- /dev/null
+++ b/tests/signature/src/android/signature/cts/DexMember.java
@@ -0,0 +1,89 @@
+/*
+ * 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.signature.cts;
+
+/**
+ * Represents one class member parsed from the reader of dex signatures.
+ */
+public abstract class DexMember {
+ private final String mName;
+ private final String mClassDescriptor;
+ private final String mType;
+
+ protected DexMember(String className, String name, String type) {
+ mName = name;
+ mClassDescriptor = className;
+ mType = type;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getDexClassName() {
+ return mClassDescriptor;
+ }
+
+ public String getJavaClassName() {
+ return dexToJavaType(mClassDescriptor);
+ }
+
+ public String getDexType() {
+ return mType;
+ }
+
+ public String getJavaType() {
+ return dexToJavaType(mType);
+ }
+
+ /**
+ * Converts `type` to a Java type.
+ */
+ protected static String dexToJavaType(String type) {
+ String javaDimension = "";
+ while (type.startsWith("[")) {
+ javaDimension += "[]";
+ type = type.substring(1);
+ }
+
+ String javaType = null;
+ if ("V".equals(type)) {
+ javaType = "void";
+ } else if ("Z".equals(type)) {
+ javaType = "boolean";
+ } else if ("B".equals(type)) {
+ javaType = "byte";
+ } else if ("C".equals(type)) {
+ javaType = "char";
+ } else if ("S".equals(type)) {
+ javaType = "short";
+ } else if ("I".equals(type)) {
+ javaType = "int";
+ } else if ("J".equals(type)) {
+ javaType = "long";
+ } else if ("F".equals(type)) {
+ javaType = "float";
+ } else if ("D".equals(type)) {
+ javaType = "double";
+ } else if (type.startsWith("L") && type.endsWith(";")) {
+ javaType = type.substring(1, type.length() - 1).replace('/', '.');
+ } else {
+ throw new IllegalStateException("Unexpected type " + type);
+ }
+
+ return javaType + javaDimension;
+ }
+}
diff --git a/tests/signature/src/android/signature/cts/DexMemberChecker.java b/tests/signature/src/android/signature/cts/DexMemberChecker.java
index 3800682..a1bfdb6 100644
--- a/tests/signature/src/android/signature/cts/DexMemberChecker.java
+++ b/tests/signature/src/android/signature/cts/DexMemberChecker.java
@@ -23,20 +23,18 @@
public class DexMemberChecker {
public interface Observer {
- void classAccessible(boolean accessible, DexApiDocumentParser.DexMember member);
- void fieldAccessibleViaReflection(boolean accessible, DexApiDocumentParser.DexField field);
- void fieldAccessibleViaJni(boolean accessible, DexApiDocumentParser.DexField field);
- void methodAccessibleViaReflection(boolean accessible,
- DexApiDocumentParser.DexMethod method);
- void methodAccessibleViaJni(boolean accessible, DexApiDocumentParser.DexMethod method);
+ void classAccessible(boolean accessible, DexMember member);
+ void fieldAccessibleViaReflection(boolean accessible, DexField field);
+ void fieldAccessibleViaJni(boolean accessible, DexField field);
+ void methodAccessibleViaReflection(boolean accessible, DexMethod method);
+ void methodAccessibleViaJni(boolean accessible, DexMethod method);
}
public static void init() {
System.loadLibrary("cts_dexchecker");
}
- public static void checkSingleMember(DexApiDocumentParser.DexMember dexMember,
- DexMemberChecker.Observer observer) {
+ public static void checkSingleMember(DexMember dexMember, DexMemberChecker.Observer observer) {
Class<?> klass = findClass(dexMember);
if (klass == null) {
// Class not found. Therefore its members are not visible.
@@ -45,17 +43,16 @@
}
observer.classAccessible(true, dexMember);
- StringBuilder error = new StringBuilder("Hidden ");
- if (dexMember instanceof DexApiDocumentParser.DexField) {
- DexApiDocumentParser.DexField field = (DexApiDocumentParser.DexField) dexMember;
+ if (dexMember instanceof DexField) {
+ DexField field = (DexField) dexMember;
observer.fieldAccessibleViaReflection(
hasMatchingField_Reflection(klass, field),
field);
observer.fieldAccessibleViaJni(
hasMatchingField_JNI(klass, field),
field);
- } else if (dexMember instanceof DexApiDocumentParser.DexMethod) {
- DexApiDocumentParser.DexMethod method = (DexApiDocumentParser.DexMethod) dexMember;
+ } else if (dexMember instanceof DexMethod) {
+ DexMethod method = (DexMethod) dexMember;
observer.methodAccessibleViaReflection(
hasMatchingMethod_Reflection(klass, method),
method);
@@ -79,8 +76,7 @@
return true;
}
- private static Class<?> findClass(DexApiDocumentParser.DexMember dexMember) {
- Class<?> klass = null;
+ private static Class<?> findClass(DexMember dexMember) {
try {
return Class.forName(dexMember.getJavaClassName());
} catch (ClassNotFoundException ex) {
@@ -88,8 +84,7 @@
}
}
- private static boolean hasMatchingField_Reflection(Class<?> klass,
- DexApiDocumentParser.DexField dexField) {
+ private static boolean hasMatchingField_Reflection(Class<?> klass, DexField dexField) {
try {
klass.getDeclaredField(dexField.getName());
return true;
@@ -98,8 +93,7 @@
}
}
- private static boolean hasMatchingField_JNI(Class<?> klass,
- DexApiDocumentParser.DexField dexField) {
+ private static boolean hasMatchingField_JNI(Class<?> klass, DexField dexField) {
try {
getField_JNI(klass, dexField.getName(), dexField.getDexType());
return true;
@@ -113,8 +107,7 @@
return false;
}
- private static boolean hasMatchingMethod_Reflection(Class<?> klass,
- DexApiDocumentParser.DexMethod dexMethod) {
+ private static boolean hasMatchingMethod_Reflection(Class<?> klass, DexMethod dexMethod) {
List<String> methodParams = dexMethod.getJavaParameterTypes();
if (dexMethod.isConstructor()) {
@@ -134,8 +127,7 @@
return false;
}
- private static boolean hasMatchingMethod_JNI(Class<?> klass,
- DexApiDocumentParser.DexMethod dexMethod) {
+ private static boolean hasMatchingMethod_JNI(Class<?> klass, DexMethod dexMethod) {
try {
getMethod_JNI(klass, dexMethod.getName(), dexMethod.getDexSignature());
return true;
diff --git a/tests/signature/src/android/signature/cts/DexMethod.java b/tests/signature/src/android/signature/cts/DexMethod.java
new file mode 100644
index 0000000..ad21c7a
--- /dev/null
+++ b/tests/signature/src/android/signature/cts/DexMethod.java
@@ -0,0 +1,107 @@
+/*
+ * 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.signature.cts;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class DexMethod extends DexMember {
+ private final List<String> mParamTypeList;
+
+ public DexMethod(String className, String name, String signature) {
+ super(className, name, parseDexReturnType(signature));
+ mParamTypeList = parseDexTypeList(signature);
+ }
+
+ public String getDexSignature() {
+ return "(" + String.join("", mParamTypeList) + ")" + getDexType();
+ }
+
+ public List<String> getJavaParameterTypes() {
+ return mParamTypeList.stream().map(DexMember::dexToJavaType).collect(Collectors.toList());
+ }
+
+ public boolean isConstructor() {
+ return "<init>".equals(getName()) && "V".equals(getDexType());
+ }
+
+ public boolean isStaticConstructor() {
+ return "<clinit>".equals(getName()) && "V".equals(getDexType());
+ }
+
+ @Override
+ public String toString() {
+ return getJavaType() + " " + getJavaClassName() + "." + getName()
+ + "(" + String.join(", ", getJavaParameterTypes()) + ")";
+ }
+
+ private static Matcher matchSignature(String signature) {
+ Matcher m = Pattern.compile("^\\((.*)\\)(.*)$").matcher(signature);
+ if (!m.matches()) {
+ throw new RuntimeException("Could not parse method signature: " + signature);
+ }
+ return m;
+ }
+
+ private static String parseDexReturnType(String signature) {
+ return matchSignature(signature).group(2);
+ }
+
+ private static List<String> parseDexTypeList(String signature) {
+ String typeSequence = matchSignature(signature).group(1);
+ List<String> list = new ArrayList<String>();
+ while (!typeSequence.isEmpty()) {
+ String type = firstDexTypeFromList(typeSequence);
+ list.add(type);
+ typeSequence = typeSequence.substring(type.length());
+ }
+ return list;
+ }
+
+ /**
+ * Returns the first dex type in `typeList` or throws a ParserException
+ * if a dex type is not recognized. The input is not changed.
+ */
+ private static String firstDexTypeFromList(String typeList) {
+ String dexDimension = "";
+ while (typeList.startsWith("[")) {
+ dexDimension += "[";
+ typeList = typeList.substring(1);
+ }
+
+ String type = null;
+ if (typeList.startsWith("V")
+ || typeList.startsWith("Z")
+ || typeList.startsWith("B")
+ || typeList.startsWith("C")
+ || typeList.startsWith("S")
+ || typeList.startsWith("I")
+ || typeList.startsWith("J")
+ || typeList.startsWith("F")
+ || typeList.startsWith("D")) {
+ type = typeList.substring(0, 1);
+ } else if (typeList.startsWith("L") && typeList.indexOf(";") > 0) {
+ type = typeList.substring(0, typeList.indexOf(";") + 1);
+ } else {
+ throw new RuntimeException("Unexpected dex type in \"" + typeList + "\"");
+ }
+
+ return dexDimension + type;
+ }
+}
diff --git a/tests/signature/src/android/signature/cts/ExcludingClassProvider.java b/tests/signature/src/android/signature/cts/ExcludingClassProvider.java
index 290cbf5..fd9cc6c 100644
--- a/tests/signature/src/android/signature/cts/ExcludingClassProvider.java
+++ b/tests/signature/src/android/signature/cts/ExcludingClassProvider.java
@@ -45,4 +45,9 @@
return base.getAllClasses()
.filter(clazz -> !testForExclusion.test(clazz.getCanonicalName()));
}
+
+ @Override
+ public Stream<DexMember> getAllMembers(Class<?> klass) {
+ return base.getAllMembers(klass);
+ }
}
diff --git a/tests/signature/tests/src/android/signature/cts/tests/TestClassesProvider.java b/tests/signature/tests/src/android/signature/cts/tests/TestClassesProvider.java
index 80176de..6ddcbcf 100644
--- a/tests/signature/tests/src/android/signature/cts/tests/TestClassesProvider.java
+++ b/tests/signature/tests/src/android/signature/cts/tests/TestClassesProvider.java
@@ -18,6 +18,7 @@
import java.util.stream.Stream;
import android.signature.cts.ClassProvider;
+import android.signature.cts.DexMember;
import android.signature.cts.tests.data.AbstractClass;
import android.signature.cts.tests.data.SystemApiClass;
import android.signature.cts.tests.data.FinalClass;
@@ -46,4 +47,8 @@
return builder.build();
}
+ @Override
+ public Stream<DexMember> getAllMembers(Class<?> klass) {
+ return null;
+ }
}
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 2543f4a..084c7b3 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -332,8 +332,12 @@
public void testManageStorage() {
assertCanBeHandled(new Intent(StorageManager.ACTION_MANAGE_STORAGE));
}
-
+
public void testVoiceCommand() {
+ if (FeatureUtil.isLowRam()) {
+ // Low ram devices do not support voice command, skip this test
+ return;
+ }
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
diff --git a/tests/tests/content/src/android/content/cts/ContextMoreTest.java b/tests/tests/content/src/android/content/cts/ContextMoreTest.java
new file mode 100644
index 0000000..545bc43
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/ContextMoreTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.content.cts;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ContextMoreTest extends AndroidTestCase {
+ private static final String TAG = "ContextMoreTest";
+
+ /**
+ * Test for {@link Context#getSystemService)}.
+ *
+ * Call it repeatedly from multiple threads, and:
+ * - Make sure getSystemService(ActivityManager) will always return non-null.
+ * - If ContextImpl.mServiceCache is accessible via reflection, clear it once in a while and
+ * make sure getSystemService() still returns non-null.
+ */
+ @LargeTest
+ public void testGetSystemService_multiThreaded() throws Exception {
+ // # of times the tester Runnable has been executed.
+ final AtomicInteger totalCount = new AtomicInteger(0);
+
+ // # of times the tester Runnable has failed.
+ final AtomicInteger failCount = new AtomicInteger(0);
+
+ // Run the threads until this becomes true.
+ final AtomicBoolean stop = new AtomicBoolean(false);
+
+ final Context context = getContext();
+ final Object[] serviceCache = findServiceCache(context);
+ if (serviceCache == null) {
+ Log.w(TAG, "mServiceCache not found.");
+ }
+
+ final Runnable tester = () -> {
+ for (;;) {
+ final int pass = totalCount.incrementAndGet();
+
+ final Object service = context.getSystemService(ActivityManager.class);
+ if (service == null) {
+ failCount.incrementAndGet(); // Fail!
+ }
+
+ if (stop.get()) {
+ return;
+ }
+
+ // Yield the CPU.
+ try {
+ Thread.sleep(0);
+ } catch (InterruptedException e) {
+ }
+
+ // Once in a while, force clear mServiceCache.
+ if ((serviceCache != null) && ((pass % 7) == 0)) {
+ Arrays.fill(serviceCache, null);
+ }
+ }
+ };
+
+ final int NUM_THREADS = 20;
+
+ // Create and start the threads...
+ final Thread[] threads = new Thread[NUM_THREADS];
+ for (int i = 0; i < NUM_THREADS; i++) {
+ threads[i] = new Thread(tester);
+ }
+ for (int i = 0; i < NUM_THREADS; i++) {
+ threads[i].start();
+ }
+
+ Thread.sleep(10 * 1000);
+
+ stop.set(true);
+
+ // Wait for them to stop...
+ for (int i = 0; i < NUM_THREADS; i++) {
+ threads[i].join();
+ }
+
+ assertEquals(0, failCount.get());
+ assertTrue("totalCount must be bigger than " + NUM_THREADS
+ + " but was " + totalCount.get(), totalCount.get() > NUM_THREADS);
+ }
+
+ /**
+ * Find a field by name using reflection.
+ */
+ private static Object readField(Object instance, String fieldName) {
+ final Field f;
+ try {
+ f = instance.getClass().getDeclaredField(fieldName);
+ f.setAccessible(true);
+ final Object ret = f.get(instance);
+ if (ret == null) {
+ return null;
+ }
+ return ret;
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Try to find the mServiceCache field from a Context. Returns null if none found.
+ */
+ private static Object[] findServiceCache(Context context) {
+ // Find the base context.
+ while (context instanceof ContextWrapper) {
+ context = ((ContextWrapper) context).getBaseContext();
+ }
+ // Try to find the mServiceCache field.
+ final Object serviceCache = readField(context, "mServiceCache");
+ if (serviceCache instanceof Object[]) {
+ return (Object[]) serviceCache;
+ }
+ return null;
+ }
+}
diff --git a/tests/tests/content/src/android/content/cts/TestPagingContentProvider.java b/tests/tests/content/src/android/content/cts/TestPagingContentProvider.java
index 6e38809..2a1d11e 100644
--- a/tests/tests/content/src/android/content/cts/TestPagingContentProvider.java
+++ b/tests/tests/content/src/android/content/cts/TestPagingContentProvider.java
@@ -26,7 +26,6 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.util.Log;
-import android.util.MathUtils;
import javax.annotation.Nullable;
@@ -109,7 +108,8 @@
Bundle extras = c.getExtras();
// Calculate the number of items to include in the cursor.
- int numItems = MathUtils.constrain(recordsetSize - offset, 0, limit);
+ int numItems = recordsetSize - offset;
+ numItems = numItems < 0 ? 0 : (numItems > limit ? limit : numItems);
// Build the paged result set.
for (int i = offset; i < offset + numItems; i++) {
diff --git a/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java b/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
index bcbd868..87f80b0 100644
--- a/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
@@ -116,10 +116,6 @@
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
mAm = mContext.getSystemService(ActivityManager.class);
- executeShellCommand("pm grant " + mContext.getPackageName()
- + " android.permission.MANAGE_ACTIVITY_STACKS");
- executeShellCommand("pm grant " + mContext.getPackageName()
- + " android.permission.ACTIVITY_EMBEDDING");
pressWakeupButton();
pressUnlockButton();
diff --git a/tests/tests/media/res/raw/color_176x144_bt2020_lr_hlg_h265.mp4 b/tests/tests/media/res/raw/color_176x144_bt2020_lr_hlg_h265.mp4
new file mode 100644
index 0000000..748151c
--- /dev/null
+++ b/tests/tests/media/res/raw/color_176x144_bt2020_lr_hlg_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/color_176x144_bt2020_lr_smpte2084_h265.mp4 b/tests/tests/media/res/raw/color_176x144_bt2020_lr_smpte2084_h265.mp4
new file mode 100644
index 0000000..f21237f
--- /dev/null
+++ b/tests/tests/media/res/raw/color_176x144_bt2020_lr_smpte2084_h265.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 34e8a82..eae9d51 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -352,6 +352,18 @@
R.raw.color_176x144_srgb_lr_sdr_h265, 4 /* testId */,
MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709,
2 /* MediaFormat.COLOR_TRANSFER_SRGB */);
+ // Test the main10 streams with surface as the decoder might
+ // support opaque buffers only.
+ testColorAspects(
+ R.raw.color_176x144_bt2020_lr_smpte2084_h265, 5 /* testId */,
+ MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT2020,
+ MediaFormat.COLOR_TRANSFER_ST2084,
+ getActivity().getSurfaceHolder().getSurface());
+ testColorAspects(
+ R.raw.color_176x144_bt2020_lr_hlg_h265, 6 /* testId */,
+ MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT2020,
+ MediaFormat.COLOR_TRANSFER_HLG,
+ getActivity().getSurfaceHolder().getSurface());
}
/**
@@ -392,6 +404,13 @@
private void testColorAspects(
int res, int testId, int expectRange, int expectStandard, int expectTransfer)
throws Exception {
+ testColorAspects(
+ res, testId, expectRange, expectStandard, expectTransfer, null /*surface*/);
+ }
+
+ private void testColorAspects(
+ int res, int testId, int expectRange, int expectStandard, int expectTransfer,
+ Surface surface) throws Exception {
MediaFormat format = MediaUtils.getTrackFormatForResource(mContext, res, "video");
MediaFormat mimeFormat = new MediaFormat();
mimeFormat.setString(MediaFormat.KEY_MIME, format.getString(MediaFormat.KEY_MIME));
@@ -400,21 +419,21 @@
if (!MediaUtils.supports(decoderName, format)) {
MediaUtils.skipTest(decoderName + " cannot play resource " + res);
} else {
- testColorAspects(
- decoderName, res, testId, expectRange, expectStandard, expectTransfer);
+ testColorAspects(decoderName, res, testId,
+ expectRange, expectStandard, expectTransfer, surface);
}
}
}
private void testColorAspects(
String decoderName, int res, int testId, int expectRange,
- int expectStandard, int expectTransfer) throws Exception {
+ int expectStandard, int expectTransfer, Surface surface) throws Exception {
AssetFileDescriptor fd = mResources.openRawResourceFd(res);
MediaExtractor ex = new MediaExtractor();
ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
MediaFormat format = ex.getTrackFormat(0);
MediaCodec dec = MediaCodec.createByCodecName(decoderName);
- dec.configure(format, null, null, 0);
+ dec.configure(format, surface, null, 0);
dec.start();
ByteBuffer[] buf = dec.getInputBuffers();
ex.selectTrack(0);
@@ -494,6 +513,8 @@
}
log.submit(getInstrumentation());
+ assertTrue(rangeMatch && colorMatch && transferMatch);
+
dec.release();
ex.release();
fd.close();
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index ff6402c..604ec5f 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -116,23 +116,31 @@
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
}
-// Draws the following checkerboard pattern using glScissor and glClear:
-// +----+----+ (W, H)
-// | OR | Ob |
-// +----+----+ TB = transparent black
-// | TB | OR | OR = opaque red
-// (0, 0) +----+----+ Ob = opaque blue
+// Draws the following checkerboard pattern using glScissor and glClear.
+// The number is the depth value.
+// +-----+-----+ (W, H)
+// | OR | Ob |
+// | 0.5 | 0.0 |
+// +-----+-----+ TB = transparent black
+// | TB | OR | OR = opaque red
+// | 1.0 | 0.5 | Ob = opaque blue
+// (0, 0) +-----+-----+
+//
void DrawCheckerboard(int width, int height) {
glEnable(GL_SCISSOR_TEST);
glClearColor(1.f, 0.f, 0.f, 1.f);
+ glClearDepthf(0.5f);
glScissor(0, 0, width, height);
- glClear(GL_COLOR_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.f, 0.f, 0.f, 0.f);
+ glClearDepthf(1.0f);
glScissor(0, 0, width / 2, height / 2);
- glClear(GL_COLOR_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.f, 0.f, 1.f, 1.f);
+ glClearDepthf(0.f);
glScissor(width / 2, height / 2, width / 2, height / 2);
- glClear(GL_COLOR_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glDisable(GL_SCISSOR_TEST);
}
void CompileProgram(const char* vertex_source, const char* fragment_source, GLuint* program_out) {
@@ -175,22 +183,28 @@
void CheckGoldenPixel(const GoldenPixel& golden, uint8_t* pixel, bool alpha_format) {
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
- EXPECT_EQ(golden.color == kRed ? 255 : 0, pixel[0]);
- EXPECT_EQ(0, pixel[1]);
- EXPECT_EQ(golden.color == kBlue ? 255 : 0, pixel[2]);
+ EXPECT_EQ(golden.color == kRed ? 255 : 0, pixel[0])
+ << "Red doesn't match at X=" << golden.x << ", Y=" << golden.y;
+ EXPECT_EQ(0, pixel[1])
+ << "Green doesn't match at X=" << golden.x << ", Y=" << golden.y;
+ EXPECT_EQ(golden.color == kBlue ? 255 : 0, pixel[2])
+ << "Blue doesn't match at X=" << golden.x << ", Y=" << golden.y;
// Formats without alpha should be read as opaque.
- EXPECT_EQ((golden.color != kZero || !alpha_format) ? 255 : 0,
- pixel[3]);
+ EXPECT_EQ((golden.color != kZero || !alpha_format) ? 255 : 0, pixel[3])
+ << "Alpha doesn't match at X=" << golden.x << ", Y=" << golden.y;
}
void CheckGoldenPixel(const GoldenPixel& golden, float* pixel, bool alpha_format) {
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
- EXPECT_EQ(golden.color == kRed ? 1.f : 0.f, pixel[0]);
- EXPECT_EQ(0.f, pixel[1]);
- EXPECT_EQ(golden.color == kBlue ? 1.f : 0.f, pixel[2]);
+ EXPECT_EQ(golden.color == kRed ? 1.f : 0.f, pixel[0])
+ << "Red doesn't match at X=" << golden.x << ", Y=" << golden.y;
+ EXPECT_EQ(0.f, pixel[1])
+ << "Green doesn't match at X=" << golden.x << ", Y=" << golden.y;
+ EXPECT_EQ(golden.color == kBlue ? 1.f : 0.f, pixel[2])
+ << "Blue doesn't match at X=" << golden.x << ", Y=" << golden.y;
// Formats without alpha should be read as opaque.
- EXPECT_EQ((golden.color != kZero || !alpha_format) ? 1.f : 0.f,
- pixel[3]);
+ EXPECT_EQ((golden.color != kZero || !alpha_format) ? 1.f : 0.f, pixel[3])
+ << "Alpha doesn't match at X=" << golden.x << ", Y=" << golden.y;
}
void CheckGoldenPixels(const std::vector<GoldenPixel>& goldens, bool float_format, bool alpha_format) {
@@ -239,16 +253,17 @@
#version 100
attribute vec2 aPosition;
attribute float aDepth;
+ uniform mediump float uScale;
varying mediump vec2 vTexCoords;
void main() {
vTexCoords = (vec2(1.0) + aPosition) * 0.5;
- gl_Position.xy = aPosition * 0.5;
+ gl_Position.xy = aPosition * uScale;
gl_Position.z = aDepth;
gl_Position.w = 1.0;
}
)glsl";
-const char* kFragmentShader = R"glsl(
+const char* kTextureFragmentShader = R"glsl(
#version 100
precision mediump float;
varying mediump vec2 vTexCoords;
@@ -258,14 +273,24 @@
}
)glsl";
+const char* kColorFragmentShader = R"glsl(
+ #version 100
+ precision mediump float;
+ uniform lowp vec4 uColor;
+ void main() {
+ gl_FragColor = uColor;
+ }
+)glsl";
+
const char* kVertexShaderEs3 = R"glsl(
#version 300 es
in vec2 aPosition;
in float aDepth;
+ uniform mediump float uScale;
out mediump vec2 vTexCoords;
void main() {
vTexCoords = (vec2(1.0) + aPosition) * 0.5;
- gl_Position.xy = aPosition * 0.5;
+ gl_Position.xy = aPosition * uScale;
gl_Position.z = aDepth;
gl_Position.w = 1.0;
}
@@ -277,7 +302,7 @@
in mediump vec2 vTexCoords;
uniform lowp sampler2DArray uTexture;
uniform mediump float uLayer;
- out lowp vec4 color;
+ out mediump vec4 color;
void main() {
color = texture(uTexture, vec3(vTexCoords, uLayer));
}
@@ -295,23 +320,29 @@
// It looks like this:
//
// +---+ 1, 1
-// |\ /|
+// |\ /|
// | x |
// |/ \|
// -1, -1 +---+
-/*const float kPyramidPositions[] = {
+const float kPyramidPositions[] = {
-1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, 1.f, -1.f,
-1.f, 1.f, -1.f, 0.f, 0.f, 1.f, 1.f, 1.f, -1.f,
1.f, 1.f, -1.f, 0.f, 0.f, 1.f, 1.f, -1.f, -1.f,
1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, -1.f, -1.f,
-};*/
+};
} // namespace
-class AHardwareBufferGLTest : public ::testing::Test {
+class AHardwareBufferGLTest : public ::testing::TestWithParam<AHardwareBuffer_Desc> {
public:
void SetUp() override;
+ virtual bool SetUpBuffer(const AHardwareBuffer_Desc& desc);
+ void TearDownBuffer();
void TearDown() override;
+ void MakeCurrent(int which) {
+ if (GetParam().stride != 0) return;
+ eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[which]);
+ }
protected:
std::set<std::string> mEGLExtensions;
@@ -319,6 +350,10 @@
EGLSurface mSurface = EGL_NO_SURFACE;
EGLContext mContext[2];
int mGLVersion = 0;
+
+ AHardwareBuffer* mBuffer = nullptr;
+ EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
+ GLenum mTexTarget = GL_NONE;
};
void AHardwareBufferGLTest::SetUp() {
@@ -374,6 +409,34 @@
ASSERT_EQ(EGLBoolean{EGL_TRUE}, result);
}
+bool AHardwareBufferGLTest::SetUpBuffer(const AHardwareBuffer_Desc& desc) {
+ mTexTarget = desc.layers > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
+ if (desc.layers > 1 && mGLVersion < 3) return false;
+ // Nonzero stride indicates that desc.format should be interpreted as a GL format
+ // and the test should be run in a single context, without using AHardwareBuffer.
+ // This simplifies verifying that the test behaves as expected even if the
+ // AHardwareBuffer format under test is not supported.
+ if (desc.stride != 0) return true;
+
+ int result = AHardwareBuffer_allocate(&desc, &mBuffer);
+ // Skip if this format cannot be allocated.
+ if (result != NO_ERROR) return false;
+
+ const EGLint attrib_list[] = { EGL_NONE };
+ mEGLImage = eglCreateImageKHR(
+ mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ eglGetNativeClientBufferANDROID(mBuffer), attrib_list);
+ EXPECT_NE(EGL_NO_IMAGE_KHR, mEGLImage);
+ return mEGLImage != EGL_NO_IMAGE_KHR;
+}
+
+void AHardwareBufferGLTest::TearDownBuffer() {
+ if (mBuffer != nullptr) {
+ eglDestroyImageKHR(mDisplay, mEGLImage);
+ AHardwareBuffer_release(mBuffer);
+ }
+}
+
void AHardwareBufferGLTest::TearDown() {
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
for (int i = 0; i < 2; ++i) {
@@ -385,39 +448,27 @@
eglTerminate(mDisplay);
}
-class AHardwareBufferColorFormatTest
- : public AHardwareBufferGLTest,
- public ::testing::WithParamInterface<AHardwareBuffer_Desc> {};
+class AHardwareBufferColorFormatTest : public AHardwareBufferGLTest {};
// Verify that when allocating an AHardwareBuffer succeeds with GPU_COLOR_OUTPUT,
// it can be bound as a framebuffer attachment, glClear'ed and then read from
// another context using glReadPixels.
TEST_P(AHardwareBufferColorFormatTest, GpuColorOutputIsRenderable) {
- AHardwareBuffer* buffer = NULL;
AHardwareBuffer_Desc desc = GetParam();
desc.width = 100;
desc.height = 100;
desc.usage = AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
// This test does not make sense for layered buffers - don't bother testing them.
if (desc.layers > 1) return;
-
- int result = AHardwareBuffer_allocate(&desc, &buffer);
- // Skip if this format cannot be allocated.
- if (result != NO_ERROR) return;
-
- const EGLint attrib_list[] = { EGL_NONE };
- EGLImageKHR egl_image = eglCreateImageKHR(
- mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- eglGetNativeClientBufferANDROID(buffer), attrib_list);
- ASSERT_NE(EGL_NO_IMAGE_KHR, egl_image);
+ if (!SetUpBuffer(desc)) return;
// Bind the EGLImage to renderbuffers and framebuffers in both contexts.
GLuint renderbuffer[2], fbo[2];
for (int i = 0; i < 2; ++i) {
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[i]);
+ MakeCurrent(i);
glGenRenderbuffers(1, &renderbuffer[i]);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[i]);
- glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, static_cast<GLeglImageOES>(egl_image));
+ glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, static_cast<GLeglImageOES>(mEGLImage));
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
glGenFramebuffers(1, &fbo[i]);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
@@ -432,7 +483,7 @@
glFinish();
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[0]);
+ MakeCurrent(0);
std::vector<GoldenPixel> goldens{
{10, 90, kRed}, {40, 90, kRed}, {60, 90, kBlue}, {90, 90, kBlue},
{10, 60, kRed}, {40, 60, kRed}, {60, 60, kBlue}, {90, 60, kBlue},
@@ -443,43 +494,29 @@
// Clean up GL objects
for (int i = 0; i < 2; ++i) {
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[i]);
+ MakeCurrent(i);
glDeleteFramebuffers(1, &fbo[i]);
glDeleteRenderbuffers(1, &renderbuffer[i]);
}
- eglDestroyImageKHR(mDisplay, egl_image);
- AHardwareBuffer_release(buffer);
+ TearDownBuffer();
}
// Verify that when allocating an AHardwareBuffer succeeds with GPU_SAMPLED_IMAGE,
// it can be bound as a texture, set to a color with glTexSubImage2D and sampled
// from in a fragment shader.
TEST_P(AHardwareBufferColorFormatTest, GpuSampledImageCanBeSampled) {
- AHardwareBuffer* buffer = NULL;
AHardwareBuffer_Desc desc = GetParam();
desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
-
- // Skip texture arrays if the device doesn't support OpenGL ES 3.
- if (desc.layers > 1 && mGLVersion < 3) return;
- const GLenum textarget = desc.layers > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
-
- int result = AHardwareBuffer_allocate(&desc, &buffer);
- // Skip if this format cannot be allocated.
- if (result != NO_ERROR) return;
-
- const EGLint attrib_list[] = { EGL_NONE };
- EGLImageKHR egl_image = eglCreateImageKHR(
- mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- eglGetNativeClientBufferANDROID(buffer), attrib_list);
+ if (!SetUpBuffer(desc)) return;
// Bind the EGLImage to textures in both contexts.
GLuint texture[2];
for (int i = 0; i < 2; ++i) {
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[i]);
+ MakeCurrent(i);
glGenTextures(1, &texture[i]);
glActiveTexture(GL_TEXTURE1);
- glBindTexture(textarget, texture[i]);
- glEGLImageTargetTexture2DOES(textarget, static_cast<GLeglImageOES>(egl_image));
+ glBindTexture(mTexTarget, texture[i]);
+ glEGLImageTargetTexture2DOES(mTexTarget, static_cast<GLeglImageOES>(mEGLImage));
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
}
// In the second context, upload opaque red to the texture.
@@ -487,13 +524,13 @@
glFinish();
// In the first context, draw a quad that samples from the texture.
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[0]);
+ MakeCurrent(0);
GLuint fbo, renderbuffer;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, 40, 40);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 40, 40);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
ASSERT_EQ(GLenum{GL_FRAMEBUFFER_COMPLETE}, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glClearColor(0.f, 0.f, 0.f, 0.f);
@@ -502,7 +539,7 @@
// Compile the shader.
GLuint program = 0;
CompileProgram(desc.layers > 1 ? kVertexShaderEs3 : kVertexShader,
- desc.layers > 1 ? kArrayFragmentShaderEs3 : kFragmentShader,
+ desc.layers > 1 ? kArrayFragmentShaderEs3 : kTextureFragmentShader,
&program);
glUseProgram(program);
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
@@ -514,6 +551,7 @@
glVertexAttrib1f(a_depth_location, 0.f);
glEnableVertexAttribArray(a_position_location);
glUniform1i(glGetUniformLocation(program, "uTexture"), 1);
+ glUniform1f(glGetUniformLocation(program, "uScale"), 0.5f);
if (desc.layers > 1) {
glUniform1f(glGetUniformLocation(program, "uLayer"), static_cast<float>(desc.layers - 1));
}
@@ -535,41 +573,26 @@
glDeleteFramebuffers(1, &fbo);
glDeleteRenderbuffers(1, &renderbuffer);
glDeleteTextures(1, &texture[0]);
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[1]);
+ MakeCurrent(1);
glDeleteTextures(1, &texture[1]);
- eglDestroyImageKHR(mDisplay, egl_image);
- AHardwareBuffer_release(buffer);
+ TearDownBuffer();
}
// Verify that buffers which have both GPU_SAMPLED_IMAGE and GPU_COLOR_OUTPUT
// can be both rendered and sampled as a texture.
TEST_P(AHardwareBufferColorFormatTest, GpuColorOutputAndSampledImage) {
- AHardwareBuffer* buffer = NULL;
AHardwareBuffer_Desc desc = GetParam();
desc.usage = AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
-
- // Skip texture arrays if the device doesn't support OpenGL ES 3.
- if (desc.layers > 1 && mGLVersion < 3) return;
- const GLenum textarget = desc.layers > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
-
- int result = AHardwareBuffer_allocate(&desc, &buffer);
- // Skip if this format cannot be allocated.
- if (result != NO_ERROR) return;
-
- const EGLint attrib_list[] = { EGL_NONE };
- EGLImageKHR egl_image = eglCreateImageKHR(
- mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- eglGetNativeClientBufferANDROID(buffer), attrib_list);
- ASSERT_NE(EGL_NO_IMAGE_KHR, egl_image);
+ if (!SetUpBuffer(desc)) return;
// Bind the EGLImage to textures in both contexts.
GLuint texture[2];
for (int i = 0; i < 2; ++i) {
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[i]);
+ MakeCurrent(i);
glGenTextures(1, &texture[i]);
glActiveTexture(GL_TEXTURE1);
- glBindTexture(textarget, texture[i]);
- glEGLImageTargetTexture2DOES(textarget, static_cast<GLeglImageOES>(egl_image));
+ glBindTexture(mTexTarget, texture[i]);
+ glEGLImageTargetTexture2DOES(mTexTarget, static_cast<GLeglImageOES>(mEGLImage));
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
}
@@ -590,13 +613,13 @@
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
// In the first context, draw a quad that samples from the texture.
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[0]);
+ MakeCurrent(0);
GLuint fbo, renderbuffer;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, 40, 40);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 40, 40);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
ASSERT_EQ(GLenum{GL_FRAMEBUFFER_COMPLETE}, glCheckFramebufferStatus(GL_FRAMEBUFFER));
glClearColor(0.f, 0.f, 0.f, 0.f);
@@ -605,7 +628,7 @@
// Compile the shader.
GLuint program = 0;
CompileProgram(desc.layers > 1 ? kVertexShaderEs3 : kVertexShader,
- desc.layers > 1 ? kArrayFragmentShaderEs3 : kFragmentShader,
+ desc.layers > 1 ? kArrayFragmentShaderEs3 : kTextureFragmentShader,
&program);
glUseProgram(program);
EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
@@ -617,6 +640,7 @@
glVertexAttrib1f(a_depth_location, 0.f);
glEnableVertexAttribArray(a_position_location);
glUniform1i(glGetUniformLocation(program, "uTexture"), 1);
+ glUniform1f(glGetUniformLocation(program, "uScale"), 0.5f);
if (desc.layers > 1) {
glUniform1f(glGetUniformLocation(program, "uLayer"), static_cast<float>(desc.layers - 1));
}
@@ -641,11 +665,10 @@
glDeleteFramebuffers(1, &fbo);
glDeleteRenderbuffers(1, &renderbuffer);
glDeleteTextures(1, &texture[0]);
- eglMakeCurrent(mDisplay, mSurface, mSurface, mContext[1]);
+ MakeCurrent(1);
glDeleteFramebuffers(1, &texture_fbo);
glDeleteTextures(1, &texture[1]);
- eglDestroyImageKHR(mDisplay, egl_image);
- AHardwareBuffer_release(buffer);
+ TearDownBuffer();
}
INSTANTIATE_TEST_CASE_P(
@@ -671,4 +694,218 @@
AHardwareBuffer_Desc{20, 20, 4, AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT, 0, 0, 0, 0},
AHardwareBuffer_Desc{30, 20, 16, AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM, 0, 0, 0, 0}));
+
+class AHardwareBufferDepthFormatTest : public AHardwareBufferGLTest {
+public:
+ bool SetUpBuffer(const AHardwareBuffer_Desc& desc) override {
+ // ES 2.0 only supports GL_DEPTH_COMPONENT16 for depth renderbuffers.
+ if (desc.stride != 0 && mGLVersion < 3 && desc.format != GL_DEPTH_COMPONENT16) return false;
+ return AHardwareBufferGLTest::SetUpBuffer(desc);
+ }
+};
+
+// Verify that depth testing against a depth buffer rendered in another context
+// works correctly.
+TEST_P(AHardwareBufferDepthFormatTest, DepthAffectsDrawAcrossContexts) {
+ AHardwareBuffer_Desc desc = GetParam();
+ desc.width = 40;
+ desc.height = 40;
+ desc.usage = AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
+ // This test does not make sense for layered buffers - don't bother testing them.
+ if (desc.layers > 1) return;
+ if (!SetUpBuffer(desc)) return;
+
+ // Bind the EGLImage to renderbuffers and framebuffers in both contexts.
+ // The depth buffer is shared, but the color buffer is not.
+ GLuint fbo[2], color_rb[2], depth_rb[2];
+ for (int i = 0; i < 2; ++i) {
+ MakeCurrent(i);
+ glGenFramebuffers(1, &fbo[i]);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
+ glGenRenderbuffers(1, &color_rb[i]);
+ glBindRenderbuffer(GL_RENDERBUFFER, color_rb[i]);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 40, 40);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_rb[i]);
+ glGenRenderbuffers(1, &depth_rb[i]);
+ glBindRenderbuffer(GL_RENDERBUFFER, depth_rb[i]);
+ if (desc.stride == 0) {
+ glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, static_cast<GLeglImageOES>(mEGLImage));
+ } else {
+ glRenderbufferStorage(GL_RENDERBUFFER, desc.format, 40, 40);
+ }
+ EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_rb[i]);
+ ASSERT_EQ(GLenum{GL_FRAMEBUFFER_COMPLETE},
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ }
+
+ // In the second context, clear the depth buffer to a checkerboard pattern.
+ DrawCheckerboard(40, 40);
+ glFinish();
+ EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
+
+ // In the first context, clear the color buffer only, then draw a red pyramid.
+ MakeCurrent(0);
+ glClearColor(0.f, 0.f, 0.f, 0.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ GLuint program = 0;
+ CompileProgram(kVertexShader, kColorFragmentShader, &program);
+ glUseProgram(program);
+ EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
+
+ GLint a_position_location = glGetAttribLocation(program, "aPosition");
+ GLint a_depth_location = glGetAttribLocation(program, "aDepth");
+ glVertexAttribPointer(a_position_location, 2, GL_FLOAT, GL_TRUE, 3 * sizeof(float), kPyramidPositions);
+ glVertexAttribPointer(a_depth_location, 1, GL_FLOAT, GL_TRUE, 3 * sizeof(float), kPyramidPositions + 2);
+ glEnableVertexAttribArray(a_position_location);
+ glEnableVertexAttribArray(a_depth_location);
+ glUniform4f(glGetUniformLocation(program, "uColor"), 1.f, 0.f, 0.f, 1.f);
+ glUniform1f(glGetUniformLocation(program, "uScale"), 1.0f);
+ glViewport(0, 0, 40, 40);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDrawArrays(GL_TRIANGLES, 0, 12);
+ EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
+ glFinish();
+
+ // Check golden pixels.
+ std::vector<GoldenPixel> goldens{
+ {5, 35, kRed}, {15, 35, kRed}, {25, 35, kZero}, {35, 35, kZero},
+ {5, 25, kRed}, {15, 25, kZero}, {25, 25, kZero}, {35, 25, kZero},
+ {5, 15, kRed}, {15, 15, kRed}, {25, 15, kZero}, {35, 15, kRed},
+ {5, 5, kRed}, {15, 5, kRed}, {25, 5, kRed}, {35, 5, kRed},
+ };
+ CheckGoldenPixels(goldens, /*float_format=*/false, /*alpha_format=*/true);
+
+ // Tear down the GL objects.
+ glDeleteProgram(program);
+ for (int i = 0; i < 2; ++i) {
+ MakeCurrent(i);
+ glDeleteFramebuffers(1, &fbo[i]);
+ glDeleteRenderbuffers(1, &color_rb[i]);
+ glDeleteRenderbuffers(1, &depth_rb[i]);
+ }
+ TearDownBuffer();
+}
+
+// Verify that depth buffers with usage GPU_SAMPLED_IMAGE can be used as textures.
+TEST_P(AHardwareBufferDepthFormatTest, DepthCanBeSampled) {
+ AHardwareBuffer_Desc desc = GetParam();
+ desc.usage = AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ // ES 2.0 does not support depth textures. There is an extension OES_depth_texture, but it is
+ // incompatible with ES 3.x depth texture support.
+ if (mGLVersion < 3) return;
+ if (!SetUpBuffer(desc)) return;
+
+ // Bind the EGLImage to renderbuffers and framebuffers in both contexts.
+ // The depth buffer is shared, but the color buffer is not.
+ GLuint fbo[2], depth_texture[2], color_rb;
+ for (int i = 0; i < 2; ++i) {
+ MakeCurrent(i);
+ glGenFramebuffers(1, &fbo[i]);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
+ glGenTextures(1, &depth_texture[i]);
+ glActiveTexture(GL_TEXTURE3);
+ glBindTexture(mTexTarget, depth_texture[i]);
+ if (desc.stride == 0) {
+ glEGLImageTargetTexture2DOES(mTexTarget, static_cast<GLeglImageOES>(mEGLImage));
+ } else {
+ if (desc.layers > 1) {
+ glTexStorage3D(mTexTarget, 1, desc.format, desc.width, desc.height, desc.layers);
+ } else {
+ glTexStorage2D(mTexTarget, 1, desc.format, desc.width, desc.height);
+ }
+ }
+ glTexParameteri(mTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(mTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
+ }
+
+ // In the second context, attach the depth texture to the framebuffer and clear to 1.
+ if (desc.layers > 1) {
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_texture[1], 0, desc.layers - 1);
+ } else {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mTexTarget, depth_texture[1], 0);
+ }
+ ASSERT_EQ(GLenum{GL_FRAMEBUFFER_COMPLETE},
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ glClearDepthf(1.f);
+ glClear(GL_DEPTH_BUFFER_BIT);
+ glFinish();
+
+ // In the first context, draw a quad using the depth texture.
+ MakeCurrent(0);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
+ glGenRenderbuffers(1, &color_rb);
+ glBindRenderbuffer(GL_RENDERBUFFER, color_rb);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 40, 40);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_rb);
+ ASSERT_EQ(GLenum{GL_FRAMEBUFFER_COMPLETE},
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ glClearColor(0.f, 0.f, 0.f, 0.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ GLuint program = 0;
+ CompileProgram(desc.layers > 1 ? kVertexShaderEs3 : kVertexShader,
+ desc.layers > 1 ? kArrayFragmentShaderEs3 : kTextureFragmentShader,
+ &program);
+ glUseProgram(program);
+ EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
+
+ GLint a_position_location = glGetAttribLocation(program, "aPosition");
+ GLint a_depth_location = glGetAttribLocation(program, "aDepth");
+ glVertexAttribPointer(a_position_location, 2, GL_FLOAT, GL_TRUE, 0, kQuadPositions);
+ glVertexAttrib1f(a_depth_location, 0.f);
+ glEnableVertexAttribArray(a_position_location);
+ glUniform1i(glGetUniformLocation(program, "uTexture"), 3);
+ glUniform1f(glGetUniformLocation(program, "uScale"), 0.5f);
+ if (desc.layers > 1) {
+ glUniform1f(glGetUniformLocation(program, "uLayer"), static_cast<float>(desc.layers - 1));
+ }
+ glViewport(0, 0, 40, 40);
+ glDrawArrays(GL_TRIANGLES, 0, 12);
+ EXPECT_EQ(GLenum{GL_NO_ERROR}, glGetError());
+ glFinish();
+
+ // Check the rendered pixels. There should be a red square in the middle.
+ std::vector<GoldenPixel> goldens{
+ {5, 35, kZero}, {15, 35, kZero}, {25, 35, kZero}, {35, 35, kZero},
+ {5, 25, kZero}, {15, 25, kRed}, {25, 25, kRed}, {35, 25, kZero},
+ {5, 15, kZero}, {15, 15, kRed}, {25, 15, kRed}, {35, 15, kZero},
+ {5, 5, kZero}, {15, 5, kZero}, {25, 5, kZero}, {35, 5, kZero},
+ };
+ CheckGoldenPixels(goldens, /*float_format=*/false, /*alpha_format=*/true);
+
+ glDeleteRenderbuffers(1, &color_rb);
+ for (int i = 0; i < 2; ++i) {
+ MakeCurrent(i);
+ glDeleteTextures(1, &depth_texture[i]);
+ glDeleteFramebuffers(1, &fbo[i]);
+ }
+}
+
+// See comment in SetUpBuffer for explanation of nonzero stride and GL format.
+INSTANTIATE_TEST_CASE_P(
+ SingleLayer,
+ AHardwareBufferDepthFormatTest,
+ ::testing::Values(
+ AHardwareBuffer_Desc{16, 24, 1, GL_DEPTH_COMPONENT16, 0, 1, 0, 0},
+ AHardwareBuffer_Desc{16, 24, 1, AHARDWAREBUFFER_FORMAT_D16_UNORM, 0, 0, 0, 0},
+ AHardwareBuffer_Desc{44, 21, 1, AHARDWAREBUFFER_FORMAT_D24_UNORM, 0, 0, 0, 0},
+ AHardwareBuffer_Desc{57, 33, 1, AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT, 0, 0, 0, 0},
+ AHardwareBuffer_Desc{20, 10, 1, AHARDWAREBUFFER_FORMAT_D32_FLOAT, 0, 0, 0, 0},
+ AHardwareBuffer_Desc{57, 33, 1, AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT, 0, 0, 0, 0}));
+
+
+INSTANTIATE_TEST_CASE_P(
+ MultipleLayers,
+ AHardwareBufferDepthFormatTest,
+ ::testing::Values(
+ AHardwareBuffer_Desc{16, 24, 6, GL_DEPTH_COMPONENT16, 0, 1, 0, 0},
+ AHardwareBuffer_Desc{16, 24, 6, AHARDWAREBUFFER_FORMAT_D16_UNORM, 0, 0, 0, 0},
+ AHardwareBuffer_Desc{44, 21, 4, AHARDWAREBUFFER_FORMAT_D24_UNORM, 0, 0, 0, 0},
+ AHardwareBuffer_Desc{57, 33, 7, AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT, 0, 0, 0, 0},
+ AHardwareBuffer_Desc{20, 10, 5, AHARDWAREBUFFER_FORMAT_D32_FLOAT, 0, 0, 0, 0},
+ AHardwareBuffer_Desc{57, 33, 3, AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT, 0, 0, 0, 0}));
+
} // namespace android
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
index f48b766..324ab9e 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
@@ -320,4 +320,35 @@
AHardwareBuffer_release(buffer);
}
+TEST(AHardwareBufferTest, ProtectedContentAndCpuReadIncompatible) {
+ AHardwareBuffer* buffer = NULL;
+ AHardwareBuffer_Desc desc = {};
+ desc.width = 120;
+ desc.width = 240;
+ desc.layers = 1;
+ desc.usage = AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
+ desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+
+ // Allocation of a CPU-readable buffer should succeed...
+ int err = AHardwareBuffer_allocate(&desc, &buffer);
+ EXPECT_EQ(NO_ERROR, err);
+ AHardwareBuffer_release(buffer);
+ buffer = nullptr;
+
+ // ...but not if it's a protected buffer.
+ desc.usage =
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+ AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
+ err = AHardwareBuffer_allocate(&desc, &buffer);
+ EXPECT_NE(NO_ERROR, err);
+
+ desc.usage =
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
+ AHARDWAREBUFFER_USAGE_CPU_READ_RARELY |
+ AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
+ err = AHardwareBuffer_allocate(&desc, &buffer);
+ EXPECT_NE(NO_ERROR, err);
+}
+
} // namespace android
diff --git a/tests/tests/nativehardware/src/android/hardware/cts/HardwareBufferVrTest.java b/tests/tests/nativehardware/src/android/hardware/cts/HardwareBufferVrTest.java
index 922fc91..daf0a82 100644
--- a/tests/tests/nativehardware/src/android/hardware/cts/HardwareBufferVrTest.java
+++ b/tests/tests/nativehardware/src/android/hardware/cts/HardwareBufferVrTest.java
@@ -29,22 +29,27 @@
if (!pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
return;
}
-
final long flags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT;
- HardwareBuffer buffer = HardwareBuffer.create(
- 2, 4, HardwareBuffer.RGBA_8888, 2, flags);
- assertEquals(2, buffer.getWidth());
- assertEquals(4, buffer.getHeight());
- assertEquals(2, buffer.getLayers());
- assertEquals(HardwareBuffer.RGBA_8888, buffer.getFormat());
- assertEquals(flags, buffer.getUsage());
+ final int formats[] = {
+ HardwareBuffer.RGB_565,
+ HardwareBuffer.RGBA_8888,
+ HardwareBuffer.RGBA_1010102,
+ HardwareBuffer.RGBA_FP16,
+ };
+ for (final int format : formats) {
+ HardwareBuffer buffer = HardwareBuffer.create(2, 4, format, 2, flags);
+ assertEquals(2, buffer.getWidth());
+ assertEquals(4, buffer.getHeight());
+ assertEquals(2, buffer.getLayers());
+ assertEquals(format, buffer.getFormat());
+ assertEquals(flags, buffer.getUsage());
- buffer = HardwareBuffer.create(
- 4, 12, HardwareBuffer.RGB_565, 3, flags);
- assertEquals(4, buffer.getWidth());
- assertEquals(12, buffer.getHeight());
- assertEquals(3, buffer.getLayers());
- assertEquals(HardwareBuffer.RGB_565, buffer.getFormat());
- assertEquals(flags, buffer.getUsage());
+ buffer = HardwareBuffer.create(345, 231, format, 5, flags);
+ assertEquals(345, buffer.getWidth());
+ assertEquals(231, buffer.getHeight());
+ assertEquals(5, buffer.getLayers());
+ assertEquals(format, buffer.getFormat());
+ assertEquals(flags, buffer.getUsage());
+ }
}
}
diff --git a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
index c862c77..2a5ef31 100644
--- a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
@@ -30,6 +30,35 @@
.hasCapability(NET_CAPABILITY_MMS));
}
+ public void testUnwantedCapabilities() {
+ assertTrue(new NetworkRequest.Builder()
+ .addUnwantedCapability(NET_CAPABILITY_MMS)
+ .build()
+ .hasUnwantedCapability(NET_CAPABILITY_MMS));
+ assertFalse(new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_MMS)
+ .build()
+ .hasCapability(NET_CAPABILITY_MMS));
+ }
+
+ public void testCapabilityMutualExclusivity() {
+ NetworkRequest.Builder reqBuilder = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_MMS);
+
+ assertTrue(reqBuilder.build().hasCapability(NET_CAPABILITY_MMS));
+ assertFalse(reqBuilder.build().hasUnwantedCapability(NET_CAPABILITY_MMS));
+
+ // Move capability to unwanted list
+ reqBuilder.addUnwantedCapability(NET_CAPABILITY_MMS);
+ assertFalse(reqBuilder.build().hasCapability(NET_CAPABILITY_MMS));
+ assertTrue(reqBuilder.build().hasUnwantedCapability(NET_CAPABILITY_MMS));
+
+ // Move it back to the list of capabilities
+ reqBuilder.addCapability(NET_CAPABILITY_MMS);
+ assertTrue(reqBuilder.build().hasCapability(NET_CAPABILITY_MMS));
+ assertFalse(reqBuilder.build().hasUnwantedCapability(NET_CAPABILITY_MMS));
+ }
+
public void testTransports() {
assertTrue(new NetworkRequest.Builder().addTransportType(TRANSPORT_BLUETOOTH).build()
.hasTransport(TRANSPORT_BLUETOOTH));
diff --git a/tests/tests/secure_element/Android.mk b/tests/tests/secure_element/Android.mk
new file mode 100644
index 0000000..5c7187e
--- /dev/null
+++ b/tests/tests/secure_element/Android.mk
@@ -0,0 +1,16 @@
+# 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.
+
+include $(call all-subdir-makefiles)
+
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
new file mode 100644
index 0000000..8e16c21
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
@@ -0,0 +1,38 @@
+# 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 := CtsSecureElementAccessControlTestCases1
+
+# 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 := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/AndroidManifest.xml b/tests/tests/secure_element/access_control/AccessControlApp1/AndroidManifest.xml
new file mode 100644
index 0000000..87a7aa5
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/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.omapi.accesscontrol1.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for Open Mobile API"
+ android:targetPackage="android.omapi.accesscontrol1.cts">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/AndroidTest.xml b/tests/tests/secure_element/access_control/AccessControlApp1/AndroidTest.xml
new file mode 100644
index 0000000..26d0f39
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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 OMAPI 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.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="CtsSecureElementAccessControlTestCases1.apk"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.omapi.accesscontrol1.cts"/>
+ <option name="runtime-hint" value="10m10s"/>
+ </test>
+</configuration>
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java
new file mode 100644
index 0000000..3727c60
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ */
+
+/* Contributed by Orange */
+
+package android.omapi.accesscontrol1.cts;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
+
+import android.os.RemoteException;
+import android.se.omapi.Channel;
+import android.se.omapi.Reader;
+import android.se.omapi.SEService;
+import android.se.omapi.SEService.OnConnectedListener;
+import android.se.omapi.Session;
+import android.test.AndroidTestCase;
+
+public class AccessControlTest extends AndroidTestCase {
+ private final static String UICC_READER_PREFIX = "SIM";
+ private final static String ESE_READER_PREFIX = "eSE";
+ private final static String SD_READER_PREFIX = "SD";
+
+ private final static byte[] AID_40 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x40 };
+ private final static byte[] AID_41 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x41 };
+ private final static byte[] AID_42 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x42 };
+ private final static byte[] AID_43 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x43 };
+ private final static byte[] AID_44 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x44 };
+ private final static byte[] AID_45 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x45 };
+ private final static byte[] AID_46 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x46 };
+ private final static byte[] AID_47 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x47 };
+ private final static byte[] AID_48 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x48 };
+ private final static byte[] AID_49 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x49 };
+ private final static byte[] AID_4A = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4A };
+ private final static byte[] AID_4B = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4B };
+ private final static byte[] AID_4C = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4C };
+ private final static byte[] AID_4D = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4D };
+ private final static byte[] AID_4E = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4E };
+ private final static byte[] AID_4F = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4F };
+
+ private final static byte[][] AUTHORIZED_AID = new byte[][] { AID_40,
+ AID_41, AID_42, AID_44, AID_45, AID_47, AID_48, AID_49, AID_4A,
+ AID_4B, AID_4C, AID_4D, AID_4E, AID_4F };
+ private final static byte[][] UNAUTHORIZED_AID = new byte[][] { AID_43,
+ AID_46 };
+
+ /* Authorized APDU for AID_40 */
+ private final static byte[][] AUTHORIZED_APDU_AID_40 = new byte[][] {
+ { 0x00, 0x06, 0x00, 0x00 }, { (byte) 0xA0, 0x06, 0x00, 0x00 },};
+ /* Unauthorized APDU for AID_40 */
+ private final static byte[][] UNAUTHORIZED_APDU_AID_40 = new byte[][] {
+ { 0x00, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x80, 0x06, 0x00, 0x00 },
+ { (byte) 0xA0, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x94, 0x06, 0x00, 0x00, 0x00 }, };
+
+ /* Authorized APDU for AID_41 */
+ private final static byte[][] AUTHORIZED_APDU_AID_41 = new byte[][] {
+ { (byte) 0x94, 0x06, 0x00, 0x00 },
+ { (byte) 0x94, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x94, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0x94, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA } };
+ /* Unauthorized APDU for AID_41 */
+ private final static byte[][] UNAUTHORIZED_APDU_AID_41 = new byte[][] {
+ { 0x00, 0x06, 0x00, 0x00 }, { (byte) 0x80, 0x06, 0x00, 0x00 },
+ { (byte) 0xA0, 0x06, 0x00, 0x00 },
+ { 0x00, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x00, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0x80, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0xA0, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0x80, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0xA0, 0x08, 0x00, 0x00, 0x00 },
+ { 0x00, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0x80, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0xA0, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ };
+
+ private final long SERVICE_CONNECTION_TIME_OUT = 3000;
+ private SEService seService;
+ private Object serviceMutex = new Object();
+ private Timer connectionTimer;
+ private ServiceConnectionTimerTask mTimerTask = new ServiceConnectionTimerTask();
+ private boolean connected = false;
+
+ private final OnConnectedListener mListener = new OnConnectedListener() {
+ public void onConnected() {
+ synchronized (serviceMutex) {
+ connected = true;
+ serviceMutex.notify();
+ }
+ }
+ };
+
+ class SynchronousExecutor implements Executor {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ seService = new SEService(getContext(), new SynchronousExecutor(), mListener);
+ connectionTimer = new Timer();
+ connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (seService != null && seService.isConnected()) {
+ seService.shutdown();
+ connected = false;
+ }
+ }
+
+ private void waitForConnection() throws TimeoutException {
+ synchronized (serviceMutex) {
+ if (!connected) {
+ try {
+ serviceMutex.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (!connected) {
+ throw new TimeoutException(
+ "Service could not be connected after "
+ + SERVICE_CONNECTION_TIME_OUT + " ms");
+ }
+ if (connectionTimer != null) {
+ connectionTimer.cancel();
+ }
+ }
+ }
+
+ public void testAuthorizedAID() {
+ for (byte[] aid : AUTHORIZED_AID) {
+ testSelectableAid(aid);
+ }
+ }
+
+ public void testUnauthorizedAID() {
+ for (byte[] aid : UNAUTHORIZED_AID) {
+ testUnauthorisedAid(aid);
+ }
+ }
+
+ public void testAuthorizedAPDUAID40() {
+ for (byte[] apdu : AUTHORIZED_APDU_AID_40) {
+ testTransmitAPDU(AID_40, apdu);
+ }
+ }
+
+ public void testUnauthorisedAPDUAID40() {
+ for (byte[] apdu : UNAUTHORIZED_APDU_AID_40) {
+ testUnauthorisedAPDU(AID_40, apdu);
+ }
+ }
+
+ public void testAuthorizedAPDUAID41() {
+ for (byte[] apdu : AUTHORIZED_APDU_AID_41) {
+ testTransmitAPDU(AID_41, apdu);
+ }
+ }
+
+ public void testUnauthorisedAPDUAID41() {
+ for (byte[] apdu : UNAUTHORIZED_APDU_AID_41) {
+ testUnauthorisedAPDU(AID_41, apdu);
+ }
+ }
+
+ private void testSelectableAid(byte[] aid) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte) 0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ } finally{
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+ }
+
+ private void testUnauthorisedAid(byte[] aid) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte) 0x00);
+ fail("SecurityException Expected ");
+ }
+ } catch(SecurityException ex){ }
+ catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+
+ private void testTransmitAPDU(byte[] aid, byte[] apdu) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte) 0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ byte[] apduResponse = channel.transmit(apdu);
+ assertNotNull("Null Channel", apduResponse);
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+
+ private void testUnauthorisedAPDU(byte[] aid, byte[] apdu) {
+ Session session = null;
+ Channel channel = null;
+ boolean exceptionOnTransmit = false;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte) 0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ exceptionOnTransmit = true;
+ channel.transmit(apdu);
+ fail("Security Exception is expected");
+ }
+ } catch (SecurityException ex) {
+ if (!exceptionOnTransmit) {
+ fail("Unexpected SecurityException onSelect" + ex);
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ } finally {
+ if(channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+ }
+
+ /**
+ * Verifies TLV data
+ *
+ * @param tlv
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ private static boolean verifyBerTlvData(byte[] tlv) {
+ if (tlv == null || tlv.length == 0) {
+ throw new RuntimeException("Invalid tlv, null");
+ }
+
+ int i = 0;
+ byte[] key = new byte[2];
+ key[0] = tlv[i];
+ if ((key[0] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ key[1] = tlv[i = i + 1];
+ }
+
+ int len = tlv[i = i + 1] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength - 1; j >= 0; j--) {
+ len += (tlv[i = i + 1] & 0xFF) * Math.pow(10, j);
+ }
+ }
+ return tlv.length == (i + len + 3);
+ }
+
+ class ServiceConnectionTimerTask extends TimerTask {
+ @Override
+ public void run() {
+ synchronized (serviceMutex) {
+ serviceMutex.notifyAll();
+ }
+ }
+ }
+}
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
new file mode 100644
index 0000000..2ef5b3a
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
@@ -0,0 +1,38 @@
+# 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 := CtsSecureElementAccessControlTestCases2
+
+# 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 := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/AndroidManifest.xml b/tests/tests/secure_element/access_control/AccessControlApp2/AndroidManifest.xml
new file mode 100644
index 0000000..e92a577
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/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.omapi.accesscontrol2.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for Open Mobile API"
+ android:targetPackage="android.omapi.accesscontrol2.cts">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/AndroidTest.xml b/tests/tests/secure_element/access_control/AccessControlApp2/AndroidTest.xml
new file mode 100644
index 0000000..50eec10
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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 OMAPI 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.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="CtsSecureElementAccessControlTestCases2.apk"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.omapi.accesscontrol2.cts"/>
+ <option name="runtime-hint" value="10m10s"/>
+ </test>
+</configuration>
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java
new file mode 100644
index 0000000..8b51205
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java
@@ -0,0 +1,375 @@
+/*
+ * 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.
+ */
+
+/* Contributed by Orange */
+
+package android.omapi.accesscontrol2.cts;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
+
+import android.os.RemoteException;
+import android.se.omapi.Channel;
+import android.se.omapi.Reader;
+import android.se.omapi.SEService;
+import android.se.omapi.SEService.OnConnectedListener;
+import android.se.omapi.Session;
+import android.test.AndroidTestCase;
+
+public class AccessControlTest extends AndroidTestCase {
+ private final static String UICC_READER_PREFIX = "SIM";
+ private final static String ESE_READER_PREFIX = "eSE";
+ private final static String SD_READER_PREFIX = "SD";
+
+ private final static byte[] AID_40 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x40 };
+ private final static byte[] AID_41 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x41 };
+ private final static byte[] AID_42 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x42 };
+ private final static byte[] AID_43 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x43 };
+ private final static byte[] AID_44 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x44 };
+ private final static byte[] AID_45 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x45 };
+ private final static byte[] AID_46 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x46 };
+ private final static byte[] AID_47 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x47 };
+ private final static byte[] AID_48 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x48 };
+ private final static byte[] AID_49 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x49 };
+ private final static byte[] AID_4A = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4A };
+ private final static byte[] AID_4B = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4B };
+ private final static byte[] AID_4C = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4C };
+ private final static byte[] AID_4D = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4D };
+ private final static byte[] AID_4E = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4E };
+ private final static byte[] AID_4F = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4F };
+
+ private final static byte[][] AUTHORIZED_AID = new byte[][] { AID_40,
+ AID_41, AID_43, AID_45, AID_46 };
+ private final static byte[][] UNAUTHORIZED_AID = new byte[][] { AID_42,
+ AID_44, AID_47, AID_48, AID_49, AID_4A, AID_4B, AID_4C, AID_4D, AID_4E, AID_4F};
+
+ /* Authorized APDU for AID_40 */
+ private final static byte[][] AUTHORIZED_APDU_AID_40 = new byte[][] {
+ { 0x00, 0x06, 0x00, 0x00 }, { (byte) 0xA0, 0x06, 0x00, 0x00 },};
+ /* Unauthorized APDU for AID_40 */
+ private final static byte[][] UNAUTHORIZED_APDU_AID_40 = new byte[][] {
+ { 0x00, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x80, 0x06, 0x00, 0x00 },
+ { (byte) 0xA0, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x94, 0x06, 0x00, 0x00, 0x00 }, };
+
+ /* Authorized APDU for AID_41 */
+ private final static byte[][] AUTHORIZED_APDU_AID_41 = new byte[][] {
+ { (byte) 0x94, 0x06, 0x00, 0x00 },
+ { (byte) 0x94, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x94, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0x94, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA } };
+ /* Unauthorized APDU for AID_41 */
+ private final static byte[][] UNAUTHORIZED_APDU_AID_41 = new byte[][] {
+ { 0x00, 0x06, 0x00, 0x00 }, { (byte) 0x80, 0x06, 0x00, 0x00 },
+ { (byte) 0xA0, 0x06, 0x00, 0x00 },
+ { 0x00, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x00, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0x80, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0xA0, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0x80, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0xA0, 0x08, 0x00, 0x00, 0x00 },
+ { 0x00, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0x80, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0xA0, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ };
+
+ private final long SERVICE_CONNECTION_TIME_OUT = 3000;
+ private SEService seService;
+ private Object serviceMutex = new Object();
+ private Timer connectionTimer;
+ private ServiceConnectionTimerTask mTimerTask = new ServiceConnectionTimerTask();
+ private boolean connected = false;
+
+ private final OnConnectedListener mListener = new OnConnectedListener() {
+ public void onConnected() {
+ synchronized (serviceMutex) {
+ connected = true;
+ serviceMutex.notify();
+ }
+ }
+ };
+
+ class SynchronousExecutor implements Executor {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ seService = new SEService(getContext(), new SynchronousExecutor(), mListener);
+ connectionTimer = new Timer();
+ connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (seService != null && seService.isConnected()) {
+ seService.shutdown();
+ connected = false;
+ }
+ }
+
+ private void waitForConnection() throws TimeoutException {
+ synchronized (serviceMutex) {
+ if (!connected) {
+ try {
+ serviceMutex.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (!connected) {
+ throw new TimeoutException(
+ "Service could not be connected after "
+ + SERVICE_CONNECTION_TIME_OUT + " ms");
+ }
+ if (connectionTimer != null) {
+ connectionTimer.cancel();
+ }
+ }
+ }
+
+ public void testAuthorizedAID() {
+ for (byte[] aid : AUTHORIZED_AID) {
+ testSelectableAid(aid);
+ }
+ }
+
+ public void testUnauthorizedAID() {
+ for (byte[] aid : UNAUTHORIZED_AID) {
+ testUnauthorisedAid(aid);
+ }
+ }
+
+ public void testAuthorizedAPDUAID40() {
+ for (byte[] apdu : AUTHORIZED_APDU_AID_40) {
+ testTransmitAPDU(AID_40, apdu);
+ }
+ }
+
+ public void testUnauthorisedAPDUAID40() {
+ for (byte[] apdu : UNAUTHORIZED_APDU_AID_40) {
+ testUnauthorisedAPDU(AID_40, apdu);
+ }
+ }
+
+ public void testAuthorizedAPDUAID41() {
+ for (byte[] apdu : AUTHORIZED_APDU_AID_41) {
+ testTransmitAPDU(AID_41, apdu);
+ }
+ }
+
+ public void testUnauthorisedAPDUAID41() {
+ for (byte[] apdu : UNAUTHORIZED_APDU_AID_41) {
+ testUnauthorisedAPDU(AID_41, apdu);
+ }
+ }
+
+ private void testSelectableAid(byte[] aid) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte) 0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ } finally{
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+ }
+
+ private void testUnauthorisedAid(byte[] aid) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte)0x00);
+ fail("SecurityException Expected ");
+ }
+ } catch(SecurityException ex){ }
+ catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+
+ private void testTransmitAPDU(byte[] aid, byte[] apdu) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte)0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ byte[] apduResponse = channel.transmit(apdu);
+ assertNotNull("Null Channel", apduResponse);
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+
+ private void testUnauthorisedAPDU(byte[] aid, byte[] apdu) {
+ Session session = null;
+ Channel channel = null;
+ boolean exceptionOnTransmit = false;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte) 0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ exceptionOnTransmit = true;
+ channel.transmit(apdu);
+ fail("Security Exception is expected");
+ }
+ } catch (SecurityException ex) {
+ if (!exceptionOnTransmit) {
+ fail("Unexpected SecurityException onSelect" + ex);
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ } finally {
+ if(channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+ }
+
+ /**
+ * Verifies TLV data
+ *
+ * @param tlv
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ private static boolean verifyBerTlvData(byte[] tlv) {
+ if (tlv == null || tlv.length == 0) {
+ throw new RuntimeException("Invalid tlv, null");
+ }
+
+ int i = 0;
+ byte[] key = new byte[2];
+ key[0] = tlv[i];
+ if ((key[0] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ key[1] = tlv[i = i + 1];
+ }
+
+ int len = tlv[i = i + 1] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength - 1; j >= 0; j--) {
+ len += (tlv[i = i + 1] & 0xFF) * Math.pow(10, j);
+ }
+ }
+ return tlv.length == (i + len + 3);
+ }
+
+ class ServiceConnectionTimerTask extends TimerTask {
+ @Override
+ public void run() {
+ synchronized (serviceMutex) {
+ serviceMutex.notifyAll();
+ }
+ }
+ }
+}
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
new file mode 100644
index 0000000..e52f2f9
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
@@ -0,0 +1,38 @@
+# 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 := CtsSecureElementAccessControlTestCases3
+
+# 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 := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/AndroidManifest.xml b/tests/tests/secure_element/access_control/AccessControlApp3/AndroidManifest.xml
new file mode 100644
index 0000000..dd6375c
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/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.omapi.accesscontrol3.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for Open Mobile API"
+ android:targetPackage="android.omapi.accesscontrol3.cts">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/AndroidTest.xml b/tests/tests/secure_element/access_control/AccessControlApp3/AndroidTest.xml
new file mode 100644
index 0000000..6dcac02
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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 OMAPI 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.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="CtsSecureElementAccessControlTestCases3.apk"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.omapi.accesscontrol3.cts"/>
+ <option name="runtime-hint" value="10m10s"/>
+ </test>
+</configuration>
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java
new file mode 100644
index 0000000..5ca0c51
--- /dev/null
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java
@@ -0,0 +1,379 @@
+/*
+ * 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.
+ */
+
+/* Contributed by Orange */
+
+package android.omapi.accesscontrol3.cts;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
+
+import android.os.RemoteException;
+import android.se.omapi.Channel;
+import android.se.omapi.Reader;
+import android.se.omapi.SEService;
+import android.se.omapi.SEService.OnConnectedListener;
+import android.se.omapi.Session;
+import android.test.AndroidTestCase;
+
+public class AccessControlTest extends AndroidTestCase {
+ private final static String UICC_READER_PREFIX = "SIM";
+ private final static String ESE_READER_PREFIX = "eSE";
+ private final static String SD_READER_PREFIX = "SD";
+
+ private final static byte[] AID_40 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x40 };
+ private final static byte[] AID_41 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x41 };
+ private final static byte[] AID_42 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x42 };
+ private final static byte[] AID_43 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x43 };
+ private final static byte[] AID_44 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x44 };
+ private final static byte[] AID_45 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x45 };
+ private final static byte[] AID_46 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x46 };
+ private final static byte[] AID_47 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x47 };
+ private final static byte[] AID_48 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x48 };
+ private final static byte[] AID_49 = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, 0x49 };
+ private final static byte[] AID_4A = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4A };
+ private final static byte[] AID_4B = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4B };
+ private final static byte[] AID_4C = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4C };
+ private final static byte[] AID_4D = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4D };
+ private final static byte[] AID_4E = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4E };
+ private final static byte[] AID_4F = new byte[] { (byte) 0xA0, 0x00, 0x00,
+ 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54,
+ 0x53, (byte) 0x4F };
+
+ private final static byte[][] AUTHORIZED_AID = new byte[][] { AID_40,
+ AID_41, AID_45, AID_46 };
+ private final static byte[][] UNAUTHORIZED_AID = new byte[][] { AID_42,
+ AID_43, AID_44, AID_47, AID_48, AID_49, AID_4A, AID_4B, AID_4C, AID_4D, AID_4E,
+ AID_4F };
+
+ /* Authorized APDU for AID_40 */
+ private final static byte[][] AUTHORIZED_APDU_AID_40 = new byte[][] {
+ { 0x00, 0x06, 0x00, 0x00 }, { (byte) 0x80, 0x06, 0x00, 0x00 },
+ { (byte) 0xA0, 0x06, 0x00, 0x00 },
+ { (byte) 0x94, 0x06, 0x00, 0x00 },
+ { 0x00, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0x80, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0xA0, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0x94, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { 0x00, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x80, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0xA0, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x94, 0x08, 0x00, 0x00, 0x00 },
+ { 0x00, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0x80, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0xA0, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0x94, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 }};
+
+ /* Authorized APDU for AID_41 */
+ private final static byte[][] AUTHORIZED_APDU_AID_41 = new byte[][] {
+ { (byte) 0x94, 0x06, 0x00, 0x00 },
+ { (byte) 0x94, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x94, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0x94, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA } };
+ /* Unauthorized APDU for AID_41 */
+ private final static byte[][] UNAUTHORIZED_APDU_AID_41 = new byte[][] {
+ { 0x00, 0x06, 0x00, 0x00 }, { (byte) 0x80, 0x06, 0x00, 0x00 },
+ { (byte) 0xA0, 0x06, 0x00, 0x00 },
+ { 0x00, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0x00, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0x80, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0xA0, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA },
+ { (byte) 0x80, 0x08, 0x00, 0x00, 0x00 },
+ { (byte) 0xA0, 0x08, 0x00, 0x00, 0x00 },
+ { 0x00, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0x80, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ { (byte) 0xA0, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00 },
+ };
+
+ private final long SERVICE_CONNECTION_TIME_OUT = 3000;
+ private SEService seService;
+ private Object serviceMutex = new Object();
+ private Timer connectionTimer;
+ private ServiceConnectionTimerTask mTimerTask = new ServiceConnectionTimerTask();
+ private boolean connected = false;
+
+ private final OnConnectedListener mListener = new OnConnectedListener() {
+ @Override
+ public void onConnected() {
+ synchronized (serviceMutex) {
+ connected = true;
+ serviceMutex.notify();
+ }
+ }
+ };
+
+ class SynchronousExecutor implements Executor {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ seService = new SEService(getContext(), new SynchronousExecutor(), mListener);
+ connectionTimer = new Timer();
+ connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (seService != null && seService.isConnected()) {
+ seService.shutdown();
+ connected = false;
+ }
+ }
+
+ private void waitForConnection() throws TimeoutException {
+ synchronized (serviceMutex) {
+ if (!connected) {
+ try {
+ serviceMutex.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (!connected) {
+ throw new TimeoutException(
+ "Service could not be connected after "
+ + SERVICE_CONNECTION_TIME_OUT + " ms");
+ }
+ if (connectionTimer != null) {
+ connectionTimer.cancel();
+ }
+ }
+ }
+
+ public void testAuthorizedAID() {
+ for (byte[] aid : AUTHORIZED_AID) {
+ testSelectableAid(aid);
+ }
+ }
+
+ public void testUnauthorizedAID() {
+ for (byte[] aid : UNAUTHORIZED_AID) {
+ testUnauthorisedAid(aid);
+ }
+ }
+
+ public void testAuthorizedAPDUAID40() {
+ for (byte[] apdu : AUTHORIZED_APDU_AID_40) {
+ testTransmitAPDU(AID_40, apdu);
+ }
+ }
+
+ public void testAuthorizedAPDUAID41() {
+ for (byte[] apdu : AUTHORIZED_APDU_AID_41) {
+ testTransmitAPDU(AID_41, apdu);
+ }
+ }
+
+ public void testUnauthorisedAPDUAID41() {
+ for (byte[] apdu : UNAUTHORIZED_APDU_AID_41) {
+ testUnauthorisedAPDU(AID_41, apdu);
+ }
+ }
+
+ private void testSelectableAid(byte[] aid) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte)0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ } finally{
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+ }
+
+ private void testUnauthorisedAid(byte[] aid) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte)0x00);
+ fail("SecurityException Expected ");
+ }
+ } catch(SecurityException ex){ }
+ catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+
+ private void testTransmitAPDU(byte[] aid, byte[] apdu) {
+ Session session = null;
+ Channel channel = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte)0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ byte[] apduResponse = channel.transmit(apdu);
+ assertNotNull("Null Channel", apduResponse);
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ if (channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+
+ private void testUnauthorisedAPDU(byte[] aid, byte[] apdu) {
+ Session session = null;
+ Channel channel = null;
+ boolean exceptionOnTransmit = false;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("Null Session", session);
+ channel = session.openLogicalChannel(aid, (byte)0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ exceptionOnTransmit = true;
+ channel.transmit(apdu);
+ fail("Security Exception is expected");
+ }
+ } catch (SecurityException ex) {
+ if (!exceptionOnTransmit) {
+ fail("Unexpected SecurityException onSelect" + ex);
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ } finally {
+ if(channel != null)
+ channel.close();
+ if (session != null)
+ session.close();
+ }
+ }
+
+ /**
+ * Verifies TLV data
+ *
+ * @param tlv
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ private static boolean verifyBerTlvData(byte[] tlv) {
+ if (tlv == null || tlv.length == 0) {
+ throw new RuntimeException("Invalid tlv, null");
+ }
+
+ int i = 0;
+ byte[] key = new byte[2];
+ key[0] = tlv[i];
+ if ((key[0] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ key[1] = tlv[i = i + 1];
+ }
+
+ int len = tlv[i = i + 1] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength - 1; j >= 0; j--) {
+ len += (tlv[i = i + 1] & 0xFF) * Math.pow(10, j);
+ }
+ }
+ return tlv.length == (i + len + 3);
+ }
+
+ class ServiceConnectionTimerTask extends TimerTask {
+ @Override
+ public void run() {
+ synchronized (serviceMutex) {
+ serviceMutex.notifyAll();
+ }
+ }
+ }
+}
diff --git a/tests/tests/secure_element/access_control/Android.mk b/tests/tests/secure_element/access_control/Android.mk
new file mode 100644
index 0000000..5c7187e
--- /dev/null
+++ b/tests/tests/secure_element/access_control/Android.mk
@@ -0,0 +1,16 @@
+# 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.
+
+include $(call all-subdir-makefiles)
+
diff --git a/tests/tests/secure_element/omapi/Android.mk b/tests/tests/secure_element/omapi/Android.mk
new file mode 100644
index 0000000..f12aa75
--- /dev/null
+++ b/tests/tests/secure_element/omapi/Android.mk
@@ -0,0 +1,38 @@
+# 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 := CtsOmapiTestCases
+
+# 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 := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
+LOCAL_JAVA_LIBRARIES += android.test.base
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/secure_element/omapi/AndroidManifest.xml b/tests/tests/secure_element/omapi/AndroidManifest.xml
new file mode 100644
index 0000000..ce5ccc9
--- /dev/null
+++ b/tests/tests/secure_element/omapi/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.omapi.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for Open Mobile API"
+ android:targetPackage="android.omapi.cts">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/secure_element/omapi/AndroidTest.xml b/tests/tests/secure_element/omapi/AndroidTest.xml
new file mode 100644
index 0000000..faeb9c3
--- /dev/null
+++ b/tests/tests/secure_element/omapi/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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 OMAPI 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.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="CtsOmapiTestCases.apk"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.omapi.cts"/>
+ <option name="runtime-hint" value="10m10s"/>
+ </test>
+</configuration>
diff --git a/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java b/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
new file mode 100644
index 0000000..93eefa3
--- /dev/null
+++ b/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
@@ -0,0 +1,564 @@
+/*
+ * 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.
+ */
+
+/* Contributed by Orange */
+
+package android.omapi.cts;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
+
+import android.se.omapi.Channel;
+import android.se.omapi.Reader;
+import android.se.omapi.SEService;
+import android.se.omapi.SEService.OnConnectedListener;
+import android.se.omapi.Session;
+import android.test.AndroidTestCase;
+
+public class OmapiTest extends AndroidTestCase {
+
+ private final static String UICC_READER_PREFIX = "SIM";
+ private final static String ESE_READER_PREFIX = "eSE";
+ private final static String SD_READER_PREFIX = "SD";
+ private final static byte[] SELECTABLE_AID =
+ new byte[]{(byte) 0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31};
+ private final static byte[] LONG_SELECT_RESPONSE_AID =
+ new byte[]{(byte) 0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x32};
+ private final static byte[] NON_SELECTABLE_AID =
+ new byte[]{(byte) 0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, (byte) 0xFF};
+
+ /* MANAGE open/close and SELECT AID */
+ private final static byte[][] ILLEGAL_COMMANDS_TRANSMIT = new byte[][]{{0x00, 0x70, 0x00, 0x00},
+ {0x00, 0x70, (byte) 0x80, 0x00},
+ {0x00, (byte) 0xA4, 0x04, 0x04, 0x10, 0x4A, 0x53,
+ 0x52, 0x31, 0x37, 0x37, 0x54, 0x65, 0x73,
+ 0x74, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x30}
+ };
+
+ /* OMAPI APDU Test case 1 and 3 */
+ private final static byte[][] NO_DATA_APDU = new byte[][]{{0x00, 0x06, 0x00, 0x00},
+ {(byte) 0x80, 0x06, 0x00, 0x00},
+ {(byte) 0xA0, 0x06, 0x00, 0x00},
+ {(byte) 0x94, 0x06, 0x00, 0x00},
+ {0x00, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA},
+ {(byte) 0x80, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA},
+ {(byte) 0xA0, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA},
+ {(byte) 0x94, 0x0A, 0x00, 0x00, 0x01, (byte) 0xAA}
+ };
+ /* OMAPI APDU Test case 2 and 4 */
+ private final static byte[][] DATA_APDU = new byte[][]{{0x00, 0x08, 0x00, 0x00, 0x00},
+ {(byte) 0x80, 0x08, 0x00, 0x00, 0x00},
+ {(byte) 0xA0, 0x08, 0x00, 0x00, 0x00},
+ {(byte) 0x94, 0x08, 0x00, 0x00, 0x00},
+ {0x00, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00},
+ {(byte) 0x80, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00},
+ {(byte) 0xA0, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00},
+ {(byte) 0x94, (byte) 0xC0, 0x00, 0x00, 0x01, (byte) 0xAA, 0x00}
+ };
+
+ private final static byte[] CHECK_SELECT_P2_APDU = new byte[]{0x00, (byte) 0xF4, 0x00, 0x00};
+
+ /* OMAPI APDU Test case 1 and 3 */
+ private final static byte[][] SW_62xx_NO_DATA_APDU =
+ new byte[][]{{0x00, (byte) 0xF3, 0x00, 0x06},
+ {0x00, (byte) 0xF3, 0x00, 0x0A, 0x01, (byte) 0xAA}
+ };
+ /* OMAPI APDU Test case 2 and 4 */
+ private final static byte[] SW_62xx_DATA_APDU = new byte[]{0x00, (byte) 0xF3, 0x00, 0x08, 0x00};
+ private final static byte[] SW_62xx_VALIDATE_DATA_APDU =
+ new byte[]{0x00, (byte) 0xF3, 0x00, 0x0C, 0x01, (byte) 0xAA, 0x00};
+ private final static byte[][] SW_62xx =
+ new byte[][]{{0x62, 0x00}, {0x62, (byte) 0x81}, {0x62, (byte) 0x82},
+ {0x62, (byte) 0x83},
+ {0x62, (byte) 0x85}, {0x62, (byte) 0xF1}, {0x62, (byte) 0xF2},
+ {0x63, (byte) 0xF1},
+ {0x63, (byte) 0xF2}, {0x63, (byte) 0xC2}, {0x62, 0x02}, {0x62, (byte) 0x80},
+ {0x62, (byte) 0x84}, {0x62, (byte) 0x86}, {0x63, 0x00}, {0x63, (byte) 0x81}
+ };
+ private final static byte[][] SEGMENTED_RESP_APDU = new byte[][]{
+ //Get response Case2 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, (byte) 0xC2, 0x08, 0x00, 0x00},
+ //Get response Case4 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, (byte) 0xC4, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00},
+ //Get response Case2 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, (byte) 0xC6, 0x08, 0x00, 0x00},
+ //Get response Case4 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, (byte) 0xC8, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00},
+ //Test device buffer capacity 7FFF data
+ {0x00, (byte) 0xC2, (byte) 0x7F, (byte) 0xFF, 0x00},
+ //Get response 6CFF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, (byte) 0xCF, 0x08, 0x00, 0x00},
+ //Get response with another CLA with answer length (P1P2) of 0x0800, 2048 bytes
+ {(byte) 0x94, (byte) 0xC2, 0x08, 0x00, 0x00}
+ };
+ private final long SERVICE_CONNECTION_TIME_OUT = 3000;
+ private SEService seService;
+ private Object serviceMutex = new Object();
+ private Timer connectionTimer;
+ private ServiceConnectionTimerTask mTimerTask = new ServiceConnectionTimerTask();
+ private boolean connected = false;
+ private final OnConnectedListener mListener = new OnConnectedListener() {
+ @Override
+ public void onConnected() {
+ synchronized (serviceMutex) {
+ connected = true;
+ serviceMutex.notify();
+ }
+ }
+ };
+
+ class SynchronousExecutor implements Executor {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }
+
+ private void assertGreaterOrEqual(long greater, long lesser) {
+ assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
+ greater >= lesser);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ seService = new SEService(getContext(), new SynchronousExecutor(), mListener);
+ connectionTimer = new Timer();
+ connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (seService != null && seService.isConnected()) {
+ seService.shutdown();
+ connected = false;
+ }
+ }
+
+ private void waitForConnection() throws TimeoutException {
+ synchronized (serviceMutex) {
+ if (!connected) {
+ try {
+ serviceMutex.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (!connected) {
+ throw new TimeoutException(
+ "Service could not be connected after " + SERVICE_CONNECTION_TIME_OUT
+ + " ms");
+ }
+ if (connectionTimer != null) {
+ connectionTimer.cancel();
+ }
+ }
+ }
+
+ /** Tests getReaders API */
+ public void testGetReaders() {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ String name = reader.getName();
+ if (!(name.startsWith(UICC_READER_PREFIX) || name.startsWith(ESE_READER_PREFIX)
+ || name.startsWith(SD_READER_PREFIX))) {
+ fail("Incorrect Reader name");
+ }
+ assertNotNull("getseService returned null", reader.getSEService());
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ }
+
+ /** Tests getATR API */
+ public void testATR() {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+ ArrayList<Reader> uiccReaders = new ArrayList<Reader>();
+ if (readers != null && readers.length > 0) {
+ for (int i = 0; i < readers.length; i++) {
+ if (readers[i].getName().startsWith(UICC_READER_PREFIX)) {
+ uiccReaders.add(readers[i]);
+ }
+ }
+
+ for (Reader reader : uiccReaders) {
+ Session session = null;
+ try {
+ session = reader.openSession();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ assertNotNull("Could not open session", session);
+ byte[] atr = session.getATR();
+ session.close();
+ assertNotNull("ATR is Null", atr);
+ }
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ }
+
+ /** Tests OpenBasicChannel API when aid is null */
+ public void testOpenBasicChannelNullAid() {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ Session session = reader.openSession();
+ assertNotNull("Could not open session", session);
+ Channel channel = session.openBasicChannel(null, (byte)0x00);
+ if (reader.getName().startsWith(UICC_READER_PREFIX)) {
+ assertNull("Basic channel on UICC can be opened", channel);
+ } else {
+ assertNotNull("Basic Channel cannot be opened", channel);
+ }
+ if (channel != null) {
+ channel.close();
+ }
+ session.close();
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ }
+
+ /** Tests OpenBasicChannel API when aid is provided */
+ public void testOpenBasicChannelNonNullAid() {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ Session session = reader.openSession();
+ assertNotNull("Could not open session", session);
+ Channel channel = session.openBasicChannel(SELECTABLE_AID, (byte)0x00);
+ if (reader.getName().startsWith(UICC_READER_PREFIX)) {
+ assertNull("Basic channel on UICC can be opened", channel);
+ } else {
+ assertNotNull("Basic Channel cannot be opened", channel);
+ }
+ if (channel != null) {
+ channel.close();
+ }
+ session.close();
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ }
+
+ /** Tests Select API */
+ public void testSelectableAid() {
+ testSelectableAid(SELECTABLE_AID);
+ }
+
+ public void testLongSelectResponse() {
+ byte[] selectResponse = testSelectableAid(LONG_SELECT_RESPONSE_AID);
+ assertTrue("Select Response is not complete", verifyBerTlvData(selectResponse));
+ }
+
+
+ private byte[] testSelectableAid(byte[] aid) {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ Session session = reader.openSession();
+ assertNotNull("Null Session", session);
+ Channel channel = session.openLogicalChannel(aid, (byte)0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertGreaterOrEqual(selectResponse.length, 2);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ channel.close();
+ session.close();
+ return selectResponse;
+ }
+ } catch (Exception e) {
+ fail("Unexpected Exception " + e);
+ }
+ return null;
+ }
+
+ /** Tests if NoSuchElementException in Select */
+ public void testWrongAid() {
+ testNonSelectableAid(NON_SELECTABLE_AID);
+ }
+
+ public void testNonSelectableAid(byte[] aid) {
+ boolean exception = false;
+ Session session = null;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("null session", session);
+ Channel channel = session.openLogicalChannel(aid, (byte)0x00);
+ }
+ } catch (NoSuchElementException e) {
+ exception = true;
+ if (session != null) {
+ session.close();
+ }
+ } catch (Exception e) {
+ fail("unexpected exception " + e);
+ }
+ assertTrue(exception);
+ }
+
+ /** Tests if Security Exception in Transmit */
+ public void testSecurityExceptionInTransmit() {
+ boolean exception = false;
+ Session session;
+ Channel channel;
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ assertTrue(reader.isSecureElementPresent());
+ session = reader.openSession();
+ assertNotNull("null session", session);
+ channel = session.openLogicalChannel(SELECTABLE_AID, (byte)0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertGreaterOrEqual(selectResponse.length, 2);
+ assertEquals(selectResponse[selectResponse.length - 1] & 0xFF, 0x00);
+ assertEquals(selectResponse[selectResponse.length - 2] & 0xFF, 0x90);
+ for (byte[] cmd : ILLEGAL_COMMANDS_TRANSMIT) {
+ try {
+ exception = false;
+ byte[] response = channel.transmit(cmd);
+ } catch (SecurityException e) {
+ exception = true;
+ }
+ assertTrue(exception);
+ }
+ channel.close();
+ session.close();
+ }
+ } catch (Exception e) {
+ fail("unexpected exception " + e);
+ }
+ }
+
+ private byte[] internalTransmitApdu(Reader reader, byte[] apdu) {
+ try {
+ assertTrue(reader.isSecureElementPresent());
+ Session session = reader.openSession();
+ assertNotNull("null session", session);
+ Channel channel = session.openLogicalChannel(SELECTABLE_AID, (byte)0x00);
+ assertNotNull("Null Channel", channel);
+ byte[] selectResponse = channel.getSelectResponse();
+ assertNotNull("Null Select Response", selectResponse);
+ assertGreaterOrEqual(selectResponse.length, 2);
+ byte[] transmitResponse = channel.transmit(apdu);
+ channel.close();
+ session.close();
+ return transmitResponse;
+ } catch (Exception e) {
+ fail("unexpected exception " + e);
+ }
+ return null;
+ }
+
+ /**
+ * Tests Transmit API for all readers.
+ *
+ * Checks the return status and verifies the size of the
+ * response.
+ */
+ public void testTransmitApdu() {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ for (byte[] apdu : NO_DATA_APDU) {
+ byte[] response = internalTransmitApdu(reader, apdu);
+ assertEquals(response.length, 2);
+ assertEquals(response[response.length - 1] & 0xFF, 0x00);
+ assertEquals(response[response.length - 2] & 0xFF, 0x90);
+ }
+
+ for (byte[] apdu : DATA_APDU) {
+ byte[] response = internalTransmitApdu(reader, apdu);
+ /* 256 byte data and 2 bytes of status word */
+ assertEquals(response.length, 258);
+ assertEquals(response[response.length - 1] & 0xFF, 0x00);
+ assertEquals(response[response.length - 2] & 0xFF, 0x90);
+ }
+ }
+ } catch (Exception e) {
+ fail("unexpected exception " + e);
+ }
+ }
+
+ /**
+ * Tests if underlying implementations returns the correct Status Word
+ *
+ * TO verify that :
+ * - the device does not modify the APDU sent to the Secure Element
+ * - the warning code is properly received by the application layer as SW answer
+ * - the verify that the application layer can fetch the additionnal data (when present)
+ */
+ public void testStatusWordTransmit() {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ for (byte[] apdu : SW_62xx_NO_DATA_APDU) {
+ for (byte i = 0x00; i < SW_62xx.length; i++) {
+ apdu[2] = (byte)(i+1);
+ byte[] response = internalTransmitApdu(reader, apdu);
+ byte[] SW = SW_62xx[i];
+ assertEquals(response[response.length - 1], SW[1]);
+ assertEquals(response[response.length - 2], SW[0]);
+ }
+ }
+
+ for (byte i = 0x00; i < SW_62xx.length; i++) {
+ byte[] apdu = SW_62xx_DATA_APDU;
+ apdu[2] = (byte)(i+1);
+ byte[] response = internalTransmitApdu(reader, apdu);
+ byte[] SW = SW_62xx[i];
+ assertGreaterOrEqual(response.length, 3);
+ assertEquals(response[response.length - 1], SW[1]);
+ assertEquals(response[response.length - 2], SW[0]);
+ }
+
+ for (byte i = 0x00; i < SW_62xx.length; i++) {
+ byte[] apdu = SW_62xx_VALIDATE_DATA_APDU;
+ apdu[2] = (byte)(i+1);
+ byte[] response = internalTransmitApdu(reader, apdu);
+ assertGreaterOrEqual(response.length, apdu.length + 2);
+ byte[] responseSubstring = Arrays.copyOfRange(response, 0, apdu.length);
+ apdu[0] = 0x01;
+ assertTrue(Arrays.equals(responseSubstring, apdu));
+ byte[] SW = SW_62xx[i];
+ assertEquals(response[response.length - 1], SW[1]);
+ assertEquals(response[response.length - 2], SW[0]);
+ }
+ }
+ } catch (Exception e) {
+ fail("unexpected exception " + e);
+ }
+ }
+
+ /** Test if the responses are segmented by the underlying implementation */
+ public void testSegmentedResponseTransmit() {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ for (byte[] apdu : SEGMENTED_RESP_APDU) {
+ byte[] response = internalTransmitApdu(reader, apdu);
+ byte[] b = { 0x00, 0x00, apdu[2], apdu[3] };
+ ByteBuffer wrapped = ByteBuffer.wrap(b);
+ int expectedLength = wrapped.getInt();
+ assertEquals(response.length, expectedLength + 2);
+ assertEquals(response[response.length - 1] & 0xFF, 0x00);
+ assertEquals(response[response.length - 2] & 0xFF, 0x90);
+ assertEquals(response[response.length - 3] & 0xFF, 0xFF);
+ }
+ }
+ } catch (Exception e) {
+ fail("unexpected exception " + e);
+ }
+ }
+
+ /** Test the P2 value of the select command sent by the underlying implementation */
+ public void testP2Value() {
+ try {
+ waitForConnection();
+ Reader[] readers = seService.getReaders();
+
+ for (Reader reader : readers) {
+ byte[] response = internalTransmitApdu(reader, CHECK_SELECT_P2_APDU);
+ assertGreaterOrEqual(response.length, 3);
+ assertEquals(response[response.length - 1] & 0xFF, 0x00);
+ assertEquals(response[response.length - 2] & 0xFF, 0x90);
+ assertEquals(response[response.length - 3] & 0xFF, 0x00);
+ }
+ } catch (Exception e) {
+ fail("unexpected exception " + e);
+ }
+ }
+
+ /**
+ * Verifies TLV data
+ * @param tlv
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ private static boolean verifyBerTlvData(byte[] tlv){
+ if (tlv == null || tlv.length == 0) {
+ throw new RuntimeException("Invalid tlv, null");
+ }
+ int i = 0;
+ if ((tlv[i++] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ i++;
+ }
+
+ int len = tlv[i++] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len-128;
+ len = 0;
+ for(int j = bytesLength; j > 0; j--) {
+ len += (len << 8) + (tlv[i++] & 0xFF);
+ }
+ }
+ // Additional 2 bytes for the SW
+ return (tlv.length == (i+len+2));
+ }
+
+ class ServiceConnectionTimerTask extends TimerTask {
+ @Override
+ public void run() {
+ synchronized (serviceMutex) {
+ serviceMutex.notifyAll();
+ }
+ }
+ }
+}
diff --git a/tests/tests/security/res/raw/bug_33751193_avc.mp4 b/tests/tests/security/res/raw/bug_33751193_avc.mp4
new file mode 100644
index 0000000..a621fab
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_33751193_avc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_73172046.mp4 b/tests/tests/security/res/raw/bug_73172046.mp4
new file mode 100644
index 0000000..4de44ba
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_73172046.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_13276.mp4 b/tests/tests/security/res/raw/cve_2017_13276.mp4
new file mode 100644
index 0000000..6cd86d3
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_13276.mp4
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 4104e32..506f2a1 100755
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -27,6 +27,8 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.SurfaceTexture;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
@@ -377,6 +379,12 @@
doStagefrightTest(R.raw.bug_34097672);
}
+
+ @SecurityTest
+ public void testStagefright_bug_33751193() throws Exception {
+ doStagefrightTestRawBlob(R.raw.bug_33751193_avc, "video/avc", 320, 240);
+ }
+
@SecurityTest
public void testStagefright_bug_33818508() throws Exception {
doStagefrightTest(R.raw.bug_33818508);
@@ -579,6 +587,11 @@
***********************************************************/
@SecurityTest
+ public void testStagefright_cve_2017_13276() throws Exception {
+ doStagefrightTest(R.raw.cve_2017_13276);
+ }
+
+ @SecurityTest
public void testStagefright_cve_2016_6764() throws Exception {
doStagefrightTest(R.raw.cve_2016_6764);
}
@@ -608,6 +621,15 @@
doStagefrightTest(R.raw.bug_37093318, (4 * 60 * 1000));
}
+ @SecurityTest
+ public void testStagefright_bug_73172046() throws Exception {
+ doStagefrightTest(R.raw.bug_73172046);
+
+ Bitmap bitmap = BitmapFactory.decodeResource(
+ getInstrumentation().getContext().getResources(), R.raw.bug_73172046);
+ bitmap.recycle();
+ }
+
private void doStagefrightTest(final int rid) throws Exception {
doStagefrightTestMediaPlayer(rid);
doStagefrightTestMediaCodec(rid);
@@ -649,7 +671,7 @@
in2.close();
Log.i(TAG, "checked server");
}
-
+
private void doStagefrightTest(final int rid, int timeout) throws Exception {
runWithTimeout(new Runnable() {
@Override
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerNegativeTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerNegativeTest.java
index 0fca1fa..bae8b0f 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerNegativeTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerNegativeTest.java
@@ -57,7 +57,7 @@
private static void callMethodExpectingSecurityException(Object instance, String name,
String expectedMessage, Object... args)
- throws NoSuchMethodException, IllegalAccessException {
+ throws IllegalAccessException {
Method m = null;
for (Method method : instance.getClass().getDeclaredMethods()) {
@@ -90,7 +90,7 @@
try {
callMethodExpectingSecurityException(readField(manager, "mService"), method,
expectedMessage, args);
- } catch (NoSuchFieldException|NoSuchMethodException e) {
+ } catch (NoSuchFieldException | LinkageError e) {
if (DISALLOW_REFLECTION_ERROR) {
throw new RuntimeException(e);
} else {
diff --git a/tests/tests/telecom/src/android/telecom/cts/PhoneAccountOperationsTest.java b/tests/tests/telecom/src/android/telecom/cts/PhoneAccountOperationsTest.java
index c9b5000..6d080b2 100644
--- a/tests/tests/telecom/src/android/telecom/cts/PhoneAccountOperationsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/PhoneAccountOperationsTest.java
@@ -171,8 +171,11 @@
setIsEnabled = PhoneAccount.Builder.class.getDeclaredMethod(
"setIsEnabled", boolean.class);
} catch (NoSuchMethodException e) {
- fail("Failed to find setIsEnabled method.");
+ // This is the ideal case; ideally we should NOT be able to even reflect this method
+ // since its hidden.
+ return;
}
+ // However, if reflection somehow finds the @hide method, we'll try executing it.
setIsEnabled.invoke(phoneAccountBuilder, true);
final PhoneAccount phoneAccount = phoneAccountBuilder.build();
mTelecomManager.registerPhoneAccount(phoneAccount);
diff --git a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
index 2b67f95..20f971f 100644
--- a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
@@ -20,6 +20,7 @@
import android.os.Bundle;
import android.telecom.CallAudioState;
import android.telecom.Connection;
+import android.telecom.ConnectionService;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -39,7 +40,8 @@
public class SelfManagedConnectionServiceTest extends BaseTelecomTestWithMockServices {
private Uri TEST_ADDRESS_1 = Uri.fromParts("sip", "call1@test.com", null);
- private Uri TEST_ADDRESS_2 = Uri.fromParts("sip", "call2@test.com", null);
+ private Uri TEST_ADDRESS_2 = Uri.fromParts("tel", "650-555-1212", null);
+ private Uri TEST_ADDRESS_3 = Uri.fromParts("tel", "650-555-1213", null);
@Override
protected void setUp() throws Exception {
@@ -100,17 +102,26 @@
PhoneAccount registeredAccount = mTelecomManager.getPhoneAccount(
TestUtils.TEST_SELF_MANAGED_HANDLE_1);
+ assertPhoneAccountRegistered(TestUtils.TEST_SELF_MANAGED_HANDLE_2);
+ assertPhoneAccountEnabled(TestUtils.TEST_SELF_MANAGED_HANDLE_2);
+ PhoneAccount registeredAccount2 = mTelecomManager.getPhoneAccount(
+ TestUtils.TEST_SELF_MANAGED_HANDLE_2);
+
// It should exist and be the same as the previously registered one.
assertNotNull(registeredAccount);
+ assertNotNull(registeredAccount2);
// We cannot just check for equality of the PhoneAccount since the one we registered is not
// enabled, and the one we get back after registration is.
assertPhoneAccountEquals(TestUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_1, registeredAccount);
+ assertPhoneAccountEquals(TestUtils.TEST_SELF_MANAGED_PHONE_ACCOUNT_2, registeredAccount2);
// An important assumption is that self-managed PhoneAccounts are automatically
// enabled by default.
assertTrue("Self-managed PhoneAccounts must be enabled by default.",
registeredAccount.isEnabled());
+ assertTrue("Self-managed PhoneAccounts must be enabled by default.",
+ registeredAccount2.isEnabled());
}
/**
@@ -198,15 +209,20 @@
return;
}
- TestUtils.addIncomingCall(getInstrumentation(), mTelecomManager,
- TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_1);
+ addAndVerifyIncomingCall(TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_1);
+ addAndVerifyIncomingCall(TestUtils.TEST_SELF_MANAGED_HANDLE_2, TEST_ADDRESS_3);
+ }
+
+ private void addAndVerifyIncomingCall(PhoneAccountHandle handle, Uri address)
+ throws Exception {
+ TestUtils.addIncomingCall(getInstrumentation(), mTelecomManager, handle, address);
// Ensure Telecom bound to the self managed CS
if (!CtsSelfManagedConnectionService.waitForBinding()) {
fail("Could not bind to Self-Managed ConnectionService");
}
- SelfManagedConnection connection = TestUtils.waitForAndGetConnection(TEST_ADDRESS_1);
+ SelfManagedConnection connection = TestUtils.waitForAndGetConnection(address);
// Expect callback indicating that UI should be shown.
connection.getOnShowIncomingUiInvokeCounter().waitForCount(1);
@@ -222,7 +238,7 @@
* Tests ensures that Telecom disallow to place outgoing self-managed call when the ongoing
* managed call can not be held.
*/
- public void testDisallowOutgoingCallWhileOngoingManagedCallCanNotBeHeld() {
+ public void testDisallowOutgoingCallWhileOngoingManagedCallCanNotBeHeld() throws Exception {
if (!mShouldTestTelecom) {
return;
}
@@ -240,34 +256,47 @@
TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_1);
// THEN the new outgoing call is failed.
+ CtsSelfManagedConnectionService.waitForBinding();
assertTrue(CtsSelfManagedConnectionService.getConnectionService().waitForUpdate(
CtsSelfManagedConnectionService.CREATE_OUTGOING_CONNECTION_FAILED_LOCK));
}
/**
* Tests ability to add a new self-managed outgoing connection.
+ * <p>
+ * A self-managed {@link ConnectionService} shall be able to place an outgoing call to tel or
+ * sip {@link Uri}s without being interrupted by system UX or other Telephony-related logic.
*/
public void testAddSelfManagedOutgoingConnection() throws Exception {
if (!mShouldTestTelecom) {
return;
}
- TestUtils.placeOutgoingCall(getInstrumentation(), mTelecomManager,
- TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_1);
+ placeAndVerifyOutgoingCall(TestUtils.TEST_SELF_MANAGED_HANDLE_1, TEST_ADDRESS_1);
+ }
+
+ private void placeAndVerifyOutgoingCall(PhoneAccountHandle handle, Uri address) throws Exception {
+
+ TestUtils.placeOutgoingCall(getInstrumentation(), mTelecomManager, handle, address);
// Ensure Telecom bound to the self managed CS
if (!CtsSelfManagedConnectionService.waitForBinding()) {
fail("Could not bind to Self-Managed ConnectionService");
}
- SelfManagedConnection connection = TestUtils.waitForAndGetConnection(TEST_ADDRESS_1);
- assert(!connection.isIncomingCall());
+ SelfManagedConnection connection = TestUtils.waitForAndGetConnection(address);
+ assertNotNull("Self-Managed Connection should NOT be null.", connection);
+ assertTrue("Self-Managed Connection should be outgoing.", !connection.isIncomingCall());
+ // The self-managed ConnectionService must NOT have been prompted to show its incoming call
+ // UI for an outgoing call.
assertEquals(connection.getOnShowIncomingUiInvokeCounter().getInvokeCount(), 0);
setActiveAndVerify(connection);
// Expect there to be no managed calls at the moment.
assertFalse(mTelecomManager.isInManagedCall());
+ // But there should be a call (including self-managed).
+ assertTrue(mTelecomManager.isInCall());
setDisconnectedAndVerify(connection);
}
diff --git a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
index 572b31a..7323f30 100644
--- a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
@@ -78,6 +78,7 @@
config.getBoolean(CarrierConfigManager.KEY_VVM_PREFETCH_BOOL), true);
assertEquals("KEY_CARRIER_VVM_PACKAGE_NAME_STRING doesn't match static default.",
config.getString(CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING), "");
+ assertFalse(CarrierConfigManager.isConfigForIdentifiedCarrier(config));
}
}
diff --git a/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
index 8a13349..7745497 100644
--- a/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.os.Looper;
+import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
@@ -28,6 +29,8 @@
import com.android.compatibility.common.util.TestThread;
+import java.util.List;
+
public class PhoneStateListenerTest extends AndroidTestCase{
public static final long WAIT_TIME = 1000;
@@ -38,7 +41,9 @@
private boolean mOnUserMobileDataStateChanged;
private boolean mOnDataActivityCalled;
private boolean mOnDataConnectionStateChangedCalled;
+ private boolean mOnDataConnectionStateChangedWithNetworkTypeCalled;
private boolean mOnMessageWaitingIndicatorChangedCalled;
+ private boolean mOnCellInfoChangedCalled;
private boolean mOnServiceStateChangedCalled;
private boolean mOnSignalStrengthChangedCalled;
private SignalStrength mSignalStrength;
@@ -371,6 +376,13 @@
mLock.notify();
}
}
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ synchronized(mLock) {
+ mOnDataConnectionStateChangedWithNetworkTypeCalled = true;
+ mLock.notify();
+ }
+ }
};
mTelephonyManager.listen(
mListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
@@ -380,15 +392,18 @@
});
assertFalse(mOnDataConnectionStateChangedCalled);
+ assertFalse(mOnDataConnectionStateChangedWithNetworkTypeCalled);
t.start();
synchronized (mLock) {
- while(!mOnDataConnectionStateChangedCalled){
+ while(!mOnDataConnectionStateChangedCalled ||
+ !mOnDataConnectionStateChangedWithNetworkTypeCalled){
mLock.wait();
}
}
t.checkException();
assertTrue(mOnDataConnectionStateChangedCalled);
+ assertTrue(mOnDataConnectionStateChangedWithNetworkTypeCalled);
}
public void testOnDataActivity() throws Throwable {
@@ -428,6 +443,43 @@
assertTrue(mOnDataActivityCalled);
}
+ public void testOnCellInfoChanged() throws Throwable {
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+ return;
+ }
+
+ TestThread t = new TestThread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+
+ mListener = new PhoneStateListener() {
+ @Override
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ synchronized(mLock) {
+ mOnCellInfoChangedCalled = true;
+ mLock.notify();
+ }
+ }
+ };
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_CELL_INFO);
+
+ Looper.loop();
+ }
+ });
+
+ assertFalse(mOnDataActivityCalled);
+ t.start();
+
+ synchronized (mLock) {
+ while(!mOnCellInfoChangedCalled){
+ mLock.wait();
+ }
+ }
+ t.checkException();
+ assertTrue(mOnCellInfoChangedCalled);
+ }
+
public void testOnUserMobileDataStateChanged() throws Throwable {
if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
diff --git a/tests/vr/jni/VrExtensionsJni.cpp b/tests/vr/jni/VrExtensionsJni.cpp
index c93ca62..4ab4e2a 100644
--- a/tests/vr/jni/VrExtensionsJni.cpp
+++ b/tests/vr/jni/VrExtensionsJni.cpp
@@ -177,6 +177,8 @@
const int formats[] = {
AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
+ AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
// Do not test AHARDWAREBUFFER_FORMAT_BLOB, it isn't color-renderable.
};
const int samples[] = {1, 2, 4};
diff --git a/tools/cts-tradefed/res/config/retry.xml b/tools/cts-tradefed/res/config/retry.xml
index d425d8e..3a67455 100644
--- a/tools/cts-tradefed/res/config/retry.xml
+++ b/tools/cts-tradefed/res/config/retry.xml
@@ -37,7 +37,6 @@
<option name="plan" value="cts-retry" />
<option name="test-tag" value="cts" />
- <option name="skip-device-info" value="true" />
<option name="enable-root" value="false" />
<!-- retain 200MB of host log -->