Merge "Update TypefaceTest to reflect API changes." into oc-dev
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index b1c8177..9e98b79 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -19,11 +19,26 @@
import subprocess
import time
import sys
-import textwrap
+
+import its.caps
import its.device
from its.device import ItsSession
CHART_DELAY = 1 # seconds
+FACING_EXTERNAL = 2
+SKIP_RET_CODE = 101 # note this must be same as tests/scene*/test_*
+
+
+def skip_sensor_fusion():
+ """Determine if sensor fusion test is skipped for this camera."""
+
+ skip_code = SKIP_RET_CODE
+ with ItsSession() as cam:
+ props = cam.get_camera_properties()
+ if (its.caps.sensor_fusion(props) and its.caps.manual_sensor(props) and
+ props['android.lens.facing'] is not FACING_EXTERNAL):
+ skip_code = None
+ return skip_code
def main():
@@ -45,61 +60,58 @@
all android devices.
"""
- SKIP_RET_CODE = 101
-
# Not yet mandated tests
NOT_YET_MANDATED = {
- "scene0":[
+ "scene0": [
"test_jitter"
- ],
- "scene1":[
+ ],
+ "scene1": [
"test_ae_af",
"test_ae_precapture_trigger",
"test_crop_region_raw",
"test_ev_compensation_advanced",
"test_ev_compensation_basic",
"test_yuv_plus_jpeg"
- ],
- "scene2":[],
- "scene3":[
+ ],
+ "scene2": [],
+ "scene3": [
"test_lens_movement_reporting",
"test_lens_position"
- ],
- "scene4":[],
- "scene5":[],
- "sensor_fusion":[]
+ ],
+ "scene4": [],
+ "scene5": [],
+ "sensor_fusion": []
}
- all_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5"]
+ all_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5",
+ "sensor_fusion"]
auto_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4"]
scene_req = {
- "scene0" : None,
- "scene1" : "A grey card covering at least the middle 30% of the scene",
- "scene2" : "A picture containing human faces",
- "scene3" : "The ISO 12233 chart",
- "scene4" : "A specific test page of a circle covering at least the "
- "middle 50% of the scene. See CameraITS.pdf section 2.3.4 "
- "for more details",
- "scene5" : "Capture images with a diffuser attached to the camera. See "
- "CameraITS.pdf section 2.3.4 for more details",
- "sensor_fusion" : "Rotating checkboard pattern. See "
- "sensor_fusion/SensorFusion.pdf for detailed "
- "instructions. Note that this test will be skipped "
- "on devices not supporting REALTIME camera timestamp."
- "If that is the case, no scene setup is required and "
- "you can just answer Y when being asked if the scene "
- "is okay"
+ "scene0": None,
+ "scene1": "A grey card covering at least the middle 30% of the scene",
+ "scene2": "A picture containing human faces",
+ "scene3": "The ISO 12233 chart",
+ "scene4": "A specific test page of a circle covering at least the "
+ "middle 50% of the scene. See CameraITS.pdf section 2.3.4 "
+ "for more details",
+ "scene5": "Capture images with a diffuser attached to the camera. See "
+ "CameraITS.pdf section 2.3.4 for more details",
+ "sensor_fusion": "Rotating checkboard pattern. See "
+ "sensor_fusion/SensorFusion.pdf for detailed "
+ "instructions.\nNote that this test will be skipped "
+ "on devices not supporting REALTIME camera timestamp."
}
scene_extra_args = {
- "scene5" : ["doAF=False"]
+ "scene5": ["doAF=False"]
}
camera_ids = []
scenes = []
chart_host_id = None
result_device_id = None
+ rot_rig_id = None
for s in sys.argv[1:]:
if s[:7] == "camera=" and len(s) > 7:
@@ -110,6 +122,9 @@
chart_host_id = s[6:]
elif s[:7] == 'result=' and len(s) > 7:
result_device_id = s[7:]
+ elif s[:8] == 'rot_rig=' and len(s) > 8:
+ rot_rig_id = s[8:] # valid values: 'default' or '$VID:$PID:$CH'
+ # The default '$VID:$PID:$CH' is '04d8:fc73:1'
auto_scene_switch = chart_host_id is not None
merge_result_switch = result_device_id is not None
@@ -128,7 +143,6 @@
else:
try:
# Try replace "X" to "sceneX"
- scene_num = int(s)
scene_str = "scene" + s
if scene_str not in possible_scenes:
valid_scenes = False
@@ -140,7 +154,7 @@
if not valid_scenes:
print "Unknown scene specifiied:", s
- assert(False)
+ assert False
scenes = temp_scenes
# Initialize test results
@@ -157,7 +171,7 @@
device_id_arg = "device=" + device_id
print "Testing device " + device_id
- #Sanity Check for devices
+ # Sanity Check for devices
device_bfp = its.device.get_device_fingerprint(device_id)
assert device_bfp is not None
@@ -167,19 +181,19 @@
if merge_result_switch:
result_device_bfp = its.device.get_device_fingerprint(result_device_id)
- assert device_bfp == result_device_bfp, \
- "Can not merge result to a different build, from %s to %s" \
- % (device_bfp, result_device_bfp)
+ assert_err_msg = ('Cannot merge result to a different build, from '
+ '%s to %s' % (device_bfp, result_device_bfp))
+ assert device_bfp == result_device_bfp, assert_err_msg
# user doesn't specify camera id, run through all cameras
if not camera_ids:
camera_ids_path = os.path.join(topdir, "camera_ids.txt")
out_arg = "out=" + camera_ids_path
cmd = ['python',
- os.path.join(os.getcwd(),"tools/get_camera_ids.py"), out_arg,
+ os.path.join(os.getcwd(), "tools/get_camera_ids.py"), out_arg,
device_id_arg]
- retcode = subprocess.call(cmd,cwd=topdir)
- assert(retcode == 0)
+ cam_code = subprocess.call(cmd, cwd=topdir)
+ assert cam_code == 0
with open(camera_ids_path, "r") as f:
for line in f:
camera_ids.append(line.replace('\n', ''))
@@ -196,8 +210,8 @@
screen_id_arg = ('screen=%s' % chart_host_id)
cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
'wake_up_screen.py'), screen_id_arg]
- retcode = subprocess.call(cmd)
- assert retcode == 0
+ wake_code = subprocess.call(cmd)
+ assert wake_code == 0
for camera_id in camera_ids:
# Loop capturing images until user confirm test scene is correct
@@ -209,8 +223,9 @@
os.mkdir(os.path.join(topdir, camera_id, d))
for scene in scenes:
- tests = [(s[:-3],os.path.join("tests", scene, s))
- for s in os.listdir(os.path.join("tests",scene))
+ skip_code = None
+ tests = [(s[:-3], os.path.join("tests", scene, s))
+ for s in os.listdir(os.path.join("tests", scene))
if s[-3:] == ".py" and s[:4] == "test"]
tests.sort()
@@ -219,36 +234,42 @@
numskip = 0
num_not_mandated_fail = 0
numfail = 0
- if scene_req[scene] != None:
+ validate_switch = True
+ if scene_req[scene] is not None:
out_path = os.path.join(topdir, camera_id, scene+".jpg")
out_arg = "out=" + out_path
+ if scene == 'sensor_fusion':
+ skip_code = skip_sensor_fusion()
+ if rot_rig_id or skip_code == SKIP_RET_CODE:
+ validate_switch = False
+ if scene == 'scene5':
+ validate_switch = False
cmd = None
if auto_scene_switch:
- if not merge_result_switch or \
- (merge_result_switch and camera_ids[0] == '0'):
- scene_arg = "scene=" + scene
+ if (not merge_result_switch or
+ (merge_result_switch and camera_ids[0] == '0')):
+ scene_arg = 'scene=' + scene
cmd = ['python',
os.path.join(os.getcwd(), 'tools/load_scene.py'),
scene_arg, screen_id_arg]
else:
time.sleep(CHART_DELAY)
else:
- # Skip scene validation for scene 5 running in parallel
- if not merge_result_switch or scene != 'scene5':
- scene_arg = "scene=" + scene_req[scene]
+ # Skip scene validation under certain conditions
+ if validate_switch and not merge_result_switch:
+ scene_arg = 'scene=' + scene_req[scene]
extra_args = scene_extra_args.get(scene, [])
cmd = ['python',
- os.path.join(os.getcwd(),"tools/validate_scene.py"),
- camera_id_arg, out_arg,
- scene_arg, device_id_arg] + extra_args
-
+ os.path.join(os.getcwd(),
+ 'tools/validate_scene.py'),
+ camera_id_arg, out_arg,
+ scene_arg, device_id_arg] + extra_args
if cmd is not None:
- retcode = subprocess.call(cmd,cwd=topdir)
- assert(retcode == 0)
+ valid_scene_code = subprocess.call(cmd, cwd=topdir)
+ assert valid_scene_code == 0
print "Start running ITS on camera %s, %s" % (camera_id, scene)
-
# Run each test, capturing stdout and stderr.
- for (testname,testpath) in tests:
+ for (testname, testpath) in tests:
if auto_scene_switch:
if merge_result_switch and camera_ids[0] == '0':
# Send an input event to keep the screen not dimmed.
@@ -260,25 +281,37 @@
cmd = ('adb -s %s shell input keyevent FOCUS'
% chart_host_id)
subprocess.call(cmd.split())
- cmd = ['python', os.path.join(os.getcwd(),testpath)] + \
- sys.argv[1:] + [camera_id_arg]
- outdir = os.path.join(topdir,camera_id,scene)
- outpath = os.path.join(outdir,testname+"_stdout.txt")
- errpath = os.path.join(outdir,testname+"_stderr.txt")
t0 = time.time()
- with open(outpath,"w") as fout, open(errpath,"w") as ferr:
- retcode = subprocess.call(
- cmd,stderr=ferr,stdout=fout,cwd=outdir)
+ outdir = os.path.join(topdir, camera_id, scene)
+ outpath = os.path.join(outdir, testname+'_stdout.txt')
+ errpath = os.path.join(outdir, testname+'_stderr.txt')
+ if scene == 'sensor_fusion':
+ if skip_code is not SKIP_RET_CODE:
+ if rot_rig_id:
+ print 'Rotating phone w/ rig %s' % rot_rig_id
+ rig = ('python tools/rotation_rig.py rotator=%s' %
+ rot_rig_id)
+ subprocess.Popen(rig.split())
+ else:
+ print 'Rotate phone 15s as shown in SensorFusion.pdf'
+ else:
+ test_code = skip_code
+ if skip_code is not SKIP_RET_CODE:
+ cmd = ['python', os.path.join(os.getcwd(), testpath)]
+ cmd += sys.argv[1:] + [camera_id_arg]
+ with open(outpath, 'w') as fout, open(errpath, 'w') as ferr:
+ test_code = subprocess.call(
+ cmd, stderr=ferr, stdout=fout, cwd=outdir)
t1 = time.time()
test_failed = False
- if retcode == 0:
+ if test_code == 0:
retstr = "PASS "
numpass += 1
- elif retcode == SKIP_RET_CODE:
+ elif test_code == SKIP_RET_CODE:
retstr = "SKIP "
numskip += 1
- elif retcode != 0 and testname in NOT_YET_MANDATED[scene]:
+ elif test_code != 0 and testname in NOT_YET_MANDATED[scene]:
retstr = "FAIL*"
num_not_mandated_fail += 1
else:
@@ -294,16 +327,15 @@
if numskip > 0:
skipstr = ", %d test%s skipped" % (
- numskip, "s" if numskip > 1 else "")
+ numskip, "s" if numskip > 1 else "")
else:
skipstr = ""
test_result = "\n%d / %d tests passed (%.1f%%)%s" % (
- numpass + num_not_mandated_fail, len(tests) - numskip,
- 100.0 * float(numpass + num_not_mandated_fail) /
- (len(tests) - numskip)
- if len(tests) != numskip else 100.0,
- skipstr)
+ numpass + num_not_mandated_fail, len(tests) - numskip,
+ 100.0 * float(numpass + num_not_mandated_fail) /
+ (len(tests) - numskip)
+ if len(tests) != numskip else 100.0, skipstr)
print test_result
if num_not_mandated_fail > 0:
@@ -316,7 +348,7 @@
passed = numfail == 0
results[scene][result_key] = (ItsSession.RESULT_PASS if passed
- else ItsSession.RESULT_FAIL)
+ else ItsSession.RESULT_FAIL)
results[scene][ItsSession.SUMMARY_KEY] = summary_path
print "Reporting ITS result to CtsVerifier"
@@ -335,15 +367,15 @@
screen_id_arg = ('screen=%s' % chart_host_id)
cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
'turn_off_screen.py'), screen_id_arg]
- retcode = subprocess.call(cmd)
- assert retcode == 0
+ screen_off_code = subprocess.call(cmd)
+ assert screen_off_code == 0
print 'Shutting down DUT screen: ', device_id
screen_id_arg = ('screen=%s' % device_id)
cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
- 'turn_off_screen.py'), screen_id_arg]
- retcode = subprocess.call(cmd)
- assert retcode == 0
+ 'turn_off_screen.py'), screen_id_arg]
+ screen_off_code = subprocess.call(cmd)
+ assert screen_off_code == 0
print "ITS tests finished. Please go back to CtsVerifier and proceed"
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 99a4ee3..341575b 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -992,6 +992,7 @@
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor" />
+ <!-- FeatureSummaryActivity is replaced by CTS SystemFeaturesTest
<activity android:name=".features.FeatureSummaryActivity" android:label="@string/feature_summary">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -999,6 +1000,7 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_features" />
</activity>
+ -->
<activity android:name=".location.GpsTestActivity"
android:label="@string/location_gps_test"
@@ -2394,6 +2396,10 @@
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_STATUS" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
+ <intent-filter>
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.BYOD_DISK_ENCRYPTION_STATUS" />
+ <category android:name="android.intent.category.DEFAULT"></category>
+ </intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_managed_provisioning" />
<meta-data android:name="test_required_features" android:value="android.software.managed_users:android.software.device_admin" />
</activity>
@@ -2420,6 +2426,7 @@
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_QUERY" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_REMOVE" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.BYOD_CHECK_DISK_ENCRYPTION" />
<action android:name="com.android.cts.verifier.managedprovisioning.action.CHECK_INTENT_FILTERS" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_IMAGE" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_VIDEO_WITH_EXTRA_OUTPUT" />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 02cb70f..1d3ea85 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2045,7 +2045,33 @@
The only way to disable the encryption is to factory reset the device.
</string>
<string name="provisioning_byod_profileowner">Profile owner installed</string>
- <string name="provisioning_byod_diskencryption">Full disk encryption enabled</string>
+ <string name="provisioning_byod_disk_encryption">Full disk encryption enabled</string>
+ <string name="provisioning_byod_disk_encryption_default_key_toast">
+ Cannot secure device with screen lock. Please re-run the test if you forgot to select
+ the \"require PIN to boot\" option.
+ </string>
+ <string name="provisioning_byod_disk_encryption_no_pin_toast">
+ No PIN is detected. Please re-run the test if you forgot to set a PIN.
+ </string>
+ <string name="provisioning_byod_set_screen_lock_dialog_message">
+ Next, you will be asked to set a screen lock for the device.\n
+ \n
+ Please set \"1111\" as the new PIN (or any other PIN that you can memorize).
+ You have to enter this PIN again later in order to finish the test.\n
+ \n
+ You may be asked whether the PIN should be required to boot the device. Please answer yes.\n
+ \n
+ Tap Go button to set a new screen lock.
+ </string>
+ <string name="provisioning_byod_remove_screen_lock_dialog_message">
+ The test is almost finished. \n
+ \n
+ Next, you will be asked to remove the screen lock that you just set. Please enter \"1111\"
+ (or your own PIN) when prompted for the old PIN, and do not set any new screen lock. This
+ is to make sure that the device returns to the initial state after this test.\n
+ \n
+ Tap Go button to remove existing screen lock.
+ </string>
<string name="provisioning_byod_profile_visible">Profile-aware accounts settings</string>
<string name="provisioning_byod_admin_visible">Profile-aware device administrator settings</string>
<string name="provisioning_byod_workapps_visible">Badged work apps visible in Launcher</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index ffe29d2..8c779c5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -118,11 +118,16 @@
ICaseResult caseResult = moduleResult.getOrCreateResult(TEST_CASE_NAME);
int count = mAdapter.getCount();
+ int notExecutedCount = 0;
for (int i = 0; i < count; i++) {
TestListItem item = mAdapter.getItem(i);
if (item.isTest()) {
ITestResult currentTestResult = caseResult.getOrCreateResult(item.testName);
- currentTestResult.setResultStatus(getTestResultStatus(mAdapter.getTestResult(i)));
+ TestStatus resultStatus = getTestResultStatus(mAdapter.getTestResult(i));
+ if (resultStatus == null) {
+ ++notExecutedCount;
+ }
+ currentTestResult.setResultStatus(resultStatus);
// TODO: report test details with Extended Device Info (EDI) or CTS metrics
// String details = mAdapter.getTestDetails(i);
@@ -133,6 +138,7 @@
}
}
moduleResult.setDone(true);
+ moduleResult.setNotExecuted(notExecutedCount);
return result;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index d1977d8..395eac0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -246,6 +246,22 @@
new Feature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, false),
new Feature(PackageManager.FEATURE_PICTURE_IN_PICTURE, false),
new Feature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, false),
+ // FEATURE_FILE_BASED_ENCRYPTION is hide
+ new Feature("android.software.file_based_encryption", false),
+ };
+
+ public static final Feature[] ALL_O_FEATURES = {
+ new Feature(PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE, false),
+ // FEATURE_TELEPHONY_CARRIERLOCK is SystemApi
+ new Feature("android.hardware.telephony.carrierlock", false),
+ new Feature(PackageManager.FEATURE_WIFI_AWARE, false),
+ new Feature(PackageManager.FEATURE_EMBEDDED, false),
+ new Feature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP, false),
+ new Feature(PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS, false),
+ new Feature(PackageManager.FEATURE_VR_HEADTRACKING, false),
+ // FEATURE_CTS is hide
+ new Feature("android.software.cts", false),
+ new Feature(PackageManager.FEATURE_WIFI_AWARE, false),
};
@Override
@@ -279,6 +295,9 @@
// add features from latest to last so that the latest requirements are put in the set first
int apiVersion = Build.VERSION.SDK_INT;
+ if (apiVersion >= Build.VERSION_CODES.O) {
+ Collections.addAll(features, ALL_O_FEATURES);
+ }
if (apiVersion >= Build.VERSION_CODES.N) {
Collections.addAll(features, ALL_NYC_FEATURES);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index f52d7eb..3b5632b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -16,6 +16,7 @@
package com.android.cts.verifier.managedprovisioning;
+import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -23,6 +24,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
@@ -30,6 +32,7 @@
import android.view.View.OnClickListener;
import android.widget.Toast;
+import com.android.compatibility.common.util.PropertyUtil;
import com.android.cts.verifier.ArrayTestListAdapter;
import com.android.cts.verifier.DialogTestListActivity;
import com.android.cts.verifier.R;
@@ -39,9 +42,10 @@
import com.android.cts.verifier.location.LocationListenerActivity;
/**
- * CTS verifier test for BYOD managed provisioning flow.
- * This activity is responsible for starting the managed provisioning flow and verify the outcome of provisioning.
- * It performs the following verifications:
+ * CTS verifier test for BYOD managed provisioning flow
+ *
+ * This activity is responsible for starting the managed provisioning flow and verify the outcome of
+ * provisioning. It performs the following verifications:
* Full disk encryption is enabled.
* Profile owner is correctly installed.
* Profile owner shows up in the Settings app.
@@ -51,15 +55,20 @@
*/
public class ByodFlowTestActivity extends DialogTestListActivity {
- private final String TAG = "ByodFlowTestActivity";
+ private static final String TAG = "ByodFlowTestActivity";
private static ConnectivityManager mCm;
private static final int REQUEST_MANAGED_PROVISIONING = 0;
private static final int REQUEST_PROFILE_OWNER_STATUS = 1;
private static final int REQUEST_INTENT_FILTERS_STATUS = 2;
+ private static final int REQUEST_CHECK_DISK_ENCRYPTION = 3;
+ private static final int REQUEST_SET_LOCK_FOR_ENCRYPTION = 4;
private ComponentName mAdminReceiverComponent;
+ private KeyguardManager mKeyguardManager;
+ private ByodFlowTestHelper mByodFlowTestHelper;
private DialogTestListItem mProfileOwnerInstalled;
+ private DialogTestListItem mDiskEncryptionTest;
private DialogTestListItem mProfileAccountVisibleTest;
private DialogTestListItem mDeviceAdminVisibleTest;
private DialogTestListItem mWorkAppVisibleTest;
@@ -109,9 +118,12 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mByodFlowTestHelper = new ByodFlowTestHelper(this);
mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
+ mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
- enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+ mByodFlowTestHelper.setup();
+
mPrepareTestButton.setText(R.string.provisioning_byod_start);
mPrepareTestButton.setOnClickListener(new OnClickListener() {
@Override
@@ -149,17 +161,24 @@
switch (requestCode) {
case REQUEST_MANAGED_PROVISIONING:
return;
- case REQUEST_PROFILE_OWNER_STATUS: {
+ case REQUEST_PROFILE_OWNER_STATUS:
// Called after queryProfileOwner()
handleStatusUpdate(resultCode, data);
- } break;
- case REQUEST_INTENT_FILTERS_STATUS: {
+ break;
+ case REQUEST_CHECK_DISK_ENCRYPTION:
+ // Called after checkDiskEncryption()
+ handleDiskEncryptionStatus(resultCode, data);
+ break;
+ case REQUEST_SET_LOCK_FOR_ENCRYPTION:
+ // Called after handleDiskEncryptionStatus() to set screen lock if necessary
+ handleSetLockForEncryption();
+ break;
+ case REQUEST_INTENT_FILTERS_STATUS:
// Called after checkIntentFilters()
handleIntentFiltersStatus(resultCode);
- } break;
- default: {
+ break;
+ default:
super.handleActivityResult(requestCode, resultCode, data);
- }
}
}
@@ -174,8 +193,7 @@
public void finish() {
// Pass and fail buttons are known to call finish() when clicked, and this is when we want to
// clean up the provisioned profile.
- Utils.requestDeleteManagedProfile(this);
- enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+ mByodFlowTestHelper.tearDown();
super.finish();
}
@@ -190,6 +208,15 @@
}
};
+ mDiskEncryptionTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_disk_encryption,
+ "BYOD_DiskEncryptionTest") {
+ @Override
+ public void performTest(DialogTestListActivity activity) {
+ checkDiskEncryption();
+ }
+ };
+
/*
* To keep the image in this test up to date, use the instructions in
* {@link ByodIconSamplerActivity}.
@@ -399,6 +426,11 @@
policyTransparencyTestIntent, null);
adapter.add(mProfileOwnerInstalled);
+ if (PropertyUtil.getFirstApiLevel() >= VERSION_CODES.N_MR1) {
+ // Previous devices were not required to entangle the disk encryption key with lock
+ // screen credentials.
+ adapter.add(mDiskEncryptionTest);
+ }
// Badge related tests
adapter.add(mWorkAppVisibleTest);
@@ -605,6 +637,59 @@
}
}
+ private void checkDiskEncryption() {
+ try {
+ Intent intent = new Intent(ByodHelperActivity.ACTION_CHECK_DISK_ENCRYPTION);
+ startActivityForResult(intent, REQUEST_CHECK_DISK_ENCRYPTION);
+ } catch (ActivityNotFoundException e) {
+ Log.d(TAG, "checkDiskEncryption: ActivityNotFoundException", e);
+ setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_FAILED);
+ Utils.showToast(this, R.string.provisioning_byod_no_activity);
+ }
+ }
+
+ private void handleDiskEncryptionStatus(int resultCode, Intent data) {
+ if (resultCode != RESULT_OK || data == null) {
+ Log.e(TAG, "Failed to get result for disk encryption, result code: " + resultCode);
+ setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_FAILED);
+ return;
+ }
+
+ final int status = data.getIntExtra(ByodHelperActivity.EXTRA_ENCRYPTION_STATUS,
+ DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED);
+ switch (status) {
+ case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
+ case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER:
+ setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_PASSED);
+ break;
+ case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY:
+ if (!mKeyguardManager.isDeviceSecure()) {
+ Utils.setScreenLock(this, REQUEST_SET_LOCK_FOR_ENCRYPTION);
+ return;
+ }
+ Log.e(TAG, "Disk encryption key is not entangled with lock screen credentials");
+ Toast.makeText(this, R.string.provisioning_byod_disk_encryption_default_key_toast,
+ Toast.LENGTH_LONG).show();
+ // fall through
+ default:
+ setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_FAILED);
+ }
+
+ if (mKeyguardManager.isDeviceSecure()) {
+ Utils.removeScreenLock(this);
+ }
+ }
+
+ private void handleSetLockForEncryption() {
+ if (mKeyguardManager.isDeviceSecure()) {
+ checkDiskEncryption();
+ } else {
+ setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_FAILED);
+ Toast.makeText(this, R.string.provisioning_byod_disk_encryption_no_pin_toast,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
private void checkIntentFilters() {
try {
// Enable component HandleIntentActivity before intent filters are checked.
@@ -640,29 +725,6 @@
intentFiltersSet ? TestResult.TEST_RESULT_PASSED : TestResult.TEST_RESULT_FAILED);
}
- /**
- * Disable or enable app components in the current profile. When they are disabled only the
- * counterpart in the other profile can respond (via cross-profile intent filter).
- * @param enabledState {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED} or
- * {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
- */
- private void enableComponent(final int enabledState) {
- final String[] components = {
- ByodHelperActivity.class.getName(),
- WorkStatusTestActivity.class.getName(),
- PermissionLockdownTestActivity.ACTIVITY_ALIAS,
- AuthenticationBoundKeyTestActivity.class.getName(),
- VpnTestActivity.class.getName(),
- RecentsRedactionActivity.class.getName(),
- CommandReceiverActivity.class.getName(),
- SetSupportMessageActivity.class.getName()
- };
- for (String component : components) {
- getPackageManager().setComponentEnabledSetting(new ComponentName(this, component),
- enabledState, PackageManager.DONT_KILL_APP);
- }
- }
-
private void setHandleIntentActivityEnabledSetting(final int enableState) {
getPackageManager().setComponentEnabledSetting(
new ComponentName(ByodFlowTestActivity.this, HandleIntentActivity.class.getName()),
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
new file mode 100644
index 0000000..d1308ad
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
@@ -0,0 +1,51 @@
+package com.android.cts.verifier.managedprovisioning;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+public class ByodFlowTestHelper {
+ private Context mContext;
+ private PackageManager mPackageManager;
+
+ public ByodFlowTestHelper(Context context) {
+ this.mContext = context;
+ this.mPackageManager = mContext.getPackageManager();
+ }
+
+ public void setup() {
+ setComponentsEnabledState(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+ }
+
+ /**
+ * Clean up things. This has to be working even it is called multiple times.
+ */
+ public void tearDown() {
+ Utils.requestDeleteManagedProfile(mContext);
+ setComponentsEnabledState(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+ }
+
+ /**
+ * Disable or enable app components in the current profile. When they are disabled only the
+ * counterpart in the other profile can respond (via cross-profile intent filter).
+ *
+ * @param enabledState {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED} or
+ * {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
+ */
+ private void setComponentsEnabledState(final int enabledState) {
+ final String[] components = {
+ ByodHelperActivity.class.getName(),
+ WorkStatusTestActivity.class.getName(),
+ PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+ AuthenticationBoundKeyTestActivity.class.getName(),
+ VpnTestActivity.class.getName(),
+ RecentsRedactionActivity.class.getName(),
+ CommandReceiverActivity.class.getName(),
+ SetSupportMessageActivity.class.getName()
+ };
+ for (String component : components) {
+ mPackageManager.setComponentEnabledSetting(new ComponentName(mContext, component),
+ enabledState, PackageManager.DONT_KILL_APP);
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 6ad1634..e9b6523 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -81,6 +81,15 @@
public static final String EXTRA_PROVISIONED = "extra_provisioned";
public static final String EXTRA_PARAMETER_1 = "extra_parameter_1";
+ // Primary -> managed intent: check if the disk of the device is encrypted
+ public static final String ACTION_CHECK_DISK_ENCRYPTION =
+ "com.android.cts.verifier.managedprovisioning.action.BYOD_CHECK_DISK_ENCRYPTION";
+ // Managed -> primary intent: update disk encryption status in primary's CtsVerifier
+ public static final String ACTION_DISK_ENCRYPTION_STATUS =
+ "com.android.cts.verifier.managedprovisioning.action.BYOD_DISK_ENCRYPTION_STATUS";
+ // Int extra field indicating the encryption status of the device storage
+ public static final String EXTRA_ENCRYPTION_STATUS = "extra_encryption_status";
+
// Primary -> managed intent: set unknown sources restriction and install package
public static final String ACTION_INSTALL_APK = "com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK";
public static final String EXTRA_ALLOW_NON_MARKET_APPS = "allow_non_market_apps";
@@ -212,6 +221,11 @@
mDevicePolicyManager.wipeData(0);
showToast(R.string.provisioning_byod_profile_deleted);
}
+ } else if (action.equals(ACTION_CHECK_DISK_ENCRYPTION)) {
+ final int status = mDevicePolicyManager.getStorageEncryptionStatus();
+ final Intent response = new Intent(ACTION_DISK_ENCRYPTION_STATUS)
+ .putExtra(EXTRA_ENCRYPTION_STATUS, status);
+ setResult(RESULT_OK, response);
} else if (action.equals(ACTION_INSTALL_APK)) {
boolean allowNonMarket = intent.getBooleanExtra(EXTRA_ALLOW_NON_MARKET_APPS, false);
boolean wasAllowed = !isUnknownSourcesRestrictionSet();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index a59abbc..1c40ac1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -85,6 +85,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
filter.addAction(ByodHelperActivity.ACTION_REMOVE_MANAGED_PROFILE);
+ filter.addAction(ByodHelperActivity.ACTION_CHECK_DISK_ENCRYPTION);
filter.addAction(ByodHelperActivity.ACTION_INSTALL_APK);
filter.addAction(ByodHelperActivity.ACTION_CHECK_INTENT_FILTERS);
filter.addAction(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_IMAGE);
@@ -122,6 +123,7 @@
// Work -> primary direction
filter = new IntentFilter();
filter.addAction(ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS);
+ filter.addAction(ByodHelperActivity.ACTION_DISK_ENCRYPTION_STATUS);
filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL);
filter.addAction(LocationListenerActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 67e7fd4..0cae6be 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -78,6 +78,10 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // Tidy up in case previous run crashed.
+ new ByodFlowTestHelper(this).tearDown();
+
if (ACTION_CHECK_DEVICE_OWNER.equals(getIntent().getAction())) {
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
Context.DEVICE_POLICY_SERVICE);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
index 1d23175..7ab8e7e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
@@ -80,6 +80,9 @@
return;
}
+ // Tidy up in case previous run crashed.
+ new ByodFlowTestHelper(this).tearDown();
+
setContentView(R.layout.requesting_bugreport_device_owner);
setInfoResources(R.string.device_owner_requesting_bugreport_tests,
R.string.device_owner_requesting_bugreport_tests_info, 0);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index eecf9a7..f231b01 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -110,7 +110,7 @@
Settings.ACTION_DEVICE_INFO_SETTINGS,
Settings.ACTION_PRIVACY_SETTINGS,
Settings.ACTION_DEVICE_INFO_SETTINGS,
- Settings.ACTION_MANAGE_EXTERNAL_SOURCES,
+ Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
Settings.ACTION_SYNC_SETTINGS,
Settings.ACTION_WIRELESS_SETTINGS,
Settings.ACTION_WIRELESS_SETTINGS,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
index 92bb555..f12d698 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
@@ -17,19 +17,20 @@
package com.android.cts.verifier.managedprovisioning;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
-import android.widget.Toast;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.util.Log;
+import android.widget.Toast;
import com.android.cts.verifier.IntentDrivenTestActivity;
import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
-import com.android.cts.verifier.managedprovisioning.ByodHelperActivity;
import com.android.cts.verifier.R;
import com.android.cts.verifier.TestListAdapter.TestListItem;
@@ -58,9 +59,8 @@
static void requestDeleteManagedProfile(Context context) {
try {
Intent intent = new Intent(ByodHelperActivity.ACTION_REMOVE_MANAGED_PROFILE);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
- String message = context.getString(R.string.provisioning_byod_delete_profile);
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
catch (ActivityNotFoundException e) {
Log.d(TAG, "requestDeleteProfileOwner: ActivityNotFoundException", e);
@@ -93,4 +93,41 @@
static void showToast(Context context, int messageId) {
Toast.makeText(context, messageId, Toast.LENGTH_SHORT).show();
}
+
+ /**
+ * Prompts the tester to set a screen lock credential, or change it if one exists.
+ *
+ * An instruction dialog is shown before the tester is sent to the ChooseLockGeneric activity
+ * in Settings.
+ *
+ * @param activity The calling activity where the result is handled
+ * @param requestCode The callback request code when the lock is set
+ */
+ static void setScreenLock(Activity activity, int requestCode) {
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
+ new AlertDialog.Builder(activity)
+ .setTitle(R.string.provisioning_byod)
+ .setMessage(R.string.provisioning_byod_set_screen_lock_dialog_message)
+ .setPositiveButton(R.string.go_button_text, (DialogInterface dialog, int which) ->
+ activity.startActivityForResult(intent, requestCode))
+ .show();
+ }
+
+ /**
+ * Prompts the tester to remove the current screen lock credential.
+ *
+ * An instruction dialog is shown before the tester is sent to the ChooseLockGeneric activity
+ * in Settings.
+ *
+ * @param activity The calling activity
+ */
+ static void removeScreenLock(Activity activity) {
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
+ new AlertDialog.Builder(activity)
+ .setTitle(R.string.provisioning_byod)
+ .setMessage(R.string.provisioning_byod_remove_screen_lock_dialog_message)
+ .setPositiveButton(R.string.go_button_text, (DialogInterface dialog, int which) ->
+ activity.startActivity(intent))
+ .show();
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
index 996a056..6794059 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
@@ -67,6 +67,7 @@
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
public class UsbDeviceTestActivity extends PassFailButtons.Activity {
@@ -842,14 +843,12 @@
runAndAssertException(() -> connection.requestWait(-1), IllegalArgumentException.class);
long startTime = now();
- UsbRequest req = connection.requestWait(100);
- assertNull(req);
+ runAndAssertException(() -> connection.requestWait(100), TimeoutException.class);
assertTrue(now() - startTime >= 100);
assertTrue(now() - startTime < 400);
startTime = now();
- req = connection.requestWait(0);
- assertNull(req);
+ runAndAssertException(() -> connection.requestWait(0), TimeoutException.class);
assertTrue(now() - startTime < 400);
}
@@ -860,7 +859,7 @@
* @param in The endpoint to receive requests from
*/
private void receiveAfterTimeout(@NonNull UsbDeviceConnection connection,
- @NonNull UsbEndpoint in, int timeout) throws InterruptedException {
+ @NonNull UsbEndpoint in, long timeout) throws InterruptedException, TimeoutException {
UsbRequest reqQueued = new UsbRequest();
ByteBuffer buffer = ByteBuffer.allocate(1);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
index 7ecdc30..a73f964 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
@@ -285,12 +285,11 @@
// 6. request network
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
- String networkSpecifier = mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(
- peerHandle) : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
- PASSPHRASE);
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
- networkSpecifier).build();
+ mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(peerHandle)
+ : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
+ PASSPHRASE)).build();
CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
@@ -374,12 +373,11 @@
// 4. Request network
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
- String networkSpecifier = mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(
- peerHandle) : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
- PASSPHRASE);
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
- networkSpecifier).build();
+ mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(peerHandle)
+ : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
+ PASSPHRASE)).build();
CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
index a2e0de5..3dc31e1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
@@ -265,13 +265,13 @@
// 6. Request network (as Responder) and wait for network
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
- String networkSpecifier = mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
- WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac)
- : mWifiAwareSession.createNetworkSpecifierPassphrase(
- WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac, PASSPHRASE);
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
- networkSpecifier).build();
+ mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac)
+ : mWifiAwareSession.createNetworkSpecifierPassphrase(
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac,
+ PASSPHRASE)).build();
CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
@@ -397,13 +397,13 @@
// 8. Request network (as Initiator) and wait for network
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
- String networkSpecifier = mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
- WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac)
- : mWifiAwareSession.createNetworkSpecifierPassphrase(
- WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac, PASSPHRASE);
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
- networkSpecifier).build();
+ mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac)
+ : mWifiAwareSession.createNetworkSpecifierPassphrase(
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac,
+ PASSPHRASE)).build();
CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
diff --git a/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java b/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
index 001b18a..7cbeccf 100644
--- a/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
+++ b/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
@@ -76,7 +76,8 @@
assertEquals(42, sizeBuffer[0]);
assertEquals(1, numRead);
- return Charset.forName("UTF-8").decode(ByteBuffer.wrap(nextTestNameBytes)).toString();
+ return Charset.forName("UTF-8").decode(
+ ByteBuffer.wrap(nextTestNameBytes)).toString().trim();
}
/**
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
new file mode 100644
index 0000000..4ac8403
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.android.compatibility.common.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A receiver that allows caller to wait for the broadcast synchronously. Notice that you should not
+ * reuse the instance. Usage is typically like this:
+ * <pre>
+ * BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(context, "action");
+ * try {
+ * receiver.register();
+ * Intent intent = receiver.awaitForBroadcast();
+ * // assert the intent
+ * } finally {
+ * receiver.unregisterQuietly();
+ * }
+ * </pre>
+ */
+public class BlockingBroadcastReceiver extends BroadcastReceiver {
+ private static final String TAG = "BlockingBroadcast";
+
+ private static final int DEFAULT_TIMEOUT_SECONDS = 10;
+
+ private final BlockingQueue<Intent> mBlockingQueue;
+ private final String mExpectedAction;
+ private final Context mContext;
+
+ public BlockingBroadcastReceiver(Context context, String expectedAction) {
+ mContext = context;
+ mExpectedAction = expectedAction;
+ mBlockingQueue = new ArrayBlockingQueue<>(1);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mExpectedAction.equals(intent.getAction())) {
+ mBlockingQueue.add(intent);
+ }
+ }
+
+ public void register() {
+ mContext.registerReceiver(this, new IntentFilter(mExpectedAction));
+ }
+
+ /**
+ * Wait until the broadcast and return the received broadcast intent. {@code null} is returned
+ * if no broadcast with expected action is received within 10 seconds.
+ */
+ public @Nullable Intent awaitForBroadcast() {
+ try {
+ return mBlockingQueue.poll(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "waitForBroadcast get interrupted: ", e);
+ }
+ return null;
+ }
+
+ public void unregisterQuietly() {
+ try {
+ mContext.unregisterReceiver(this);
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to unregister BlockingBroadcastReceiver: ", ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
index c63b54a..19278d0 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
@@ -29,13 +29,15 @@
import android.support.test.uiautomator.UiDevice;
import android.util.Log;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class SilentProvisioningTestManager {
private static final long TIMEOUT_SECONDS = 120L;
- private static final String TAG = "SilentProvisioningTestManager";
+ private static final String TAG = "SilentProvisioningTest";
private final LinkedBlockingQueue<Boolean> mProvisioningResults = new LinkedBlockingQueue(1);
@@ -51,18 +53,14 @@
};
private final Context mContext;
- private final BlockingReceiver mManagedProfileProvisionedReceiver;
- private final BlockingReceiver mManagedProfileAddedReceiver;
+ private Intent mReceivedProfileProvisionedIntent;
public SilentProvisioningTestManager(Context context) {
mContext = context.getApplicationContext();
- mManagedProfileProvisionedReceiver =
- new BlockingReceiver(mContext, ACTION_MANAGED_PROFILE_PROVISIONED);
- mManagedProfileAddedReceiver = new BlockingReceiver(mContext, ACTION_MANAGED_PROFILE_ADDED);
}
public Intent getReceviedProfileProvisionedIntent() {
- return mManagedProfileProvisionedReceiver.getReceivedIntent();
+ return mReceivedProfileProvisionedIntent;
}
public boolean startProvisioningAndWait(Intent provisioningIntent) throws InterruptedException {
@@ -82,23 +80,33 @@
}
private boolean waitManagedProfileProvisioning() throws InterruptedException {
- mManagedProfileProvisionedReceiver.register();
- mManagedProfileAddedReceiver.register();
+ BlockingBroadcastReceiver managedProfileProvisionedReceiver =
+ new BlockingBroadcastReceiver(mContext, ACTION_MANAGED_PROFILE_PROVISIONED);
+ BlockingBroadcastReceiver managedProfileAddedReceiver =
+ new BlockingBroadcastReceiver(mContext, ACTION_MANAGED_PROFILE_ADDED);
+ try {
+ managedProfileProvisionedReceiver.register();
+ managedProfileAddedReceiver.register();
- if (!pollProvisioningResult()) {
- return false;
+ if (!pollProvisioningResult()) {
+ return false;
+ }
+
+ mReceivedProfileProvisionedIntent =
+ managedProfileProvisionedReceiver.awaitForBroadcast();
+ if (mReceivedProfileProvisionedIntent == null) {
+ Log.i(TAG, "managedProfileProvisionedReceiver.awaitForBroadcast(): failed");
+ return false;
+ }
+
+ if (managedProfileAddedReceiver.awaitForBroadcast() == null) {
+ Log.i(TAG, "managedProfileAddedReceiver.awaitForBroadcast(): failed");
+ return false;
+ }
+ } finally {
+ managedProfileProvisionedReceiver.unregisterQuietly();
+ managedProfileAddedReceiver.unregisterQuietly();
}
-
- if (!mManagedProfileProvisionedReceiver.await()) {
- Log.i(TAG, "mManagedProfileProvisionedReceiver.await(): false");
- return false;
- }
-
- if (!mManagedProfileAddedReceiver.await()) {
- Log.i(TAG, "mManagedProfileAddedReceiver.await(): false");
- return false;
- }
-
return true;
}
diff --git a/common/host-side/tradefed/res/report/compatibility_failures.xsl b/common/host-side/tradefed/res/report/compatibility_failures.xsl
index be65b91..ef067f2 100644
--- a/common/host-side/tradefed/res/report/compatibility_failures.xsl
+++ b/common/host-side/tradefed/res/report/compatibility_failures.xsl
@@ -82,12 +82,6 @@
</td>
</tr>
<tr>
- <td class="rowtitle">Tests Not Executed</td>
- <td>
- <xsl:value-of select="Result/Summary/@not_executed"/>
- </td>
- </tr>
- <tr>
<td class="rowtitle">Modules Done</td>
<td>
<xsl:value-of select="Result/Summary/@modules_done"/>
@@ -134,8 +128,8 @@
<th>Module</th>
<th>Passed</th>
<th>Failed</th>
- <th>Not Executed</th>
<th>Total Tests</th>
+ <th>Done</th>
</tr>
<xsl:for-each select="Result/Module">
<tr>
@@ -155,10 +149,10 @@
<xsl:value-of select="count(TestCase/Test[@result = 'fail'])"/>
</td>
<td>
- <xsl:value-of select="count(TestCase/Test[@result = 'not_executed'])"/>
+ <xsl:value-of select="count(TestCase/Test[@result = 'fail']) + @pass"/>
</td>
<td>
- <xsl:value-of select="count(TestCase/Test[@result = 'fail']) + @pass + count(TestCase/Test[@result = 'not_executed']) "/>
+ <xsl:value-of select="@done"/>
</td>
</tr>
</xsl:for-each> <!-- end Module -->
diff --git a/common/host-side/tradefed/res/report/compatibility_result.xsd b/common/host-side/tradefed/res/report/compatibility_result.xsd
index 9b2758c..95bae85 100644
--- a/common/host-side/tradefed/res/report/compatibility_result.xsd
+++ b/common/host-side/tradefed/res/report/compatibility_result.xsd
@@ -36,7 +36,6 @@
<xs:complexType name="summaryType">
<xs:attribute name="failed" type="xs:integer"/>
- <xs:attribute name="not_executed" type="xs:integer"/>
<xs:attribute name="pass" type="xs:integer"/>
</xs:complexType>
@@ -121,7 +120,6 @@
<xs:restriction base="xs:string">
<xs:enumeration value="pass"/>
<xs:enumeration value="fail"/>
- <xs:enumeration value="not_executed"/>
</xs:restriction>
</xs:simpleType>
-</xs:schema>
\ No newline at end of file
+</xs:schema>
diff --git a/common/host-side/tradefed/res/report/compatibility_result.xsl b/common/host-side/tradefed/res/report/compatibility_result.xsl
index b8c1245..a0c337a 100644
--- a/common/host-side/tradefed/res/report/compatibility_result.xsl
+++ b/common/host-side/tradefed/res/report/compatibility_result.xsl
@@ -82,12 +82,6 @@
</td>
</tr>
<tr>
- <td class="rowtitle">Tests Not Executed</td>
- <td>
- <xsl:value-of select="Result/Summary/@not_executed"/>
- </td>
- </tr>
- <tr>
<td class="rowtitle">Modules Done</td>
<td>
<xsl:value-of select="Result/Summary/@modules_done"/>
@@ -134,8 +128,8 @@
<th>Module</th>
<th>Passed</th>
<th>Failed</th>
- <th>Not Executed</th>
<th>Total Tests</th>
+ <th>Done</th>
</tr>
<xsl:for-each select="Result/Module">
<tr>
@@ -150,10 +144,10 @@
<xsl:value-of select="count(TestCase/Test[@result = 'fail'])"/>
</td>
<td>
- <xsl:value-of select="@not_executed"/>
+ <xsl:value-of select="count(TestCase/Test)"/>
</td>
<td>
- <xsl:value-of select="count(TestCase/Test) + @not_executed"/>
+ <xsl:value-of select="@done"/>
</td>
</tr>
</xsl:for-each> <!-- end Module -->
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index 09fed81..6fe1155 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -18,7 +18,7 @@
import com.android.compatibility.SuiteInfo;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
-import com.android.compatibility.common.tradefed.result.SubPlanCreator;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
import com.android.compatibility.common.tradefed.testtype.ModuleRepo;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.ResultHandler;
@@ -361,7 +361,6 @@
Integer.toString(i),
Integer.toString(result.countResults(TestStatus.PASS)),
Integer.toString(result.countResults(TestStatus.FAIL)),
- Integer.toString(result.getNotExecuted()),
moduleProgress,
CompatibilityBuildHelper.getDirSuffix(result.getStartTime()),
result.getTestPlan(),
@@ -372,9 +371,8 @@
}
// add the table header to the beginning of the list
- table.add(0, Arrays.asList("Session", "Pass", "Fail", "Not Executed",
- "Modules Complete", "Result Directory", "Test Plan", "Device serial(s)",
- "Build ID", "Product"));
+ table.add(0, Arrays.asList("Session", "Pass", "Fail", "Modules Complete",
+ "Result Directory", "Test Plan", "Device serial(s)", "Build ID", "Product"));
tableFormatter.displayTable(table, new PrintWriter(System.out, true));
} else {
printLine(String.format("No results found"));
@@ -404,7 +402,7 @@
}
private void addSubPlan(String[] flatArgs) {
- SubPlanCreator creator = new SubPlanCreator();
+ SubPlanHelper creator = new SubPlanHelper();
try {
ArgsOptionParser optionParser = new ArgsOptionParser(creator);
optionParser.parse(Arrays.asList(flatArgs));
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index fc57e22..534ab85 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -17,7 +17,7 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest.RetryType;
+import com.android.compatibility.common.tradefed.util.RetryType;
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.IModuleResult;
@@ -106,6 +106,9 @@
@Option(name = "use-log-saver", description = "Also saves generated result with log saver")
private boolean mUseLogSaver = false;
+ @Option(name = "compress-logs", description = "Whether logs will be saved with compression")
+ private boolean mCompressLogs = true;
+
private CompatibilityBuildHelper mBuildHelper;
private File mResultDir = null;
private File mLogDir = null;
@@ -368,23 +371,10 @@
public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
mCurrentModuleResult.inProgress(false);
mCurrentModuleResult.addRuntime(elapsedTime);
- if (!mModuleWasDone) {
- // Not executed count now represents an upper-bound for a fix to b/33211104.
- // Only setNotExecuted this number if the module has already been completely executed.
- int testCountDiff = Math.max(mTotalTestsInModule - mCurrentTestNum, 0);
- if (isShardResultReporter()) {
- // reset value, which is added to total count for master shard upon merge
- mCurrentModuleResult.setNotExecuted(testCountDiff);
- } else {
- // increment value for master shard
- mCurrentModuleResult.setNotExecuted(mCurrentModuleResult.getNotExecuted()
- + testCountDiff);
- }
- if (mCanMarkDone) {
- // Only mark module done if status of the invocation allows it (mCanMarkDone) and
- // if module has not already been marked done.
- mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
- }
+ if (!mModuleWasDone && mCanMarkDone) {
+ // Only mark module done if status of the invocation allows it (mCanMarkDone) and
+ // if module has not already been marked done.
+ mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
}
if (isShardResultReporter()) {
// Forward module results to the master.
@@ -514,6 +504,7 @@
} else {
info("Test Result: %s", resultFile.getCanonicalPath());
}
+ info("Test Logs: %s", mLogDir.getCanonicalPath());
debug("Full Result: %s", zippedResults.getCanonicalPath());
saveLog(resultFile, zippedResults);
@@ -525,11 +516,10 @@
CLog.e(e);
}
// print the run results last.
- info("Invocation finished in %s. PASSED: %d, FAILED: %d, NOT EXECUTED: %d, MODULES: %s",
+ info("Invocation finished in %s. PASSED: %d, FAILED: %d, MODULES: %s",
TimeUtil.formatElapsedTime(elapsedTime),
mResult.countResults(TestStatus.PASS),
mResult.countResults(TestStatus.FAIL),
- mResult.getNotExecuted(),
moduleProgress);
}
@@ -554,7 +544,12 @@
return;
}
try {
- File logFile = mTestLogSaver.saveAndZipLogData(name, type, stream.createInputStream());
+ File logFile = null;
+ if (mCompressLogs) {
+ logFile = mTestLogSaver.saveAndGZipLogData(name, type, stream.createInputStream());
+ } else {
+ logFile = mTestLogSaver.saveLogData(name, type, stream.createInputStream());
+ }
debug("Saved logs for %s in %s", name, logFile.getAbsolutePath());
} catch (IOException e) {
warn("Failed to write log for %s", name);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
similarity index 89%
rename from common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
rename to common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
index 9dbbcbb..950a129 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
@@ -33,11 +33,15 @@
import com.android.tradefed.config.Option;
import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+import com.android.tradefed.util.StreamUtil;
import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.InputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
@@ -50,7 +54,9 @@
/**
* Class for creating subplans from compatibility result XML.
*/
-public class SubPlanCreator {
+public class SubPlanHelper {
+
+ private static final String XML_EXT = ".xml";
// result types
public static final String PASSED = "passed";
@@ -108,22 +114,45 @@
IInvocationResult mResult = null;
/**
- * Create an empty {@link SubPlanCreator}.
+ * Create an empty {@link SubPlanHelper}.
* <p/>
* All {@link Option} fields must be populated via
* {@link com.android.tradefed.config.ArgsOptionParser}
*/
- public SubPlanCreator() {}
+ public SubPlanHelper() {}
/**
- * Create a {@link SubPlanCreator} using the specified option values.
+ * Create a {@link SubPlanHelper} using the specified option values.
*/
- public SubPlanCreator(String name, int session, Collection<String> resultTypes) {
+ public SubPlanHelper(String name, int session, Collection<String> resultTypes) {
mSubPlanName = name;
mSessionId = session;
mResultTypes.addAll(resultTypes);
}
+ public static ISubPlan getSubPlanByName(CompatibilityBuildHelper buildHelper, String name) {
+ if (!name.endsWith(XML_EXT)) {
+ name = name + XML_EXT; // only append XML extension to name if not already there
+ }
+ InputStream subPlanInputStream = null;
+ try {
+ File subPlanFile = new File(buildHelper.getSubPlansDir(), name);
+ if (!subPlanFile.exists()) {
+ throw new IllegalArgumentException(
+ String.format("Could not retrieve subplan \"%s\"", name));
+ }
+ subPlanInputStream = new FileInputStream(subPlanFile);
+ ISubPlan subPlan = new SubPlan();
+ subPlan.parse(subPlanInputStream);
+ return subPlan;
+ } catch (FileNotFoundException | ParseException e) {
+ throw new RuntimeException(
+ String.format("Unable to find or parse subplan %s", name), e);
+ } finally {
+ StreamUtil.closeStream(subPlanInputStream);
+ }
+ }
+
/**
* Set the result from which to derive the subplan.
* @param result
@@ -333,7 +362,7 @@
mSubPlanName = createPlanName();
}
try {
- mSubPlanFile = new File(buildHelper.getSubPlansDir(), mSubPlanName + ".xml");
+ mSubPlanFile = new File(buildHelper.getSubPlansDir(), mSubPlanName + XML_EXT);
if (mSubPlanFile.exists()) {
throw new ConfigurationException(String.format("Subplan %s already exists",
mSubPlanName));
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index a2dad7e..9f07307 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -19,15 +19,16 @@
import com.android.compatibility.SuiteInfo;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
-import com.android.compatibility.common.tradefed.result.SubPlanCreator;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
import com.android.compatibility.common.tradefed.util.OptionHelper;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
+import com.android.compatibility.common.tradefed.util.RetryType;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.ResultHandler;
import com.android.compatibility.common.util.TestFilter;
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.config.ArgsOptionParser;
import com.android.tradefed.config.ConfigurationException;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.Option.Importance;
@@ -153,10 +154,6 @@
importance = Importance.ALWAYS)
private List<String> mTestArgs = new ArrayList<>();
- public enum RetryType {
- FAILED, NOT_EXECUTED;
- }
-
@Option(name = RETRY_OPTION,
shortName = 'r',
description = "retry a previous session's failed and not executed tests.",
@@ -366,6 +363,8 @@
// Get the tests to run in this shard
modules = mModuleRepo.getModules(getDevice().getSerialNumber(), mShardIndex);
}
+ mExcludeFilters.clear();
+ mIncludeFilters.clear();
// Update BuildInfo in each shard to store the original command-line arguments from
// the session to be retried. These arguments will be serialized in the report later.
if (mRetrySessionId != null) {
@@ -669,113 +668,45 @@
*/
void setupFilters() throws DeviceNotAvailableException {
if (mRetrySessionId != null) {
- // Track --module/-m and --test/-t options to ensure we don't overwrite non-null
- // values on retry
- String newModuleName = mModuleName;
- String newTestName = mTestName;
-
- // Load the invocation result
- IInvocationResult result = null;
- try {
- result = ResultHandler.findResult(mBuildHelper.getResultsDir(), mRetrySessionId);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
- if (result == null) {
- throw new IllegalArgumentException(String.format(
- "Could not find session with id %d", mRetrySessionId));
- }
-
- String oldBuildFingerprint = result.getBuildFingerprint();
- String currentBuildFingerprint = mDevice.getProperty("ro.build.fingerprint");
- if (oldBuildFingerprint.equals(currentBuildFingerprint)) {
- CLog.logAndDisplay(LogLevel.INFO, "Retrying session from: %s",
- CompatibilityBuildHelper.getDirSuffix(result.getStartTime()));
- } else {
- throw new IllegalArgumentException(String.format(
- "Device build fingerprint must match %s to retry session %d",
- oldBuildFingerprint, mRetrySessionId));
- }
-
- String retryCommandLineArgs = result.getCommandLineArgs();
- if (retryCommandLineArgs != null) {
- try {
- // parse the command-line string from the result file and set options
- ArgsOptionParser parser = new ArgsOptionParser(this);
- parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, this));
- } catch (ConfigurationException e) {
- throw new RuntimeException(e);
- }
- }
-
- if ((mModuleName != null && mModuleName != newModuleName)
- || (mTestName != null && mTestName != newTestName)) {
- // These options cannot be changed on retry if non-null for the previous session
- CLog.w("Cannot override non-null value(s) from session %d for option(s) \"%s\""
- + " or \"%s\" on retry", mRetrySessionId, MODULE_OPTION, TEST_OPTION);
- }
-
- SubPlanCreator retryPlanCreator = new SubPlanCreator();
- retryPlanCreator.setResult(result);
- if (RetryType.FAILED.equals(mRetryType)) {
- // retry only failed tests
- retryPlanCreator.addResultType(SubPlanCreator.FAILED);
- } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
- // retry only not executed tests
- retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
- } else {
- // retry both failed and not executed tests
- retryPlanCreator.addResultType(SubPlanCreator.FAILED);
- retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
- }
- try {
- ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuildHelper);
- mIncludeFilters.addAll(retryPlan.getIncludeFilters());
- mExcludeFilters.addAll(retryPlan.getExcludeFilters());
- } catch (ConfigurationException e) {
- throw new RuntimeException ("Failed to create subplan for retry", e);
- }
- }
- if (mSubPlan != null) {
- try {
- File subPlanFile = new File(mBuildHelper.getSubPlansDir(), mSubPlan + ".xml");
- if (!subPlanFile.exists()) {
- throw new IllegalArgumentException(
- String.format("Could not retrieve subplan \"%s\"", mSubPlan));
- }
- InputStream subPlanInputStream = new FileInputStream(subPlanFile);
- ISubPlan subPlan = new SubPlan();
- subPlan.parse(subPlanInputStream);
+ RetryFilterHelper helper = new RetryFilterHelper(mBuildHelper, mRetrySessionId);
+ helper.validateBuildFingerprint(mDevice);
+ helper.setAllOptionsFrom(this);
+ helper.setCommandLineOptionsFor(this);
+ helper.populateRetryFilters();
+ mIncludeFilters = helper.getIncludeFilters();
+ mExcludeFilters = helper.getExcludeFilters();
+ helper.tearDown();
+ } else {
+ if (mSubPlan != null) {
+ ISubPlan subPlan = SubPlanHelper.getSubPlanByName(mBuildHelper, mSubPlan);
mIncludeFilters.addAll(subPlan.getIncludeFilters());
mExcludeFilters.addAll(subPlan.getExcludeFilters());
- } catch (FileNotFoundException | ParseException e) {
- throw new RuntimeException(
- String.format("Unable to find or parse subplan %s", mSubPlan), e);
}
- }
- if (mModuleName != null) {
- try {
- List<String> modules = ModuleRepo.getModuleNamesMatching(
- mBuildHelper.getTestsDir(), mModuleName);
- if (modules.size() == 0) {
- throw new IllegalArgumentException(
- String.format("No modules found matching %s", mModuleName));
- } else if (modules.size() > 1) {
- throw new IllegalArgumentException(String.format(
- "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
- mModuleName, ArrayUtil.join("\n", modules)));
- } else {
- String module = modules.get(0);
- cleanFilters(mIncludeFilters, module);
- cleanFilters(mExcludeFilters, module);
- mIncludeFilters.add(new TestFilter(mAbiName, module, mTestName).toString());
+ if (mModuleName != null) {
+ try {
+ List<String> modules = ModuleRepo.getModuleNamesMatching(
+ mBuildHelper.getTestsDir(), mModuleName);
+ if (modules.size() == 0) {
+ throw new IllegalArgumentException(
+ String.format("No modules found matching %s", mModuleName));
+ } else if (modules.size() > 1) {
+ throw new IllegalArgumentException(String.format("Multiple modules found"
+ + " matching %s:\n%s\nWhich one did you mean?\n",
+ mModuleName, ArrayUtil.join("\n", modules)));
+ } else {
+ String module = modules.get(0);
+ cleanFilters(mIncludeFilters, module);
+ cleanFilters(mExcludeFilters, module);
+ mIncludeFilters.add(
+ new TestFilter(mAbiName, module, mTestName).toString());
+ }
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
}
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
+ } else if (mTestName != null) {
+ throw new IllegalArgumentException(
+ "Test name given without module name. Add --module <module-name>");
}
- } else if (mTestName != null) {
- throw new IllegalArgumentException(
- "Test name given without module name. Add --module <module-name>");
}
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index ad12490..794e45e 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -286,6 +286,7 @@
configFile.getName()), e);
}
}
+ mExcludeFilters.clear();
TestRunHandler.setTestRuns(new CompatibilityBuildHelper(buildInfo), shardedTestCounts);
}
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
new file mode 100644
index 0000000..9a68089
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
@@ -0,0 +1,322 @@
+/*
+ * 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 com.android.compatibility.common.tradefed.util;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.ModuleRepo;
+import com.android.compatibility.common.tradefed.testtype.ISubPlan;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.LightInvocationResult;
+import com.android.compatibility.common.util.ResultHandler;
+import com.android.compatibility.common.util.TestFilter;
+import com.android.tradefed.config.ArgsOptionParser;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
+import com.android.tradefed.config.OptionCopier;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.ArrayUtil;
+
+import java.io.FileNotFoundException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper for generating --include-filter and --exclude-filter values on compatibility retry.
+ */
+public class RetryFilterHelper {
+
+ @Option(name = CompatibilityTest.SUBPLAN_OPTION,
+ description = "the subplan to run",
+ importance = Importance.IF_UNSET)
+ protected String mSubPlan;
+
+ @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
+ description = "the include module filters to apply.",
+ importance = Importance.ALWAYS)
+ protected Set<String> mIncludeFilters = new HashSet<>();
+
+ @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
+ description = "the exclude module filters to apply.",
+ importance = Importance.ALWAYS)
+ protected Set<String> mExcludeFilters = new HashSet<>();
+
+ @Option(name = CompatibilityTest.ABI_OPTION,
+ shortName = 'a',
+ description = "the abi to test.",
+ importance = Importance.IF_UNSET)
+ protected String mAbiName = null;
+
+ @Option(name = CompatibilityTest.MODULE_OPTION,
+ shortName = 'm',
+ description = "the test module to run.",
+ importance = Importance.IF_UNSET)
+ protected String mModuleName = null;
+
+ @Option(name = CompatibilityTest.TEST_OPTION,
+ shortName = CompatibilityTest.TEST_OPTION_SHORT_NAME,
+ description = "the test run.",
+ importance = Importance.IF_UNSET)
+ protected String mTestName = null;
+
+ @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
+ description = "used with " + CompatibilityTest.RETRY_OPTION + ", retry tests"
+ + " of a certain status. Possible values include \"failed\" and \"not_executed\".",
+ importance = Importance.IF_UNSET)
+ protected RetryType mRetryType = null;
+
+ /* Instance variables handy for retreiving the result to be retried */
+ private CompatibilityBuildHelper mBuild = null;
+ private int mSessionId;
+
+ /* Sets to be populated by retry logic and returned by getter methods */
+ private Set<String> mRetryIncludes;
+ private Set<String> mRetryExcludes;
+
+ /**
+ * Constructor for a {@link RetryFilterHelper}. Requires a CompatibilityBuildHelper for
+ * retrieving previous sessions and the ID of the session to retry.
+ */
+ public RetryFilterHelper(CompatibilityBuildHelper build, int sessionId) {
+ mBuild = build;
+ mSessionId = sessionId;
+ }
+
+ /**
+ * Throws an {@link IllegalArgumentException} if the device build fingerprint doesn't match
+ * the fingerprint recorded in the previous session's result.
+ */
+ public void validateBuildFingerprint(ITestDevice device) throws DeviceNotAvailableException {
+ String oldBuildFingerprint = new LightInvocationResult(getResult()).getBuildFingerprint();
+ String currentBuildFingerprint = device.getProperty("ro.build.fingerprint");
+ if (!oldBuildFingerprint.equals(currentBuildFingerprint)) {
+ throw new IllegalArgumentException(String.format(
+ "Device build fingerprint must match %s to retry session %d",
+ oldBuildFingerprint, mSessionId));
+ }
+ }
+
+ /**
+ * Copy all applicable options from an input object to this instance of RetryFilterHelper.
+ */
+ public void setAllOptionsFrom(Object obj) {
+ clearOptions(); // Remove existing options first
+ OptionCopier.copyOptionsNoThrow(obj, this);
+ }
+
+ /**
+ * Set a single option on this instance of RetryFilterHelper
+ * @throws {@link ConfigurationException} if the option cannot be set.
+ */
+ public void setOption(String option, String value) throws ConfigurationException {
+ OptionSetter setter = new OptionSetter(this);
+ setter.setOptionValue(option, value);
+ }
+
+ /**
+ * Clear all option values of this RetryFilterHelper.
+ */
+ public void clearOptions() {
+ mSubPlan = null;
+ mIncludeFilters = new HashSet<>();
+ mExcludeFilters = new HashSet<>();
+ mModuleName = null;
+ mTestName = null;
+ mRetryType = null;
+ mAbiName = null;
+ }
+
+ /**
+ * Using command-line arguments from the previous session's result, set the input object's
+ * option values to the values applied in the previous session.
+ */
+ public void setCommandLineOptionsFor(Object obj) {
+ // only need light version to retrieve command-line args
+ IInvocationResult result = new LightInvocationResult(getResult());
+ String retryCommandLineArgs = result.getCommandLineArgs();
+ if (retryCommandLineArgs != null) {
+ try {
+ // parse the command-line string from the result file and set options
+ ArgsOptionParser parser = new ArgsOptionParser(obj);
+ parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, obj));
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve an instance of the result to retry using the instance variables referencing
+ * the build and the desired session ID. While it is faster to load this result once and
+ * store it as an instance variable, {@link IInvocationResult} objects are large, and
+ * memory is of greater concern.
+ */
+ public IInvocationResult getResult() {
+ IInvocationResult result = null;
+ try {
+ result = ResultHandler.findResult(mBuild.getResultsDir(), mSessionId);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ if (result == null) {
+ throw new IllegalArgumentException(String.format(
+ "Could not find session with id %d", mSessionId));
+ }
+ return result;
+ }
+
+ /**
+ * Populate mRetryIncludes and mRetryExcludes based on the options and the result set for
+ * this instance of RetryFilterHelper.
+ */
+ public void populateRetryFilters() {
+ mRetryIncludes = new HashSet<>(mIncludeFilters); // reset for each population
+ mRetryExcludes = new HashSet<>(mExcludeFilters); // reset for each population
+ if (RetryType.CUSTOM.equals(mRetryType)) {
+ Set<String> customIncludes = new HashSet<>(mIncludeFilters);
+ Set<String> customExcludes = new HashSet<>(mExcludeFilters);
+ if (mSubPlan != null) {
+ ISubPlan retrySubPlan = SubPlanHelper.getSubPlanByName(mBuild, mSubPlan);
+ customIncludes.addAll(retrySubPlan.getIncludeFilters());
+ customExcludes.addAll(retrySubPlan.getExcludeFilters());
+ }
+ // If includes were added, only use those includes. Also use excludes added directly
+ // or by subplan. Otherwise, default to normal retry.
+ if (!customIncludes.isEmpty()) {
+ mRetryIncludes.clear();
+ mRetryIncludes.addAll(customIncludes);
+ mRetryExcludes.addAll(customExcludes);
+ return;
+ }
+ }
+ // remove any extra filtering options
+ // TODO(aaronholden) remove non-plan includes (e.g. those in cts-vendor-interface)
+ // TODO(aaronholden) remove non-known-failure excludes
+ mModuleName = null;
+ mTestName = null;
+ mSubPlan = null;
+ populateFiltersBySubPlan();
+ populatePreviousSessionFilters();
+ }
+
+ /* Generation of filters based on previous sessions is implemented thoroughly in SubPlanHelper,
+ * and retry filter generation is just a subset of the use cases for the subplan retry logic.
+ * Use retry type to determine which result types SubPlanHelper targets. */
+ private void populateFiltersBySubPlan() {
+ SubPlanHelper retryPlanCreator = new SubPlanHelper();
+ retryPlanCreator.setResult(getResult());
+ if (RetryType.FAILED.equals(mRetryType)) {
+ // retry only failed tests
+ retryPlanCreator.addResultType(SubPlanHelper.FAILED);
+ } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
+ // retry only not executed tests
+ retryPlanCreator.addResultType(SubPlanHelper.NOT_EXECUTED);
+ } else {
+ // retry both failed and not executed tests
+ retryPlanCreator.addResultType(SubPlanHelper.FAILED);
+ retryPlanCreator.addResultType(SubPlanHelper.NOT_EXECUTED);
+ }
+ try {
+ ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuild);
+ mRetryIncludes.addAll(retryPlan.getIncludeFilters());
+ mRetryExcludes.addAll(retryPlan.getExcludeFilters());
+ } catch (ConfigurationException e) {
+ throw new RuntimeException ("Failed to create subplan for retry", e);
+ }
+ }
+
+ /* Retrieves the options set via command-line on the previous session, and generates/adds
+ * filters accordingly */
+ private void populatePreviousSessionFilters() {
+ // Temporarily store options from this instance in another instance
+ RetryFilterHelper tmpHelper = new RetryFilterHelper(mBuild, mSessionId);
+ tmpHelper.setAllOptionsFrom(this);
+ // Copy command-line args from previous session to this RetryFilterHelper's options
+ setCommandLineOptionsFor(this);
+
+ mRetryIncludes.addAll(mIncludeFilters);
+ mRetryExcludes.addAll(mExcludeFilters);
+ if (mSubPlan != null) {
+ ISubPlan retrySubPlan = SubPlanHelper.getSubPlanByName(mBuild, mSubPlan);
+ mRetryIncludes.addAll(retrySubPlan.getIncludeFilters());
+ mRetryExcludes.addAll(retrySubPlan.getExcludeFilters());
+ }
+ if (mModuleName != null) {
+ try {
+ List<String> modules = ModuleRepo.getModuleNamesMatching(
+ mBuild.getTestsDir(), mModuleName);
+ if (modules.size() == 0) {
+ throw new IllegalArgumentException(
+ String.format("No modules found matching %s", mModuleName));
+ } else if (modules.size() > 1) {
+ throw new IllegalArgumentException(String.format(
+ "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
+ mModuleName, ArrayUtil.join("\n", modules)));
+ } else {
+ String module = modules.get(0);
+ cleanFilters(mRetryIncludes, module);
+ cleanFilters(mRetryExcludes, module);
+ mRetryIncludes.add(new TestFilter(mAbiName, module, mTestName).toString());
+ }
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ } else if (mTestName != null) {
+ throw new IllegalArgumentException(
+ "Test name given without module name. Add --module <module-name>");
+ }
+
+ // Copy options for current session back to this instance
+ setAllOptionsFrom(tmpHelper);
+ }
+
+ /* Helper method designed to remove filters in a list not applicable to the given module */
+ private static void cleanFilters(Set<String> filters, String module) {
+ Set<String> cleanedFilters = new HashSet<String>();
+ for (String filter : filters) {
+ if (module.equals(TestFilter.createFrom(filter).getName())) {
+ cleanedFilters.add(filter); // Module name matches, filter passes
+ }
+ }
+ filters.clear();
+ filters.addAll(cleanedFilters);
+ }
+
+ /** Retrieve include filters to be applied on retry */
+ public Set<String> getIncludeFilters() {
+ return new HashSet<>(mRetryIncludes);
+ }
+
+ /** Retrieve exclude filters to be applied on retry */
+ public Set<String> getExcludeFilters() {
+ return new HashSet<>(mRetryExcludes);
+ }
+
+ /** Clears retry filters and internal storage of options, except buildInfo and session ID */
+ public void tearDown() {
+ clearOptions();
+ mRetryIncludes = null;
+ mRetryExcludes = null;
+ // keep references to buildInfo and session ID
+ }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java
new file mode 100644
index 0000000..b5d3cd5
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.android.compatibility.common.tradefed.util;
+
+/**
+ * Enum for --retry-type option value in compatibility testing.
+ */
+public enum RetryType {
+ FAILED,
+ NOT_EXECUTED,
+ CUSTOM;
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index bb96ed2..b6861db 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -25,7 +25,7 @@
import com.android.compatibility.common.tradefed.result.ConsoleReporterTest;
import com.android.compatibility.common.tradefed.result.MetadataReporterTest;
import com.android.compatibility.common.tradefed.result.ResultReporterTest;
-import com.android.compatibility.common.tradefed.result.SubPlanCreatorTest;
+import com.android.compatibility.common.tradefed.result.SubPlanHelperTest;
import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
import com.android.compatibility.common.tradefed.targetprep.SettingsPreparerTest;
import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBaseTest;
@@ -36,6 +36,7 @@
import com.android.compatibility.common.tradefed.testtype.SubPlanTest;
import com.android.compatibility.common.tradefed.util.CollectorUtilTest;
import com.android.compatibility.common.tradefed.util.OptionHelperTest;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelperTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@@ -67,7 +68,7 @@
ConsoleReporterTest.class,
MetadataReporterTest.class,
ResultReporterTest.class,
- SubPlanCreatorTest.class,
+ SubPlanHelperTest.class,
// targetprep
PropertyCheckTest.class,
@@ -84,6 +85,7 @@
// util
CollectorUtilTest.class,
OptionHelperTest.class,
+ RetryFilterHelperTest.class,
})
public class UnitTests {
// empty on purpose
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
index c8896a9..8ef909e 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
@@ -185,7 +185,6 @@
mReporter.invocationEnded(500);
EasyMock.verify(mMockDevice, mMockBuildInfo);
IInvocationResult result = mReporter.getResult();
- assertEquals(0, result.getNotExecuted());
assertEquals(2, result.countResults(TestStatus.PASS));
assertEquals(1, result.countResults(TestStatus.FAIL));
assertEquals(1, result.getModules().size());
@@ -212,7 +211,6 @@
mReporter.invocationEnded(500);
EasyMock.verify(mMockDevice, mMockBuildInfo);
IInvocationResult result = mReporter.getResult();
- assertEquals(1, result.getNotExecuted());
assertEquals(1, result.countResults(TestStatus.PASS));
assertEquals(1, result.countResults(TestStatus.FAIL));
// Module should not be seen as complete.
@@ -243,7 +241,6 @@
EasyMock.verify(mMockDevice, mMockBuildInfo);
IInvocationResult result = mReporter.getResult();
// FIXME: All tests should be marked as executed and not aggregating the count.
- assertEquals(3, result.getNotExecuted());
assertEquals(2, result.countResults(TestStatus.PASS));
assertEquals(1, result.countResults(TestStatus.FAIL));
// FIXME: Module should be complete since all its test have run.
@@ -283,7 +280,6 @@
mReporter.invocationEnded(500);
IInvocationResult result = mReporter.getResult();
- assertEquals(1, result.getNotExecuted());
assertEquals(1, result.countResults(TestStatus.PASS));
assertEquals(1, result.countResults(TestStatus.FAIL));
// Module should not be seen as complete.
@@ -313,7 +309,6 @@
result = mReporter.getResult();
// FIXME: We should only have 1 not_executed in the retry too. They should not aggregate
// from one run to another.
- assertEquals(2, result.getNotExecuted());
assertEquals(1, result.countResults(TestStatus.PASS));
assertEquals(1, result.countResults(TestStatus.FAIL));
// Module should not be seen as complete.
@@ -352,7 +347,6 @@
mReporter.invocationEnded(500);
IInvocationResult result = mReporter.getResult();
- assertEquals(1, result.getNotExecuted());
assertEquals(1, result.countResults(TestStatus.PASS));
assertEquals(1, result.countResults(TestStatus.FAIL));
// Module should not be seen as complete.
@@ -386,7 +380,6 @@
// Check retry results
result = mReporter.getResult();
- assertEquals(0, result.getNotExecuted());
assertEquals(3, result.countResults(TestStatus.PASS));
assertEquals(0, result.countResults(TestStatus.FAIL));
// Module should be marked as complete after retry.
@@ -489,7 +482,6 @@
EasyMock.verify(mMockDevice, mMockBuildInfo);
// Check aggregated results to make sure it's consistent.
IInvocationResult result = mReporter.getResult();
- assertEquals(0, result.getNotExecuted());
assertEquals(4, result.countResults(TestStatus.PASS));
assertEquals(4, result.countResults(TestStatus.FAIL));
assertEquals(2, result.getModules().size());
@@ -540,7 +532,6 @@
EasyMock.verify(mMockDevice, mMockBuildInfo);
// Check aggregated results to make sure it's consistent.
IInvocationResult result = mReporter.getResult();
- assertEquals(4, result.getNotExecuted());
assertEquals(4, result.countResults(TestStatus.PASS));
assertEquals(4, result.countResults(TestStatus.FAIL));
assertEquals(2, result.getModules().size());
@@ -592,7 +583,6 @@
EasyMock.verify(mMockDevice, mMockBuildInfo);
IInvocationResult result = mReporter.getResult();
- assertEquals(0, result.getNotExecuted());
assertEquals(2, result.countResults(TestStatus.PASS));
assertEquals(2, result.countResults(TestStatus.FAIL));
// FIXME: Only one module should be expected since within the one shard requested to run
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
index 763acb7..23fcb9a 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
@@ -93,7 +93,6 @@
mInvocationResult = resultReporter.getResult();
mModuleResult = mInvocationResult.getOrCreateModule("Module-1");
mModuleResult.setDone(true);
- mModuleResult.setNotExecuted(0);
ICaseResult caseResult = mModuleResult.getOrCreateResult("Case-1");
ITestResult test1 = caseResult.getOrCreateResult("Test1");
test1.passed(mReportLog);
@@ -103,7 +102,6 @@
IModuleResult moduleResult2 = mInvocationResult.getOrCreateModule("Module-2");
ICaseResult caseResult2 = moduleResult2.getOrCreateResult("Case-2");
mModuleResult.setDone(false);
- mModuleResult.setNotExecuted(1);
ITestResult test3 = caseResult2.getOrCreateResult("Test3");
test3.passed(mReportLog);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
similarity index 96%
rename from common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
rename to common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
index e3240c1..db6ca6e 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
@@ -39,7 +39,7 @@
import java.util.Arrays;
import java.util.Set;
-public class SubPlanCreatorTest extends TestCase {
+public class SubPlanHelperTest extends TestCase {
// Values used to populate mock results
private static final String SUITE_NAME = "CTS";
@@ -75,7 +75,7 @@
private static final String SP_RESULT_TYPE_NOT_EXECUTED = "not_executed";
private CompatibilityBuildHelper mBuildHelper;
- private SubPlanCreator mSubPlanCreator;
+ private SubPlanHelper mSubPlanHelper;
private File mResultsDir = null;
private File mResultDir = null;
@@ -89,8 +89,8 @@
mBuildHelper = new SpctMockCompatibilityBuildHelper(new BuildInfo("0", "", ""));
populateResults();
- mSubPlanCreator = new SubPlanCreator();
- ArgsOptionParser optionParser = new ArgsOptionParser(mSubPlanCreator);
+ mSubPlanHelper = new SubPlanHelper();
+ ArgsOptionParser optionParser = new ArgsOptionParser(mSubPlanHelper);
optionParser.parse(Arrays.asList(
"-n", SP_NAME,
"--session", SP_SESSION,
@@ -109,7 +109,7 @@
}
public void testCreateSubPlan() throws Exception {
- ISubPlan plan = mSubPlanCreator.createSubPlan(mBuildHelper);
+ ISubPlan plan = mSubPlanHelper.createSubPlan(mBuildHelper);
Set<String> planIncludes = plan.getIncludeFilters();
Set<String> planExcludes = plan.getExcludeFilters();
TestFilter mf1 = new TestFilter(ABI, NAME_A, null);
@@ -137,7 +137,6 @@
moduleATest1.setResultStatus(TestStatus.PASS);
ITestResult moduleATest2 = moduleACase.getOrCreateResult(METHOD_2);
moduleATest2.setResultStatus(null); // not executed test
- moduleA.setNotExecuted(1);
IModuleResult moduleB = result.getOrCreateModule(ID_B);
moduleB.setDone(true);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java
new file mode 100644
index 0000000..05d35ec
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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 com.android.compatibility.common.tradefed.util;
+
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.tradefed.config.OptionSetter;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link RetryFilterHelper}
+ */
+public class RetryFilterHelperTest extends TestCase {
+
+ private static final String TEST_STRING = "abcd";
+ private static final RetryType TEST_RETRY_TYPE = RetryType.FAILED;
+
+ public void testSetAllOptionsFrom() throws Exception {
+ RetryFilterHelper helper = new RetryFilterHelper(null, 0);
+ RetryFilterHelper otherObj = new RetryFilterHelper(null, 0);
+ OptionSetter otherObjSetter = new OptionSetter(otherObj);
+ otherObjSetter.setOptionValue(CompatibilityTest.SUBPLAN_OPTION, TEST_STRING);
+ helper.setAllOptionsFrom(otherObj);
+ assertEquals(TEST_STRING, helper.mSubPlan);
+ }
+
+ public void testClearOptions() throws Exception {
+ RetryFilterHelper helper = new RetryFilterHelper(null, 0);
+ OptionSetter setter = new OptionSetter(helper);
+ setter.setOptionValue(CompatibilityTest.SUBPLAN_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.INCLUDE_FILTER_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.EXCLUDE_FILTER_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.ABI_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.MODULE_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.TEST_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.TEST_OPTION, TEST_RETRY_TYPE.name());
+ helper.clearOptions();
+ assertTrue(helper.mSubPlan == null);
+ assertTrue(helper.mIncludeFilters.isEmpty());
+ assertTrue(helper.mExcludeFilters.isEmpty());
+ assertTrue(helper.mAbiName == null);
+ assertTrue(helper.mModuleName == null);
+ assertTrue(helper.mTestName == null);
+ assertTrue(helper.mRetryType == null);
+ }
+
+}
diff --git a/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java b/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
index 32fa532..ce39f38 100644
--- a/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
+++ b/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
@@ -331,7 +331,6 @@
sb.append(buildFingerprint).append(SEPARATOR)
.append(module.getId()).append(SEPARATOR)
.append(module.isDone()).append(SEPARATOR)
- .append(module.getNotExecuted()).append(SEPARATOR)
.append(module.countResults(TestStatus.FAIL));
return sb.toString();
}
diff --git a/common/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
index 6306287..5f88bcd 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -93,7 +93,6 @@
private static final String MODULES_DONE_ATTR = "modules_done";
private static final String MODULES_TOTAL_ATTR = "modules_total";
private static final String NAME_ATTR = "name";
- private static final String NOT_EXECUTED_ATTR = "not_executed";
private static final String OS_ARCH_ATTR = "os_arch";
private static final String OS_NAME_ATTR = "os_name";
private static final String OS_VERSION_ATTR = "os_version";
@@ -210,9 +209,6 @@
boolean done = Boolean.parseBoolean(parser.getAttributeValue(NS, DONE_ATTR));
IModuleResult module = invocation.getOrCreateModule(moduleId);
module.initializeDone(done);
- int notExecuted = Integer.parseInt(
- parser.getAttributeValue(NS, NOT_EXECUTED_ATTR));
- module.setNotExecuted(notExecuted);
long runtime = Long.parseLong(parser.getAttributeValue(NS, RUNTIME_ATTR));
module.addRuntime(runtime);
while (parser.nextTag() == XmlPullParser.START_TAG) {
@@ -300,7 +296,6 @@
throws IOException, XmlPullParserException {
int passed = result.countResults(TestStatus.PASS);
int failed = result.countResults(TestStatus.FAIL);
- int notExecuted = result.getNotExecuted();
File resultFile = new File(resultDir, TEST_RESULT_FILE_NAME);
OutputStream stream = new FileOutputStream(resultFile);
XmlSerializer serializer = XmlPullParserFactory.newInstance(TYPE, null).newSerializer();
@@ -371,7 +366,6 @@
serializer.startTag(NS, SUMMARY_TAG);
serializer.attribute(NS, PASS_ATTR, Integer.toString(passed));
serializer.attribute(NS, FAILED_ATTR, Integer.toString(failed));
- serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(notExecuted));
serializer.attribute(NS, MODULES_DONE_ATTR,
Integer.toString(result.getModuleCompleteCount()));
serializer.attribute(NS, MODULES_TOTAL_ATTR,
@@ -385,7 +379,6 @@
serializer.attribute(NS, ABI_ATTR, module.getAbi());
serializer.attribute(NS, RUNTIME_ATTR, String.valueOf(module.getRuntime()));
serializer.attribute(NS, DONE_ATTR, Boolean.toString(module.isDone()));
- serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(module.getNotExecuted()));
serializer.attribute(NS, PASS_ATTR,
Integer.toString(module.countResults(TestStatus.PASS)));
for (ICaseResult cr : module.getResults()) {
diff --git a/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java b/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
index 4035dd3..49c4045 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
@@ -49,8 +49,6 @@
private static final String NAME_B = "ModuleB";
private static final String DONE_A = "false";
private static final String DONE_B = "true";
- private static final String NOT_EXECUTED_A = "1";
- private static final String NOT_EXECUTED_B = "0";
private static final String RUNTIME_A = "100";
private static final String RUNTIME_B = "200";
private static final String ABI = "mips64";
@@ -105,10 +103,10 @@
" <Build build_fingerprint=\"%s\" " + BUILD_ID + "=\"%s\" " +
BUILD_PRODUCT + "=\"%s\" />\n";
private static final String XML_SUMMARY =
- " <Summary pass=\"%d\" failed=\"%d\" not_executed=\"%d\" " +
+ " <Summary pass=\"%d\" failed=\"%d\" " +
"modules_done=\"1\" modules_total=\"1\" />\n";
private static final String XML_MODULE =
- " <Module name=\"%s\" abi=\"%s\" device=\"%s\" runtime=\"%s\" done=\"%s\" not_executed=\"%s\">\n" +
+ " <Module name=\"%s\" abi=\"%s\" device=\"%s\" runtime=\"%s\" done=\"%s\">\n" +
"%s" +
" </Module>\n";
private static final String XML_CASE =
@@ -167,7 +165,6 @@
moduleATest1.setResultStatus(TestStatus.PASS);
ITestResult moduleATest2 = moduleACase.getOrCreateResult(METHOD_2);
moduleATest2.setResultStatus(null); // not executed test
- moduleA.setNotExecuted(1);
// Module B: test3 fails, test4 passes with report log, test5 passes with skip
IModuleResult moduleB = result.getOrCreateModule(ID_B);
moduleB.setDone(true);
@@ -231,7 +228,7 @@
String moduleATest = String.format(XML_TEST_PASS, METHOD_1);
String moduleACases = String.format(XML_CASE, CLASS_A, moduleATest);
String moduleA = String.format(XML_MODULE, NAME_A, ABI, DEVICE_A, RUNTIME_A, DONE_A,
- NOT_EXECUTED_A, moduleACases);
+ moduleACases);
String moduleBTest3 = String.format(XML_TEST_FAIL, METHOD_3, MESSAGE, STACK_TRACE,
BUG_REPORT, LOGCAT, SCREENSHOT);
String moduleBTest4 = String.format(XML_TEST_RESULT, METHOD_4, SUMMARY_SOURCE,
@@ -241,7 +238,7 @@
String moduleBTests = String.join("", moduleBTest3, moduleBTest4, moduleBTest5);
String moduleBCases = String.format(XML_CASE, CLASS_B, moduleBTests);
String moduleB = String.format(XML_MODULE, NAME_B, ABI, DEVICE_B, RUNTIME_B, DONE_B,
- NOT_EXECUTED_B, moduleBCases);
+ moduleBCases);
String modules = String.join("", moduleA, moduleB);
String hostName = "";
try {
@@ -265,7 +262,6 @@
static void checkLightResult(IInvocationResult lightResult) throws Exception {
assertEquals("Expected 3 passes", 3, lightResult.countResults(TestStatus.PASS));
assertEquals("Expected 1 failure", 1, lightResult.countResults(TestStatus.FAIL));
- assertEquals("Expected 1 not executed", 1, lightResult.getNotExecuted());
Map<String, String> buildInfo = lightResult.getInvocationInfo();
assertEquals("Incorrect Build ID", EXAMPLE_BUILD_ID, buildInfo.get(BUILD_ID));
@@ -287,7 +283,6 @@
static void checkResult(IInvocationResult result) throws Exception {
assertEquals("Expected 3 passes", 3, result.countResults(TestStatus.PASS));
assertEquals("Expected 1 failure", 1, result.countResults(TestStatus.FAIL));
- assertEquals("Expected 1 not executed", 1, result.getNotExecuted());
Map<String, String> buildInfo = result.getInvocationInfo();
assertEquals("Incorrect Build ID", EXAMPLE_BUILD_ID, buildInfo.get(BUILD_ID));
diff --git a/development/ide/eclipse/cts-tradefed-harness.classpath b/development/ide/eclipse/cts-tradefed-harness.classpath
new file mode 100644
index 0000000..3355a7d
--- /dev/null
+++ b/development/ide/eclipse/cts-tradefed-harness.classpath
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="cts/common/host-side/tradefed/src"/>
+ <classpathentry kind="src" path="cts/common/host-side/util/src"/>
+ <classpathentry kind="src" path="cts/common/util/src"/>
+ <classpathentry kind="src" path="cts/libs/json/src"/>
+ <classpathentry kind="src" path="tools/loganalysis/src"/>
+ <classpathentry kind="src" path="tools/tradefederation/src"/>
+ <classpathentry kind="src" path="tools/tradefederation/remote/src"/>
+ <classpathentry kind="src" path="external/easymock/src"/>
+ <classpathentry kind="src" path="external/guava/guava/src"/>
+ <classpathentry kind="src" path="external/hamcrest/hamcrest-core/src/main/java"/>
+ <classpathentry kind="src" path="external/jline/src/src/main/java"/>
+ <classpathentry kind="src" path="external/jsr305/ri/src/main/java"/>
+ <classpathentry kind="src" path="external/junit/src/main/java"/>
+ <classpathentry kind="src" path="external/mockito/src/main/java"/>
+ <classpathentry kind="src" path="external/objenesis/main/src/main/java"/>
+ <classpathentry kind="src" path="external/protobuf/java/core/src/main/java"/>
+ <classpathentry including="com/" kind="src" path="out/host/common/obj/JAVA_LIBRARIES/cts-tradefed_intermediates"/>
+ <classpathentry kind="src" path="out/host/common/obj/JAVA_LIBRARIES/host-libprotobuf-java-full_intermediates/proto/src"/>
+ <classpathentry kind="src" path="out/host/common/obj/JAVA_LIBRARIES/tradefed-protos_intermediates/proto/src"/>
+ <classpathentry kind="src" path="tools/tradefederation/tests/src"/>
+ <classpathentry kind="src" path="cts/common/host-side/tradefed/tests/src"/>
+ <classpathentry kind="src" path="cts/common/host-side/util/tests/src"/>
+ <classpathentry kind="lib" path="external/mockito/lib/byte-buddy-1.6.9.jar"/>
+ <classpathentry kind="lib" path="external/mockito/lib/byte-buddy-agent-1.6.9.jar"/>
+ <classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/classes-jarjar.jar"/>
+ <classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/conscrypt_intermediates/classes-jarjar.jar"/>
+ <classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/okhttp_intermediates/classes-jarjar.jar"/>
+ <classpathentry kind="lib" path="prebuilts/misc/common/commons-compress/commons-compress-prebuilt.jar"/>
+ <classpathentry kind="lib" path="prebuilts/misc/common/ddmlib/ddmlib-prebuilt.jar"/>
+ <classpathentry kind="lib" path="prebuilts/misc/common/devtools-annotations/devtools-annotations-prebuilt.jar"/>
+ <classpathentry kind="lib" path="prebuilts/misc/common/json/json-prebuilt.jar"/>
+ <classpathentry kind="lib" path="prebuilts/misc/common/kxml2/kxml2-2.3.0.jar"/>
+ <classpathentry kind="lib" path="prebuilts/misc/common/sdklib/sdklib-prebuilt.jar"/>
+ <classpathentry kind="lib" path="prebuilts/sdk/tools/jack-jacoco-reporter.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index 6ee51ae..e8dca57 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -291,29 +291,34 @@
final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
- enableWriteSettings(WRITE_PKG);
- runDeviceTests(
- WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testChangeDefaultUris", users[0]);
-
assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
for (int user : users) {
+ enableWriteSettings(WRITE_PKG, user);
+ runDeviceTests(
+ WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testChangeDefaultUris", user);
+
runDeviceTests(
NONE_PKG, NONE_PKG + ".ReadDefaultUris", "testPlayDefaultUris", user);
}
} finally {
// Make sure the provider and uris are reset on failure.
- runDeviceTests(
- WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testResetDefaultUris", users[0]);
+ for (int user : users) {
+ runDeviceTests(
+ WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testResetDefaultUris", user);
+ }
getDevice().uninstallPackage(NONE_PKG);
getDevice().uninstallPackage(WRITE_PKG);
removeUsersForTest(users);
}
}
- private void enableWriteSettings(String packageName) throws DeviceNotAvailableException {
+ private void enableWriteSettings(String packageName, int userId)
+ throws DeviceNotAvailableException {
StringBuilder cmd = new StringBuilder();
- cmd.append("appops set ");
+ cmd.append("appops set --user ");
+ cmd.append(userId);
+ cmd.append(" ");
cmd.append(packageName);
cmd.append(" android:write_settings allow");
getDevice().executeShellCommand(cmd.toString());
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
index 3b6cbb1..cf8a354 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
@@ -64,6 +64,11 @@
getDevice().uninstallPackage(PKG);
}
+ public void testUsesLibrary() throws Exception {
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
+ runDeviceTests(PKG, ".UsesLibraryTest", "testUsesLibrary");
+ }
+
public void testMissingLibrary() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
runDeviceTests(PKG, ".UsesLibraryTest", "testMissingLibrary");
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
index f5666d1..4b0bec3 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
@@ -103,6 +103,7 @@
* Verify we can write to our own package dirs.
*/
public void testAllPackageDirsWritable() throws Exception {
+ final long testValue = 12345000;
final List<File> paths = getAllPackageSpecificPaths(getContext());
for (File path : paths) {
assertNotNull("Valid media must be inserted during CTS", path);
@@ -122,6 +123,12 @@
assertEquals(32, readInt(directChild));
assertEquals(64, readInt(subdirChild));
+
+ assertTrue("Must be able to set last modified", directChild.setLastModified(testValue));
+ assertTrue("Must be able to set last modified", subdirChild.setLastModified(testValue));
+
+ assertEquals(testValue, directChild.lastModified());
+ assertEquals(testValue, subdirChild.lastModified());
}
for (File path : paths) {
diff --git a/hostsidetests/appsecurity/test-apps/InstantCookieApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/InstantCookieApp/AndroidManifest.xml
index 7a937d5..31f653d 100644
--- a/hostsidetests/appsecurity/test-apps/InstantCookieApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/InstantCookieApp/AndroidManifest.xml
@@ -17,7 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.instant.cookie"
android:versionCode="1"
- android:versionName="1.0">
+ android:versionName="1.0"
+ android:targetSandboxVersion="2">
<application/>
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
index 64495a4..e47f6ed 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
@@ -25,10 +25,12 @@
import junit.framework.AssertionFailedError;
+import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -152,7 +154,21 @@
return success;
}
- public static boolean shouldHaveQuota(StructUtsname uname) {
+ public static boolean shouldHaveQuota(StructUtsname uname) throws Exception {
+ try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ final String[] fields = line.split(" ");
+ final String target = fields[1];
+ final String format = fields[2];
+
+ if (target.equals("/data") && !format.equals("ext4")) {
+ Log.d(TAG, "Assuming no quota support because /data is " + format);
+ return false;
+ }
+ }
+ }
+
final Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)").matcher(uname.release);
if (!matcher.find()) {
throw new IllegalStateException("Failed to parse version: " + uname.release);
diff --git a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
index 8066f70..72a420d 100644
--- a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
@@ -201,7 +201,7 @@
final long manualSize = getSizeManual(Environment.getExternalStorageDirectory());
final long statsSize = stats.queryExternalStatsForUser(null, user).getTotalBytes();
- assertEquals(manualSize, statsSize);
+ assertMostlyEquals(manualSize, statsSize);
}
public void testVerifyCategory() throws Exception {
@@ -234,16 +234,24 @@
// Ask apps to allocate some cached data
final long targetA = doAllocateProvider(PKG_A, 0.5, 1262304000);
final long targetB = doAllocateProvider(PKG_B, 2.0, 1420070400);
+ final long totalAllocated = targetA + targetB;
// Apps using up some cache space shouldn't change how much we can
// allocate, or how much we think is free; but it should decrease real
// disk space.
- assertMostlyEquals(beforeAllocatable, sm.getAllocatableBytes(filesDir, 0),
- 10 * MB_IN_BYTES);
- assertMostlyEquals(beforeFree, stats.getFreeBytes(null),
- 10 * MB_IN_BYTES);
- assertMostlyEquals(targetA + targetB, beforeRaw - filesDir.getUsableSpace(),
- 10 * MB_IN_BYTES);
+ if (stats.isQuotaSupported(null)) {
+ assertMostlyEquals(beforeAllocatable,
+ sm.getAllocatableBytes(filesDir, 0), 10 * MB_IN_BYTES);
+ assertMostlyEquals(beforeFree,
+ stats.getFreeBytes(null), 10 * MB_IN_BYTES);
+ } else {
+ assertMostlyEquals(beforeAllocatable - totalAllocated,
+ sm.getAllocatableBytes(filesDir, 0), 10 * MB_IN_BYTES);
+ assertMostlyEquals(beforeFree - totalAllocated,
+ stats.getFreeBytes(null), 10 * MB_IN_BYTES);
+ }
+ assertMostlyEquals(beforeRaw - totalAllocated,
+ filesDir.getUsableSpace(), 10 * MB_IN_BYTES);
assertMostlyEquals(targetA, getCacheBytes(PKG_A, user));
assertMostlyEquals(targetB, getCacheBytes(PKG_B, user));
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index f8f6d92..4340c87 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -267,6 +267,11 @@
getUiDevice().pressBack();
waitForIdle();
+ if (isTv()) {
+ getUiDevice().pressHome();
+ waitForIdle();
+ }
+
// Open the app details settings
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
@@ -506,4 +511,9 @@
getInstrumentation().getUiAutomation().waitForIdle(IDLE_TIMEOUT_MILLIS,
GLOBAL_TIMEOUT_MILLIS);
}
+
+ private static boolean isTv() {
+ return getInstrumentation().getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java b/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
index c7b8f8b..0a1f012 100644
--- a/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
@@ -34,6 +34,17 @@
public class UsesLibraryTest extends InstrumentationTestCase {
private static final String TAG = "UsesLibraryTest";
+ public void testUsesLibrary() throws Exception {
+ ClassLoader loader = getClass().getClassLoader();
+ if (loader instanceof BaseDexClassLoader) {
+ Object[] dexElements = getDexElementsFromClassLoader((BaseDexClassLoader) loader);
+ for (Object dexElement : dexElements) {
+ DexFile dexFile = getDexFileFromDexElement(dexElement);
+ assertTrue(isDexFileBackedByOatFile(dexFile));
+ }
+ }
+ }
+
public void testMissingLibrary() throws Exception {
ClassLoader loader = getClass().getClassLoader();
if (loader instanceof BaseDexClassLoader) {
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
index cee1a50..398a75b 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
@@ -17,6 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.content">
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
<application>
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index fe4414b..2b08e1f 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -25,7 +25,10 @@
import android.content.Intent;
import android.content.SyncRequest;
import android.content.SyncResult;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.Bundle;
+import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
@@ -45,13 +48,17 @@
*/
@RunWith(AndroidJUnit4.class)
public class CtsSyncAccountAccessOtherCertTestCases {
- private static final long SYNC_TIMEOUT_MILLIS = 10000; // 20 sec
+ private static final long SYNC_TIMEOUT_MILLIS = 10000; // 10 sec
private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
@Test
public void testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount() throws Exception {
+ if (!hasDataConnection()) {
+ return;
+ }
+
Intent intent = new Intent(getContext(), StubActivity.class);
Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
@@ -63,6 +70,8 @@
result.getString(AccountManager.KEY_ACCOUNT_NAME),
result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+ waitForSyncManagerAccountChangeUpdate();
+
try {
CountDownLatch latch = new CountDownLatch(1);
@@ -114,4 +123,17 @@
private UiDevice getUiDevice() {
return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
}
+
+ private void waitForSyncManagerAccountChangeUpdate() {
+ // Wait for the sync manager to be notified for the new account.
+ // Unfortunately, there is no way to detect this event, sigh...
+ SystemClock.sleep(5000);
+ }
+
+ private boolean hasDataConnection() {
+ ConnectivityManager connectivityManager = getContext().getSystemService(
+ ConnectivityManager.class);
+ NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+ return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
+ }
}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
index ea46d61..2ecd27d 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
@@ -17,6 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.content">
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
<application>
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
index 17be8b6..223281b 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
@@ -27,7 +27,10 @@
import android.content.Intent;
import android.content.SyncRequest;
import android.content.SyncResult;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.Bundle;
+import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
@@ -41,10 +44,14 @@
*/
@RunWith(AndroidJUnit4.class)
public class CtsSyncAccountAccessSameCertTestCases {
- private static final long SYNC_TIMEOUT_MILLIS = 10000; // 20 sec
+ private static final long SYNC_TIMEOUT_MILLIS = 10000; // 10 sec
@Test
public void testAccountAccess_sameCertAsAuthenticatorCanSeeAccount() throws Exception {
+ if (!hasDataConnection()) {
+ return;
+ }
+
Intent intent = new Intent(getContext(), StubActivity.class);
Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
@@ -56,6 +63,8 @@
result.getString(AccountManager.KEY_ACCOUNT_NAME),
result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+ waitForSyncManagerAccountChangeUpdate();
+
try {
CountDownLatch latch = new CountDownLatch(1);
@@ -85,4 +94,17 @@
private Context getContext() {
return InstrumentationRegistry.getInstrumentation().getContext();
}
+
+ private void waitForSyncManagerAccountChangeUpdate() {
+ // Wait for the sync manager to be notified for the new account.
+ // Unfortunately, there is no way to detect this event, sigh...
+ SystemClock.sleep(5000);
+ }
+
+ private boolean hasDataConnection() {
+ ConnectivityManager connectivityManager = getContext().getSystemService(
+ ConnectivityManager.class);
+ NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+ return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
index d7c3bcf..6a01ea6 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
@@ -46,7 +46,7 @@
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
dpm.getPasswordQuality(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
String caseDescription = "initial";
assertPasswordSucceeds("1234", caseDescription);
@@ -56,7 +56,7 @@
dpm.setPasswordMinimumLength(mAdminComponent, 10);
caseDescription = "minimum password length = 10";
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient()); // length not checked for this quality
+ assertPasswordSufficiency(true); // length not checked for this quality
// TODO(ascull): fix resetPassword() logic so these succeed
assertPasswordFails("1234", caseDescription);
@@ -67,7 +67,7 @@
caseDescription = "minimum password length = 4";
assertEquals(4, dpm.getPasswordMinimumLength(
mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
assertPasswordSucceeds("1234", caseDescription);
assertPasswordSucceeds("abcd", caseDescription);
@@ -79,7 +79,7 @@
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
dpm.getPasswordQuality(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient()); // failure
+ assertPasswordSufficiency(false); // failure
String caseDescription = "initial";
assertPasswordSucceeds("1234", caseDescription);
@@ -89,7 +89,7 @@
dpm.setPasswordMinimumLength(mAdminComponent, 10);
caseDescription = "minimum password length = 10";
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -99,7 +99,7 @@
caseDescription = "minimum password length = 4";
assertEquals(4, dpm.getPasswordMinimumLength(
mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
assertPasswordSucceeds("1234", caseDescription);
assertPasswordSucceeds("abcd", caseDescription);
@@ -111,7 +111,7 @@
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
dpm.getPasswordQuality(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
String caseDescription = "initial";
assertPasswordFails("1234", caseDescription); // can't change
@@ -121,7 +121,7 @@
dpm.setPasswordMinimumLength(mAdminComponent, 10);
caseDescription = "minimum password length = 10";
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -131,7 +131,7 @@
caseDescription = "minimum password length = 4";
assertEquals(4, dpm.getPasswordMinimumLength(
mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
assertPasswordFails("1234", caseDescription);
assertPasswordSucceeds("abcd", caseDescription);
@@ -143,7 +143,7 @@
DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
dpm.getPasswordQuality(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
String caseDescription = "initial";
assertPasswordFails("1234", caseDescription);
@@ -153,7 +153,7 @@
dpm.setPasswordMinimumLength(mAdminComponent, 10);
caseDescription = "minimum password length = 10";
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -163,7 +163,7 @@
caseDescription = "minimum password length = 4";
assertEquals(4, dpm.getPasswordMinimumLength(
mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -372,6 +372,21 @@
private void assertPasswordSucceeds(String password, String restriction) {
boolean passwordResetResult = dpm.resetPassword(password, /* flags= */0);
assertTrue("Password '" + password + "' failed on " + restriction, passwordResetResult);
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
+ }
+
+ private void assertPasswordSufficiency(boolean expectPasswordSufficient) {
+ int retries = 15;
+ // isActivePasswordSufficient() gets the result asynchronously so let's retry a few times
+ while (retries >= 0 && dpm.isActivePasswordSufficient() != expectPasswordSufficient) {
+ retries--;
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ assertEquals(expectPasswordSufficient, dpm.isActivePasswordSufficient());
+
}
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
index e655d7b..a21d9e6 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
@@ -20,8 +20,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.test.ActivityInstrumentationTestCase2;
import android.support.test.InstrumentationRegistry;
+import android.test.ActivityInstrumentationTestCase2;
import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SanityTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SanityTest.java
new file mode 100644
index 0000000..28c7e25
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SanityTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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 com.android.cts.managedprofile;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+/**
+ * Basic sanity test to ensure some basic functionalities of work profile are working.
+ */
+public class SanityTest {
+ private static final ComponentName SIMPLE_APP_ACTIVITY =
+ new ComponentName(
+ "com.android.cts.launcherapps.simpleapp",
+ "com.android.cts.launcherapps.simpleapp.SimpleActivity");
+ /**
+ * Broadcast sent from com.android.cts.launcherapps.simpleapp.SimpleActivity.
+ */
+ private static final String ACTIVITY_LAUNCHED_ACTION =
+ "com.android.cts.launchertests.LauncherAppsTests.LAUNCHED_ACTION";
+
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ /**
+ * Verify that we can launch an app that installed in work profile only.
+ */
+ @Test
+ public void testLaunchAppInstalledInProfileOnly() throws Exception {
+ BlockingBroadcastReceiver receiver =
+ new BlockingBroadcastReceiver(mContext, ACTIVITY_LAUNCHED_ACTION);
+ try {
+ receiver.register();
+ Intent intent = new Intent();
+ intent.setComponent(SIMPLE_APP_ACTIVITY);
+ // Finish the activity after that.
+ intent.putExtra("finish", true);
+ mContext.startActivity(intent);
+ Intent receivedBroadcast = receiver.awaitForBroadcast();
+ assertNotNull(receivedBroadcast);
+ assertEquals(ACTIVITY_LAUNCHED_ACTION, receivedBroadcast.getAction());
+ } finally {
+ receiver.unregisterQuietly();
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
index d033785..a773737 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
@@ -27,7 +27,9 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mUserId = createUser();
+ if (isTestEnabled()) {
+ mUserId = createUser();
+ }
}
@Override
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index ba36fbf..84a08ee 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -71,6 +71,8 @@
private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
+ private static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
+ private static final String SIMPLE_APP_PKG = "com.android.cts.launcherapps.simpleapp";
private static final long TIMEOUT_USER_LOCKED_MILLIS = TimeUnit.SECONDS.toMillis(15);
@@ -870,6 +872,15 @@
}
}
+ public void testSanityCheck() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ // Install SimpleApp in work profile only and check activity in it can be launched.
+ installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
+ }
+
private void disableActivityForUser(String activityName, int userId)
throws DeviceNotAvailableException {
String command = "am start -W --user " + userId
diff --git a/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
index f76bf37..d4937aa 100644
--- a/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
+++ b/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
@@ -28,6 +28,10 @@
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
+ <service android:name=".BatteryStatsBackgroundService" android:exported="true" />
+
+ <activity android:name=".BatteryStatsForegroundActivity" android:exported="true" />
+
<service android:name=".BatteryStatsWifiTransferTests$TransferService" android:exported="true" />
<service android:name=".SimpleForegroundService" android:exported="true" />
@@ -35,9 +39,7 @@
<service android:name=".SimpleJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
- <activity android:name=".SimpleActivity" android:label="BatteryStats Test Activity" />
-
- <activity android:name=".BatteryStatsWifiScanTests$WiFiScanActivity" android:label="BatteryStats Wifi scan Test Activity" />
+ <activity android:name=".SimpleActivity" android:label="BatteryStats Test Activity" android:exported="true" />
<service android:name=".BatteryStatsAuthenticator"
android:exported="false">
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
new file mode 100644
index 0000000..a02fe83
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
@@ -0,0 +1,50 @@
+/*
+ * 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 com.android.server.cts.device.batterystats;
+
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+
+import org.junit.Assert;
+
+/** An service (to be run as a background process) which performs one of a number of actions. */
+public class BatteryStatsBackgroundService extends IntentService {
+ private static final String TAG = BatteryStatsBackgroundService.class.getSimpleName();
+
+ public BatteryStatsBackgroundService() {
+ super(BatteryStatsBackgroundService.class.getName());
+ }
+
+ @Override
+ public void onHandleIntent(Intent intent) {
+ try {
+ if (!isAppInBackground(this)) {
+ Log.e(TAG, "BackgroundService is not a background process!");
+ Assert.fail("Test requires BackgroundService to be in background.");
+ }
+ } catch(ReflectiveOperationException ex) {
+ Log.w(TAG, "Couldn't determine if app is in background. Proceeding with test anyway.");
+ }
+
+ doAction(this, intent.getStringExtra(KEY_ACTION));
+ }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
new file mode 100644
index 0000000..7528cac
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
@@ -0,0 +1,183 @@
+/*
+ * 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 com.android.server.cts.device.batterystats;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class BatteryStatsBgVsFgActions {
+ private static final String TAG = BatteryStatsBgVsFgActions.class.getSimpleName();
+
+ public static final String KEY_ACTION = "action";
+ public static final String ACTION_JOB_SCHEDULE = "action.jobs";
+ public static final String ACTION_WIFI_SCAN = "action.wifi_scan";
+
+ /** Perform the action specified by the given action code (see constants above). */
+ public static void doAction(Context ctx, String actionCode) {
+ if (actionCode == null) {
+ Log.e(TAG, "Intent was missing action.");
+ return;
+ }
+ sleep(100);
+ switch(actionCode) {
+ case ACTION_JOB_SCHEDULE:
+ doScheduleJob(ctx);
+ break;
+ case ACTION_WIFI_SCAN:
+ doWifi(ctx);
+ break;
+ default:
+ Log.e(TAG, "Intent had invalid action");
+ }
+ sleep(100);
+ }
+
+ private static void doScheduleJob(Context ctx) {
+ final ComponentName JOB_COMPONENT_NAME =
+ new ComponentName("com.android.server.cts.device.batterystats",
+ SimpleJobService.class.getName());
+ JobScheduler js = ctx.getSystemService(JobScheduler.class);
+ if (js == null) {
+ Log.e(TAG, "JobScheduler service not available");
+ return;
+ }
+ final JobInfo job = (new JobInfo.Builder(1, JOB_COMPONENT_NAME))
+ .setOverrideDeadline(0)
+ .build();
+ CountDownLatch latch = SimpleJobService.resetCountDownLatch();
+ js.schedule(job);
+ // Job starts in main thread so wait in another thread to see if job finishes.
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ waitForReceiver(null, 3_000, latch, null);
+ return null;
+ }
+ }.execute();
+ }
+
+ private static void doWifi(Context ctx) {
+ IntentFilter intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ CountDownLatch onReceiveLatch = new CountDownLatch(1);
+ BroadcastReceiver receiver = registerReceiver(ctx, onReceiveLatch, intentFilter);
+ ctx.getSystemService(WifiManager.class).startScan();
+ waitForReceiver(ctx, 3_000, onReceiveLatch, receiver);
+ }
+
+ /** Register receiver to determine when given action is complete. */
+ private static BroadcastReceiver registerReceiver(
+ Context ctx, CountDownLatch onReceiveLatch, IntentFilter intentFilter) {
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onReceiveLatch.countDown();
+ }
+ };
+ // run Broadcast receiver in a different thread since the foreground activity will wait.
+ HandlerThread handlerThread = new HandlerThread("br_handler_thread");
+ handlerThread.start();
+ Looper looper = handlerThread.getLooper();
+ Handler handler = new Handler(looper);
+ ctx.registerReceiver(receiver, intentFilter, null, handler);
+ return receiver;
+ }
+
+ /**
+ * Uses the receiver to wait until the action is complete. ctx and receiver may be null if no
+ * receiver is needed to be unregistered.
+ */
+ private static void waitForReceiver(Context ctx,
+ int maxWaitTimeMs, CountDownLatch latch, BroadcastReceiver receiver) {
+ try {
+ boolean didFinish = latch.await(maxWaitTimeMs, TimeUnit.MILLISECONDS);
+ if (didFinish) {
+ Log.v(TAG, "Finished performing action");
+ } else {
+ // This is not necessarily a problem. If we just want to make sure a count was
+ // recorded for the request, it doesn't matter if the action actually finished.
+ Log.w(TAG, "Did not finish in specified time.");
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted exception while awaiting action to finish", e);
+ }
+ if (ctx != null && receiver != null) {
+ ctx.unregisterReceiver(receiver);
+ }
+ }
+
+ /** Determines whether the package is running as a background process. */
+ public static boolean isAppInBackground(Context context) throws ReflectiveOperationException {
+ String pkgName = context.getPackageName();
+ ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
+ if (processes == null) {
+ return false;
+ }
+ for (ActivityManager.RunningAppProcessInfo r : processes){
+ // BatteryStatsImpl treats as background if procState is >=
+ // Activitymanager.PROCESS_STATE_IMPORTANT_BACKGROUND (corresponding
+ // to BatteryStats.PROCESS_STATE_BACKGROUND).
+ // Due to lack of permissions, only the current app should show up in the list of
+ // processes, which is desired in this case; but in case this changes later, we check
+ // that the package name matches anyway.
+ int processState = -1;
+ int backgroundCode = -1;
+ try {
+ processState = ActivityManager.RunningAppProcessInfo.class
+ .getField("processState").getInt(r);
+ backgroundCode = (Integer) ActivityManager.class
+ .getDeclaredField("PROCESS_STATE_IMPORTANT_BACKGROUND").get(null);
+ } catch (ReflectiveOperationException ex) {
+ Log.e(TAG, "Failed to get proc state info via reflection", ex);
+ throw ex;
+ }
+ if (processState < backgroundCode) { // if foreground process
+ for (String rpkg : r.pkgList) {
+ if (pkgName.equals(rpkg)) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /** Puts the current thread to sleep. */
+ private static void sleep(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted exception while sleeping", e);
+ }
+ }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
new file mode 100644
index 0000000..d6869a1
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.android.server.cts.device.batterystats;
+
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions
+ .ACTION_JOB_SCHEDULE;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import org.junit.Assert;
+
+/** An activity (to be run as a foreground process) which performs one of a number of actions. */
+public class BatteryStatsForegroundActivity extends Activity {
+ private static final String TAG = BatteryStatsForegroundActivity.class.getSimpleName();
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ Intent intent = this.getIntent();
+ if (intent == null) {
+ Log.e(TAG, "Intent was null.");
+ finish();
+ }
+ try {
+ if (isAppInBackground(this)) {
+ Log.e(TAG, "ForegroundActivity is a background process!");
+ Assert.fail("Test requires ForegroundActivity to be in foreground.");
+ }
+ } catch(ReflectiveOperationException ex) {
+ Log.w(TAG, "Couldn't determine if app is in foreground. Proceeding with test anyway");
+ }
+
+ doAction(this, intent.getStringExtra(KEY_ACTION));
+ finish();
+ }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiScanTests.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiScanTests.java
deleted file mode 100644
index 4bf8ec5..0000000
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiScanTests.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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 com.android.server.cts.device.batterystats;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.InstrumentationRegistry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Used by BatteryStatsValidationTest.
- */
-@RunWith(AndroidJUnit4.class)
-public class BatteryStatsWifiScanTests {
- private CountDownLatch mResultsReceivedSignal;
- private WifiManager mWifiManager;
- private Context mContext;
- private boolean mHasFeature;
-
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getTargetContext();
- mHasFeature = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
- if (!mHasFeature) {
- return;
- }
- mWifiManager = mContext.getSystemService(WifiManager.class);
- mResultsReceivedSignal = new CountDownLatch(1);
- registerReceiver(mContext, mResultsReceivedSignal);
- }
-
- @Test
- public void testBackgroundScan() throws Exception {
- if (!mHasFeature) {
- return;
- }
- mWifiManager.startScan();
- mResultsReceivedSignal.await(10, TimeUnit.SECONDS);
- }
-
- @Test
- public void testForegroundScan() throws Exception {
- if (!mHasFeature) {
- return;
- }
- WiFiScanActivity.mResultsReceivedSignal = mResultsReceivedSignal;
- Intent intent = new Intent(mContext, WiFiScanActivity.class);
- mContext.startActivity(intent);
- mResultsReceivedSignal.await(10, TimeUnit.SECONDS);
- WiFiScanActivity.instance.finish();
- }
-
- static void registerReceiver(Context ctx, CountDownLatch onReceiveLatch) {
- ctx.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onReceiveLatch.countDown();
- }
- }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
- }
-
- public static class WiFiScanActivity extends Activity {
- private static CountDownLatch mResultsReceivedSignal;
- private static Activity instance;
-
- @Override
- public void onCreate(Bundle bundle) {
- instance = this;
- super.onCreate(bundle);
- BatteryStatsWifiScanTests.registerReceiver(this, mResultsReceivedSignal);
- getSystemService(WifiManager.class).startScan();
- }
- }
-}
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index a499482..f6e9e31 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -28,12 +28,22 @@
private static final String DEVICE_SIDE_TEST_APK = "CtsBatteryStatsApp.apk";
private static final String DEVICE_SIDE_TEST_PACKAGE
= "com.android.server.cts.device.batterystats";
+ private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT
+ = "com.android.server.cts.device.batterystats/.BatteryStatsBackgroundService";
+ private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
+ = "com.android.server.cts.device.batterystats/.BatteryStatsForegroundActivity";
private static final String DEVICE_SIDE_JOB_COMPONENT
= "com.android.server.cts.device.batterystats/.SimpleJobService";
private static final String DEVICE_SIDE_SYNC_COMPONENT
= "com.android.server.cts.device.batterystats.provider/"
+ "com.android.server.cts.device.batterystats";
+
+ // Constants from BatteryStatsBgVsFgActions.java (not directly accessible here).
+ public static final String KEY_ACTION = "action";
+ public static final String ACTION_JOB_SCHEDULE = "action.jobs";
+ public static final String ACTION_WIFI_SCAN = "action.wifi_scan";
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -107,16 +117,41 @@
batteryOffScreenOn();
}
+ public void testJobBgVsFg() throws Exception {
+ batteryOnScreenOff();
+ installPackage(DEVICE_SIDE_TEST_APK, true);
+
+ // Foreground test.
+ executeForeground(ACTION_JOB_SCHEDULE);
+ Thread.sleep(4_000);
+ assertValueRange("jb", "", 6, 1, 1); // count
+ assertValueRange("jb", "", 8, 0, 0); // background_count
+
+ // Background test.
+ executeBackground(ACTION_JOB_SCHEDULE);
+ Thread.sleep(4_000);
+ assertValueRange("jb", "", 6, 2, 2); // count
+ assertValueRange("jb", "", 8, 1, 1); // background_count
+
+ batteryOffScreenOn();
+ }
+
public void testWifiScans() throws Exception {
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWifiScanTests",
- "testBackgroundScan");
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWifiScanTests",
- "testForegroundScan");
+ // Foreground count test.
+ executeForeground(ACTION_WIFI_SCAN);
+ Thread.sleep(4_000);
+ assertValueRange("wfl", "", 7, 1, 1); // scan_count
+ assertValueRange("wfl", "", 11, 0, 0); // scan_count_bg
- assertValueRange("wfl", "", 7, 2, 2);
+ // Background count test.
+ executeBackground(ACTION_WIFI_SCAN);
+ Thread.sleep(4_000);
+ assertValueRange("wfl", "", 7, 2, 2); // scan_count
+ assertValueRange("wfl", "", 11, 1, 1); // scan_count_bg
+
batteryOffScreenOn();
}
@@ -194,7 +229,7 @@
long foregroundBytes = getDownloadedBytes();
assertTrue(foregroundBytes > 0);
long min = foregroundBytes + MIN_HTTP_HEADER_BYTES;
- long max = foregroundBytes + (10 * 1024); // Add some fuzzing.
+ long max = foregroundBytes + (30 * 1024); // Add some fuzzing.
assertValueRange("nt", "", 6, min, max); // wifi_bytes_rx
assertValueRange("nt", "", 11, 1, 40); // wifi_bytes_tx
@@ -203,7 +238,7 @@
long backgroundBytes = getDownloadedBytes();
assertTrue(backgroundBytes > 0);
min += backgroundBytes + MIN_HTTP_HEADER_BYTES;
- max += backgroundBytes + (10 * 1024);
+ max += backgroundBytes + (30 * 1024);
assertValueRange("nt", "", 6, min, max); // wifi_bytes_rx
assertValueRange("nt", "", 11, 2, 80); // wifi_bytes_tx
@@ -274,6 +309,35 @@
}
/**
+ * Runs a (background) service to perform the given action.
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ */
+ private void executeBackground(String actionValue) throws Exception {
+ allowBackgroundServices();
+ getDevice().executeShellCommand(String.format(
+ "am startservice -n '%s' -e %s %s",
+ DEVICE_SIDE_BG_SERVICE_COMPONENT, KEY_ACTION, actionValue));
+ }
+
+ /** Required to successfully start a background service from adb in O. */
+ private void allowBackgroundServices() throws Exception {
+ getDevice().executeShellCommand(String.format(
+ "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE));
+ }
+
+ /**
+ * Runs an activity (in the foreground) to perform the given action.
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ */
+ private void executeForeground(String actionValue) throws Exception {
+ getDevice().executeShellCommand(String.format(
+ "am start -n '%s' -e %s %s",
+ DEVICE_SIDE_FG_ACTIVITY_COMPONENT, KEY_ACTION, actionValue));
+ }
+
+ /**
* Returns the bytes downloaded for the wifi transfer download tests.
*/
private long getDownloadedBytes() throws Exception {
diff --git a/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java b/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
index 0e27a66..10a1b77 100644
--- a/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
@@ -43,7 +43,6 @@
found = true;
assertEquals(State.POSTED, record.getState());
assertTrue(record.getImportance() > 0 /* NotificationManager.IMPORTANCE_NONE */);
- assertEquals(record.getKey(), record.getGroupKey());
// Ensure these fields exist, at least
record.getFlags();
diff --git a/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java b/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
index 3602076..d8b2816 100644
--- a/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
+++ b/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
@@ -20,96 +20,12 @@
*/
private static final String INSECURE_DEVICE_ADB_COMMAND = "find %s -type %s -perm /o=rwx 2>/dev/null";
- /**
- * Whitelist exceptions of allowed world accessbale char files under /dev
- */
- private static final Set<String> CHAR_DEV_EXCEPTIONS = new HashSet<String>(
- Arrays.asList(
- // All exceptions should be alphabetical and associated with a bug number.
- "/dev/adsprpc-smd", // b/11710243
- "/dev/alarm", // b/9035217
- "/dev/ashmem",
- "/dev/binder",
- "/dev/card0", // b/13159510
- "/dev/renderD128",
- "/dev/renderD129", // b/23798677
- "/dev/dri/card0", // b/13159510
- "/dev/dri/renderD128",
- "/dev/dri/renderD129", // b/23798677
- "/dev/felica", // b/11142586
- "/dev/felica_ant", // b/11142586
- "/dev/felica_cen", // b/11142586
- "/dev/felica_pon", // b/11142586
- "/dev/felica_rfs", // b/11142586
- "/dev/felica_rws", // b/11142586
- "/dev/felica_uicc", // b/11142586
- "/dev/full",
- "/dev/galcore",
- "/dev/genlock", // b/9035217
- "/dev/graphics/galcore",
- "/dev/hwbinder", // b/30886151
- "/dev/ion",
- "/dev/kgsl-2d0", // b/11271533
- "/dev/kgsl-2d1", // b/11271533
- "/dev/kgsl-3d0", // b/9035217
- "/dev/log/events", // b/9035217
- "/dev/log/main", // b/9035217
- "/dev/log/radio", // b/9035217
- "/dev/log/system", // b/9035217
- "/dev/mali0", // b/9106968
- "/dev/mali", // b/11142586
- "/dev/mm_interlock", // b/12955573
- "/dev/mm_isp", // b/12955573
- "/dev/mm_v3d", // b/12955573
- "/dev/msm_rotator", // b/9035217
- "/dev/null",
- "/dev/nvhost-as-gpu",
- "/dev/nvhost-ctrl", // b/9088251
- "/dev/nvhost-ctrl-gpu",
- "/dev/nvhost-dbg-gpu",
- "/dev/nvhost-gpu",
- "/dev/nvhost-gr2d", // b/9088251
- "/dev/nvhost-gr3d", // b/9088251
- "/dev/nvhost-tsec",
- "/dev/nvhost-prof-gpu",
- "/dev/nvhost-vic",
- "/dev/nvmap", // b/9088251
- "/dev/pmsg0", // b/31857082
- "/dev/ptmx", // b/9088251
- "/dev/pvrsrvkm", // b/9108170
- "/dev/pvr_sync",
- "/dev/quadd",
- "/dev/random",
- "/dev/snfc_cen", // b/11142586
- "/dev/snfc_hsel", // b/11142586
- "/dev/snfc_intu_poll", // b/11142586
- "/dev/snfc_rfs", // b/11142586
- "/dev/tegra-throughput",
- "/dev/tiler", // b/9108170
- "/dev/tty",
- "/dev/urandom",
- "/dev/ump", // b/11142586
- "/dev/xt_qtaguid", // b/9088251
- "/dev/zero",
- "/dev/fimg2d", // b/10428016
- "/dev/mobicore-user" // b/10428016
- ));
-
@Override
protected void setUp() throws Exception {
super.setUp();
mDevice = getDevice();
}
- public void testAllCharacterDevicesAreSecure() throws DeviceNotAvailableException {
- Set <String> insecure = getAllInsecureDevicesInDirAndSubdir("/dev", "c");
- Set <String> insecurePts = getAllInsecureDevicesInDirAndSubdir("/dev/pts", "c");
- insecure.removeAll(CHAR_DEV_EXCEPTIONS);
- insecure.removeAll(insecurePts);
- assertTrue("Found insecure character devices: " + insecure.toString(),
- insecure.isEmpty());
- }
-
public void testAllBlockDevicesAreSecure() throws Exception {
Set<String> insecure = getAllInsecureDevicesInDirAndSubdir("/dev", "b");
assertTrue("Found insecure block devices: " + insecure.toString(),
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 767939a..8ed60e6 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -235,8 +235,17 @@
}
}
- private boolean isFullTrebleDevice() throws Exception {
- return PropertyUtil.getFirstApiLevel(mDevice) > 25;
+ // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
+ /**
+ * Returns {@code true} if this device is required to be a full Treble device.
+ */
+ public static boolean isFullTrebleDevice(ITestDevice device)
+ throws DeviceNotAvailableException {
+ return PropertyUtil.getFirstApiLevel(device) > 25;
+ }
+
+ private boolean isFullTrebleDevice() throws DeviceNotAvailableException {
+ return isFullTrebleDevice(mDevice);
}
/**
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk b/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
index ff9e62b..3a50232 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
@@ -24,7 +24,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
-LOCAL_STATIC_JAVA_LIBRARIES := cts-amwm-util
+LOCAL_STATIC_JAVA_LIBRARIES := cts-amwm-util \
+ platform-test-annotations-host
LOCAL_CTS_TEST_PACKAGE := android.server
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index 9dda45a..3cdc754 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -59,6 +59,10 @@
android:taskAffinity="nobody.but.TranslucentActivity"
android:exported="true"
/>
+ <activity android:name=".DialogWhenLargeActivity"
+ android:exported="true"
+ android:theme="@android:style/Theme.DeviceDefault.Light.DialogWhenLarge"
+ />
<activity android:name=".NoRelaunchActivity"
android:resizeableActivity="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale"
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java
new file mode 100644
index 0000000..139c648
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.server.cts;
+
+/**
+ * Activity with DialogWhenLarge Theme.
+ */
+public class DialogWhenLargeActivity extends AbstractLifecycleLogActivity {
+ private static final String TAG = DialogWhenLargeActivity.class.getSimpleName();
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
index a7d4042..add7e42 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
@@ -40,12 +40,12 @@
private ConfigurationChangeObserver() {
}
- private boolean findConfigurationChange(String activityName)
+ private boolean findConfigurationChange(String activityName, String logSeparator)
throws DeviceNotAvailableException, InterruptedException {
int tries = 0;
boolean observedChange = false;
while (tries < 5 && !observedChange) {
- final String[] lines = getDeviceLogsForComponent(activityName);
+ final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
log("Looking at logcat");
for (int i = lines.length - 1; i >= 0; i--) {
final String line = lines[i].trim();
@@ -72,16 +72,18 @@
launchActivityInStack(TEST_ACTIVITY_NAME, FREEFORM_WORKSPACE_STACK_ID);
setDeviceRotation(0);
- clearLogcat();
+ String logSeparator = clearLogcat();
resizeActivityTask(TEST_ACTIVITY_NAME, 0, 0, 100, 100);
ConfigurationChangeObserver c = new ConfigurationChangeObserver();
- final boolean reportedSizeAfterResize = c.findConfigurationChange(TEST_ACTIVITY_NAME);
+ final boolean reportedSizeAfterResize = c.findConfigurationChange(TEST_ACTIVITY_NAME,
+ logSeparator);
assertTrue("Expected to observe configuration change when resizing",
reportedSizeAfterResize);
- clearLogcat();
+ logSeparator = clearLogcat();
setDeviceRotation(2);
- final boolean reportedSizeAfterRotation = c.findConfigurationChange(TEST_ACTIVITY_NAME);
+ final boolean reportedSizeAfterRotation = c.findConfigurationChange(TEST_ACTIVITY_NAME,
+ logSeparator);
assertFalse("Not expected to observe configuration change after flip rotation",
reportedSizeAfterRotation);
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
index b31fb16..f6df4a8 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
@@ -77,7 +77,8 @@
private void startActivityAndVerifyResult(final String entryActivity,
final String actualActivity, boolean shouldStart) throws Exception {
- clearLogcat();
+ // See TODO below
+ // final String logSeparator = clearLogcat();
// 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
@@ -96,7 +97,7 @@
// still catch most failures.
// Verify adb logcat log
- //verifyLogcat(actualActivity, shouldStart);
+ //verifyLogcat(actualActivity, shouldStart, logSeparator);
}
private static final Pattern sNotStartedWarningPattern = Pattern.compile(
@@ -149,12 +150,12 @@
private static final Pattern sDisplayTimePattern =
Pattern.compile("(.+): Displayed (.*): (\\+{0,1})([0-9]+)ms(.*)");
- void verifyLogcat(String actualActivityName, boolean shouldStart)
+ void verifyLogcat(String actualActivityName, boolean shouldStart, String logSeparator)
throws DeviceNotAvailableException {
int displayCount = 0;
String activityName = null;
- for (String line : getDeviceLogsForComponent("ActivityManager")) {
+ for (String line : getDeviceLogsForComponent("ActivityManager", logSeparator)) {
line = line.trim();
Matcher matcher = sDisplayTimePattern.matcher(line);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
index 7c3f39a..284f1a7 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
@@ -17,6 +17,8 @@
import static android.server.cts.ActivityManagerState.STATE_RESUMED;
+import android.platform.test.annotations.Presubmit;
+
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.log.LogUtil.CLog;
@@ -24,6 +26,8 @@
import java.util.ArrayList;
import java.util.List;
+import static android.server.cts.ActivityAndWindowManagersState.dpToPx;
+
/**
* Build: mmma -j32 cts/hostsidetests/services
* Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAppConfigurationTests
@@ -34,9 +38,13 @@
private static final String PORTRAIT_ACTIVITY_NAME = "PortraitOrientationActivity";
private static final String LANDSCAPE_ACTIVITY_NAME = "LandscapeOrientationActivity";
private static final String NIGHT_MODE_ACTIVITY = "NightModeActivity";
+ private static final String DIALOG_WHEN_LARGE_ACTIVITY = "DialogWhenLargeActivity";
private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
+ private static final int SMALL_WIDTH_DP = 426;
+ private static final int SMALL_HEIGHT_DP = 320;
+
/**
* Tests that the WindowManager#getDefaultDisplay() and the Configuration of the Activity
* has an updated size when the Activity is resized from fullscreen to docked state.
@@ -47,11 +55,15 @@
* docked state.
*/
public void testConfigurationUpdatesWhenResizedFromFullscreen() throws Exception {
+ String logSeparator = clearLogcat();
launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
+ logSeparator = clearLogcat();
moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
- final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
assertSizesAreSane(fullscreenSizes, dockedSizes);
}
@@ -61,11 +73,15 @@
* from docked state to fullscreen (reverse).
*/
public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
+ String logSeparator = clearLogcat();
launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
- final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
+ logSeparator = clearLogcat();
moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
assertSizesAreSane(fullscreenSizes, dockedSizes);
}
@@ -75,8 +91,10 @@
*/
public void testConfigurationUpdatesWhenRotatingWhileFullscreen() throws Exception {
setDeviceRotation(0);
+ final String logSeparator = clearLogcat();
launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
rotateAndCheckSizes(initialSizes);
}
@@ -87,6 +105,7 @@
*/
public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
setDeviceRotation(0);
+ final String logSeparator = clearLogcat();
launchActivityInDockStack(LAUNCHING_ACTIVITY);
// Launch our own activity to side in case Recents (or other activity to side) doesn't
// support rotation.
@@ -94,7 +113,8 @@
.execute();
// Launch target activity in docked stack.
getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
- final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
rotateAndCheckSizes(initialSizes);
}
@@ -106,17 +126,19 @@
public void testConfigurationUpdatesWhenRotatingToSideFromDocked() throws Exception {
setDeviceRotation(0);
+ final String logSeparator = clearLogcat();
launchActivityInDockStack(LAUNCHING_ACTIVITY);
getLaunchActivityBuilder().setToSide(true).setTargetActivityName(RESIZEABLE_ACTIVITY_NAME)
.execute();
- final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
rotateAndCheckSizes(initialSizes);
}
private void rotateAndCheckSizes(ReportedSizes prevSizes) throws Exception {
for (int rotation = 3; rotation >= 0; --rotation) {
- clearLogcat();
+ final String logSeparator = clearLogcat();
final int actualStackId = mAmWmState.getAmState().getTaskByActivityName(
RESIZEABLE_ACTIVITY_NAME).mStackId;
final int displayId = mAmWmState.getAmState().getStackById(actualStackId).mDisplayId;
@@ -131,7 +153,8 @@
return;
}
- final ReportedSizes rotatedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes rotatedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
assertSizesRotate(prevSizes, rotatedSizes);
prevSizes = rotatedSizes;
}
@@ -161,13 +184,16 @@
*/
private void moveActivityFullSplitFull(String activityName) throws Exception {
// Launch to fullscreen stack and record size.
+ String logSeparator = clearLogcat();
launchActivityInStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
- final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName);
+ final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName,
+ logSeparator);
final Rectangle displayRect = getDisplayRect(activityName);
// Move to docked stack.
+ logSeparator = clearLogcat();
moveActivityToStack(activityName, DOCKED_STACK_ID);
- final ReportedSizes dockedSizes = getActivityDisplaySize(activityName);
+ final ReportedSizes dockedSizes = getActivityDisplaySize(activityName, logSeparator);
assertSizesAreSane(initialFullscreenSizes, dockedSizes);
// Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
// will come up.
@@ -177,11 +203,13 @@
// Resize docked stack to fullscreen size. This will trigger activity relaunch with
// non-empty override configuration corresponding to fullscreen size.
+ logSeparator = clearLogcat();
runCommandAndPrintOutput("am stack resize " + DOCKED_STACK_ID + " 0 0 "
+ displayRect.width + " " + displayRect.height);
// Move activity back to fullscreen stack.
moveActivityToStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
- final ReportedSizes finalFullscreenSizes = getActivityDisplaySize(activityName);
+ final ReportedSizes finalFullscreenSizes = getActivityDisplaySize(activityName,
+ logSeparator);
// After activity configuration was changed twice it must report same size as original one.
assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes);
@@ -203,6 +231,26 @@
}
/**
+ * Tests that an activity with the DialogWhenLarge theme can transform properly when in split
+ * screen.
+ */
+ @Presubmit
+ public void testDialogWhenLargeSplitSmall() throws Exception {
+ launchActivityInStack(DIALOG_WHEN_LARGE_ACTIVITY, DOCKED_STACK_ID);
+ final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
+ .getStackById(DOCKED_STACK_ID);
+ final WindowManagerState.Display display =
+ mAmWmState.getWmState().getDisplay(stack.mDisplayId);
+ final int density = display.getDpi();
+ final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
+ final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
+
+ runCommandAndPrintOutput("am stack resize " + DOCKED_STACK_ID + " 0 0 "
+ + smallWidthPx + " " + smallHeightPx);
+ mAmWmState.waitForValidState(mDevice, DIALOG_WHEN_LARGE_ACTIVITY, DOCKED_STACK_ID);
+ }
+
+ /**
* Test that device handles consequent requested orientations and displays the activities.
*/
public void testFullscreenAppOrientationRequests() throws Exception {
@@ -320,8 +368,9 @@
*/
private void moveActivitySplitFullSplit(String activityName) throws Exception {
// Launch to docked stack and record size.
+ String logSeparator = clearLogcat();
launchActivityInStack(activityName, DOCKED_STACK_ID);
- final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName);
+ final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName, logSeparator);
// Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
// will come up.
launchActivityInStack(activityName, DOCKED_STACK_ID);
@@ -329,13 +378,15 @@
false /* compareTaskAndStackBounds */);
// Move to fullscreen stack.
+ logSeparator = clearLogcat();
moveActivityToStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
- final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName);
+ final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName, logSeparator);
assertSizesAreSane(fullscreenSizes, initialDockedSizes);
// Move activity back to docked stack.
+ logSeparator = clearLogcat();
moveActivityToStack(activityName, DOCKED_STACK_ID);
- final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName);
+ final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName, logSeparator);
// After activity configuration was changed twice it must report same size as original one.
assertSizesAreSame(initialDockedSizes, finalDockedSizes);
@@ -396,10 +447,11 @@
assertEquals(firstSize.smallestWidthDp, secondSize.smallestWidthDp);
}
- private ReportedSizes getActivityDisplaySize(String activityName) throws Exception {
+ private ReportedSizes getActivityDisplaySize(String activityName, String logSeparator)
+ throws Exception {
mAmWmState.computeState(mDevice, new String[] { activityName },
false /* compareTaskAndStackBounds */);
- final ReportedSizes details = getLastReportedSizesForActivity(activityName);
+ final ReportedSizes details = getLastReportedSizesForActivity(activityName, logSeparator);
assertNotNull(details);
return details;
}
@@ -431,8 +483,6 @@
* Test launching an activity which requests specific UI mode during creation.
*/
public void testLaunchWithUiModeChange() throws Exception {
- clearLogcat();
-
// Launch activity that changes UI mode and handles this configuration change.
launchActivity(NIGHT_MODE_ACTIVITY);
mAmWmState.waitForActivityState(mDevice, NIGHT_MODE_ACTIVITY, STATE_RESUMED);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
index ede2b87..ba56639 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
@@ -17,6 +17,7 @@
package android.server.cts;
import static android.server.cts.ActivityManagerState.STATE_RESUMED;
+import static android.server.cts.StateLogger.logE;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -52,12 +53,12 @@
public void testChangeFontScaleRelaunch() throws Exception {
// Should relaunch and receive no onConfigurationChanged()
- testChangeFontScale(TEST_ACTIVITY_NAME, true);
+ testChangeFontScale(TEST_ACTIVITY_NAME, true /* relaunch */);
}
public void testChangeFontScaleNoRelaunch() throws Exception {
// Should receive onConfigurationChanged() and no relaunch
- testChangeFontScale(NO_RELAUNCH_ACTIVITY_NAME, false);
+ testChangeFontScale(NO_RELAUNCH_ACTIVITY_NAME, false /* relaunch */);
}
private void testRotation(
@@ -72,10 +73,10 @@
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
for (int rotation = 0; rotation < 4; rotation += rotationStep) {
- clearLogcat();
+ final String logSeparator = clearLogcat();
setDeviceRotation(rotation);
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange);
+ assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange, logSeparator);
}
}
@@ -89,10 +90,11 @@
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
- clearLogcat();
+ final String logSeparator = clearLogcat();
setFontScale(fontScale);
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1);
+ assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1,
+ logSeparator);
}
}
@@ -101,29 +103,30 @@
* must be recreated and its asset sequence number must be incremented.
*/
public void testUpdateApplicationInfo() throws Exception {
- clearLogcat();
+ final String firstLogSeparator = clearLogcat();
// Launch an activity that prints applied config.
launchActivity(TEST_ACTIVITY_NAME);
- final int assetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME);
- clearLogcat();
+ final int assetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, firstLogSeparator);
+ final String logSeparator = clearLogcat();
// Update package info.
executeShellCommand("am update-appinfo all " + componentName);
mAmWmState.waitForWithAmState(mDevice, (amState) -> {
// Wait for activity to be resumed and asset seq number to be updated.
try {
- return readAssetSeqNumber(TEST_ACTIVITY_NAME) == assetSeq + 1
+ return readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator) == assetSeq + 1
&& amState.hasActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
} catch (Exception e) {
+ logE("Error waiting for valid state: " + e.getMessage());
return false;
}
}, "Waiting asset sequence number to be updated and for activity to be resumed.");
// Check if activity is relaunched and asset seq is updated.
assertRelaunchOrConfigChanged(TEST_ACTIVITY_NAME, 1 /* numRelaunch */,
- 0 /* numConfigChange */);
- final int newAssetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME);
+ 0 /* numConfigChange */, logSeparator);
+ final int newAssetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator);
assertEquals("Asset sequence number must be incremented.", assetSeq + 1, newAssetSeq);
}
@@ -131,8 +134,8 @@
"(.+): Configuration: \\{(.*) as.(\\d+)(.*)\\}");
/** Read asset sequence number in last applied configuration from logs. */
- private int readAssetSeqNumber(String activityName) throws Exception {
- final String[] lines = getDeviceLogsForComponent(activityName);
+ private int readAssetSeqNumber(String activityName, String logSeparator) throws Exception {
+ final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
for (int i = lines.length - 1; i >= 0; i--) {
final String line = lines[i].trim();
final Matcher matcher = sConfigurationPattern.matcher(line);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
index e950983..ea25e04 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
@@ -135,11 +135,42 @@
}
/**
+ * Tests that launch on secondary display is not permitted if device has the feature disabled.
+ * Activities requested to be launched on a secondary display in this case should land on the
+ * default display.
+ */
+ public void testMultiDisplayDisabled() throws Exception {
+ if (supportsMultiDisplay()) {
+ // Only check devices with the feature disabled.
+ return;
+ }
+
+ // Create new virtual display.
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
+ mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+
+ mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
+
+ // Check that activity is on the right display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack frontStack
+ = mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed",
+ getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+ assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
+ frontStack.mDisplayId);
+ mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
+ }
+
+ /**
* Tests that any new activity launch in Vr mode is in Vr display.
*/
public void testVrActivityLaunch() throws Exception {
- // VR Mode is not supported on this device, bail from this test.
- if (!supportsVrMode()) {
+ if (!supportsVrMode() || !supportsMultiDisplay()) {
+ // VR Mode is not supported on this device, bail from this test.
return;
}
@@ -185,8 +216,8 @@
* Tests that any activity already present is re-launched in Vr display in vr mode.
*/
public void testVrActivityReLaunch() throws Exception {
- // VR Mode is not supported on this device, bail from this test.
- if (!supportsVrMode()) {
+ if (!supportsVrMode() || !supportsMultiDisplay()) {
+ // VR Mode is not supported on this device, bail from this test.
return;
}
@@ -235,8 +266,8 @@
* Tests that any new activity launch post Vr mode is in the main display.
*/
public void testActivityLaunchPostVr() throws Exception {
- // VR Mode is not supported on this device, bail from this test.
- if (!supportsVrMode()) {
+ if (!supportsVrMode() || !supportsMultiDisplay()) {
+ // VR Mode is not supported on this device, bail from this test.
return;
}
@@ -304,7 +335,6 @@
DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
}
-
public void testCreateMultipleVirtualDisplays() throws Exception {
// Create new virtual display.
final List<DisplayState> newDisplays = new VirtualDisplayBuilder(this).build(3);
@@ -316,10 +346,13 @@
* Tests launching an activity on virtual display.
*/
public void testLaunchActivityOnSecondaryDisplay() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
// Launch activity on new secondary display.
+ final String logSeparator = clearLogcat();
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
@@ -337,7 +370,8 @@
mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
// Check that activity config corresponds to display config.
- final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME);
+ final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
+ logSeparator);
assertEquals("Activity launched on secondary display must have proper configuration",
CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
}
@@ -348,6 +382,8 @@
* primary display.
*/
public void testConsequentLaunchActivity() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
@@ -378,6 +414,8 @@
* first one - it must appear on the secondary display, because it was launched from there.
*/
public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
@@ -407,6 +445,8 @@
* Tests launching an activity to secondary display from activity on primary display.
*/
public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Start launching activity.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
// Create new virtual display.
@@ -435,6 +475,8 @@
* visibility is not affected.
*/
public void testLaunchActivitiesAffectsVisibility() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Start launching activity.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
@@ -465,6 +507,8 @@
* Test that move-task works when moving between displays.
*/
public void testMoveTaskBetweenDisplays() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -493,6 +537,8 @@
* This version launches virtual display creator to fullscreen stack.
*/
public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Start launching activity into docked stack.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
@@ -506,6 +552,8 @@
* This version launches virtual display creator to docked stack.
*/
public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Setup split-screen.
launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
@@ -555,6 +603,8 @@
* is moved correctly.
*/
public void testStackFocusSwitchOnStackEmptied() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Start launching activity.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
@@ -590,6 +640,8 @@
* Tests that input events on the primary display take focus from the virtual display.
*/
public void testStackFocusSwitchOnTouchEvent() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
@@ -615,6 +667,8 @@
/** Test that system is allowed to launch on secondary displays. */
public void testPermissionLaunchFromSystem() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -647,6 +701,8 @@
/** Test that launching from app that is on external display is allowed. */
public void testPermissionLaunchFromAppOnSecondary() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -688,6 +744,8 @@
/** Tests that an activity can launch an activity from a different UID into its own task. */
public void testPermissionLaunchMultiUidTask() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
@@ -719,6 +777,8 @@
* doesn't have anything on the display.
*/
public void testPermissionLaunchFromOwner() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -757,6 +817,8 @@
* that external display is not allowed.
*/
public void testPermissionLaunchFromDifferentApp() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -773,7 +835,7 @@
assertTrue("Focused stack must be on secondary display",
FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
- clearLogcat();
+ final String logSeparator = clearLogcat();
// Launch other activity with different uid and check it is launched on primary display.
final String broadcastAction = SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION";
@@ -785,7 +847,7 @@
boolean match = false;
final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
while (tries < 5 && !match) {
- String[] logs = getDeviceLogsForComponent("LaunchBroadcastReceiver");
+ String[] logs = getDeviceLogsForComponent("LaunchBroadcastReceiver", logSeparator);
for (String line : logs) {
Matcher m = pattern.matcher(line);
if (m.matches()) {
@@ -814,6 +876,8 @@
* Test that virtual display content is hidden when device is locked.
*/
public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
+ if (!supportsMultiDisplay() || !isHandheld()) { return; }
+
// Create new usual virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -838,6 +902,8 @@
* locked.
*/
public void testShowWhenLockedVirtualDisplay() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new show-with-insecure-keyguard virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this)
.setCanShowWithInsecureKeyguard(true)
@@ -863,6 +929,8 @@
* Test that only private virtual display can show content with insecure keyguard.
*/
public void testShowWhenLockedPublicVirtualDisplay() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Try to create new show-with-insecure-keyguard public virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this)
.setPublicDisplay(true)
@@ -879,6 +947,8 @@
* Test that all activities that were on the private display are destroyed on display removal.
*/
public void testContentDestroyOnDisplayRemoved() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new private virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -893,7 +963,7 @@
RESIZEABLE_ACTIVITY_NAME);
// Destroy the display and check if activities are removed from system.
- clearLogcat();
+ final String logSeparator = clearLogcat();
destroyVirtualDisplays();
final String activityName1
= ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
@@ -923,14 +993,16 @@
assertFalse("Activity windows from removed display must be destroyed",
mAmWmState.getWmState().containsWindow(windowName2));
// Check activity logs.
- assertActivityDestroyed(TEST_ACTIVITY_NAME);
- assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME);
+ assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
+ assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
}
/**
* Test that the update of display metrics updates all its content.
*/
public void testDisplayResize() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Start launching activity.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
@@ -942,22 +1014,34 @@
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
// Launch a resizeable activity on new secondary display.
- clearLogcat();
+ final String initialLogSeparator = clearLogcat();
launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
mAmWmState.assertFocusedActivity("Launched activity must be focused",
RESIZEABLE_ACTIVITY_NAME);
// Grab reported sizes and compute new with slight size change.
- final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
+ initialLogSeparator);
final Rectangle initialBounds
= mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds();
final Rectangle newBounds = new Rectangle(initialBounds.x, initialBounds.y,
initialBounds.width + SIZE_VALUE_SHIFT, initialBounds.height + SIZE_VALUE_SHIFT);
// Resize the docked stack, so that activity with virtual display will also be resized.
- clearLogcat();
+ final String logSeparator = clearLogcat();
resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
+
+ mAmWmState.waitForWithAmState(mDevice, amState -> {
+ try {
+ return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
+ && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
+ } catch (Exception e) {
+ logE("Error waiting for valid state: " + e.getMessage());
+ return false;
+ }
+ }, "Wait for the configuration change to happen and for activity to be resumed.");
+
mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY,
VIRTUAL_DISPLAY_ACTIVITY}, false /* compareTaskAndStackBounds */);
mAmWmState.assertDockedTaskBounds(newBounds.width, newBounds.height,
@@ -973,9 +1057,10 @@
// Check if activity in virtual display was resized properly.
assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
- 1 /* numConfigChange */);
+ 1 /* numConfigChange */, logSeparator);
- final ReportedSizes updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
assertTrue(updatedSize.widthDp <= initialSize.widthDp);
assertTrue(updatedSize.heightDp <= initialSize.heightDp);
assertTrue(updatedSize.displayWidth <= initialSize.displayWidth);
@@ -986,11 +1071,18 @@
widthUpdated ^ heightUpdated);
}
+ /** Read the number of configuration changes sent to activity from logs. */
+ private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
+ return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
+ }
+
/**
* Tests that when activities that handle configuration changes are moved between displays,
* they receive onMovedToDisplay and onConfigurationChanged callbacks.
*/
public void testOnMovedToDisplayCallback() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -1000,7 +1092,7 @@
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
RESIZEABLE_ACTIVITY_NAME);
- clearLogcat();
+ final String logSeparator = clearLogcat();
moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
mAmWmState.assertFocusedActivity("Focus must be on moved activity",
@@ -1009,8 +1101,8 @@
FULLSCREEN_WORKSPACE_STACK_ID);
// Check if client received the callbacks.
- assertMovedToDisplay(RESIZEABLE_ACTIVITY_NAME);
- assertMovedToDisplay("LifecycleLogView");
+ assertMovedToDisplay(RESIZEABLE_ACTIVITY_NAME, logSeparator);
+ assertMovedToDisplay("LifecycleLogView", logSeparator);
}
/**
@@ -1018,6 +1110,8 @@
* matching task on some other display - that task will moved to the target display.
*/
public void testMoveToDisplayOnLaunch() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Launch activity with unique affinity, so it will the only one in its task.
launchActivity(LAUNCHING_ACTIVITY);
@@ -1072,6 +1166,8 @@
* Tests that when primary display is rotated secondary displays are not affected.
*/
public void testRotationNotAffectingSecondaryScreen() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this)
.setResizeDisplay(false)
@@ -1079,11 +1175,12 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch activity on new secondary display.
+ String logSeparator = clearLogcat();
launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
RESIZEABLE_ACTIVITY_NAME);
final ReportedSizes initialSizes = getLastReportedSizesForActivity(
- RESIZEABLE_ACTIVITY_NAME);
+ RESIZEABLE_ACTIVITY_NAME, logSeparator);
assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
// Rotate primary display and check that activity on secondary display is not affected.
@@ -1093,12 +1190,13 @@
final int initialRotation = mAmWmState.getWmState().getRotation();
setDeviceRotation((initialRotation + 1) % 4);
+ logSeparator = clearLogcat();
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
TEST_ACTIVITY_NAME);
final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
- TEST_ACTIVITY_NAME);
+ TEST_ACTIVITY_NAME, logSeparator);
assertEquals("Sizes of secondary display must not change after rotation of primary display",
initialSizes, testActivitySizes);
}
@@ -1108,6 +1206,8 @@
* matching the task component root does.
*/
public void testTaskMatchAcrossDisplays() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
@@ -1153,6 +1253,8 @@
* even if the focused stack is not on that activity's display.
*/
public void testNewTaskSameDisplay() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
@@ -1171,7 +1273,7 @@
mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
- // Check that the second activity is launched onto the fullscren stack
+ // Check that the second activity is launched onto the fullscreen stack
final ActivityManagerState.ActivityStack fullscreenStack =
mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
assertEquals("Activity launched on default display must be resumed",
@@ -1198,9 +1300,10 @@
private void rotateAndCheckSameSizes(String activityName) throws Exception {
for (int rotation = 3; rotation >= 0; --rotation) {
- clearLogcat();
+ final String logSeparator = clearLogcat();
setDeviceRotation(rotation);
- final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName);
+ final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
+ logSeparator);
assertNull("Sizes must not change after rotation", rotatedSizes);
}
}
@@ -1343,9 +1446,9 @@
}
/** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
- private void assertMovedToDisplay(String componentName) throws Exception {
+ private void assertMovedToDisplay(String componentName, String logSeparator) throws Exception {
final ActivityLifecycleCounts lifecycleCounts
- = new ActivityLifecycleCounts(componentName);
+ = new ActivityLifecycleCounts(componentName, logSeparator);
if (lifecycleCounts.mDestroyCount != 0) {
fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
+ " time(s), wasn't expecting any");
@@ -1681,4 +1784,9 @@
return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
" --es command destroy_display";
}
+
+ /** Checks if the device supports multi-display. */
+ private boolean supportsMultiDisplay() throws Exception {
+ return hasDeviceFeature("android.software.activities_on_secondary_displays");
+ }
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
index fb87ec2..1cf60f4 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
@@ -379,7 +379,7 @@
final Rectangle initialDockBounds =
mAmWmState.getWmState().getStack(DOCKED_STACK_ID).getBounds();
- clearLogcat();
+ final String logSeparator = clearLogcat();
Rectangle newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
@@ -392,8 +392,8 @@
resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
mAmWmState.computeState(mDevice,
new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
- assertActivityLifecycle(TEST_ACTIVITY_NAME, true);
- assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false);
+ assertActivityLifecycle(TEST_ACTIVITY_NAME, true /* relaunched */, logSeparator);
+ assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false /* relaunched */, logSeparator);
}
private Rectangle computeNewDockBounds(
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
index cc36af1..df583d9 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
@@ -101,14 +101,14 @@
mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
- clearLogcat();
+ final String logSeparator = clearLogcat();
resizeActivityTask(TEST_ACTIVITY,
TEST_TASK_OFFSET, TEST_TASK_OFFSET, TEST_TASK_SIZE_2, TEST_TASK_SIZE_1);
resizeActivityTask(NO_RELAUNCH_ACTIVITY,
TEST_TASK_OFFSET_2, TEST_TASK_OFFSET_2, TEST_TASK_SIZE_2, TEST_TASK_SIZE_1);
mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
- assertActivityLifecycle(TEST_ACTIVITY, true);
- assertActivityLifecycle(NO_RELAUNCH_ACTIVITY, false);
+ assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */, logSeparator);
+ assertActivityLifecycle(NO_RELAUNCH_ACTIVITY, false /* relaunched */, logSeparator);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
index 9da97a2..666ca73 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -715,17 +715,17 @@
// 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);
- clearLogcat();
+ String logSeparator = clearLogcat();
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
assertPinnedStackExists();
- assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY);
+ assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
// Trigger it to go back to fullscreen and ensure that only triggered one configuration
// change as well
- clearLogcat();
+ logSeparator = clearLogcat();
launchActivity(PIP_ACTIVITY);
- assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY);
+ assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
}
public void testPreventSetAspectRatioWhileExpanding() throws Exception {
@@ -864,8 +864,10 @@
* Asserts that the activity received exactly one of each of the callbacks when entering and
* exiting picture-in-picture.
*/
- private void assertValidPictureInPictureCallbackOrder(String activityName) throws Exception {
- final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+ private void assertValidPictureInPictureCallbackOrder(String activityName, String logSeparator)
+ throws Exception {
+ final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+ logSeparator);
if (lifecycleCounts.mConfigurationChangedCount != 1) {
fail(activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
index 4afd607..28161bc 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
@@ -98,7 +98,7 @@
if (!isHandheld()) {
return;
}
- clearLogcat();
+ final String logSeparator = clearLogcat();
gotoKeyguard();
mAmWmState.computeState(mDevice, null);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -108,20 +108,20 @@
mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardMethodActivity"});
mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- assertOnDismissSucceededInLogcat();
+ assertOnDismissSucceededInLogcat(logSeparator);
}
public void testDismissKeyguardActivity_method_cancelled() throws Exception {
if (!isHandheld()) {
return;
}
- clearLogcat();
+ final String logSeparator = clearLogcat();
gotoKeyguard();
mAmWmState.computeState(mDevice, null);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity("DismissKeyguardMethodActivity");
pressBackButton();
- assertOnDismissCancelledInLogcat();
+ assertOnDismissCancelledInLogcat(logSeparator);
mAmWmState.computeState(mDevice, new String[] {});
mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
index ca86637..d578644 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
@@ -37,23 +37,24 @@
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
}
- protected void assertOnDismissSucceededInLogcat() throws Exception {
- assertInLogcat("KeyguardDismissLoggerCallback", "onDismissSucceeded");
+ protected void assertOnDismissSucceededInLogcat(String logSeparator) throws Exception {
+ assertInLogcat("KeyguardDismissLoggerCallback", "onDismissSucceeded", logSeparator);
}
- protected void assertOnDismissCancelledInLogcat() throws Exception {
- assertInLogcat("KeyguardDismissLoggerCallback", "onDismissCancelled");
+ protected void assertOnDismissCancelledInLogcat(String logSeparator) throws Exception {
+ assertInLogcat("KeyguardDismissLoggerCallback", "onDismissCancelled", logSeparator);
}
- protected void assertOnDismissErrorInLogcat() throws Exception {
- assertInLogcat("KeyguardDismissLoggerCallback", "onDismissError");
+ protected void assertOnDismissErrorInLogcat(String logSeparator) throws Exception {
+ assertInLogcat("KeyguardDismissLoggerCallback", "onDismissError", logSeparator);
}
- private void assertInLogcat(String activityName, String entry) throws Exception {
+ private void assertInLogcat(String activityName, String entry, String logSeparator)
+ throws Exception {
final Pattern pattern = Pattern.compile("(.+)" + entry);
int tries = 0;
while (tries < 5) {
- final String[] lines = getDeviceLogsForComponent(activityName);
+ final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
log("Looking at logcat");
for (int i = lines.length - 1; i >= 0; i--) {
final String line = lines[i].trim();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
index fa611ad..f17221ef 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
@@ -174,7 +174,7 @@
if (!isHandheld()) {
return;
}
- clearLogcat();
+ final String logSeparator = clearLogcat();
gotoKeyguard();
mAmWmState.computeState(mDevice, null);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -183,28 +183,28 @@
mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardMethodActivity"});
mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- assertOnDismissSucceededInLogcat();
+ assertOnDismissSucceededInLogcat(logSeparator);
}
public void testDismissKeyguardActivity_method_notTop() throws Exception {
if (!isHandheld()) {
return;
}
- clearLogcat();
+ final String logSeparator = clearLogcat();
gotoKeyguard();
mAmWmState.computeState(mDevice, null);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity("BroadcastReceiverActivity");
launchActivity("TestActivity");
executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguardMethod true");
- assertOnDismissErrorInLogcat();
+ assertOnDismissErrorInLogcat(logSeparator);
}
public void testDismissKeyguardActivity_method_turnScreenOn() throws Exception {
if (!isHandheld()) {
return;
}
- clearLogcat();
+ final String logSeparator = clearLogcat();
sleepDevice();
mAmWmState.computeState(mDevice, null);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -213,7 +213,7 @@
mAmWmState.computeState(mDevice, new String[] { "TurnScreenOnDismissKeyguardActivity"});
mAmWmState.assertVisibility("TurnScreenOnDismissKeyguardActivity", true);
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- assertOnDismissSucceededInLogcat();
+ assertOnDismissSucceededInLogcat(logSeparator);
}
public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
index 4f9c3ac..4483947 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
@@ -28,6 +28,7 @@
import java.lang.String;
import java.util.HashSet;
import java.util.List;
+import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -39,6 +40,7 @@
public abstract class ActivityManagerTestBase extends DeviceTestCase {
private static final boolean PRETEND_DEVICE_SUPPORTS_PIP = false;
private static final boolean PRETEND_DEVICE_SUPPORTS_FREEFORM = false;
+ private static final String LOG_SEPARATOR = "LOG_SEPARATOR";
// Constants copied from ActivityManager.StackId. If they are changed there, these must be
// updated.
@@ -687,13 +689,22 @@
return output;
}
- protected void clearLogcat() throws DeviceNotAvailableException {
+ /**
+ * 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.
+ * @return Unique log separator.
+ */
+ protected String clearLogcat() throws DeviceNotAvailableException {
mDevice.executeAdbCommand("logcat", "-c");
+ final String uniqueString = UUID.randomUUID().toString();
+ executeShellCommand("log -t " + LOG_SEPARATOR + " " + uniqueString);
+ return uniqueString;
}
- protected void assertActivityLifecycle(String activityName, boolean relaunched)
- throws DeviceNotAvailableException {
- final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+ protected void assertActivityLifecycle(String activityName, boolean relaunched,
+ String logSeparator) throws DeviceNotAvailableException {
+ final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+ logSeparator);
if (relaunched) {
if (lifecycleCounts.mDestroyCount < 1) {
@@ -722,9 +733,10 @@
}
protected void assertRelaunchOrConfigChanged(
- String activityName, int numRelaunch, int numConfigChange)
+ String activityName, int numRelaunch, int numConfigChange, String logSeparator)
throws DeviceNotAvailableException {
- final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+ final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+ logSeparator);
if (lifecycleCounts.mDestroyCount != numRelaunch) {
fail(activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
@@ -738,8 +750,10 @@
}
}
- protected void assertActivityDestroyed(String activityName) throws DeviceNotAvailableException {
- final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+ protected void assertActivityDestroyed(String activityName, String logSeparator)
+ throws DeviceNotAvailableException {
+ final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+ logSeparator);
if (lifecycleCounts.mDestroyCount != 1) {
fail(activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
@@ -753,20 +767,37 @@
}
}
- protected String[] getDeviceLogsForComponent(String componentName)
+ protected String[] getDeviceLogsForComponent(String componentName, String logSeparator)
throws DeviceNotAvailableException {
- return mDevice.executeAdbCommand(
- "logcat", "-v", "brief", "-d", componentName + ":I", "*:S").split("\\n");
+ return getDeviceLogsForComponents(new String[]{componentName}, logSeparator);
}
- protected String[] getDeviceLogsForComponents(final String[] componentNames)
- throws DeviceNotAvailableException {
- String filters = "";
- for (int i = 0; i < componentNames.length; i++) {
- filters += componentNames[i] + ":I ";
+ protected String[] getDeviceLogsForComponents(final String[] componentNames,
+ String logSeparator) throws DeviceNotAvailableException {
+ String filters = LOG_SEPARATOR + ":I ";
+ for (String component : componentNames) {
+ filters += component + ":I ";
}
- return mDevice.executeAdbCommand(
+ final String[] result = mDevice.executeAdbCommand(
"logcat", "-v", "brief", "-d", filters, "*:S").split("\\n");
+ if (logSeparator == null) {
+ return result;
+ }
+
+ // Make sure that we only check logs after the separator.
+ int i = 0;
+ boolean lookingForSeparator = true;
+ while (i < result.length && lookingForSeparator) {
+ if (result[i].contains(logSeparator)) {
+ lookingForSeparator = false;
+ }
+ i++;
+ }
+ final String[] filteredResult = new String[result.length - i];
+ for (int curPos = 0; i < result.length; curPos++, i++) {
+ filteredResult[curPos] = result[i];
+ }
+ return filteredResult;
}
private static final Pattern sCreatePattern = Pattern.compile("(.+): onCreate");
@@ -819,9 +850,9 @@
}
}
- ReportedSizes getLastReportedSizesForActivity(String activityName)
+ ReportedSizes getLastReportedSizesForActivity(String activityName, String logSeparator)
throws DeviceNotAvailableException {
- final String[] lines = getDeviceLogsForComponent(activityName);
+ final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
for (int i = lines.length - 1; i >= 0; i--) {
final String line = lines[i].trim();
final Matcher matcher = sNewConfigPattern.matcher(line);
@@ -852,9 +883,10 @@
int mLastPictureInPictureModeChangedLineIndex;
int mDestroyCount;
- public ActivityLifecycleCounts(String activityName) throws DeviceNotAvailableException {
+ public ActivityLifecycleCounts(String activityName, String logSeparator)
+ throws DeviceNotAvailableException {
int lineIndex = 0;
- for (String line : getDeviceLogsForComponent(activityName)) {
+ for (String line : getDeviceLogsForComponent(activityName, logSeparator)) {
line = line.trim();
lineIndex++;
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
index 8cb377d..46c9b03 100755
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
@@ -22,7 +22,7 @@
<activity android:name=".DialogTestActivity"
android:exported="true"
/>
- <activity android:name=".MovingPopupTestActivity"
+ <activity android:name=".MovingChildTestActivity"
android:exported="true"
/>
</application>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
similarity index 67%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
index 91b9788..de6f597 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
@@ -18,23 +18,23 @@
import android.app.Activity;
import android.content.Intent;
+import android.content.Context;
import android.os.Bundle;
import android.view.WindowManager;
import android.view.Window;
import android.view.Gravity;
import android.view.View;
import android.widget.Space;
-import android.widget.PopupWindow;
import android.widget.Button;
import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
import android.widget.FrameLayout;
-// This activity will parent a Popup to the main window, and then move
-// the main window around. We can use this to verify the Popup
+// This activity will parent a Child to the main window, and then move
+// the main window around. We can use this to verify the Child
// is properly updated.
-public class MovingPopupTestActivity extends Activity {
+public class MovingChildTestActivity extends Activity {
Space mView;
- PopupWindow mPopupWindow;
int mX = 0;
int mY = 0;
@@ -43,6 +43,7 @@
public void run() {
final Window w = getWindow();
final WindowManager.LayoutParams attribs = w.getAttributes();
+ attribs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
attribs.x = mX % 1000;
attribs.y = mY % 1000;
w.setAttributes(attribs);
@@ -52,16 +53,19 @@
}
};
- final Runnable makePopup = new Runnable() {
+ final Runnable makeChild = new Runnable() {
@Override
public void run() {
- Button b = new Button(MovingPopupTestActivity.this);
+ Button b = new Button(MovingChildTestActivity.this);
+ WindowManager.LayoutParams p = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_APPLICATION_PANEL);
+ p.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ p.x = 0;
+ p.y = 0;
+ p.token = mView.getWindowToken();
+ p.setTitle("ChildWindow");
- mPopupWindow = new PopupWindow(MovingPopupTestActivity.this);
- mPopupWindow.setContentView(b);
- mPopupWindow.setWidth(50);
- mPopupWindow.setHeight(50);
- mPopupWindow.showAtLocation(mView, 0, 0, 0);
+ ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).addView(b, p);
mView.postDelayed(moveWindow, 50);
}
@@ -76,6 +80,6 @@
mView = new Space(this);
setContentView(mView, p);
- mView.post(makePopup);
+ mView.post(makeChild);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
index 6c7cad0..218fcfe 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
@@ -32,17 +32,17 @@
import android.server.cts.ActivityManagerTestBase;
import android.server.cts.WindowManagerState.WindowState;
-public class PopupMovementTests extends ParentChildTestBase {
+public class ChildMovementTests extends ParentChildTestBase {
private List<WindowState> mWindowList = new ArrayList();
@Override
String intentKey() {
- return "android.server.FrameTestApp.PopupTestCase";
+ return "android.server.FrameTestApp.ChildTestCase";
}
@Override
String activityName() {
- return "MovingPopupTestActivity";
+ return "MovingChildTestActivity";
}
WindowState getSingleWindow(String fullWindowName) {
@@ -66,7 +66,7 @@
}
void doSingleTest(ParentChildTest t) throws Exception {
- String popupName = "PopupWindow";
+ String popupName = "ChildWindow";
final String[] waitForVisible = new String[] { popupName };
mAmWmState.setUseActivityNamesForWindowNames(false);
@@ -85,7 +85,7 @@
SurfaceTraceReceiver.SurfaceObserver observer = new SurfaceTraceReceiver.SurfaceObserver() {
int transactionCount = 0;
- boolean sawPopupMove = false;
+ boolean sawChildMove = false;
boolean sawMainMove = false;
int timesSeen = 0;
@@ -93,7 +93,7 @@
public void openTransaction() {
transactionCount++;
if (transactionCount == 1) {
- sawPopupMove = false;
+ sawChildMove = false;
sawMainMove = false;
}
}
@@ -105,7 +105,7 @@
return;
}
synchronized (monitor) {
- if (sawPopupMove ^ sawMainMove ) {
+ if (sawChildMove ^ sawMainMove ) {
monitor.notifyAll();
return;
}
@@ -119,7 +119,7 @@
@Override
public void setPosition(String windowName, float x, float y) {
if (windowName.equals(popupName)) {
- sawPopupMove = true;
+ sawChildMove = true;
timesSeen++;
} else if (windowName.equals(mainName)) {
sawMainMove = true;
@@ -128,10 +128,10 @@
};
/**
- * Here we test that a Popup moves in the same transaction
- * as its parent. We launch an activity with a Popup which will
+ * Here we test that a Child moves in the same transaction
+ * as its parent. We launch an activity with a Child which will
* move around its own main window. Then we listen to WindowManager transactions.
- * Since the Popup is static within the window, if we ever see one of
+ * Since the Child is static within the window, if we ever see one of
* them move xor the other one we have a problem!
*/
public void testSurfaceMovesWithParent() throws Exception {
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
index 56b1b40..6fb5fbd 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
@@ -135,7 +135,9 @@
// With FLAG_LAYOUT_NO_LIMITS we should get the size we request, even if its much
// larger than the screen.
public void testOversizedDimensionsNoLimits() throws Exception {
- doParentChildTest("OversizedDimensionsNoLimits",
+ // TODO(b/36890978): We only run this in fullscreen because of the
+ // unclear status of NO_LIMITS for non-child surfaces in MW modes
+ doFullscreenTest("OversizedDimensionsNoLimits",
(WindowState parent, WindowState dialog) -> {
Rectangle contentFrame = parent.getContentFrame();
Rectangle expectedFrame = new Rectangle(contentFrame.x, contentFrame.y,
diff --git a/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java b/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
index d599546..a8871a3 100644
--- a/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
+++ b/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
@@ -99,6 +99,8 @@
getDevice().executeShellCommand(SLEEP_COMMAND);
// Start the APK and wait for it to complete.
getDevice().executeShellCommand(START_COMMAND);
+ // Give the activity some time to start
+ Thread.sleep(500);
// Dump logcat.
String logs = getDevice().executeAdbCommand(
"logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
diff --git a/hostsidetests/webkit/AndroidTest.xml b/hostsidetests/webkit/AndroidTest.xml
index 8641395..5a0eeca 100644
--- a/hostsidetests/webkit/AndroidTest.xml
+++ b/hostsidetests/webkit/AndroidTest.xml
@@ -17,7 +17,6 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsWebViewStartupApp.apk" />
- <option name="test-file-name" value="CtsWebViewRendererCrash.apk" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/hostsidetests/webkit/renderprocesscrash/Android.mk b/hostsidetests/webkit/renderprocesscrash/Android.mk
deleted file mode 100644
index 846cdc8..0000000
--- a/hostsidetests/webkit/renderprocesscrash/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES :=
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsWebViewRendererCrash
-
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/webkit/renderprocesscrash/AndroidManifest.xml b/hostsidetests/webkit/renderprocesscrash/AndroidManifest.xml
deleted file mode 100644
index d1744e5..0000000
--- a/hostsidetests/webkit/renderprocesscrash/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.webkit.renderprocesscrash">
-
- <uses-permission android:name="android.permission.INTERNET" />
-
- <application android:maxRecents="1">
- <activity android:name=".RenderProcessCrashActivity"
- android:label="RenderProcessCrashActivity"
- android:screenOrientation="nosensor">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/hostsidetests/webkit/src/com/android/cts/webkit/RenderProcessCrashTest.java b/hostsidetests/webkit/src/com/android/cts/webkit/RenderProcessCrashTest.java
deleted file mode 100644
index 2f65afc..0000000
--- a/hostsidetests/webkit/src/com/android/cts/webkit/RenderProcessCrashTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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 com.android.cts.webkit;
-
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDeviceOptions;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-import java.util.Scanner;
-
-/**
- * This test lanuches RenderProcessCrashActivity which crashes render process, and
- * checks specific crash log in Logcat to verify Render process crashed.
- */
-public class RenderProcessCrashTest extends DeviceTestCase {
- /**
- * The package name of the APK.
- */
- private static final String PACKAGE = "com.android.cts.webkit.renderprocesscrash";
-
- /**
- * The class name of the main activity in the APK.
- */
- private static final String CLASS = "RenderProcessCrashActivity";
-
- /**
- * The command to launch the main activity.
- */
- private static final String START_COMMAND = String.format(
- "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
-
- /**
- * The test string to look for.
- */
- private static final String TEST_STRING = "kill (OOM or update) wasn't handed by all associated"
- + " webviews, killing application.";
-
- /**
- * Tests the string was successfully logged to Logcat from the activity.
- *
- * @throws Exception
- */
- public void testCrash() throws Exception {
- ITestDevice device = getDevice();
- // Clear logcat.
- device.executeAdbCommand("logcat", "-c");
- TestDeviceOptions options = new TestDeviceOptions();
- options.setLogcatOptions("-v brief -d chromium:E *:S");
- device.setOptions(options);
-
- // Start the APK.
- device.executeShellCommand(START_COMMAND);
- // Dump logcat.
- device.startLogcat();
- // Search for string.
- Scanner in = new Scanner(device.getLogcat().createInputStream());
- boolean found = false;
- int tryTimes = 10;
- while (tryTimes-- > 0) {
- while (in.hasNextLine()) {
- String line = in.nextLine().trim();
- if(line.endsWith(TEST_STRING)) {
- found = true;
- break;
- }
- }
- if (found) break;
- Thread.sleep(1000);
- }
- in.close();
- device.stopLogcat();
- assertTrue("Can't not find crash log " + TEST_STRING, found);
- }
-}
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
index e914ee2..7d06682 100644
--- a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -17,14 +17,21 @@
package android.jobscheduler;
import android.annotation.TargetApi;
+import android.app.job.JobInfo;
import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
import android.app.job.JobService;
+import android.app.job.JobWorkItem;
import android.content.ClipData;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Process;
import android.util.Log;
+import junit.framework.Assert;
+
+import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -40,15 +47,29 @@
/** Wait this long before timing out the test. */
private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds.
+ private JobParameters mParams;
+
+ ArrayList<Intent> mReceivedWork = new ArrayList<Intent>();
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (TestEnvironment.getTestEnvironment().getExpectedWork() != null) {
+ TestEnvironment.getTestEnvironment().notifyExecution(mParams, 0, 0, mReceivedWork,
+ null);
+ }
+ }
+
@Override
public void onCreate() {
super.onCreate();
- Log.e(TAG, "Created test service.");
+ Log.i(TAG, "Created test service.");
}
@Override
public boolean onStartJob(JobParameters params) {
Log.i(TAG, "Test job executing: " + params.getJobId());
+ mParams = params;
int permCheckRead = PackageManager.PERMISSION_DENIED;
int permCheckWrite = PackageManager.PERMISSION_DENIED;
@@ -60,8 +81,44 @@
Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
- TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead, permCheckWrite);
- return false; // No work to do.
+ TestWorkItem[] expectedWork = TestEnvironment.getTestEnvironment().getExpectedWork();
+ if (expectedWork != null) {
+ try {
+ if (TestEnvironment.getTestEnvironment().awaitDoWork()) {
+ TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
+ permCheckWrite, null, "Spent too long waiting to start executing work");
+ return false;
+ }
+ } catch (InterruptedException e) {
+ TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
+ permCheckWrite, null, "Failed waiting for work: " + e);
+ return false;
+ }
+ JobWorkItem work;
+ int index = 0;
+ while ((work = params.dequeueWork()) != null) {
+ Log.i(TAG, "Received work #" + index + ": " + work.getIntent());
+ mReceivedWork.add(work.getIntent());
+ params.completeWork(work);
+ if (index < expectedWork.length && expectedWork[index].subitems != null) {
+ final TestWorkItem[] sub = expectedWork[index].subitems;
+ final JobInfo ji = expectedWork[index].jobInfo;
+ final JobScheduler js = (JobScheduler) getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ for (int subi = 0; subi < sub.length; subi++) {
+ js.enqueue(ji, new JobWorkItem(sub[subi].intent));
+ }
+ }
+ }
+ Log.i(TAG, "Done with all work at #" + index);
+ // We don't notifyExecution here because we want to make sure the job properly
+ // stops itself.
+ return true;
+ } else {
+ TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
+ permCheckWrite, null, null);
+ return false; // No work to do.
+ }
}
@Override
@@ -69,6 +126,24 @@
return false;
}
+ public static final class TestWorkItem {
+ public final Intent intent;
+ public final JobInfo jobInfo;
+ public final TestWorkItem[] subitems;
+
+ public TestWorkItem(Intent _intent) {
+ intent = _intent;
+ jobInfo = null;
+ subitems = null;
+ }
+
+ public TestWorkItem(Intent _intent, JobInfo _jobInfo, TestWorkItem[] _subitems) {
+ intent = _intent;
+ jobInfo = _jobInfo;
+ subitems = _subitems;
+ }
+ }
+
/**
* Configures the expected behaviour for each test. This object is shared across consecutive
* tests, so to clear state each test is responsible for calling
@@ -80,9 +155,13 @@
//public static final int INVALID_JOB_ID = -1;
private CountDownLatch mLatch;
+ private CountDownLatch mDoWorkLatch;
+ private TestWorkItem[] mExpectedWork;
private JobParameters mExecutedJobParameters;
private int mExecutedPermCheckRead;
private int mExecutedPermCheckWrite;
+ private ArrayList<Intent> mExecutedReceivedWork;
+ private String mExecutedErrorMessage;
public static TestEnvironment getTestEnvironment() {
if (kTestEnvironment == null) {
@@ -91,6 +170,10 @@
return kTestEnvironment;
}
+ public TestWorkItem[] getExpectedWork() {
+ return mExpectedWork;
+ }
+
public JobParameters getLastJobParameters() {
return mExecutedJobParameters;
}
@@ -103,12 +186,23 @@
return mExecutedPermCheckWrite;
}
+ public ArrayList<Intent> getLastReceivedWork() {
+ return mExecutedReceivedWork;
+ }
+
+ public String getLastErrorMessage() {
+ return mExecutedErrorMessage;
+ }
+
/**
* Block the test thread, waiting on the JobScheduler to execute some previously scheduled
* job on this service.
*/
public boolean awaitExecution() throws InterruptedException {
final boolean executed = mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ if (getLastErrorMessage() != null) {
+ Assert.fail(getLastErrorMessage());
+ }
return executed;
}
@@ -121,11 +215,18 @@
return !mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}
- private void notifyExecution(JobParameters params, int permCheckRead, int permCheckWrite) {
+ public boolean awaitDoWork() throws InterruptedException {
+ return !mDoWorkLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ }
+
+ private void notifyExecution(JobParameters params, int permCheckRead, int permCheckWrite,
+ ArrayList<Intent> receivedWork, String errorMsg) {
//Log.d(TAG, "Job executed:" + params.getJobId());
mExecutedJobParameters = params;
mExecutedPermCheckRead = permCheckRead;
mExecutedPermCheckWrite = permCheckWrite;
+ mExecutedReceivedWork = receivedWork;
+ mExecutedErrorMessage = errorMsg;
mLatch.countDown();
}
@@ -138,6 +239,15 @@
}
}
+ public void setExpectedWork(TestWorkItem[] work) {
+ mExpectedWork = work;
+ mDoWorkLatch = new CountDownLatch(1);
+ }
+
+ public void readyToWork() {
+ mDoWorkLatch.countDown();
+ }
+
/** Called in each testCase#setup */
public void setUp() {
mLatch = null;
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
index be3f9c9..5ffa347 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
@@ -54,8 +54,6 @@
mBuilder = new JobInfo.Builder(BATTERY_JOB_ID, kJobServiceComponent);
SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler monitor-battery on");
- String res = SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
- + mContext.getPackageName() + " false");
}
@Override
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
index 9a8b642..47089d4 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
@@ -19,16 +19,10 @@
import android.annotation.TargetApi;
import android.app.job.JobInfo;
-import android.content.ClipData;
import android.content.ContentProviderClient;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.os.Bundle;
import android.os.Process;
-import android.os.SystemClock;
-
-import com.android.compatibility.common.util.SystemUtil;
/**
* Schedules jobs with the {@link android.app.job.JobScheduler} that grant permissions through
@@ -41,16 +35,7 @@
/** Unique identifier for the job scheduled by this suite of tests. */
public static final int CLIP_DATA_JOB_ID = ClipDataJobTest.class.hashCode();
- static final String MY_PACKAGE = "android.jobscheduler.cts";
-
- static final String JOBPERM_PACKAGE = "android.jobscheduler.cts.jobperm";
- static final String JOBPERM_AUTHORITY = "android.jobscheduler.cts.jobperm.provider";
- static final String JOBPERM_PERM = "android.jobscheduler.cts.jobperm.perm";
-
- private JobInfo.Builder mBuilder;
- private Uri mFirstUri;
- private Bundle mFirstUriBundle;
- private ClipData mClipData;
+ JobInfo.Builder mBuilder;
private ContentProviderClient mProvider;
@Override
@@ -58,62 +43,15 @@
super.setUp();
mBuilder = new JobInfo.Builder(CLIP_DATA_JOB_ID, kJobServiceComponent);
- mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo");
- mFirstUriBundle = new Bundle();
- mFirstUriBundle.putParcelable("uri", mFirstUri);
- mClipData = new ClipData("JobPerm", new String[] { "application/*" },
- new ClipData.Item(mFirstUri));
mProvider = getContext().getContentResolver().acquireContentProviderClient(mFirstUri);
- String res = SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
- + mContext.getPackageName() + " false");
+ assertNotNull(mProvider);
}
@Override
public void tearDown() throws Exception {
+ super.tearDown();
mProvider.close();
mJobScheduler.cancel(CLIP_DATA_JOB_ID);
- // Put storage service back in to normal operation.
- SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
- }
-
- // Note we are just using storage state as a way to control when the job gets executed.
- void setStorageState(boolean low) throws Exception {
- String res;
- if (low) {
- res = SystemUtil.runShellCommand(getInstrumentation(),
- "cmd devicestoragemonitor force-low -f");
- } else {
- res = SystemUtil.runShellCommand(getInstrumentation(),
- "cmd devicestoragemonitor force-not-low -f");
- }
- int seq = Integer.parseInt(res.trim());
- long startTime = SystemClock.elapsedRealtime();
-
- // Wait for the storage update to be processed by job scheduler before proceeding.
- int curSeq;
- do {
- curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
- "cmd jobscheduler get-storage-seq").trim());
- if (curSeq == seq) {
- return;
- }
- } while ((SystemClock.elapsedRealtime()-startTime) < 1000);
-
- fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
- }
-
- void waitPermissionRevoke(Uri uri, int access, long timeout) {
- long startTime = SystemClock.elapsedRealtime();
- while (getContext().checkUriPermission(uri, Process.myPid(), Process.myUid(), access)
- != PackageManager.PERMISSION_GRANTED) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- }
- if ((SystemClock.elapsedRealtime()-startTime) >= timeout) {
- fail("Timed out waiting for permission revoke");
- }
- }
}
/**
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
index b2cf29c..c92e521 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
@@ -17,14 +17,26 @@
import android.annotation.TargetApi;
import android.app.Instrumentation;
+import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.jobscheduler.MockJobService;
import android.jobscheduler.TriggerContentJobService;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
/**
* Common functionality from which the other test case classes derive.
@@ -47,10 +59,37 @@
Context mContext;
+ static final String MY_PACKAGE = "android.jobscheduler.cts";
+
+ static final String JOBPERM_PACKAGE = "android.jobscheduler.cts.jobperm";
+ static final String JOBPERM_AUTHORITY = "android.jobscheduler.cts.jobperm.provider";
+ static final String JOBPERM_PERM = "android.jobscheduler.cts.jobperm.perm";
+
+ Uri mFirstUri;
+ Bundle mFirstUriBundle;
+ ClipData mClipData;
+
+ boolean mStorageStateChanged;
+
@Override
public void injectInstrumentation(Instrumentation instrumentation) {
super.injectInstrumentation(instrumentation);
mContext = instrumentation.getContext();
+ kJobServiceComponent = new ComponentName(getContext(), MockJobService.class);
+ kTriggerContentServiceComponent = new ComponentName(getContext(),
+ TriggerContentJobService.class);
+ mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo");
+ mFirstUriBundle = new Bundle();
+ mFirstUriBundle.putParcelable("uri", mFirstUri);
+ mClipData = new ClipData("JobPerm", new String[] { "application/*" },
+ new ClipData.Item(mFirstUri));
+ try {
+ SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
+ + mContext.getPackageName() + " false");
+ } catch (IOException e) {
+ Log.w("ConstraintTest", "Failed setting inactive false", e);
+ }
}
public Context getContext() {
@@ -62,13 +101,18 @@
super.setUp();
kTestEnvironment.setUp();
kTriggerTestEnvironment.setUp();
- kJobServiceComponent = new ComponentName(getContext(), MockJobService.class);
- kTriggerContentServiceComponent = new ComponentName(getContext(),
- TriggerContentJobService.class);
- mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
mJobScheduler.cancelAll();
}
+ @Override
+ public void tearDown() throws Exception {
+ if (mStorageStateChanged) {
+ // Put storage service back in to normal operation.
+ SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
+ mStorageStateChanged = false;
+ }
+ }
+
/**
* The scheduler will usually only flush its queue of unexpired jobs when the device is
* considered to be on stable power - that is, plugged in for a period of 2 minutes.
@@ -77,4 +121,45 @@
protected void sendExpediteStableChargingBroadcast() {
getContext().sendBroadcast(EXPEDITE_STABLE_CHARGING);
}
+
+ void waitPermissionRevoke(Uri uri, int access, long timeout) {
+ long startTime = SystemClock.elapsedRealtime();
+ while (getContext().checkUriPermission(uri, Process.myPid(), Process.myUid(), access)
+ != PackageManager.PERMISSION_GRANTED) {
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ }
+ if ((SystemClock.elapsedRealtime()-startTime) >= timeout) {
+ fail("Timed out waiting for permission revoke");
+ }
+ }
+ }
+
+ // Note we are just using storage state as a way to control when the job gets executed.
+ void setStorageState(boolean low) throws Exception {
+ mStorageStateChanged = true;
+ String res;
+ if (low) {
+ res = SystemUtil.runShellCommand(getInstrumentation(),
+ "cmd devicestoragemonitor force-low -f");
+ } else {
+ res = SystemUtil.runShellCommand(getInstrumentation(),
+ "cmd devicestoragemonitor force-not-low -f");
+ }
+ int seq = Integer.parseInt(res.trim());
+ long startTime = SystemClock.elapsedRealtime();
+
+ // Wait for the storage update to be processed by job scheduler before proceeding.
+ int curSeq;
+ do {
+ curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
+ "cmd jobscheduler get-storage-seq").trim());
+ if (curSeq == seq) {
+ return;
+ }
+ } while ((SystemClock.elapsedRealtime()-startTime) < 1000);
+
+ fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
+ }
}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
new file mode 100644
index 0000000..ad5a20f
--- /dev/null
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.jobscheduler.cts;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.app.job.JobWorkItem;
+import android.content.ContentProviderClient;
+import android.content.Intent;
+import android.jobscheduler.MockJobService.TestWorkItem;
+
+import java.util.ArrayList;
+
+/**
+ * Schedules jobs with the {@link android.app.job.JobScheduler} by enqueue work in to
+ * them and processing it.
+ */
+@TargetApi(26)
+public class EnqueueJobWorkTest extends ConstraintTest {
+ private static final String TAG = "ClipDataJobTest";
+
+ /** Unique identifier for the job scheduled by this suite of tests. */
+ public static final int ENQUEUE_WORK_JOB_ID = EnqueueJobWorkTest.class.hashCode();
+
+ private JobInfo.Builder mBuilder;
+ private ContentProviderClient mProvider;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mBuilder = new JobInfo.Builder(ENQUEUE_WORK_JOB_ID, kJobServiceComponent);
+ //mProvider = getContext().getContentResolver().acquireContentProviderClient(mFirstUri);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ //mProvider.close();
+ mJobScheduler.cancel(ENQUEUE_WORK_JOB_ID);
+ }
+
+ private boolean intentEquals(Intent i1, Intent i2) {
+ if (i1 == i2) {
+ return true;
+ }
+ if (i1 == null || i2 == null) {
+ return false;
+ }
+ return i1.filterEquals(i2);
+ }
+
+ private void compareWork(TestWorkItem[] expected, ArrayList<Intent> received) {
+ if (received == null) {
+ fail("Didn't receive any expected work.");
+ }
+ ArrayList<TestWorkItem> expectedArray = new ArrayList<>();
+ for (int i = 0; i < expected.length; i++) {
+ expectedArray.add(expected[i]);
+ }
+ for (int i = 0; i < received.size(); i++) {
+ Intent work = received.get(i);
+ if (i >= expected.length) {
+ fail("Received more than " + expected.length + " work items, first extra is "
+ + work);
+ }
+ if (!intentEquals(work, expectedArray.get(i).intent)) {
+ fail("Received work #" + i + " " + work + " but expected " + expected[i]);
+ }
+ if (i < expected.length && expected[i].subitems != null) {
+ TestWorkItem[] sub = expected[i].subitems;
+ for (int j = 0; j < sub.length; j++) {
+ expectedArray.add(sub[j]);
+ }
+ }
+ }
+ if (received.size() < expected.length) {
+ fail("Received only " + received.size() + " work items, but expected "
+ + expected.length);
+ }
+ }
+
+ /**
+ * Test basic enqueueing of work.
+ */
+ public void testEnqueueOneWork() throws Exception {
+ Intent work1 = new Intent("work1");
+ TestWorkItem[] work = new TestWorkItem[] { new TestWorkItem(work1) };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWork(work);
+ mJobScheduler.enqueue(mBuilder.setOverrideDeadline(0).build(), new JobWorkItem(work1));
+ kTestEnvironment.readyToWork();
+ assertTrue("Job with work enqueued did not fire.",
+ kTestEnvironment.awaitExecution());
+ compareWork(work, kTestEnvironment.getLastReceivedWork());
+ if (kTestEnvironment.getLastErrorMessage() != null) {
+ fail(kTestEnvironment.getLastErrorMessage());
+ }
+ }
+
+ /**
+ * Test basic enqueueing batches of work.
+ */
+ public void testEnqueueMultipleWork() throws Exception {
+ Intent work1 = new Intent("work1");
+ Intent work2 = new Intent("work2");
+ Intent work3 = new Intent("work3");
+ Intent work4 = new Intent("work4");
+ Intent work5 = new Intent("work5");
+ Intent work6 = new Intent("work6");
+ Intent work7 = new Intent("work7");
+ Intent work8 = new Intent("work8");
+ TestWorkItem[] work = new TestWorkItem[] {
+ new TestWorkItem(work1), new TestWorkItem(work2), new TestWorkItem(work3),
+ new TestWorkItem(work4), new TestWorkItem(work5), new TestWorkItem(work6),
+ new TestWorkItem(work7), new TestWorkItem(work8) };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWork(work);
+ JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+ mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work3));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work4));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work5));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work6));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work7));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work8));
+ kTestEnvironment.readyToWork();
+ assertTrue("Job with work enqueued did not fire.",
+ kTestEnvironment.awaitExecution());
+ compareWork(work, kTestEnvironment.getLastReceivedWork());
+ }
+
+ /**
+ * Test basic enqueueing batches of work, with new work coming in while processing existing
+ * work.
+ */
+ public void testEnqueueMultipleSubWork() throws Exception {
+ Intent work1 = new Intent("work1");
+ Intent work2 = new Intent("work2");
+ Intent work3 = new Intent("work3");
+ Intent work4 = new Intent("work4");
+ Intent work5 = new Intent("work5");
+ Intent work6 = new Intent("work6");
+ Intent work7 = new Intent("work7");
+ Intent work8 = new Intent("work8");
+ JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+ TestWorkItem[] work = new TestWorkItem[]{
+ new TestWorkItem(work1), new TestWorkItem(work2), new TestWorkItem(work3),
+ new TestWorkItem(work4, ji, new TestWorkItem[] {
+ new TestWorkItem(work5), new TestWorkItem(work6),
+ new TestWorkItem(work7), new TestWorkItem(work8)})
+ };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWork(work);
+ mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work3));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work4));
+ kTestEnvironment.readyToWork();
+ assertTrue("Job with work enqueued did not fire.",
+ kTestEnvironment.awaitExecution());
+ compareWork(work, kTestEnvironment.getLastReceivedWork());
+ }
+}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/StorageConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/StorageConstraintTest.java
index f40dd66..aea4d84 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/StorageConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/StorageConstraintTest.java
@@ -40,40 +40,12 @@
super.setUp();
mBuilder = new JobInfo.Builder(STORAGE_JOB_ID, kJobServiceComponent);
- String res = SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
- + mContext.getPackageName() + " false");
}
@Override
public void tearDown() throws Exception {
+ super.tearDown();
mJobScheduler.cancel(STORAGE_JOB_ID);
- // Put storage service back in to normal operation.
- SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
- }
-
- void setStorageState(boolean low) throws Exception {
- String res;
- if (low) {
- res = SystemUtil.runShellCommand(getInstrumentation(),
- "cmd devicestoragemonitor force-low -f");
- } else {
- res = SystemUtil.runShellCommand(getInstrumentation(),
- "cmd devicestoragemonitor force-not-low -f");
- }
- int seq = Integer.parseInt(res.trim());
- long startTime = SystemClock.elapsedRealtime();
-
- // Wait for the storage update to be processed by job scheduler before proceeding.
- int curSeq;
- do {
- curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
- "cmd jobscheduler get-storage-seq").trim());
- if (curSeq == seq) {
- return;
- }
- } while ((SystemClock.elapsedRealtime()-startTime) < 1000);
-
- fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
}
// --------------------------------------------------------------------------------------------
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TriggerContentTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/TriggerContentTest.java
index 8b71171..fe48950 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TriggerContentTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TriggerContentTest.java
@@ -42,7 +42,7 @@
*/
@TargetApi(23)
public class TriggerContentTest extends ConstraintTest {
- public static final int TRIGGER_CONTENT_JOB_ID = ConnectivityConstraintTest.class.hashCode();
+ public static final int TRIGGER_CONTENT_JOB_ID = TriggerContentTest.class.hashCode();
// The root URI of the media provider, to monitor for generic changes to its content.
static final Uri MEDIA_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/");
@@ -120,7 +120,7 @@
}
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
for (int i=0; i<mActiveFiles.length; i++) {
cleanupActive(i);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index 8291633..b67fc28 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -14,6 +14,7 @@
package android.accessibilityservice.cts;
import android.app.Instrumentation;
+import android.content.pm.PackageManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.media.AudioManager;
@@ -31,16 +32,25 @@
public class AccessibilityVolumeTest {
Instrumentation mInstrumentation;
AudioManager mAudioManager;
+ // If a platform collects all volumes into one, these tests aren't relevant
+ boolean mSingleVolume;
@Before
public void setUp() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mAudioManager =
(AudioManager) mInstrumentation.getContext().getSystemService(AUDIO_SERVICE);
+ // TVs have a single volume
+ PackageManager pm = mInstrumentation.getContext().getPackageManager();
+ mSingleVolume = (pm != null) && (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION));
}
@Test
public void testChangeAccessibilityVolume_outsideValidAccessibilityService_shouldFail() {
+ if (mSingleVolume) {
+ return;
+ }
int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume, 0);
@@ -50,6 +60,9 @@
@Test
public void testChangeAccessibilityVolume_inAccessibilityService_shouldWork() {
+ if (mSingleVolume) {
+ return;
+ }
int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
diff --git a/tests/app/src/android/app/cts/ActionBarTest.java b/tests/app/src/android/app/cts/ActionBarTest.java
index e6da6cb..351339b 100644
--- a/tests/app/src/android/app/cts/ActionBarTest.java
+++ b/tests/app/src/android/app/cts/ActionBarTest.java
@@ -24,6 +24,7 @@
import android.test.UiThreadTest;
import android.view.KeyEvent;
import android.view.Window;
+import org.junit.Assume;
public class ActionBarTest extends ActivityInstrumentationTestCase2<ActionBarActivity> {
@@ -84,6 +85,7 @@
}
public void testOptionsMenuKey() {
+ Assume.assumeTrue(mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL));
final boolean menuIsVisible[] = {false};
mActivity.getActionBar().addOnMenuVisibilityListener(
isVisible -> menuIsVisible[0] = isVisible);
diff --git a/tests/app/src/android/app/cts/ToolbarActionBarTest.java b/tests/app/src/android/app/cts/ToolbarActionBarTest.java
index 53ee982..d625802 100644
--- a/tests/app/src/android/app/cts/ToolbarActionBarTest.java
+++ b/tests/app/src/android/app/cts/ToolbarActionBarTest.java
@@ -21,6 +21,7 @@
import android.view.KeyEvent;
import android.view.Window;
+import org.junit.Assume;
public class ToolbarActionBarTest extends ActivityInstrumentationTestCase2<ToolbarActivity> {
@@ -39,6 +40,7 @@
}
public void testOptionsMenuKey() {
+ Assume.assumeTrue(mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL));
final boolean menuIsVisible[] = {false};
mActivity.getActionBar().addOnMenuVisibilityListener(
isVisible -> menuIsVisible[0] = isVisible);
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index aca9453..663fe40 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -31,6 +31,7 @@
<activity android:name=".WelcomeActivity"/>
<activity android:name=".ViewAttributesTestActivity" />
<activity android:name=".AuthenticationActivity" />
+ <activity android:name=".ManualAuthenticationActivity" />
<activity android:name=".CheckoutActivity"/>
<activity android:name=".InitializedCheckoutActivity" />
<activity android:name=".DatePickerCalendarActivity" />
@@ -49,6 +50,9 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".EmptyActivity"/>
+ <activity android:name=".OutOfProcessLoginActivity"
+ android:process="android.autofillservice.cts.outside"/>
<service
android:name=".InstrumentedAutoFillService"
diff --git a/tests/autofillservice/res/layout/empty.xml b/tests/autofillservice/res/layout/empty.xml
new file mode 100644
index 0000000..7687408
--- /dev/null
+++ b/tests/autofillservice/res/layout/empty.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/tests/autofillservice/res/layout/single_button_activity.xml b/tests/autofillservice/res/layout/single_button_activity.xml
new file mode 100644
index 0000000..9c68b98
--- /dev/null
+++ b/tests/autofillservice/res/layout/single_button_activity.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="press me"
+ android:id="@+id/button" />
+</LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index 7e8fa9c..7289b5a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -241,7 +241,7 @@
mActivity.onAddress((v) -> v.check(R.id.work_address));
mActivity.onSaveCc((v) -> v.setChecked(false));
mActivity.tapBuy();
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_CREDIT_CARD, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_CREDIT_CARD);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
// Assert sanitization on save: everything should be available!
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
index fb050c3..1d4f652 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
@@ -90,7 +90,7 @@
activity.setDate(2010, 11, 12);
activity.tapOk();
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_GENERIC, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
diff --git a/hostsidetests/webkit/renderprocesscrash/src/com/android/cts/webkit/renderprocesscrash/RenderProcessCrashActivity.java b/tests/autofillservice/src/android/autofillservice/cts/EmptyActivity.java
similarity index 69%
rename from hostsidetests/webkit/renderprocesscrash/src/com/android/cts/webkit/renderprocesscrash/RenderProcessCrashActivity.java
rename to tests/autofillservice/src/android/autofillservice/cts/EmptyActivity.java
index d599eba..1a64b8c 100644
--- a/hostsidetests/webkit/renderprocesscrash/src/com/android/cts/webkit/renderprocesscrash/RenderProcessCrashActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/EmptyActivity.java
@@ -14,19 +14,20 @@
* limitations under the License.
*/
-package com.android.cts.webkit.renderprocesscrash;
+package android.autofillservice.cts;
+import android.support.annotation.Nullable;
import android.app.Activity;
import android.os.Bundle;
-import android.webkit.WebView;
-public class RenderProcessCrashActivity extends Activity {
-
+/**
+ * Empty activity
+ */
+public class EmptyActivity extends Activity {
@Override
- public void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- WebView webView = new WebView(this);
- setContentView(webView);
- webView.loadUrl("chrome://kill");
+
+ setContentView(R.layout.empty);
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 37fb159..e19fa0c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -28,6 +28,7 @@
import android.os.UserManager;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
+import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -58,6 +59,12 @@
static final String ID_LOGIN = "login";
static final String ID_OUTPUT = "output";
+ /** Pass to {@link #setOrientation(int)} to change the display to portrait mode */
+ public static int PORTRAIT = 0;
+
+ /** Pass to {@link #setOrientation(int)} to change the display to landscape mode */
+ public static int LANDSCAPE = 1;
+
/**
* Timeout (in milliseconds) until framework binds / unbinds from service.
*/
@@ -88,6 +95,13 @@
*/
static final int RETRY_MS = 100;
+ private final static String ACCELLEROMETER_CHANGE =
+ "content insert --uri content://settings/system --bind name:s:accelerometer_rotation "
+ + "--bind value:i:%d";
+ private final static String ORIENTATION_CHANGE =
+ "content insert --uri content://settings/system --bind name:s:user_rotation --bind "
+ + "value:i:%d";
+
/**
* Runs a {@code r}, ignoring all {@link RuntimeException} and {@link Error} until the
* {@link #UI_TIMEOUT_MS} is reached.
@@ -338,7 +352,7 @@
final AutofillValue value = node.getAutofillValue();
assertWithMessage("null autofill value on %s", node).that(value).isNotNull();
assertWithMessage("wrong autofill type on %s", node).that(value.isText()).isTrue();
- assertWithMessage("wrong autofill value on %s", node).that(value.getTextValue())
+ assertWithMessage("wrong autofill value on %s", node).that(value.getTextValue().toString())
.isEqualTo(expectedText);
return node;
}
@@ -533,6 +547,56 @@
return requiredIds;
}
+ /**
+ * Prevents the screen to rotate by itself
+ */
+ public static void disableAutoRotation() {
+ runShellCommand(ACCELLEROMETER_CHANGE, 0);
+ setOrientation(PORTRAIT);
+ }
+
+ /**
+ * Allows the screen to rotate by itself
+ */
+ public static void allowAutoRotation() {
+ runShellCommand(ACCELLEROMETER_CHANGE, 1);
+ }
+
+ /**
+ * Changes the screen orientation. This triggers a activity lifecycle (destroy -> create) for
+ * activities that do not handle this config change such as {@link OutOfProcessLoginActivity}.
+ *
+ * @param value {@link #PORTRAIT} or {@link #LANDSCAPE};
+ */
+ public static void setOrientation(int value) {
+ runShellCommand(ORIENTATION_CHANGE, value);
+ }
+
+ /**
+ * Wait until a process starts and returns the process ID of the process.
+ *
+ * @return The pid of the process
+ */
+ public static int getOutOfProcessPid(@NonNull String processName) throws InterruptedException {
+ long startTime = System.currentTimeMillis();
+
+ while (System.currentTimeMillis() - startTime < UI_TIMEOUT_MS) {
+ String[] allProcessDescs = runShellCommand("ps -eo PID,ARGS=CMD").split("\n");
+
+ for (String processDesc : allProcessDescs) {
+ String[] pidAndName = processDesc.trim().split(" ");
+
+ if (pidAndName[1].equals(processName)) {
+ return Integer.parseInt(pidAndName[0]);
+ }
+ }
+
+ Thread.sleep(RETRY_MS);
+ }
+
+ throw new IllegalStateException("process not found");
+ }
+
private Helper() {
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 2d5a33c..e294c33 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -57,10 +57,12 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.os.Bundle;
-import android.os.SystemClock;
import android.support.test.rule.ActivityTestRule;
import android.support.test.uiautomator.UiObject2;
import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.autofill.AutofillManager;
import org.junit.After;
@@ -82,7 +84,7 @@
*
* Save
* - test cases where only non-savable-ids are changed
- * - test case where 'no thanks' is tapped (similar to testSaveSnackBarGoesAway())
+ * - test case where 'no thanks' or 'x' is tapped (similar to former testSaveSnackBarGoesAway())
*
* Other assertions
* - illegal state thrown on callback calls
@@ -90,11 +92,8 @@
*/
public class LoginActivityTest extends AutoFillServiceTestCase {
- // TODO(b/33197203 , b/36855717): remove when bug 36855717 is fixed
- private static final boolean BUG_36855717_FIXED = false;
-
// TODO(b/33197203 , b/35707731): remove when fixed
- private static final boolean SUPPORTS_PARTIOTINED_AUTH = false;
+ private static final boolean SUPPORTS_PARTITIONED_AUTH = false;
@Rule
public final ActivityTestRule<LoginActivity> mActivityRule =
@@ -174,6 +173,27 @@
}
@Test
+ public void testAutoFillWhenViewHasChildAccessibilityNodes() throws Exception {
+ mActivity.onUsername((v) -> v.setAccessibilityDelegate(new AccessibilityDelegate() {
+ @Override
+ public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
+ return new AccessibilityNodeProvider() {
+ @Override
+ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+ final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ if (virtualViewId == View.NO_ID) {
+ info.addChild(v, 108);
+ }
+ return info;
+ }
+ };
+ }
+ }));
+
+ testAutoFillOneDataset();
+ }
+
+ @Test
public void testAutoFillOneDatasetAndMoveFocusAround() throws Exception {
// Set service.
enableService();
@@ -364,7 +384,7 @@
assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
// Assert the snack bar is shown and tap "Save".
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
@@ -777,7 +797,7 @@
assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
// Assert the snack bar is shown and tap "Save".
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
@@ -791,53 +811,6 @@
assertNoDanglingSessions();
}
- private void setSnackBarLifetimeMs(long timeout) {
- runShellCommand("cmd autofill set save_timeout %s", timeout);
- }
-
- @Test
- public void testSaveSnackBarGoesAway() throws Exception {
- enableService();
- final int timeout = 1000;
- setSnackBarLifetimeMs(timeout);
-
- try {
- // Set expectations.
- sReplier.addResponse(new CannedFillResponse.Builder()
- .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
- .build());
-
- // Trigger auto-fill.
- mActivity.onUsername(View::requestFocus);
-
- // Wait for onFill() before proceeding, otherwise the fields might be changed before
- // the session started
- sReplier.getNextFillRequest();
-
- // Sanity check.
- sUiBot.assertNoDatasets();
-
- // Set credentials...
- mActivity.onUsername((v) -> v.setText("malkovich"));
- mActivity.onPassword((v) -> v.setText("malkovich"));
-
- // ...and login
- final String expectedMessage = getWelcomeMessage("malkovich");
- final String actualMessage = mActivity.tapLogin();
- assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
-
- // Assert the snack bar is shown.
- sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
- SystemClock.sleep(timeout);
- sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
-
- // Sanity check: once timed out, session should be finsihed.
- assertNoDanglingSessions();
- } finally {
- setSnackBarLifetimeMs(5000);
- }
- }
-
@Test
public void testGenericSave() throws Exception {
customizedSaveTest(SAVE_DATA_TYPE_GENERIC);
@@ -899,7 +872,7 @@
assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
// Assert the snack bar is shown and tap "Save".
- final UiObject2 saveSnackBar = sUiBot.assertSaveShowing(type, saveDescription);
+ final UiObject2 saveSnackBar = sUiBot.assertSaveShowing(saveDescription, type);
sUiBot.saveForAutofill(saveSnackBar, true);
// Assert save was called.
@@ -975,13 +948,9 @@
sUiBot.assertNotShownByText("Tap to auth response");
// ...and select it this time
- if (BUG_36855717_FIXED) {
- callback.assertUiShownEvent(username);
- }
+ callback.assertUiShownEvent(username);
sUiBot.selectDataset("Dataset");
- if (BUG_36855717_FIXED) {
- callback.assertUiHiddenEvent(username);
- }
+ callback.assertUiHiddenEvent(username);
sUiBot.assertNoDatasets();
sUiBot.assertNotShownByText("Tap to auth response");
@@ -1036,7 +1005,7 @@
callback.assertUiShownEvent(username);
sUiBot.assertShownByText("Tap to auth response");
- if (SUPPORTS_PARTIOTINED_AUTH) {
+ if (SUPPORTS_PARTITIONED_AUTH) {
// Make sure UI is not show on 2nd field
final View password = mActivity.getPassword();
mActivity.onPassword(View::requestFocus);
@@ -1065,13 +1034,9 @@
callback.assertUiHiddenEvent(username);
sUiBot.assertNotShownByText("Tap to auth response");
- if (BUG_36855717_FIXED) {
- callback.assertUiShownEvent(username);
- }
+ callback.assertUiShownEvent(username);
sUiBot.selectDataset("Dataset");
- if (BUG_36855717_FIXED) {
- callback.assertUiHiddenEvent(username);
- }
+ callback.assertUiHiddenEvent(username);
sUiBot.assertNoDatasets();
sUiBot.assertNotShownByText("Tap to auth response");
@@ -1132,53 +1097,18 @@
}
@Test
- public void testDisableSelfWhenConnected() throws Exception {
+ public void testDisableSelf() throws Exception {
enableService();
- // Set no-op behavior.
- sReplier.addResponse(new CannedFillResponse.Builder()
- .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
- .build());
-
- // Trigger auto-fill.
- mActivity.onUsername(View::requestFocus);
- waitUntilConnected();
- sReplier.getNextFillRequest();
-
// Can disable while connected.
- mActivity.runOnUiThread(() ->
- InstrumentedAutoFillService.peekInstance().disableSelf());
+ mActivity.runOnUiThread(() -> getContext().getSystemService(
+ AutofillManager.class).disableOwnedAutofillServices());
// Ensure disabled.
assertServiceDisabled();
}
@Test
- public void testDisableSelfWhenDisconnected() throws Exception {
- enableService();
-
- // Set no-op behavior.
- sReplier.addResponse(new CannedFillResponse.Builder()
- .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
- .build());
-
- // Trigger auto-fill.
- mActivity.onUsername(View::requestFocus);
- waitUntilConnected();
- sReplier.getNextFillRequest();
-
- // Wait until we timeout and disconnect.
- waitUntilDisconnected();
-
- // Cannot disable while disconnected.
- mActivity.runOnUiThread(() ->
- InstrumentedAutoFillService.peekInstance().disableSelf());
-
- // Ensure enabled.
- assertServiceEnabled();
- }
-
- @Test
public void testCustomNegativeSaveButton() throws Exception {
enableService();
@@ -1218,7 +1148,7 @@
}, intentFilter);
// Trigger the negative button.
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, false);
+ sUiBot.saveForAutofill(false, SAVE_DATA_TYPE_PASSWORD);
// Wait for the custom action.
assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
@@ -1414,7 +1344,7 @@
mActivity.tapSave();
// Assert the snack bar is shown and tap "Save".
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java
new file mode 100644
index 0000000..c2af4cc1
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java
@@ -0,0 +1,75 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Activity;
+import android.app.assist.AssistStructure;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.autofill.AutofillManager;
+
+/**
+ * An activity that authenticates on button press
+ */
+public class ManualAuthenticationActivity extends Activity {
+ private static CannedFillResponse sResponse;
+ private static CannedFillResponse.CannedDataset sDataset;
+
+ public static void setResponse(CannedFillResponse response) {
+ sResponse = response;
+ sDataset = null;
+ }
+
+ public static void setDataset(CannedFillResponse.CannedDataset dataset) {
+ sDataset = dataset;
+ sResponse = null;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.single_button_activity);
+
+ findViewById(R.id.button).setOnClickListener((v) -> {
+ // We should get the assist structure
+ AssistStructure structure = getIntent().getParcelableExtra(
+ AutofillManager.EXTRA_ASSIST_STRUCTURE);
+ assertWithMessage("structure not called").that(structure).isNotNull();
+
+ Parcelable result = null;
+ if (sResponse != null) {
+ result = sResponse.asFillResponse(structure);
+ } else if (sDataset != null) {
+ result = sDataset.asDataset(structure);
+ } else {
+ throw new IllegalStateException("no dataset or response");
+ }
+
+ // Pass on the auth result
+ Intent intent = new Intent();
+ intent.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, result);
+ setResult(RESULT_OK, intent);
+
+ // Done
+ finish();
+ });
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
index 3b75868..16a633d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
@@ -130,7 +130,7 @@
mActivity.save();
// Assert the snack bar is shown and tap "Save".
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_ADDRESS, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
@@ -330,7 +330,7 @@
mActivity.save();
// Assert the snack bar is shown and tap "Save".
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_ADDRESS, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
new file mode 100644
index 0000000..6dd2d11
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
@@ -0,0 +1,74 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Simple activity showing R.layout.login_activity. Started outside of the test process.
+ */
+public class OutOfProcessLoginActivity extends Activity {
+ private static final String LOG_TAG = OutOfProcessLoginActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ Log.i(LOG_TAG, "onCreate(" + savedInstanceState + ")");
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.login_activity);
+
+ findViewById(R.id.login).setOnClickListener((v) -> {
+ finish();
+ });
+ }
+
+ @Override
+ protected void onStop() {
+ Log.i(LOG_TAG, "onStop()");
+ super.onStop();
+
+ try {
+ getStoppedMarker(this).createNewFile();
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "cannot write stopped filed");
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.i(LOG_TAG, "onDestroy()");
+ super.onDestroy();
+ }
+
+ /**
+ * Get the file that signals that the activity has entered {@link Activity#onStop()}.
+ *
+ * @param context Context of the app
+ * @return The marker file that is written onStop()
+ */
+ @NonNull public static File getStoppedMarker(@NonNull Context context) {
+ return new File(context.getFilesDir(), "stopped");
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index 8c350aa..49ddf8f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -25,7 +25,11 @@
import static android.autofillservice.cts.GridActivity.ID_L4C2;
import static android.autofillservice.cts.Helper.assertTextIsSanitized;
import static android.autofillservice.cts.Helper.assertValue;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -210,7 +214,6 @@
.setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
.setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
.build())
- .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
.setExtras(extras)
.build();
sReplier.addResponse(response1);
@@ -272,6 +275,7 @@
.setField(ID_L4C1, "l4c1", createPresentation("l4c1"))
.setField(ID_L4C2, "l4c2", createPresentation("l4c2"))
.build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
.setExtras(extras)
.build();
sReplier.addResponse(response4);
@@ -285,7 +289,7 @@
mActivity.setText(1, 1, "L1C1");
mActivity.save();
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
assertWithMessage("wrong number of extras on save request bundle")
@@ -293,6 +297,308 @@
assertThat(saveRequest.data.getString("numbers4")).isEqualTo("2342");
}
+ @Test
+ public void testSaveOneSaveInfoOnFirstPartitionWithIdsOnSecond() throws Exception {
+ // Set service.
+ enableService();
+
+ // Trigger 1st partition.
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+ .build())
+ .build();
+ sReplier.addResponse(response1);
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 2nd partition.
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+ .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L2C1)
+ .build();
+ sReplier.addResponse(response2);
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger save
+ mActivity.setText(2, 1, "L2C1");
+ mActivity.save();
+
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+ }
+
+ @Test
+ public void testSaveOneSaveInfoOnSecondPartitionWithIdsOnFirst() throws Exception {
+ // Set service.
+ enableService();
+
+ // Trigger 1st partition.
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+ .build())
+ .build();
+ sReplier.addResponse(response1);
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 2nd partition.
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+ .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+ .build();
+ sReplier.addResponse(response2);
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger save
+ mActivity.setText(1, 1, "L1C1");
+ mActivity.save();
+
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+ }
+
+ @Test
+ public void testSaveTwoSaveInfosDifferentTypes() throws Exception {
+ // Set service.
+ enableService();
+
+ // Trigger 1st partition.
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+ .build();
+ sReplier.addResponse(response1);
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 2nd partition.
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+ .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD,
+ ID_L2C1)
+ .build();
+ sReplier.addResponse(response2);
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger save
+ mActivity.setText(1, 1, "L1C1");
+ mActivity.setText(2, 1, "L2C1");
+ mActivity.save();
+
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_CREDIT_CARD);
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+ assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+ }
+
+ @Test
+ public void testSaveThreeSaveInfosDifferentTypes() throws Exception {
+ // Set service.
+ enableService();
+
+ // Trigger 1st partition.
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+ .build();
+ sReplier.addResponse(response1);
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 2nd partition.
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+ .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD,
+ ID_L2C1)
+ .build();
+ sReplier.addResponse(response2);
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 3rd partition.
+ final CannedFillResponse response3 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L3C1, "l3c1", createPresentation("l3c1"))
+ .setField(ID_L3C2, "l3c2", createPresentation("l3c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD
+ | SAVE_DATA_TYPE_USERNAME, ID_L3C1)
+ .build();
+ sReplier.addResponse(response3);
+ mActivity.focusCell(3, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger save
+ mActivity.setText(1, 1, "L1C1");
+ mActivity.setText(2, 1, "L2C1");
+ mActivity.setText(3, 1, "L3C1");
+ mActivity.save();
+
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_CREDIT_CARD,
+ SAVE_DATA_TYPE_USERNAME);
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+ assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+ assertValue(saveRequest.structure, ID_L3C1, "L3C1");
+ }
+
+ @Test
+ public void testSaveThreeSaveInfosDifferentTypesIncludingGeneric() throws Exception {
+ // Set service.
+ enableService();
+
+ // Trigger 1st partition.
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+ .build();
+ sReplier.addResponse(response1);
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 2nd partition.
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+ .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_GENERIC, ID_L2C1)
+ .build();
+ sReplier.addResponse(response2);
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 3rd partition.
+ final CannedFillResponse response3 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L3C1, "l3c1", createPresentation("l3c1"))
+ .setField(ID_L3C2, "l3c2", createPresentation("l3c2"))
+ .build())
+ .setRequiredSavableIds(
+ SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_GENERIC | SAVE_DATA_TYPE_USERNAME,
+ ID_L3C1)
+ .build();
+ sReplier.addResponse(response3);
+ mActivity.focusCell(3, 1);
+ sReplier.getNextFillRequest();
+
+
+ // Trigger save
+ mActivity.setText(1, 1, "L1C1");
+ mActivity.setText(2, 1, "L2C1");
+ mActivity.setText(3, 1, "L3C1");
+ mActivity.save();
+
+ // Make sure GENERIC type is not shown on snackbar
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_USERNAME);
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+ assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+ assertValue(saveRequest.structure, ID_L3C1, "L3C1");
+ }
+
+ @Test
+ public void testSaveMoreThanThreeSaveInfosDifferentTypes() throws Exception {
+ // Set service.
+ enableService();
+
+ // Trigger 1st partition.
+ final CannedFillResponse response1 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+ .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+ .build();
+ sReplier.addResponse(response1);
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 2nd partition.
+ final CannedFillResponse response2 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+ .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD,
+ ID_L2C1)
+ .build();
+ sReplier.addResponse(response2);
+ mActivity.focusCell(2, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 3rd partition.
+ final CannedFillResponse response3 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L3C1, "l3c1", createPresentation("l3c1"))
+ .setField(ID_L3C2, "l3c2", createPresentation("l3c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD
+ | SAVE_DATA_TYPE_USERNAME, ID_L3C1)
+ .build();
+ sReplier.addResponse(response3);
+ mActivity.focusCell(3, 1);
+ sReplier.getNextFillRequest();
+
+ // Trigger 4th partition.
+ final CannedFillResponse response4 = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_L4C1, "l4c1", createPresentation("l4c1"))
+ .setField(ID_L4C2, "l4c2", createPresentation("l4c2"))
+ .build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD
+ | SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_ADDRESS, ID_L4C1)
+ .build();
+ sReplier.addResponse(response4);
+ mActivity.focusCell(4, 1);
+ sReplier.getNextFillRequest();
+
+
+ // Trigger save
+ mActivity.setText(1, 1, "L1C1");
+ mActivity.setText(2, 1, "L2C1");
+ mActivity.setText(3, 1, "L3C1");
+ mActivity.setText(4, 1, "L4C1");
+ mActivity.save();
+
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+ assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+ assertValue(saveRequest.structure, ID_L3C1, "L3C1");
+ assertValue(saveRequest.structure, ID_L4C1, "L4C1");
+ }
+
// TODO(b/33197203, b/35707731): test force autofill after autofilled
- // TODO(b/33197203, b/35707731): add test for saving (1, 2, 3, or more unique save types)
+ // TODO(b/33197203, b/35707731): test save with different subtitles and custom no button
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
index f0e8f9f..349149f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
@@ -93,7 +93,7 @@
mActivity.tapLogin();
// Assert the snack bar is shown and tap "Save".
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
// Assert sanitization on save: everything should be available!
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
new file mode 100644
index 0000000..0454277
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.ID_LOGIN;
+import static android.autofillservice.cts.Helper.ID_PASSWORD;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.LANDSCAPE;
+import static android.autofillservice.cts.Helper.PORTRAIT;
+import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.eventually;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
+import static android.autofillservice.cts.Helper.getOutOfProcessPid;
+import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.autofillservice.cts.Helper.setOrientation;
+import static android.autofillservice.cts.OutOfProcessLoginActivity.getStoppedMarker;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.PendingIntent;
+import android.app.assist.AssistStructure;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.support.test.rule.ActivityTestRule;
+import android.view.autofill.AutofillValue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test the lifecycle of a autofill session
+ */
+public class SessionLifecycleTest extends AutoFillServiceTestCase {
+ private static final String USERNAME_FULL_ID = "android.autofillservice.cts:id/" + ID_USERNAME;
+ private static final String PASSWORD_FULL_ID = "android.autofillservice.cts:id/" + ID_PASSWORD;
+ private static final String LOGIN_FULL_ID = "android.autofillservice.cts:id/" + ID_LOGIN;
+ private static final String BUTTON_FULL_ID = "android.autofillservice.cts:id/button";
+
+ /**
+ * Use an activity as background so that orientation change always works (Home screen does not
+ * allow rotation
+ */
+ @Rule
+ public final ActivityTestRule<EmptyActivity> mActivityRule =
+ new ActivityTestRule<>(EmptyActivity.class);
+
+ @Before
+ public void removeAllSessions() {
+ destroyAllSessions();
+ }
+
+ /**
+ * Prevents the screen to rotate by itself
+ */
+ @Before
+ public void disableAutoRotation() {
+ Helper.disableAutoRotation();
+ }
+
+ /**
+ * Allows the screen to rotate by itself
+ */
+ @After
+ public void allowAutoRotation() {
+ Helper.allowAutoRotation();
+ }
+
+ @Test
+ public void testSessionRetainedWhileAutofilledAppIsLifecycled() throws Exception {
+ // Set service.
+ enableService();
+
+ try {
+ // Start activity that is autofilled in a separate process so it can be killed
+ Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
+ OutOfProcessLoginActivity.class);
+ getContext().startActivity(outOfProcessAcvitityStartIntent);
+
+ // Set expectations.
+ final Bundle extras = new Bundle();
+ extras.putString("numbers", "4815162342");
+
+ // Create the authentication intent (launching a full screen activity)
+ IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
+ new Intent(getContext(), ManualAuthenticationActivity.class),
+ 0).getIntentSender();
+
+ // Prepare the authenticated response
+ ManualAuthenticationActivity.setResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedFillResponse.CannedDataset.Builder()
+ .setField(ID_USERNAME, AutofillValue.forText("autofilled username"))
+ .setPresentation(createPresentation("dataset")).build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+ .setExtras(extras).build());
+
+ CannedFillResponse response = new CannedFillResponse.Builder()
+ .setAuthentication(authentication)
+ .setPresentation(createPresentation("authenticate"))
+ .build();
+ sReplier.addResponse(response);
+
+ // Trigger autofill on username
+ sUiBot.selectById(USERNAME_FULL_ID);
+
+ // Wait for fill request to be processed
+ sReplier.getNextFillRequest();
+
+ // Wait until authentication is shown
+ sUiBot.assertShownByText("authenticate");
+
+ // Change orientation which triggers a destroy -> create in the app as the activity
+ // cannot deal with such situations
+ setOrientation(LANDSCAPE);
+
+ // Delete stopped marker
+ getStoppedMarker(getContext()).delete();
+
+ // Authenticate
+ sUiBot.selectByText("authenticate");
+
+ // Waiting for activity to stop (stop marker appears)
+ eventually(() -> assertThat(getStoppedMarker(getContext()).exists()).isTrue());
+
+ // onStop might not be finished, hence wait more
+ Thread.sleep(1000);
+
+ // Kill activity that is in the background
+ runShellCommand("kill -9 %d",
+ getOutOfProcessPid("android.autofillservice.cts.outside"));
+
+ // Change orientation which triggers a destroy -> create in the app as the activity
+ // cannot deal with such situations
+ setOrientation(PORTRAIT);
+
+ // Approve authentication
+ sUiBot.selectById(BUTTON_FULL_ID);
+
+ // Wait for dataset to be shown
+ sUiBot.assertShownByText("dataset");
+
+ // Change orientation which triggers a destroy -> create in the app as the activity
+ // cannot deal with such situations
+ setOrientation(LANDSCAPE);
+
+ // Select dataset
+ sUiBot.selectDataset("dataset");
+
+ // Check the results.
+ eventually(() -> assertThat(sUiBot.getTextById(USERNAME_FULL_ID)).isEqualTo(
+ "autofilled username"));
+
+ // Set password
+ sUiBot.setTextById(PASSWORD_FULL_ID, "new password");
+
+ // Login
+ sUiBot.selectById(LOGIN_FULL_ID);
+
+ // Wait for save UI to be shown
+ sUiBot.assertShownById("android:id/autofill_save_yes");
+
+ // Change orientation to make sure save UI can handle this
+ setOrientation(PORTRAIT);
+
+ // Tap "Save".
+ sUiBot.selectById("android:id/autofill_save_yes");
+
+ // Get save request
+ InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
+
+ // Make sure data is correctly saved
+ final AssistStructure.ViewNode username = findNodeByResourceId(saveRequest.structure,
+ ID_USERNAME);
+ assertTextAndValue(username, "autofilled username");
+ final AssistStructure.ViewNode password = findNodeByResourceId(saveRequest.structure,
+ ID_PASSWORD);
+ assertTextAndValue(password, "new password");
+
+ // Make sure extras were passed back on onSave()
+ assertThat(saveRequest.data).isNotNull();
+ final String extraValue = saveRequest.data.getString("numbers");
+ assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
+
+ eventually(() -> assertNoDanglingSessions());
+ } finally {
+ disableService();
+ }
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
index 872ccc1..b96e5e3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
@@ -89,7 +89,7 @@
activity.setTime(10, 40);
activity.tapOk();
- sUiBot.saveForAutofill(SAVE_DATA_TYPE_GENERIC, true);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index be9b44b..1f98171 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -39,6 +39,7 @@
import android.util.Log;
import android.view.accessibility.AccessibilityWindowInfo;
+import java.util.Arrays;
import java.util.List;
/**
@@ -59,7 +60,7 @@
private static final String RESOURCE_STRING_SAVE_TYPE_CREDIT_CARD =
"autofill_save_type_credit_card";
private static final String RESOURCE_STRING_SAVE_TYPE_USERNAME = "autofill_save_type_username";
- private static final String RESOURCE_STRING_SAVE_TYPE_EMAIL =
+ private static final String RESOURCE_STRING_SAVE_TYPE_EMAIL_ADDRESS =
"autofill_save_type_email_address";
private static final String RESOURCE_STRING_AUTOFILL = "autofill";
private static final String RESOURCE_STRING_DATASET_PICKER_ACCESSIBILITY_TITLE =
@@ -161,10 +162,43 @@
}
/**
+ * Selects a view by id.
+ */
+ void selectById(String id) {
+ Log.v(TAG, "selectById(): " + id);
+
+ final UiObject2 view = waitForObject(By.res(id));
+ view.click();
+ }
+
+ /**
+ * Asserts the id is shown on the screen.
+ */
+ void assertShownById(String id) {
+ assertThat(waitForObject(By.res(id))).isNotNull();
+ }
+
+ /**
+ * Gets the text set on a view.
+ */
+ String getTextById(String id) {
+ UiObject2 view = waitForObject(By.res(id));
+ return view.getText();
+ }
+
+ /**
+ * Sets a new text on a view.
+ */
+ void setTextById(String id, String newText) {
+ UiObject2 view = waitForObject(By.res(id));
+ view.setText(newText);
+ }
+
+ /**
* Asserts the save snackbar is showing and returns it.
*/
UiObject2 assertSaveShowing(int type) {
- return assertSaveShowing(type, null);
+ return assertSaveShowing(null, type);
}
/**
@@ -181,46 +215,63 @@
throw new AssertionError("snack bar is showing");
}
- UiObject2 assertSaveShowing(int type, String description) {
+ private String getSaveTypeString(int type) {
+ final String typeResourceName;
+ switch (type) {
+ case SAVE_DATA_TYPE_PASSWORD:
+ typeResourceName = RESOURCE_STRING_SAVE_TYPE_PASSWORD;
+ break;
+ case SAVE_DATA_TYPE_ADDRESS:
+ typeResourceName = RESOURCE_STRING_SAVE_TYPE_ADDRESS;
+ break;
+ case SAVE_DATA_TYPE_CREDIT_CARD:
+ typeResourceName = RESOURCE_STRING_SAVE_TYPE_CREDIT_CARD;
+ break;
+ case SAVE_DATA_TYPE_USERNAME:
+ typeResourceName = RESOURCE_STRING_SAVE_TYPE_USERNAME;
+ break;
+ case SAVE_DATA_TYPE_EMAIL_ADDRESS:
+ typeResourceName = RESOURCE_STRING_SAVE_TYPE_EMAIL_ADDRESS;
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported type: " + type);
+ }
+ return getString(typeResourceName);
+ }
+
+ UiObject2 assertSaveShowing(String description, int... types) {
final UiObject2 snackbar = waitForObject(By.res("android", RESOURCE_ID_SAVE_SNACKBAR),
SAVE_TIMEOUT_MS);
final UiObject2 titleView = snackbar.findObject(By.res("android", RESOURCE_ID_SAVE_TITLE));
assertWithMessage("save title (%s)", RESOURCE_ID_SAVE_TITLE).that(titleView).isNotNull();
- final String serviceLabel = InstrumentedAutoFillService.class.getSimpleName();
- final String expectedTitle;
- if (type == SAVE_DATA_TYPE_GENERIC) {
- expectedTitle = getString(RESOURCE_STRING_SAVE_TITLE, serviceLabel);
- } else {
- final String typeResourceName;
- switch (type) {
- case SAVE_DATA_TYPE_PASSWORD:
- typeResourceName = RESOURCE_STRING_SAVE_TYPE_PASSWORD;
- break;
- case SAVE_DATA_TYPE_ADDRESS:
- typeResourceName = RESOURCE_STRING_SAVE_TYPE_ADDRESS;
- break;
- case SAVE_DATA_TYPE_CREDIT_CARD:
- typeResourceName = RESOURCE_STRING_SAVE_TYPE_CREDIT_CARD;
- break;
- case SAVE_DATA_TYPE_USERNAME:
- typeResourceName = RESOURCE_STRING_SAVE_TYPE_USERNAME;
- break;
- case SAVE_DATA_TYPE_EMAIL_ADDRESS:
- typeResourceName = RESOURCE_STRING_SAVE_TYPE_EMAIL;
- break;
- default:
- throw new IllegalArgumentException("Unsupported type: " + type);
- }
- final String typeString = getString(typeResourceName);
- expectedTitle = getString(RESOURCE_STRING_SAVE_TITLE_WITH_TYPE, typeString,
- serviceLabel);
- }
-
final String actualTitle = titleView.getText();
Log.d(TAG, "save title: " + actualTitle);
- assertThat(actualTitle).isEqualTo(expectedTitle);
+
+ final String serviceLabel = InstrumentedAutoFillService.class.getSimpleName();
+ switch (types.length) {
+ case 1:
+ final String expectedTitle = (types[0] == SAVE_DATA_TYPE_GENERIC)
+ ? getString(RESOURCE_STRING_SAVE_TITLE, serviceLabel)
+ : getString(RESOURCE_STRING_SAVE_TITLE_WITH_TYPE,
+ getSaveTypeString(types[0]), serviceLabel);
+ assertThat(actualTitle).isEqualTo(expectedTitle);
+ break;
+ case 2:
+ // We cannot predict the order...
+ assertThat(actualTitle).contains(getSaveTypeString(types[0]));
+ assertThat(actualTitle).contains(getSaveTypeString(types[1]));
+ break;
+ case 3:
+ // We cannot predict the order...
+ assertThat(actualTitle).contains(getSaveTypeString(types[0]));
+ assertThat(actualTitle).contains(getSaveTypeString(types[1]));
+ assertThat(actualTitle).contains(getSaveTypeString(types[2]));
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid types: " + Arrays.toString(types));
+ }
if (description != null) {
final UiObject2 saveSubTitle = snackbar.findObject(By.text(description));
@@ -233,11 +284,11 @@
/**
* Taps an option in the save snackbar.
*
- * @param type expected type of save info.
* @param yesDoIt {@code true} for 'YES', {@code false} for 'NO THANKS'.
+ * @param types expected types of save info.
*/
- void saveForAutofill(int type, boolean yesDoIt) {
- final UiObject2 saveSnackBar = assertSaveShowing(type, null);
+ void saveForAutofill(boolean yesDoIt, int... types) {
+ final UiObject2 saveSnackBar = assertSaveShowing(null, types);
saveForAutofill(saveSnackBar, yesDoIt);
}
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 6bb3421..e4ca5af 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -240,6 +240,16 @@
bug: 23827982
},
{
+ description: "VP9 encoder is not a standard requirement of android as of O.",
+ names: [
+ "android.media.cts.VideoEncoderDecoderTest#testVp9Goog0Perf0320x0180",
+ "android.media.cts.VideoEncoderDecoderTest#testVp9Goog0Perf0640x0360",
+ "android.media.cts.VideoEncoderDecoderTest#testVp9Goog0Perf1280x0720",
+ "android.media.cts.VideoEncoderDecoderTest#testVp9Goog0Perf1920x1080"
+ ],
+ bug: 33090965
+},
+{
description: "protected broadcast not working",
names: [
"android.permission2.cts.ProtectedBroadcastsTest#testSendProtectedBroadcasts"
diff --git a/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java b/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
index 0ce4aa3..1b28290 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
@@ -35,23 +35,16 @@
public ActivityTestRule<NonConfigOnStopActivity> mActivityRule =
new ActivityTestRule<>(NonConfigOnStopActivity.class);
- @After
- public void resetActivity() {
- FragmentTestUtil.resetOrientation();
- }
-
/**
* When a fragment is added during onStop(), it shouldn't show up in non-config
* state when restored.
*/
@Test
public void nonConfigStop() throws Throwable {
- if (!FragmentTestUtil.switchOrientation()) {
- return; // nothing to do -- we can't change the orientation
- }
+ NonConfigOnStopActivity activity = FragmentTestUtil.recreateActivity(mActivityRule,
+ mActivityRule.getActivity());
// A fragment was added in onStop(), but we shouldn't see it here...
- Activity activity = OrientationChangeActivity.sActivity;
assertTrue(activity.getFragmentManager().getFragments().isEmpty());
}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
index 4b2ae16..c175164 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
@@ -24,8 +24,6 @@
import android.app.FragmentManager;
import android.app.FragmentManagerNonConfig;
import android.app.Instrumentation;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
import android.os.Looper;
import android.os.Parcelable;
import android.support.test.InstrumentationRegistry;
@@ -35,8 +33,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -218,57 +214,28 @@
}
/**
- * Switches the orientation of the OrientationChangeActivity.
+ * Restarts the RecreatedActivity and waits for the new activity to be resumed.
*
- * @return {@code true} if the orientation changed or {@code false} if the screen is square
- * or some other error happens
+ * @return The newly-restarted Activity
*/
- public static boolean switchOrientation() throws InterruptedException {
- OrientationChangeActivity activity = OrientationChangeActivity.sActivity;
-
- int currentOrientation = activity.getResources().getConfiguration().orientation;
-
- int nextOrientation;
- int expectedOrientation;
- if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
- nextOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- expectedOrientation = Configuration.ORIENTATION_PORTRAIT;
- } else if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
- nextOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
- expectedOrientation = Configuration.ORIENTATION_LANDSCAPE;
- } else {
- return false; // Don't know what to do with square or unknown orientations
- }
-
+ public static <T extends RecreatedActivity> T recreateActivity(
+ ActivityTestRule<? extends Activity> rule, T activity) throws InterruptedException {
// Now switch the orientation
- LoaderActivity.sResumed = new CountDownLatch(1);
- LoaderActivity.sDestroyed = new CountDownLatch(1);
+ RecreatedActivity.sResumed = new CountDownLatch(1);
+ RecreatedActivity.sDestroyed = new CountDownLatch(1);
- activity.setRequestedOrientation(nextOrientation);
- assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
- assertTrue(LoaderActivity.sDestroyed.await(1, TimeUnit.SECONDS));
+ runOnUiThreadRethrow(rule, () -> {
+ activity.recreate();
+ });
+ assertTrue(RecreatedActivity.sResumed.await(1, TimeUnit.SECONDS));
+ assertTrue(RecreatedActivity.sDestroyed.await(1, TimeUnit.SECONDS));
+ T newActivity = (T) RecreatedActivity.sActivity;
- int switchedOrientation =
- LoaderActivity.sActivity.getResources().getConfiguration().orientation;
- assertEquals(expectedOrientation, switchedOrientation);
- return true;
+ waitForExecution(rule);
+
+ RecreatedActivity.clearState();
+ return newActivity;
}
+}
- /**
- * After calling {@link #switchOrientation()}, this must be called in an After.
- */
- public static void resetOrientation() {
- final OrientationChangeActivity activity = OrientationChangeActivity.sActivity;
- final int unspecifiedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- if (activity != null && activity.getRequestedOrientation() != unspecifiedOrientation) {
- OrientationChangeActivity.sResumed = new CountDownLatch(1);
- activity.setRequestedOrientation(unspecifiedOrientation);
- // Wait for the orientation change to settle, if there was a change
- try {
- OrientationChangeActivity.sResumed.await(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- // I guess there wasn't a change in orientation after all
- }
- }
- OrientationChangeActivity.clearState();
- }}
+
diff --git a/tests/fragment/src/android/fragment/cts/LoaderActivity.java b/tests/fragment/src/android/fragment/cts/LoaderActivity.java
index 5654b42..9c69f75 100644
--- a/tests/fragment/src/android/fragment/cts/LoaderActivity.java
+++ b/tests/fragment/src/android/fragment/cts/LoaderActivity.java
@@ -15,7 +15,6 @@
*/
package android.fragment.cts;
-import android.app.Activity;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.Context;
@@ -24,13 +23,11 @@
import android.view.ViewGroup;
import android.widget.TextView;
-import java.util.concurrent.CountDownLatch;
-
/**
* This Activity sets the text when loading completes. It also tracks the Activity in
* a static variable, so it must be cleared in test tear down.
*/
-public class LoaderActivity extends OrientationChangeActivity {
+public class LoaderActivity extends RecreatedActivity {
public TextView textView;
public TextView textViewB;
diff --git a/tests/fragment/src/android/fragment/cts/LoaderTest.java b/tests/fragment/src/android/fragment/cts/LoaderTest.java
index 880d13e..2eb897e 100755
--- a/tests/fragment/src/android/fragment/cts/LoaderTest.java
+++ b/tests/fragment/src/android/fragment/cts/LoaderTest.java
@@ -22,14 +22,10 @@
import android.app.Fragment;
import android.app.FragmentManager;
-import android.app.Instrumentation;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.Context;
-import android.content.Intent;
import android.content.Loader;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
@@ -37,7 +33,6 @@
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
-import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,25 +50,17 @@
public ActivityTestRule<LoaderActivity> mActivityRule =
new ActivityTestRule<>(LoaderActivity.class);
- @After
- public void resetActivity() {
- FragmentTestUtil.resetOrientation();
- }
-
/**
* Test to ensure that there is no Activity leak due to Loader
*/
@Test
public void testLeak() throws Throwable {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent(mActivityRule.getActivity(), LoaderActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- LoaderActivity.sResumed = new CountDownLatch(1);
- instrumentation.startActivitySync(intent);
- assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
-
+ // Restart the activity because mActivityRule keeps a strong reference to the
+ // old activity.
+ LoaderActivity activity = FragmentTestUtil.recreateActivity(mActivityRule,
+ mActivityRule.getActivity());
LoaderFragment fragment = new LoaderFragment();
- FragmentManager fm = LoaderActivity.sActivity.getFragmentManager();
+ FragmentManager fm = activity.getFragmentManager();
fm.beginTransaction()
.add(fragment, "1")
@@ -87,12 +74,11 @@
.commit();
FragmentTestUtil.executePendingTransactions(mActivityRule, fm);
+ fm = null; // clear it so that it can be released
WeakReference<LoaderActivity> weakActivity = new WeakReference(LoaderActivity.sActivity);
- if (!FragmentTestUtil.switchOrientation()) {
- return; // can't switch orientation for square screens
- }
+ activity = FragmentTestUtil.recreateActivity(mActivityRule, activity);
// Wait for everything to settle. We have to make sure that the old Activity
// is ready to be collected.
@@ -113,12 +99,9 @@
assertEquals("Loaded!", activity.textView.getText().toString());
- if (!FragmentTestUtil.switchOrientation()) {
- return; // can't switch orientation for square screens
- }
+ activity = FragmentTestUtil.recreateActivity(mActivityRule, activity);
// After orientation change, the text should still be loaded properly
- activity = (LoaderActivity) LoaderActivity.sActivity;
assertEquals("Loaded!", activity.textView.getText().toString());
}
diff --git a/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java b/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
index e61bd50..a93b616 100644
--- a/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
+++ b/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
@@ -17,7 +17,7 @@
import android.app.Fragment;
-public class NonConfigOnStopActivity extends OrientationChangeActivity {
+public class NonConfigOnStopActivity extends RecreatedActivity {
@Override
protected void onStop() {
super.onStop();
diff --git a/tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java b/tests/fragment/src/android/fragment/cts/RecreatedActivity.java
similarity index 92%
rename from tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java
rename to tests/fragment/src/android/fragment/cts/RecreatedActivity.java
index d8aeb31..82b32a9 100644
--- a/tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java
+++ b/tests/fragment/src/android/fragment/cts/RecreatedActivity.java
@@ -20,9 +20,9 @@
import java.util.concurrent.CountDownLatch;
-public class OrientationChangeActivity extends Activity {
+public class RecreatedActivity extends Activity {
// These must be cleared after each test using clearState()
- public static OrientationChangeActivity sActivity;
+ public static RecreatedActivity sActivity;
public static CountDownLatch sResumed;
public static CountDownLatch sDestroyed;
diff --git a/tests/signature/src/android/signature/cts/JDiffClassDescription.java b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
index 2779ea7..2d13ed2 100644
--- a/tests/signature/src/android/signature/cts/JDiffClassDescription.java
+++ b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
@@ -58,6 +58,8 @@
// list are probably not meant to be implemented in an application.
HIDDEN_INTERFACE_WHITELIST.add("public abstract boolean android.companion.DeviceFilter.matches(D)");
HIDDEN_INTERFACE_WHITELIST.add("public static <D> boolean android.companion.DeviceFilter.matches(android.companion.DeviceFilter<D>,D)");
+ HIDDEN_INTERFACE_WHITELIST.add("public abstract java.lang.String android.companion.DeviceFilter.getDeviceDisplayName(D)");
+ HIDDEN_INTERFACE_WHITELIST.add("public abstract int android.companion.DeviceFilter.getMediumType()");
HIDDEN_INTERFACE_WHITELIST.add("public abstract void android.nfc.tech.TagTechnology.reconnect() throws java.io.IOException");
HIDDEN_INTERFACE_WHITELIST.add("public abstract void android.os.IBinder.shellCommand(java.io.FileDescriptor,java.io.FileDescriptor,java.io.FileDescriptor,java.lang.String[],android.os.ShellCallback,android.os.ResultReceiver) throws android.os.RemoteException");
HIDDEN_INTERFACE_WHITELIST.add("public abstract int android.text.ParcelableSpan.getSpanTypeIdInternal()");
diff --git a/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java b/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
index c52aba6..a0d7f16 100644
--- a/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
+++ b/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
@@ -179,8 +179,11 @@
wipeContactsProvider();
+ // Accessing CP2 to make sure the process starts.
+ getDatabaseCreationTimestamp();
+
assertTrue("Didn't receive content change notification",
- latch.await(60, TimeUnit.SECONDS));
+ latch.await(120, TimeUnit.SECONDS));
assertEquals(ProviderStatus.CONTENT_URI, notifiedUri.get());
}
@@ -201,7 +204,10 @@
wipeContactsProvider();
+ // Accessing CP2 to make sure the process starts.
+ getDatabaseCreationTimestamp();
+
assertTrue("Didn't receive contacts wipe broadcast",
- latch.await(60, TimeUnit.SECONDS));
+ latch.await(120, TimeUnit.SECONDS));
}
}
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index b69a8c5..096eef9 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -276,6 +276,9 @@
}
public void testAlarmClockShowTimers() {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
+ return;
+ }
Intent intent = new Intent(AlarmClock.ACTION_SHOW_TIMERS);
assertCanBeHandled(intent);
}
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
index 45418cf..3015b07 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
@@ -49,6 +49,11 @@
// Check that all four orientations report the same configuration value.
for (int i = 0; i < ORIENTATIONS.length; i++) {
Activity activity = startOrientationActivity(ORIENTATIONS[i]);
+ if (activity.isInMultiWindowMode()) {
+ // activity.setRequestedOrientation has no effect in multiwindow mode.
+ tearDown();
+ return;
+ }
Configuration mConfig = activity.getResources().getConfiguration();
int actualSize = mConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
int actualLong = mConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
diff --git a/tests/tests/graphics/assets/almost-red-adobe.png b/tests/tests/graphics/assets/almost-red-adobe.png
new file mode 100644
index 0000000..531b5a4
--- /dev/null
+++ b/tests/tests/graphics/assets/almost-red-adobe.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/alpha_mask.png b/tests/tests/graphics/res/drawable-nodpi/alpha_mask.png
new file mode 100644
index 0000000..1d6177f
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/alpha_mask.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
index a137784..5454e2f 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
index d5288d1..ea6441a 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
index 237c86e..06eccb8 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
index 0a4b40f..64a6476 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
index 194f36c..5012e1c 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
@@ -26,6 +26,7 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,11 +41,14 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BitmapColorSpaceTest {
+ private static final String LOG_TAG = "BitmapColorSpaceTest";
+
private Resources mResources;
@Before
@@ -52,12 +56,92 @@
mResources = InstrumentationRegistry.getTargetContext().getResources();
}
+ @SuppressWarnings("deprecation")
+ @Test
+ public void createWithColorSpace() {
+ Bitmap b;
+ ColorSpace cs;
+
+ // We don't test HARDWARE configs because they are not compatible with mutable bitmaps
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true, null);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true,
+ ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGBA_F16, true, null);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGBA_F16, true,
+ ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGB_565, true, null);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGB_565, true,
+ ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.ALPHA_8, true, null);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.ALPHA_8, true,
+ ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_4444, true, null);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_4444, true,
+ ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void createWithNonRgbColorSpace() {
+ Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true,
+ ColorSpace.get(ColorSpace.Named.CIE_LAB));
+ }
+
@Test
public void sRGB() {
Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot);
ColorSpace cs = b.getColorSpace();
assertNotNull(cs);
assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
}
@Test
@@ -67,6 +151,16 @@
ColorSpace cs = b.getColorSpace();
assertNotNull(cs);
assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+
+ b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
} catch (IOException e) {
fail();
}
@@ -79,6 +173,16 @@
ColorSpace cs = b.getColorSpace();
assertNotNull(cs);
assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+ b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+ cs = b.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
} catch (IOException e) {
fail();
}
@@ -139,7 +243,7 @@
@Test
public void getPixel() {
- verifyGetPixel("green-p3.png", 0x75fb4cff, 0xff03ff00);
+ verifyGetPixel("green-p3.png", 0x75fb4cff, 0xff00ff00);
verifyGetPixel("translucent-green-p3.png", 0x3a7d267f, 0x7f00ff00); // 50% translucent
}
@@ -151,22 +255,34 @@
assertNotNull(cs);
assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
- ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
- b.copyPixelsToBuffer(dst);
- dst.rewind();
- // Stored as RGBA
- assertEquals(rawColor, dst.asIntBuffer().get());
+ verifyGetPixel(b, rawColor, srgbColor);
- int srgb = b.getPixel(31, 31);
- assertEquals(srgbColor, srgb);
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+ verifyGetPixel(b, rawColor, srgbColor);
+
+ b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+ verifyGetPixel(b, rawColor, srgbColor);
} catch (IOException e) {
fail();
}
}
+ private static void verifyGetPixel(@NonNull Bitmap b,
+ @ColorInt int rawColor, @ColorInt int srgbColor) {
+ ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
+ b.copyPixelsToBuffer(dst);
+ dst.rewind();
+
+ // Stored as RGBA
+ assertEquals(rawColor, dst.asIntBuffer().get());
+
+ int srgb = b.getPixel(15, 15);
+ almostEqual(srgbColor, srgb, 3, 15 * b.getWidth() + 15);
+ }
+
@Test
public void getPixels() {
- verifyGetPixels("green-p3.png", 0xff03ff00);
+ verifyGetPixels("green-p3.png", 0xff00ff00);
verifyGetPixels("translucent-green-p3.png", 0x7f00ff00); // 50% translucent
}
@@ -177,16 +293,28 @@
assertNotNull(cs);
assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
- int[] pixels = new int[b.getWidth() * b.getHeight()];
- b.getPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
- for (int pixel : pixels) {
- assertEquals(expected, pixel);
- }
+ verifyGetPixels(b, expected);
+
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+ verifyGetPixels(b, expected);
+
+ b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+ verifyGetPixels(b, expected);
} catch (IOException e) {
fail();
}
}
+ private static void verifyGetPixels(@NonNull Bitmap b, @ColorInt int expected) {
+ int[] pixels = new int[b.getWidth() * b.getHeight()];
+ b.getPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
+
+ for (int i = 0; i < pixels.length; i++) {
+ int pixel = pixels[i];
+ almostEqual(expected, pixel, 3, i);
+ }
+ }
+
@Test
public void setPixel() {
verifySetPixel("green-p3.png", 0xffff0000, 0xea3323ff);
@@ -204,18 +332,29 @@
assertNotNull(cs);
assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
- b.setPixel(0, 0, newColor);
+ verifySetPixel(b, newColor, expectedColor);
- ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
- b.copyPixelsToBuffer(dst);
- dst.rewind();
- // Stored as RGBA
- assertEquals(expectedColor, dst.asIntBuffer().get());
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+ verifySetPixel(b, newColor, expectedColor);
+
+ b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+ verifySetPixel(b, newColor, expectedColor);
} catch (IOException e) {
fail();
}
}
+ private static void verifySetPixel(@NonNull Bitmap b,
+ @ColorInt int newColor, @ColorInt int expectedColor) {
+ b.setPixel(0, 0, newColor);
+
+ ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
+ b.copyPixelsToBuffer(dst);
+ dst.rewind();
+ // Stored as RGBA
+ assertEquals(expectedColor, dst.asIntBuffer().get());
+ }
+
@Test
public void setPixels() {
verifySetPixels("green-p3.png", 0xffff0000, 0xea3323ff);
@@ -233,25 +372,36 @@
assertNotNull(cs);
assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
- int[] pixels = new int[b.getWidth() * b.getHeight()];
- Arrays.fill(pixels, newColor);
- b.setPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
+ verifySetPixels(b, newColor, expectedColor);
- ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
- b.copyPixelsToBuffer(dst);
- dst.rewind();
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+ verifySetPixels(b, newColor, expectedColor);
- IntBuffer buffer = dst.asIntBuffer();
- //noinspection ForLoopReplaceableByForEach
- for (int i = 0; i < pixels.length; i++) {
- // Stored as RGBA
- assertEquals(expectedColor, buffer.get());
- }
+ b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+ verifySetPixels(b, newColor, expectedColor);
} catch (IOException e) {
fail();
}
}
+ private static void verifySetPixels(@NonNull Bitmap b,
+ @ColorInt int newColor, @ColorInt int expectedColor) {
+ int[] pixels = new int[b.getWidth() * b.getHeight()];
+ Arrays.fill(pixels, newColor);
+ b.setPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
+
+ ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
+ b.copyPixelsToBuffer(dst);
+ dst.rewind();
+
+ IntBuffer buffer = dst.asIntBuffer();
+ //noinspection ForLoopReplaceableByForEach
+ for (int i = 0; i < pixels.length; i++) {
+ // Stored as RGBA
+ assertEquals(expectedColor, buffer.get());
+ }
+ }
+
@Test
public void writeColorSpace() {
verifyColorSpaceMarshalling("green-srgb.png", ColorSpace.get(ColorSpace.Named.SRGB));
@@ -400,4 +550,65 @@
fail();
}
}
+
+ @Test
+ public void copy() {
+ Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot);
+ Bitmap c = b.copy(Bitmap.Config.ARGB_8888, false);
+ ColorSpace cs = c.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ c = b.copy(Bitmap.Config.ARGB_8888, true);
+ cs = c.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+ try (InputStream in = mResources.getAssets().open("green-p3.png")) {
+ b = BitmapFactory.decodeStream(in);
+ c = b.copy(Bitmap.Config.ARGB_8888, false);
+ cs = c.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+
+ c = b.copy(Bitmap.Config.ARGB_8888, true);
+ cs = c.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+ } catch (IOException e) {
+ fail();
+ }
+
+ try (InputStream in = mResources.getAssets().open("prophoto-rgba16f.png")) {
+ b = BitmapFactory.decodeStream(in);
+ c = b.copy(Bitmap.Config.RGBA_F16, false);
+ cs = c.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+ c = b.copy(Bitmap.Config.RGBA_F16, true);
+ cs = c.getColorSpace();
+ assertNotNull(cs);
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+ } catch (IOException e) {
+ fail();
+ }
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ private static void almostEqual(@ColorInt int expected,
+ @ColorInt int pixel, int threshold, int index) {
+ int diffA = Math.abs(expected >>> 24 - pixel >>> 24);
+ int diffR = Math.abs((expected >> 16) & 0xff - (pixel >> 16) & 0xff);
+ int diffG = Math.abs((expected >> 8) & 0xff - (pixel >> 8) & 0xff);
+ int diffB = Math.abs((expected ) & 0xff - (pixel ) & 0xff);
+
+ boolean pass = diffA + diffR + diffG + diffB < threshold;
+ if (!pass) {
+ Log.d(LOG_TAG, "Expected 0x" + Integer.toHexString(expected) +
+ " but was 0x" + Integer.toHexString(pixel) + " with index " + index);
+ }
+
+ assertTrue(pass);
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
index 114a385..02c9425 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
@@ -15,38 +15,41 @@
*/
package android.graphics.cts;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BitmapRGBAF16Test {
private Bitmap mOpaqueBitmap;
private Bitmap mTransparentBitmap;
+ private Resources mResources;
@Before
public void setup() {
- Resources resources = InstrumentationRegistry.getTargetContext().getResources();
+ mResources = InstrumentationRegistry.getTargetContext().getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
// The bitmaps are in raw-nodpi/ to guarantee aapt and framework leave them untouched
- mOpaqueBitmap = BitmapFactory.decodeResource(resources, R.raw.p3_opaque, options);
- mTransparentBitmap = BitmapFactory.decodeResource(resources, R.raw.p3_transparent, options);
+ mOpaqueBitmap = BitmapFactory.decodeResource(mResources, R.raw.p3_opaque, options);
+ mTransparentBitmap = BitmapFactory.decodeResource(mResources, R.raw.p3_transparent, options);
}
@Test
@@ -133,4 +136,16 @@
assertTrue(before != after);
assertEquals(0x7f102030, after);
}
+
+ @Test
+ public void testCopyFromA8() {
+ Bitmap res = BitmapFactory.decodeResource(mResources, R.drawable.alpha_mask);
+ Bitmap mask = Bitmap.createBitmap(res.getWidth(), res.getHeight(),
+ Bitmap.Config.ALPHA_8);
+ Canvas c = new Canvas(mask);
+ c.drawBitmap(res, 0, 0, null);
+
+ Bitmap b = mask.copy(Config.RGBA_F16, false);
+ assertNotNull(b);
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index 64dba77..69dbcd8 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -51,12 +51,15 @@
import android.text.SpannedString;
import android.util.DisplayMetrics;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Vector;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class CanvasTest {
@@ -163,6 +166,35 @@
}
@Test
+ public void testSetBitmapCleanClip() {
+ mCanvas.setBitmap(Bitmap.createBitmap(10, 10, Config.ARGB_8888));
+ Rect r = new Rect(2, 2, 8, 8);
+ mCanvas.save();
+ mCanvas.clipRect(r);
+ assertEquals(r, mCanvas.getClipBounds());
+
+ // "reset" the canvas, and then check that the clip is wide open
+ // and not the previous value
+
+ mCanvas.setBitmap(Bitmap.createBitmap(20, 20, Config.ARGB_8888));
+ r = new Rect(0, 0, 20, 20);
+ assertEquals(r, mCanvas.getClipBounds());
+ }
+
+ @Test
+ public void testSetBitmapSaveCount() {
+ Canvas c = new Canvas(Bitmap.createBitmap(10, 10, Config.ARGB_8888));
+ int initialSaveCount = c.getSaveCount();
+
+ c.save();
+ assertEquals(c.getSaveCount(), initialSaveCount + 1);
+
+ // setBitmap should restore the saveCount to its original/base value
+ c.setBitmap(Bitmap.createBitmap(10, 10, Config.ARGB_8888));
+ assertEquals(c.getSaveCount(), initialSaveCount);
+ }
+
+ @Test
public void testIsOpaque() {
assertFalse(mCanvas.isOpaque());
}
@@ -2114,4 +2146,24 @@
assertEquals(matrices.elementAt(i), mCanvas.getMatrix());
}
}
+
+ @Test
+ public void testDrawBitmapColorBehavior() {
+ try {
+ // Create a wide gamut bitmap where the pixel value is slightly less than max red.
+ Resources resources = InstrumentationRegistry.getTargetContext().getResources();
+ InputStream in = resources.getAssets().open("almost-red-adobe.png");
+ Bitmap bitmap = BitmapFactory.decodeStream(in);
+
+ // Draw the bitmap to an sRGB canvas.
+ Bitmap canvasBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(canvasBitmap);
+ canvas.drawBitmap(bitmap, 0, 0, null);
+
+ // Verify that the pixel is now max red.
+ Assert.assertEquals(0xFFFF0000, canvasBitmap.getPixel(0, 0));
+ } catch (IOException e) {
+ Assert.fail();
+ }
+ }
}
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 3b890f2..16a202e 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -71,11 +71,14 @@
}
static bool already_loaded(const std::string& library, const std::string& err) {
- if (err.find("dlopen failed: library \"" + library + "\"") != 0 ||
- err.find("is not accessible for the namespace \"classloader-namespace\"") == std::string::npos) {
- return false;
+ // SELinux denials for /vendor libraries may return with library not found
+ if (err.find("dlopen failed: library \"" + library + "\"") == 0 &&
+ (err.find("not found") != std::string::npos ||
+ err.find("is not accessible for the namespace \"classloader-namespace\"") != std::string::npos)) {
+ return true;
}
- return true;
+
+ return false;
}
static bool check_lib(const std::string& path,
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 8bfd79b..f01b833 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -56,6 +56,7 @@
import android.os.SystemProperties;
import android.security.KeyStoreException;
import android.security.keystore.AttestationUtils;
+import android.security.keystore.DeviceIdAttestationException;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.test.AndroidTestCase;
@@ -357,9 +358,9 @@
}
public void testDeviceIdAttestation() throws Exception {
- testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_SERIAL);
- testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_IMEI);
- testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_MEID);
+ testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_SERIAL, null);
+ testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_IMEI, "Unable to retrieve IMEI");
+ testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_MEID, "Unable to retrieve MEID");
}
@SuppressWarnings("deprecation")
@@ -855,13 +856,23 @@
}
}
- private void testDeviceIdAttestationFailure(int idType) throws Exception {
+ private void testDeviceIdAttestationFailure(int idType,
+ String acceptableDeviceIdAttestationFailureMessage) throws Exception {
try {
AttestationUtils.attestDeviceIds(getContext(), new int[] {idType}, "123".getBytes());
fail("Attestation should have failed.");
} catch (SecurityException e) {
- // Attestation is expected to fail with a SecurityException as we do not hold
+ // Attestation is expected to fail. If the device has the device ID type we are trying
+ // to attest, it should fail with a SecurityException as we do not hold
// READ_PRIVILEGED_PHONE_STATE permission.
+ } catch (DeviceIdAttestationException e) {
+ // Attestation is expected to fail. If the device does not have the device ID type we
+ // are trying to attest (e.g. no IMEI on devices without a radio), it should fail with
+ // a corresponding DeviceIdAttestationException.
+ if (acceptableDeviceIdAttestationFailureMessage == null ||
+ !acceptableDeviceIdAttestationFailureMessage.equals(e.getMessage())) {
+ throw e;
+ }
}
}
}
diff --git a/tests/tests/location/src/android/location/cts/LocationTest.java b/tests/tests/location/src/android/location/cts/LocationTest.java
index 18faaee..bec6350 100644
--- a/tests/tests/location/src/android/location/cts/LocationTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationTest.java
@@ -36,6 +36,9 @@
public class LocationTest extends AndroidTestCase {
private static final float DELTA = 0.1f;
private final float TEST_ACCURACY = 1.0f;
+ private final float TEST_VERTICAL_ACCURACY = 2.0f;
+ private final float TEST_SPEED_ACCURACY = 3.0f;
+ private final float TEST_BEARING_ACCURACY = 4.0f;
private final double TEST_ALTITUDE = 1.0;
private final double TEST_LATITUDE = 50;
private final float TEST_BEARING = 1.0f;
@@ -51,6 +54,7 @@
private static final String ENABLED_KEY = "enabled";
private static final String MESSENGER_KEY = "messenger";
+
public void testConstructor() {
new Location("LocationProvider");
@@ -271,6 +275,46 @@
assertFalse(location.hasAccuracy());
}
+ public void testAccessVerticalAccuracy() {
+ Location location = new Location("");
+ assertFalse(location.hasVerticalAccuracy());
+
+ location.setVerticalAccuracyMeters(1.0f);
+ assertEquals(1.0, location.getVerticalAccuracyMeters(), DELTA);
+ assertTrue(location.hasVerticalAccuracy());
+
+ location.removeVerticalAccuracy();
+ assertEquals(0.0, location.getVerticalAccuracyMeters(), DELTA);
+ assertFalse(location.hasVerticalAccuracy());
+ }
+
+ public void testAccessSpeedAccuracy() {
+ Location location = new Location("");
+ assertFalse(location.hasSpeedAccuracy());
+
+ location.setSpeedAccuracyMetersPerSecond(1.0f);
+ assertEquals(1.0, location.getSpeedAccuracyMetersPerSecond(), DELTA);
+ assertTrue(location.hasSpeedAccuracy());
+
+ location.removeSpeedAccuracy();
+ assertEquals(0.0, location.getSpeedAccuracyMetersPerSecond(), DELTA);
+ assertFalse(location.hasSpeedAccuracy());
+ }
+
+ public void testAccessBearingAccuracy() {
+ Location location = new Location("");
+ assertFalse(location.hasBearingAccuracy());
+
+ location.setBearingAccuracyDegrees(1.0f);
+ assertEquals(1.0, location.getBearingAccuracyDegrees(), DELTA);
+ assertTrue(location.hasBearingAccuracy());
+
+ location.removeBearingAccuracy();
+ assertEquals(0.0, location.getBearingAccuracyDegrees(), DELTA);
+ assertFalse(location.hasBearingAccuracy());
+ }
+
+
public void testAccessAltitude() {
Location location = new Location("");
assertFalse(location.hasAltitude());
@@ -411,6 +455,15 @@
assertFalse(location.hasBearing());
assertEquals(0, location.getAccuracy(), DELTA);
assertFalse(location.hasAccuracy());
+
+ assertEquals(0, location.getVerticalAccuracyMeters(), DELTA);
+ assertEquals(0, location.getSpeedAccuracyMetersPerSecond(), DELTA);
+ assertEquals(0, location.getBearingAccuracyDegrees(), DELTA);
+
+ assertFalse(location.hasVerticalAccuracy());
+ assertFalse(location.hasSpeedAccuracy());
+ assertFalse(location.hasBearingAccuracy());
+
assertNull(location.getExtras());
}
@@ -486,6 +539,9 @@
assertNotNull(l);
assertEquals(TEST_PROVIDER, l.getProvider());
assertEquals(TEST_ACCURACY, l.getAccuracy(), DELTA);
+ assertEquals(TEST_VERTICAL_ACCURACY, l.getVerticalAccuracyMeters(), DELTA);
+ assertEquals(TEST_SPEED_ACCURACY, l.getSpeedAccuracyMetersPerSecond(), DELTA);
+ assertEquals(TEST_BEARING_ACCURACY, l.getBearingAccuracyDegrees(), DELTA);
assertEquals(TEST_ALTITUDE, l.getAltitude(), DELTA);
assertEquals(TEST_LATITUDE, l.getLatitude(), DELTA);
assertEquals(TEST_BEARING, l.getBearing(), DELTA);
@@ -498,6 +554,10 @@
private Location createTestLocation() {
Location l = new Location(TEST_PROVIDER);
l.setAccuracy(TEST_ACCURACY);
+ l.setVerticalAccuracyMeters(TEST_VERTICAL_ACCURACY);
+ l.setSpeedAccuracyMetersPerSecond(TEST_SPEED_ACCURACY);
+ l.setBearingAccuracyDegrees(TEST_BEARING_ACCURACY);
+
l.setAltitude(TEST_ALTITUDE);
l.setLatitude(TEST_LATITUDE);
l.setBearing(TEST_BEARING);
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index b2ae259..1fb5460 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -45,23 +45,26 @@
LOCAL_MULTILIB := both
LOCAL_STATIC_JAVA_LIBRARIES := \
- ctsmediautil \
compatibility-device-util \
- ctstestserver \
+ ctsdeviceutillegacy \
+ ctsmediautil \
ctstestrunner \
- ndkaudio \
+ ctstestserver \
junit \
- legacy-android-test
+ legacy-android-test \
+ ndkaudio
LOCAL_JNI_SHARED_LIBRARIES := \
- libctsimagereader_jni \
- libctsmediacodec_jni \
libaudio_jni \
+ libctsimagereader_jni \
+ libctsmediadrm_jni \
+ libctsmediacodec_jni \
libnativehelper_compat_libc++ \
libndkaudioLib
# do not compress VP9 video files
LOCAL_AAPT_FLAGS := -0 .vp9
+LOCAL_AAPT_FLAGS += -0 .ts
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/media/libmediandkjni/AMediaObjects.h b/tests/tests/media/libmediandkjni/AMediaObjects.h
new file mode 100644
index 0000000..c4d5397
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/AMediaObjects.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AMEDIAOBJECTS_H_
+#define AMEDIAOBJECTS_H_
+
+#include <utils/Log.h>
+
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+
+namespace {
+
+// Simple class to manage deletion of AMedia objects
+class AMediaObjects {
+ public:
+ AMediaObjects();
+ virtual ~AMediaObjects();
+
+ void setCrypto(AMediaCrypto* const theCrypto) {
+ mCrypto = theCrypto;
+ }
+ void setDrm(AMediaDrm* const theDrm) {
+ mDrm = theDrm;
+ }
+ void setVideoExtractor(AMediaExtractor* const theExtractor) {
+ mVideoExtractor = theExtractor;
+ }
+ void setAudioExtractor(AMediaExtractor* const theExtractor) {
+ mAudioExtractor = theExtractor;
+ }
+
+ AMediaCrypto* getCrypto() const { return mCrypto; }
+ AMediaDrm* getDrm() const { return mDrm; }
+ AMediaExtractor* getAudioExtractor() const { return mAudioExtractor; }
+ AMediaExtractor* getVideoExtractor() const { return mVideoExtractor; }
+
+ private:
+ AMediaCrypto *mCrypto;
+ AMediaDrm* mDrm;
+ AMediaExtractor* mAudioExtractor;
+ AMediaExtractor* mVideoExtractor;
+
+ // Disallow copy and assignment
+ AMediaObjects(const AMediaObjects&);
+ void operator=(const AMediaObjects&);
+};
+
+AMediaObjects::AMediaObjects(void) : mCrypto(NULL), mDrm(NULL),
+ mAudioExtractor(NULL), mVideoExtractor(NULL) {
+}
+
+AMediaObjects::~AMediaObjects() {
+ if (mCrypto) {
+ AMediaCrypto_delete(mCrypto);
+ }
+ if (mAudioExtractor) {
+ AMediaExtractor_delete(mAudioExtractor);
+ }
+ if (mVideoExtractor) {
+ AMediaExtractor_delete(mVideoExtractor);
+ }
+ if (mDrm) {
+ AMediaDrm_release(mDrm);
+ }
+}
+
+} // anonymous namespace
+#endif // AMEDIAOBJECTS_H_
+
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
index 39dbea3..e42d388 100644
--- a/tests/tests/media/libmediandkjni/Android.mk
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -14,27 +14,67 @@
#
LOCAL_PATH := $(call my-dir)
+#------------------------------------------------------------------------------
+# Builds libctsmediacodec_jni.so
+#
include $(CLEAR_VARS)
-LOCAL_MODULE := libctsmediacodec_jni
+LOCAL_MODULE := libctsmediacodec_jni
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
- native-media-jni.cpp \
- codec-utils-jni.cpp \
- md5_utils.cpp
+ native-media-jni.cpp \
+ codec-utils-jni.cpp \
+ md5_utils.cpp
LOCAL_C_INCLUDES := \
- $(JNI_H_INCLUDE) \
- system/core/include
+ $(JNI_H_INCLUDE) \
+ system/core/include
LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog libmediandk libEGL
+LOCAL_SHARED_LIBRARIES := \
+ libandroid libnativehelper_compat_libc++ \
+ liblog libmediandk libEGL
LOCAL_SDK_VERSION := current
LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES
include $(BUILD_SHARED_LIBRARY)
+
+#------------------------------------------------------------------------------
+# Builds libctsmediadrm_jni.so
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctsmediadrm_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ CtsMediaDrmJniOnLoad.cpp \
+ codec-utils-jni.cpp \
+ md5_utils.cpp \
+ native-mediadrm-jni.cpp \
+
+LOCAL_C_INCLUDES := \
+ $(JNI_H_INCLUDE) \
+ system/core/include
+
+
+LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid libnativehelper_compat_libc++ \
+ liblog libmediandk libdl libEGL
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES
+
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
new file mode 100644
index 0000000..24714a3
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_media_cts_NativeClearKeySystemTest(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM *vm, void */*reserved*/) {
+ JNIEnv *env = NULL;
+
+ if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+ return JNI_ERR;
+ }
+
+ if (register_android_media_cts_NativeClearKeySystemTest(env)) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/media/libmediandkjni/md5_utils.cpp b/tests/tests/media/libmediandkjni/md5_utils.cpp
index f4f893a..8e520e1 100644
--- a/tests/tests/media/libmediandkjni/md5_utils.cpp
+++ b/tests/tests/media/libmediandkjni/md5_utils.cpp
@@ -157,7 +157,7 @@
*/
void
MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) {
- register UWORD32 a, b, c, d;
+ UWORD32 a, b, c, d;
a = buf[0];
b = buf[1];
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
new file mode 100644
index 0000000..b98a6af
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TAG "NativeMediaDrm"
+
+#include <utils/Log.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <assert.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <android/native_window_jni.h>
+
+#include "AMediaObjects.h"
+
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaMuxer.h"
+
+typedef std::vector<uint8_t> Uuid;
+
+struct fields_t {
+ jfieldID surface;
+ jfieldID mimeType;
+ jfieldID audioUrl;
+ jfieldID videoUrl;
+};
+
+struct PlaybackParams {
+ jobject surface;
+ jstring mimeType;
+ jstring audioUrl;
+ jstring videoUrl;
+};
+
+static fields_t gFieldIds;
+static bool gGotVendorDefinedEvent = false;
+
+static const size_t kPlayTimeSeconds = 30;
+static const size_t kUuidSize = 16;
+
+static const uint8_t kWidevineUuid[kUuidSize] = {
+ 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce,
+ 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed
+};
+
+// The test content is not packaged with clearkey UUID,
+// we have to use a canned clearkey pssh for the test.
+static const uint8_t kClearkeyPssh[] = {
+ // BMFF box header (4 bytes size + 'pssh')
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+ // full box header (version = 1 flags = 0)
+ 0x01, 0x00, 0x00, 0x00,
+ // system id
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ // number of key ids
+ 0x00, 0x00, 0x00, 0x01,
+ // key id
+ 0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
+ 0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e,
+ // size of data, must be zero
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t kKeyRequestData[] = {
+ 0x7b, 0x22, 0x6b, 0x69, 0x64,
+ 0x73, 0x22, 0x3a, 0x5b, 0x22,
+ 0x59, 0x41, 0x59, 0x65, 0x41,
+ 0x58, 0x35, 0x48, 0x66, 0x6f,
+ 0x64, 0x2b, 0x56, 0x39, 0x41,
+ 0x4e, 0x48, 0x74, 0x41, 0x4e,
+ 0x48, 0x67, 0x22, 0x5d, 0x2c,
+ 0x22, 0x74, 0x79, 0x70, 0x65,
+ 0x22, 0x3a, 0x22, 0x74, 0x65,
+ 0x6d, 0x70, 0x6f, 0x72, 0x61,
+ 0x72, 0x79, 0x22, 0x7d,
+};
+
+static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
+
+// base 64 encoded JSON response string, must not contain padding character '='
+static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
+ "\"kid\":\"YAYeAX5Hfod+V9ANHtANHg\",\"k\":" \
+ "\"GoogleTestKeyBase64ggg\"}]}";
+
+static bool isUuidSizeValid(Uuid uuid) {
+ return (uuid.size() == kUuidSize);
+}
+
+static std::vector<uint8_t> jbyteArrayToVector(
+ JNIEnv* env, jbyteArray const &byteArray) {
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(
+ env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
+ std::vector<uint8_t> vector;
+ for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
+ vector.push_back(buffer[i]);
+ }
+ return vector;
+}
+
+static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
+ Uuid juuid;
+ juuid.resize(0);
+ if (uuid != NULL) {
+ juuid = jbyteArrayToVector(env, uuid);
+ }
+ return juuid;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
+
+ if (NULL == uuid) {
+ jniThrowException(env, "java/lang/NullPointerException", "null uuid");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (isUuidSizeValid(juuid)) {
+ return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
+ } else {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ }
+ return JNI_FALSE;
+}
+
+void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams ¶ms) {
+ params.surface = env->GetObjectField(
+ playbackParams, gFieldIds.surface);
+
+ params.mimeType = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.mimeType));
+
+ params.audioUrl = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.audioUrl));
+
+ params.videoUrl = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.videoUrl));
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative(
+ JNIEnv* env, jclass clazz, jbyteArray uuid,
+ jstring name, jobject outValue) {
+
+ if (NULL == uuid || NULL == name || NULL == outValue) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "One or more null input parameters");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ AMediaObjects aMediaObjects;
+ aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+ if (NULL == aMediaObjects.getDrm()) {
+ jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+ return JNI_FALSE;
+ }
+
+ const char *utf8_name = env->GetStringUTFChars(name, NULL);
+ const char *utf8_outValue = NULL;
+ media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+ utf8_name, &utf8_outValue);
+ env->ReleaseStringUTFChars(name, utf8_name);
+
+ if (NULL != utf8_outValue) {
+ clazz = env->GetObjectClass(outValue);
+ jmethodID mId = env->GetMethodID (clazz, "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+ jstring outString = env->NewStringUTF(
+ static_cast<const char *>(utf8_outValue));
+ env->CallObjectMethod(outValue, mId, outString);
+ } else {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "get property string returns %d", status);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest__testPsshNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
+
+ if (NULL == uuid || NULL == videoUrl) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "null uuid or null videoUrl");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ AMediaObjects aMediaObjects;
+ aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+ const char* url = env->GetStringUTFChars(videoUrl, 0);
+ if (url) {
+ media_status_t status = AMediaExtractor_setDataSource(
+ aMediaObjects.getVideoExtractor(), url);
+ env->ReleaseStringUTFChars(videoUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set video data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
+ if (psshInfo == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
+ return JNI_FALSE;
+ }
+
+ jboolean testResult = JNI_FALSE;
+ for (size_t i = 0; i < psshInfo->numentries; i++) {
+ PsshEntry *entry = &psshInfo->entries[i];
+
+ // We do not have clearkey content that contains ClearKey UUID in the
+ // pssh box. So we have to test if it has Widevine UUID instead.
+ // TODO: Replace kWidevineUuid with uuid when test content contains
+ // ClearKey UUID.
+ if (0 == memcmp(entry->uuid, kWidevineUuid, sizeof(entry->uuid))) {
+ aMediaObjects.setCrypto(
+ AMediaCrypto_new(entry->uuid, entry->data, entry->datalen));
+ if (aMediaObjects.getCrypto()) {
+ testResult = JNI_TRUE;
+ } else {
+ ALOGE("Failed to create media crypto=%zd", i);
+ testResult = JNI_FALSE;
+ }
+ break;
+ }
+ }
+ return testResult;
+}
+
+static bool isVideo(const char* mime) {
+ return !strncmp(mime, "video/", 6) ? true : false;
+}
+
+static bool isAudio(const char* mime) {
+ return !strncmp(mime, "audio/", 6) ? true : false;
+}
+
+static void addTrack(const AMediaFormat* format,
+ const char* mime, const AMediaCrypto* crypto,
+ const ANativeWindow* window, AMediaCodec** codec) {
+
+ *codec = AMediaCodec_createDecoderByType(mime);
+ if (codec == NULL) {
+ ALOGE("cannot create codec for %s", mime);
+ return;
+ }
+
+ AMediaCodec_configure(*codec, format,
+ const_cast<ANativeWindow*>(window),
+ const_cast<AMediaCrypto*>(crypto), 0);
+}
+
+static void addTracks(const AMediaExtractor* extractor,
+ const AMediaCrypto* crypto, const ANativeWindow* window,
+ AMediaCodec** codec) {
+ size_t numTracks = AMediaExtractor_getTrackCount(
+ const_cast<AMediaExtractor*>(extractor));
+ AMediaFormat* trackFormat = NULL;
+ for (size_t i = 0; i < numTracks; ++i) {
+ trackFormat = AMediaExtractor_getTrackFormat(
+ const_cast<AMediaExtractor*>(extractor), i);
+ if (trackFormat) {
+ ALOGV("track %zd format: %s", i,
+ AMediaFormat_toString(trackFormat));
+
+ const char* mime = "";
+ if (!AMediaFormat_getString(
+ trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+ ALOGE("no mime type");
+ AMediaFormat_delete(trackFormat);
+ return;
+ } else if (isAudio(mime) || isVideo(mime)) {
+ AMediaExtractor_selectTrack(
+ const_cast<AMediaExtractor*>(extractor), i);
+ ALOGV("track %zd codec format: %s", i,
+ AMediaFormat_toString(trackFormat));
+
+ addTrack(trackFormat, mime, crypto, window, codec);
+ AMediaCodec_start(*codec);
+ AMediaCodec_flush(*codec);
+ AMediaExtractor_seekTo(
+ const_cast<AMediaExtractor*>(extractor), 0,
+ AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+ }
+ AMediaFormat_delete(trackFormat);
+ }
+ }
+}
+
+static int64_t getSystemNanoTime() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
+ int64_t* presentationTimeUs, bool* eosReached) {
+ media_status_t status = AMEDIA_OK;
+
+ ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
+ if (bufferIndex >= 0) {
+ size_t bufsize;
+ uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
+
+ int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
+ if (sampleSize < 0) {
+ sampleSize = 0;
+ *eosReached = true;
+ }
+
+ *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
+
+ AMediaCodecCryptoInfo *cryptoInfo =
+ AMediaExtractor_getSampleCryptoInfo(extractor);
+ if (cryptoInfo) {
+ status = AMediaCodec_queueSecureInputBuffer(
+ codec, bufferIndex, 0, cryptoInfo,
+ *presentationTimeUs,
+ *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ AMediaCodecCryptoInfo_delete(cryptoInfo);
+ } else {
+ status = AMediaCodec_queueInputBuffer(
+ codec, bufferIndex, 0, sampleSize,
+ *presentationTimeUs,
+ *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ }
+ AMediaExtractor_advance(extractor);
+ }
+}
+
+static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
+ int64_t* startTimeNano) {
+
+ AMediaCodecBufferInfo info;
+ ssize_t bufferIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
+ if (bufferIndex >= 0) {
+ if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+ return true; // eos reached
+ }
+
+ if (*startTimeNano < 0) {
+ *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
+ }
+ int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
+ getSystemNanoTime();
+ if (delay > 0) {
+ usleep(delay / 1000);
+ }
+
+ AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
+ } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ ALOGV("output buffers changed");
+ } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
+ ALOGV("format changed to: %s", AMediaFormat_toString(format));
+ AMediaFormat_delete(format);
+ } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ ALOGV("no output buffer right now");
+ usleep(20000);
+ } else {
+ ALOGV("unexpected info code: %zd", bufferIndex);
+ }
+ return false;
+}
+
+static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
+ PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
+
+ ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
+ AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
+ AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
+
+ AMediaCodec* audioCodec = NULL;
+ AMediaCodec* videoCodec = NULL;
+ AMediaCrypto* crypto = NULL;
+
+ crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
+ if (crypto == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "failed to create crypto object");
+ return JNI_FALSE;
+ }
+
+ addTracks(audioExtractor, NULL, NULL, &audioCodec);
+ addTracks(videoExtractor, crypto, window, &videoCodec);
+
+ bool sawAudioInputEos = false;
+ bool sawAudioOutputEos = false;
+ bool sawVideoInputEos = false;
+ bool sawVideoOutputEos = false;
+ int64_t videoPresentationTimeUs = 0;
+ int64_t videoStartTimeNano = -1;
+ struct timespec timeSpec;
+ clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+ time_t startTimeSec = timeSpec.tv_sec;
+
+ while (!sawAudioOutputEos && !sawVideoOutputEos) {
+ if (!sawVideoInputEos) {
+ fillDecoder(videoCodec, videoExtractor,
+ &videoPresentationTimeUs, &sawVideoInputEos);
+ }
+
+ if (!sawAudioInputEos) {
+ // skip audio, still need to advance the audio extractor
+ AMediaExtractor_advance(audioExtractor);
+ }
+
+ if (!sawVideoOutputEos) {
+ sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
+ &videoStartTimeNano);
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+ if (timeSpec.tv_sec >= static_cast<time_t>(
+ (startTimeSec + kPlayTimeSeconds))) {
+ // stop reading samples and drain the output buffers
+ sawAudioInputEos = sawVideoInputEos = true;
+ sawAudioOutputEos = true; // ignore audio
+ }
+ }
+
+ if (audioCodec) {
+ AMediaCodec_stop(audioCodec);
+ AMediaCodec_delete(audioCodec);
+ }
+ if (videoCodec) {
+ AMediaCodec_stop(videoCodec);
+ AMediaCodec_delete(videoCodec);
+ }
+
+ AMediaCrypto_delete(crypto);
+ ANativeWindow_release(window);
+ return JNI_TRUE;
+}
+
+static void listener(
+ AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
+ AMediaDrmEventType eventType,
+ int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
+
+ switch (eventType) {
+ case EVENT_PROVISION_REQUIRED:
+ ALOGD("EVENT_PROVISION_REQUIRED received");
+ break;
+ case EVENT_KEY_REQUIRED:
+ ALOGD("EVENT_KEY_REQUIRED received");
+ break;
+ case EVENT_KEY_EXPIRED:
+ ALOGD("EVENT_KEY_EXPIRED received");
+ break;
+ case EVENT_VENDOR_DEFINED:
+ gGotVendorDefinedEvent = true;
+ ALOGD("EVENT_VENDOR_DEFINED received");
+ break;
+ default:
+ ALOGD("Unknown event received");
+ break;
+ }
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
+ if (NULL == uuid || NULL == playbackParams) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "null uuid or null playback parameters");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ PlaybackParams params;
+ initPlaybackParams(env, playbackParams, params);
+
+ AMediaObjects aMediaObjects;
+ media_status_t status = AMEDIA_OK;
+ aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+ if (NULL == aMediaObjects.getDrm()) {
+ jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+ return JNI_FALSE;
+ }
+
+ status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "setOnEventListener failed");
+ return JNI_FALSE;
+ }
+
+ aMediaObjects.setAudioExtractor(AMediaExtractor_new());
+ const char* url = env->GetStringUTFChars(params.audioUrl, 0);
+ if (url) {
+ status = AMediaExtractor_setDataSource(
+ aMediaObjects.getAudioExtractor(), url);
+ env->ReleaseStringUTFChars(params.audioUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set audio data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+ url = env->GetStringUTFChars(params.videoUrl, 0);
+ if (url) {
+ status = AMediaExtractor_setDataSource(
+ aMediaObjects.getVideoExtractor(), url);
+ env->ReleaseStringUTFChars(params.videoUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set video data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ AMediaDrmSessionId sessionId;
+ status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "openSession failed");
+ return JNI_FALSE;
+ }
+
+ // Pointer to keyRequest memory, which remains until the next
+ // AMediaDrm_getKeyRequest call or until the drm object is released.
+ const uint8_t* keyRequest;
+ size_t keyRequestSize = 0;
+
+ // The server recognizes "video/mp4" but not "video/avc".
+ status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(), &sessionId,
+ kClearkeyPssh, sizeof(kClearkeyPssh),
+ "video/mp4" /*mimeType*/, KEY_TYPE_STREAMING,
+ NULL, 0, &keyRequest, &keyRequestSize);
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "getKeyRequest failed, error = %d", status);
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+
+ if (kKeyRequestSize != keyRequestSize) {
+ ALOGE("Invalid keyRequestSize %zd", keyRequestSize);
+ return JNI_FALSE;
+ }
+
+ if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
+ ALOGE("Invalid keyRequest data is returned");
+ return JNI_FALSE;
+ }
+
+ AMediaDrmKeySetId keySetId;
+ gGotVendorDefinedEvent = false;
+ status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
+ reinterpret_cast<const uint8_t*>(kResponse),
+ sizeof(kResponse), &keySetId);
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "provideKeyResponse failed, error = %d", status);
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+
+ // Check if the event listener has received the expected event sent by
+ // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
+ const char *utf8_outValue = NULL;
+ status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+ "listenerTestSupport", &utf8_outValue);
+ if (status == AMEDIA_OK && NULL != utf8_outValue) {
+ std::string eventType(utf8_outValue);
+ if (eventType.compare("true") == 0) {
+ int count = 0;
+ while (!gGotVendorDefinedEvent && count++ < 5) {
+ // Prevents race condition when the event arrives late
+ usleep(1000);
+ }
+ if (!gGotVendorDefinedEvent) {
+ ALOGE("Event listener did not receive the expected event.");
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "Event listener did not receive the expected event.");
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ playContent(env, aMediaObjects, params, sessionId, juuid);
+
+ status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "closeSession failed");
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "isCryptoSchemeSupportedNative", "([B)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative },
+
+ { "testClearKeyPlaybackNative",
+ "([BLandroid/media/cts/NativeClearKeySystemTest$PlaybackParams;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative },
+
+ { "testGetPropertyStringNative",
+ "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative },
+
+ { "testPsshNative", "([BLjava/lang/String;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest__testPsshNative },
+};
+
+int register_android_media_cts_NativeClearKeySystemTest(JNIEnv* env) {
+ jint result = JNI_ERR;
+ jclass testClass =
+ env->FindClass("android/media/cts/NativeClearKeySystemTest");
+ if (testClass) {
+ jclass playbackParamsClass = env->FindClass(
+ "android/media/cts/NativeClearKeySystemTest$PlaybackParams");
+ if (playbackParamsClass) {
+ jclass surfaceClass =
+ env->FindClass("android/view/Surface");
+ if (surfaceClass) {
+ gFieldIds.surface = env->GetFieldID(playbackParamsClass,
+ "surface", "Landroid/view/Surface;");
+ } else {
+ gFieldIds.surface = NULL;
+ }
+ gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
+ "mimeType", "Ljava/lang/String;");
+ gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
+ "audioUrl", "Ljava/lang/String;");
+ gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
+ "videoUrl", "Ljava/lang/String;");
+ } else {
+ ALOGE("PlaybackParams class not found");
+ }
+
+ } else {
+ ALOGE("NativeClearKeySystemTest class not found");
+ }
+
+ result = env->RegisterNatives(testClass, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+ return result;
+}
diff --git a/tests/tests/media/res/raw/segment000001.ts b/tests/tests/media/res/raw/segment000001.ts
new file mode 100644
index 0000000..fb112ec
--- /dev/null
+++ b/tests/tests/media/res/raw/segment000001.ts
Binary files differ
diff --git a/tests/tests/media/res/raw/segment000001_scrambled.ts b/tests/tests/media/res/raw/segment000001_scrambled.ts
new file mode 100644
index 0000000..4c7d368
--- /dev/null
+++ b/tests/tests/media/res/raw/segment000001_scrambled.ts
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index d15643f..ba657bf 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -481,12 +481,12 @@
Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
int maxMusicVolume = mAudioManager.getStreamMaxVolume(STREAM_MUSIC);
- for (int i = 0; i < streams.length; i++) {
+ for (int stream : streams) {
// set ringer mode to back normal to not interfere with volume tests
mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
- int maxVolume = mAudioManager.getStreamMaxVolume(streams[i]);
- int minVolume = mAudioManager.getStreamMinVolume(streams[i]);
+ int maxVolume = mAudioManager.getStreamMaxVolume(stream);
+ int minVolume = mAudioManager.getStreamMinVolume(stream);
// validate min
assertTrue(String.format("minVolume(%d) must be >= 0", minVolume), minVolume >= 0);
@@ -494,14 +494,15 @@
maxVolume),
minVolume < maxVolume);
- mAudioManager.setStreamVolume(streams[i], 1, 0);
+ mAudioManager.setStreamVolume(stream, 1, 0);
if (mUseFixedVolume) {
- assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+ assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
continue;
}
- assertEquals(1, mAudioManager.getStreamVolume(streams[i]));
+ assertEquals(String.format("stream=%d", stream),
+ 1, mAudioManager.getStreamVolume(stream));
- if (streams[i] == AudioManager.STREAM_MUSIC && mAudioManager.isWiredHeadsetOn()) {
+ if (stream == AudioManager.STREAM_MUSIC && mAudioManager.isWiredHeadsetOn()) {
// due to new regulations, music sent over a wired headset may be volume limited
// until the user explicitly increases the limit, so we can't rely on being able
// to set the volume to getStreamMaxVolume(). Instead, determine the current limit
@@ -511,52 +512,52 @@
int prevvol = 0;
do {
prevvol = curvol;
- mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
- curvol = mAudioManager.getStreamVolume(streams[i]);
+ mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
+ curvol = mAudioManager.getStreamVolume(stream);
} while (curvol != prevvol);
maxVolume = maxMusicVolume = curvol;
}
- mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
- mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
- assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+ mAudioManager.setStreamVolume(stream, maxVolume, 0);
+ mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
+ assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
- volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
- mAudioManager.adjustSuggestedStreamVolume(ADJUST_LOWER, streams[i], 0);
+ volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+ mAudioManager.adjustSuggestedStreamVolume(ADJUST_LOWER, stream, 0);
Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
- assertEquals(maxVolume - volumeDelta, mAudioManager.getStreamVolume(streams[i]));
+ assertEquals(maxVolume - volumeDelta, mAudioManager.getStreamVolume(stream));
// volume lower
- mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
- volume = mAudioManager.getStreamVolume(streams[i]);
+ mAudioManager.setStreamVolume(stream, maxVolume, 0);
+ volume = mAudioManager.getStreamVolume(stream);
while (volume > minVolume) {
- volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
- mAudioManager.adjustStreamVolume(streams[i], ADJUST_LOWER, 0);
+ volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+ mAudioManager.adjustStreamVolume(stream, ADJUST_LOWER, 0);
assertEquals(Math.max(0, volume - volumeDelta),
- mAudioManager.getStreamVolume(streams[i]));
- volume = mAudioManager.getStreamVolume(streams[i]);
+ mAudioManager.getStreamVolume(stream));
+ volume = mAudioManager.getStreamVolume(stream);
}
- mAudioManager.adjustStreamVolume(streams[i], ADJUST_SAME, 0);
+ mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
// volume raise
- mAudioManager.setStreamVolume(streams[i], 1, 0);
- volume = mAudioManager.getStreamVolume(streams[i]);
+ mAudioManager.setStreamVolume(stream, 1, 0);
+ volume = mAudioManager.getStreamVolume(stream);
while (volume < maxVolume) {
- volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
- mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
+ volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+ mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
assertEquals(Math.min(volume + volumeDelta, maxVolume),
- mAudioManager.getStreamVolume(streams[i]));
- volume = mAudioManager.getStreamVolume(streams[i]);
+ mAudioManager.getStreamVolume(stream));
+ volume = mAudioManager.getStreamVolume(stream);
}
// volume same
- mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
+ mAudioManager.setStreamVolume(stream, maxVolume, 0);
for (int k = 0; k < maxVolume; k++) {
- mAudioManager.adjustStreamVolume(streams[i], ADJUST_SAME, 0);
- assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+ mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
+ assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
}
- mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
+ mAudioManager.setStreamVolume(stream, maxVolume, 0);
}
if (mUseFixedVolume) {
@@ -659,18 +660,18 @@
AudioManager.STREAM_NOTIFICATION,
AudioManager.STREAM_SYSTEM};
if (mUseFixedVolume) {
- for (int i = 0; i < streams.length; i++) {
- mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
+ for (int stream : streams) {
+ mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
assertFalse("Muting should not affect a fixed volume device.",
- mAudioManager.isStreamMute(streams[i]));
+ mAudioManager.isStreamMute(stream));
- mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE, 0);
+ mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE, 0);
assertFalse("Toggling mute should not affect a fixed volume device.",
- mAudioManager.isStreamMute(streams[i]));
+ mAudioManager.isStreamMute(stream));
- mAudioManager.setStreamMute(streams[i], true);
+ mAudioManager.setStreamMute(stream, true);
assertFalse("Muting should not affect a fixed volume device.",
- mAudioManager.isStreamMute(streams[i]));
+ mAudioManager.isStreamMute(stream));
}
}
}
@@ -688,16 +689,16 @@
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), false);
// Verify streams cannot be unmuted without policy access.
- for (int i = 0; i < streams.length; i++) {
+ for (int stream : streams) {
try {
- mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_UNMUTE, 0);
+ mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_UNMUTE, 0);
assertEquals("Apps without Notification policy access can't change ringer mode",
RINGER_MODE_SILENT, mAudioManager.getRingerMode());
} catch (SecurityException e) {
}
try {
- mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE,
+ mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE,
0);
assertEquals("Apps without Notification policy access can't change ringer mode",
RINGER_MODE_SILENT, mAudioManager.getRingerMode());
@@ -705,7 +706,7 @@
}
try {
- mAudioManager.setStreamMute(streams[i], false);
+ mAudioManager.setStreamMute(stream, false);
assertEquals("Apps without Notification policy access can't change ringer mode",
RINGER_MODE_SILENT, mAudioManager.getRingerMode());
} catch (SecurityException e) {
@@ -716,37 +717,37 @@
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true);
mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
- for (int i = 0; i < streams.length; i++) {
+ for (int stream : streams) {
// ensure each stream is on and turned up.
- mAudioManager.setStreamVolume(streams[i],
- mAudioManager.getStreamMaxVolume(streams[i]),
+ mAudioManager.setStreamVolume(stream,
+ mAudioManager.getStreamMaxVolume(stream),
0);
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), false);
try {
- mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
+ mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
assertEquals("Apps without Notification policy access can't change ringer mode",
RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
} catch (SecurityException e) {
}
try {
mAudioManager.adjustStreamVolume(
- streams[i], AudioManager.ADJUST_TOGGLE_MUTE, 0);
+ stream, AudioManager.ADJUST_TOGGLE_MUTE, 0);
assertEquals("Apps without Notification policy access can't change ringer mode",
RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
} catch (SecurityException e) {
}
try {
- mAudioManager.setStreamMute(streams[i], true);
+ mAudioManager.setStreamMute(stream, true);
assertEquals("Apps without Notification policy access can't change ringer mode",
RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
} catch (SecurityException e) {
}
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), true);
- testStreamMuting(streams[i]);
+ testStreamMuting(stream);
}
} finally {
Utils.toggleNotificationPolicyAccess(
@@ -779,25 +780,25 @@
mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
Utils.toggleNotificationPolicyAccess(
mContext.getPackageName(), getInstrumentation(), false);
- for (int i = 0; i < streams.length; i++) {
+ for (int stream : streams) {
// ensure each stream is on and turned up.
- mAudioManager.setStreamVolume(streams[i],
- mAudioManager.getStreamMaxVolume(streams[i]),
+ mAudioManager.setStreamVolume(stream,
+ mAudioManager.getStreamMaxVolume(stream),
0);
- if (((1 << streams[i]) & muteAffectedStreams) == 0) {
- mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
- assertFalse("Stream " + streams[i] + " should not be affected by mute.",
- mAudioManager.isStreamMute(streams[i]));
- mAudioManager.setStreamMute(streams[i], true);
- assertFalse("Stream " + streams[i] + " should not be affected by mute.",
- mAudioManager.isStreamMute(streams[i]));
- mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE,
+ if (((1 << stream) & muteAffectedStreams) == 0) {
+ mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
+ assertFalse("Stream " + stream + " should not be affected by mute.",
+ mAudioManager.isStreamMute(stream));
+ mAudioManager.setStreamMute(stream, true);
+ assertFalse("Stream " + stream + " should not be affected by mute.",
+ mAudioManager.isStreamMute(stream));
+ mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE,
0);
- assertFalse("Stream " + streams[i] + " should not be affected by mute.",
- mAudioManager.isStreamMute(streams[i]));
+ assertFalse("Stream " + stream + " should not be affected by mute.",
+ mAudioManager.isStreamMute(stream));
continue;
}
- testStreamMuting(streams[i]);
+ testStreamMuting(stream);
}
} finally {
Utils.toggleNotificationPolicyAccess(
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
index efe8bb4..61edbba 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
@@ -36,12 +36,13 @@
import java.util.List;
public class AudioRecordingConfigurationTest extends CtsAndroidTestCase {
- private final static String TAG = "AudioRecordingConfigurationTest";
+ private static final String TAG = "AudioRecordingConfigurationTest";
- private final static int TEST_SAMPLE_RATE = 16000;
- private final static int TEST_AUDIO_SOURCE = MediaRecorder.AudioSource.VOICE_RECOGNITION;
+ private static final int TEST_SAMPLE_RATE = 16000;
+ private static final int TEST_AUDIO_SOURCE = MediaRecorder.AudioSource.VOICE_RECOGNITION;
- private final static int TEST_TIMING_TOLERANCE_MS = 70;
+ private static final int TEST_TIMING_TOLERANCE_MS = 70;
+ private static final long SLEEP_AFTER_STOP_FOR_INACTIVITY_MS = 1000;
private AudioRecord mAudioRecord;
private Looper mLooper;
@@ -93,6 +94,7 @@
mAudioRecord.stop();
mAudioRecord.release();
mLooper.quit();
+ Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS);
}
super.tearDown();
}
@@ -127,10 +129,10 @@
// stopping recording: verify there are less active record configurations
mAudioRecord.stop();
- Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+ Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS);
configs = am.getActiveRecordingConfigurations();
- assertTrue("end of recording not reported in record configs",
- configs.size() < nbConfigsDuringRecording);
+ assertEquals("Unexpected number of recording configs after stop",
+ configs.size(), 0);
}
public void testCallback() throws Exception {
@@ -169,15 +171,23 @@
assertEquals(AudioRecord.RECORDSTATE_RECORDING, mAudioRecord.getRecordingState());
Thread.sleep(TEST_TIMING_TOLERANCE_MS);
- assertTrue("AudioRecordingCallback not called", callback.mCalled);
- assertTrue("Expected record configuration was not found", callback.mParamMatch);
+ assertTrue("AudioRecordingCallback not called after start", callback.mCalled);
+ Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+
+ final AudioDeviceInfo testDevice = mAudioRecord.getRoutedDevice();
+ assertTrue("AudioRecord null routed device after start", testDevice != null);
+ final boolean match = verifyAudioConfig(mAudioRecord.getAudioSource(),
+ mAudioRecord.getAudioSessionId(), mAudioRecord.getFormat(),
+ testDevice, callback.mConfigs);
+ assertTrue("Expected record configuration was not found", match);
// stopping recording: callback is called with no match
callback.reset();
mAudioRecord.stop();
- Thread.sleep(TEST_TIMING_TOLERANCE_MS);
- assertTrue("AudioRecordingCallback not called", callback.mCalled);
- assertFalse("Should not have found test record configuration", callback.mParamMatch);
+ Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS);
+ assertTrue("AudioRecordingCallback not called after stop", callback.mCalled);
+ assertEquals("Should not have found record configurations", callback.mConfigs.size(),
+ 0);
// unregister callback and start recording again
am.unregisterAudioRecordingCallback(callback);
@@ -231,14 +241,14 @@
class MyAudioRecordingCallback extends AudioManager.AudioRecordingCallback {
boolean mCalled = false;
- boolean mParamMatch = false;
+ List<AudioRecordingConfiguration> mConfigs;
final AudioManager mAM;
final int mTestSource;
final int mTestSession;
void reset() {
mCalled = false;
- mParamMatch = false;
+ mConfigs = null;
}
MyAudioRecordingCallback(int session, int source) {
@@ -250,8 +260,7 @@
@Override
public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
mCalled = true;
- mParamMatch = verifyAudioConfig(mTestSource, mTestSession, mAudioRecord.getFormat(),
- mAudioRecord.getRoutedDevice(), configs);
+ mConfigs = configs;
}
}
@@ -266,6 +275,8 @@
final Iterator<AudioRecordingConfiguration> confIt = configs.iterator();
while (confIt.hasNext()) {
final AudioRecordingConfiguration config = confIt.next();
+ final AudioDeviceInfo configDevice = config.getAudioDevice();
+ assertTrue("Current recording config has null device", configDevice != null);
if ((config.getClientAudioSource() == source)
&& (config.getClientAudioSessionId() == session)
// test the client format matches that requested (same as the AudioRecord's)
@@ -281,7 +292,7 @@
&& ((config.getFormat().getChannelMask() != AudioFormat.CHANNEL_INVALID)
|| (config.getFormat().getChannelIndexMask() !=
AudioFormat.CHANNEL_INVALID))
- && deviceMatch(device, config.getAudioDevice())) {
+ && deviceMatch(device, configDevice)) {
return true;
}
}
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
index 69f3ee8..5ea33ba 100644
--- a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -66,8 +66,10 @@
private static final int SLEEP_TIME_MS = 1000;
private static final int VIDEO_WIDTH_CENC = 1280;
private static final int VIDEO_HEIGHT_CENC = 720;
- private static final int VIDEO_WIDTH_WEBM = 320;
- private static final int VIDEO_HEIGHT_WEBM = 180;
+ private static final int VIDEO_WIDTH_WEBM = 352;
+ private static final int VIDEO_HEIGHT_WEBM = 288;
+ private static final int VIDEO_WIDTH_MPEG2TS = 320;
+ private static final int VIDEO_HEIGHT_MPEG2TS = 240;
private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(25, TimeUnit.SECONDS);
private static final String MIME_VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
private static final String MIME_VIDEO_VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
@@ -78,6 +80,10 @@
"http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car_cenc-20120827-88.mp4");
private static final Uri WEBM_URL = Uri.parse(
"android.resource://android.media.cts/" + R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt);
+ private static final Uri MPEG2TS_SCRAMBLED_URL = Uri.parse(
+ "android.resource://android.media.cts/" + R.raw.segment000001_scrambled);
+ private static final Uri MPEG2TS_CLEAR_URL = Uri.parse(
+ "android.resource://android.media.cts/" + R.raw.segment000001);
private static final UUID CLEARKEY_SCHEME_UUID =
new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
@@ -327,15 +333,20 @@
String initDataType, byte[][] clearKeys,
Uri audioUrl, boolean audioEncrypted,
Uri videoUrl, boolean videoEncrypted,
- int videoWidth, int videoHeight) throws Exception {
- MediaDrm drm = startDrm(clearKeys, initDataType);
- if (null == drm) {
- throw new Error("Failed to create drm.");
- }
+ int videoWidth, int videoHeight, boolean scrambled) throws Exception {
+ MediaDrm drm = null;
+ mSessionId = null;
+ if (!scrambled) {
+ drm = startDrm(clearKeys, initDataType);
+ if (null == drm) {
+ throw new Error("Failed to create drm.");
+ }
- if (!drm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) {
- stopDrm(drm);
- throw new Error("Crypto scheme is not supported.");
+ if (!drm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) {
+ stopDrm(drm);
+ throw new Error("Crypto scheme is not supported.");
+ }
+ mSessionId = openSession(drm);
}
if (!isResolutionSupported(videoMime, videoFeatures, videoWidth, videoHeight)) {
@@ -364,19 +375,19 @@
}
connectionStatus.testConnection(videoUrl);
- mSessionId = openSession(drm);
mMediaCodecPlayer = new MediaCodecClearKeyPlayer(
getActivity().getSurfaceHolder(),
- mSessionId,
+ mSessionId, scrambled,
mContext.getResources());
mMediaCodecPlayer.setAudioDataSource(audioUrl, null, audioEncrypted);
mMediaCodecPlayer.setVideoDataSource(videoUrl, null, videoEncrypted);
mMediaCodecPlayer.start();
mMediaCodecPlayer.prepare();
- mDrmInitData = mMediaCodecPlayer.getDrmInitData();
-
- getKeys(mDrm, initDataType, mSessionId, mDrmInitData, clearKeys);
+ if (!scrambled) {
+ mDrmInitData = mMediaCodecPlayer.getDrmInitData();
+ getKeys(mDrm, initDataType, mSessionId, mDrmInitData, clearKeys);
+ }
// starts video playback
mMediaCodecPlayer.startThread();
@@ -392,8 +403,10 @@
Log.d(TAG, "playVideo player.reset()");
mMediaCodecPlayer.reset();
- closeSession(drm, mSessionId);
- stopDrm(drm);
+ if (!scrambled) {
+ closeSession(drm, mSessionId);
+ stopDrm(drm);
+ }
}
public void testClearKeyPlaybackCenc() throws Exception {
@@ -403,7 +416,7 @@
"cenc", new byte[][] { CLEAR_KEY_CENC },
CENC_AUDIO_URL, false,
CENC_VIDEO_URL, true,
- VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+ VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false);
}
public void testClearKeyPlaybackWebm() throws Exception {
@@ -412,6 +425,24 @@
"webm", new byte[][] { CLEAR_KEY_WEBM },
WEBM_URL, true,
WEBM_URL, true,
- VIDEO_WIDTH_WEBM, VIDEO_WIDTH_WEBM);
+ VIDEO_WIDTH_WEBM, VIDEO_HEIGHT_WEBM, false);
+ }
+
+ public void testClearKeyPlaybackMpeg2ts() throws Exception {
+ testClearKeyPlayback(
+ MIME_VIDEO_AVC, new String[0],
+ "mpeg2ts", null,
+ MPEG2TS_SCRAMBLED_URL, false,
+ MPEG2TS_SCRAMBLED_URL, false,
+ VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, true);
+ }
+
+ public void testPlaybackMpeg2ts() throws Exception {
+ testClearKeyPlayback(
+ MIME_VIDEO_AVC, new String[0],
+ "mpeg2ts", null,
+ MPEG2TS_CLEAR_URL, false,
+ MPEG2TS_CLEAR_URL, false,
+ VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, false);
}
}
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 51be918..12ebeae 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -322,12 +322,10 @@
mSawOutputEOS = true;
- // We need to stop the audio track so that all audio frames are played
- // and the video codec can consume all of its data.
- // After audio track stop, getAudioTimeUs will return 0.
- if (mAudioTrack != null) {
- mAudioTrack.stop();
- }
+ // Do not stop audio track here. Video presentation may not finish
+ // yet, stopping the auido track now would result in getAudioTimeUs
+ // returning 0 and prevent video samples from being presented.
+ // We stop the audio track before the playback thread exits.
return false;
}
@@ -390,4 +388,10 @@
mAudioTrack.process();
}
}
+
+ public void stop() {
+ if (mAudioTrack != null) {
+ mAudioTrack.stop();
+ }
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
index 1d95463..253a5ab 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
@@ -19,11 +19,15 @@
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.DrmInitData;
+import android.media.MediaCas;
+import android.media.MediaCasException;
+import android.media.MediaCasException.UnsupportedCasException;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaCrypto;
import android.media.MediaCryptoException;
+import android.media.MediaDescrambler;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
@@ -60,6 +64,7 @@
private boolean mEncryptedVideo;
private volatile boolean mThreadStarted = false;
private byte[] mSessionId;
+ private boolean mScrambled;
private CodecState mAudioTrackState;
private int mMediaFormatHeight;
private int mMediaFormatWidth;
@@ -72,6 +77,8 @@
private Map<String, String> mVideoHeaders;
private Map<UUID, byte[]> mPsshInitData;
private MediaCrypto mCrypto;
+ private MediaCas mMediaCas;
+ private MediaDescrambler mDescrambler;
private MediaExtractor mAudioExtractor;
private MediaExtractor mVideoExtractor;
private SurfaceHolder mSurfaceHolder;
@@ -91,6 +98,19 @@
"00000000" // Size of Data, must be zero
);
+ // ClearKey CAS/Descrambler test provision string
+ private static final String sProvisionStr =
+ "{ " +
+ " \"id\": 21140844, " +
+ " \"name\": \"Test Title\", " +
+ " \"lowercase_organization_name\": \"Android\", " +
+ " \"asset_key\": { " +
+ " \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\" " +
+ " }, " +
+ " \"cas_type\": 1, " +
+ " \"track_types\": [ ] " +
+ "} " ;
+
/**
* Convert a hex string into byte array.
*/
@@ -108,8 +128,9 @@
* Media player class to stream CENC content using MediaCodec class.
*/
public MediaCodecClearKeyPlayer(
- SurfaceHolder holder, byte[] sessionId, Resources resources) {
+ SurfaceHolder holder, byte[] sessionId, boolean scrambled, Resources resources) {
mSessionId = sessionId;
+ mScrambled = scrambled;
mSurfaceHolder = holder;
mResources = resources;
mState = STATE_IDLE;
@@ -127,6 +148,9 @@
Log.d(TAG, "Thread interrupted");
}
}
+ if (mAudioTrackState != null) {
+ mAudioTrackState.stop();
+ }
}
});
}
@@ -206,6 +230,8 @@
private void prepareVideo() throws IOException {
boolean hasVideo = false;
+ android.media.DrmInitData drmInitData = mVideoExtractor.getDrmInitData();
+
for (int i = mVideoExtractor.getTrackCount(); i-- > 0;) {
MediaFormat format = mVideoExtractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
@@ -218,6 +244,14 @@
Log.d(TAG, "video track #" + i + " " + format + " " + mime +
" Width:" + mMediaFormatWidth + ", Height:" + mMediaFormatHeight);
+ if (mScrambled && drmInitData != null && mime.startsWith("video/")) {
+ android.media.DrmInitData.SchemeInitData schemeInitData =
+ drmInitData.get(new UUID(0, i));
+ if (schemeInitData != null) {
+ mDescrambler.setMediaCasSession(schemeInitData.data);
+ }
+ }
+
if (!hasVideo) {
mVideoExtractor.selectTrack(i);
addTrack(i, format, mEncryptedVideo);
@@ -239,11 +273,10 @@
}
}
}
- return;
}
private void setDataSource(MediaExtractor extractor, Uri uri, Map<String, String> headers)
- throws IOException {
+ throws IOException, MediaCasException {
String scheme = uri.getScheme();
if (scheme.startsWith("http")) {
extractor.setDataSource(uri.toString(), headers);
@@ -256,26 +289,30 @@
}
}
- public void prepare() throws IOException, MediaCryptoException {
- if (null == mAudioExtractor) {
- mAudioExtractor = new MediaExtractor();
- if (null == mAudioExtractor) {
- Log.e(TAG, "Cannot create Audio extractor.");
- return;
+ private void initCasAndDescrambler(MediaExtractor extractor) throws MediaCasException {
+ int trackCount = extractor.getTrackCount();
+ android.media.DrmInitData drmInitData = extractor.getDrmInitData();
+ for (int trackId = 0; trackId < trackCount; trackId++) {
+ android.media.MediaFormat format = extractor.getTrackFormat(trackId);
+ String mime = format.getString(android.media.MediaFormat.KEY_MIME);
+ Log.d(TAG, "track "+ trackId + ": " + mime);
+ if ("video/scrambled".equals(mime) || "audio/scrambled".equals(mime)) {
+ if (drmInitData == null) {
+ throw new IllegalArgumentException("found scrambled track without drmInitData!");
+ }
+ android.media.DrmInitData.SchemeInitData schemeInitData =
+ drmInitData.get(new UUID(0, trackId));
+ int CA_system_id = (schemeInitData.data[0] & 0xff)
+ | ((schemeInitData.data[1] & 0xff) << 8);
+ mMediaCas = new MediaCas(CA_system_id);
+ mDescrambler = new MediaDescrambler(CA_system_id);
+ mMediaCas.provision(sProvisionStr);
+ extractor.setMediaCas(mMediaCas);
}
}
+ }
- if (null == mVideoExtractor){
- mVideoExtractor = new MediaExtractor();
- if (null == mVideoExtractor) {
- Log.e(TAG, "Cannot create Video extractor.");
- return;
- }
- }
-
- setDataSource(mAudioExtractor, mAudioUri, mAudioHeaders);
- setDataSource(mVideoExtractor, mVideoUri, mVideoHeaders);
-
+ public void prepare() throws IOException, MediaCryptoException, MediaCasException {
if (null == mCrypto && (mEncryptedVideo || mEncryptedAudio)) {
try {
byte[] initData = new byte[0];
@@ -288,8 +325,29 @@
mCrypto.setMediaDrmSession(mSessionId);
} else {
reset();
- mCrypto.release();
- mCrypto = null;
+ }
+
+ if (null == mAudioExtractor) {
+ mAudioExtractor = new MediaExtractor();
+ if (null == mAudioExtractor) {
+ Log.e(TAG, "Cannot create Audio extractor.");
+ return;
+ }
+ }
+ setDataSource(mAudioExtractor, mAudioUri, mAudioHeaders);
+
+ if (mScrambled) {
+ initCasAndDescrambler(mAudioExtractor);
+ mVideoExtractor = mAudioExtractor;
+ } else {
+ if (null == mVideoExtractor){
+ mVideoExtractor = new MediaExtractor();
+ if (null == mVideoExtractor) {
+ Log.e(TAG, "Cannot create Video extractor.");
+ return;
+ }
+ }
+ setDataSource(mVideoExtractor, mVideoUri, mVideoHeaders);
}
if (null == mVideoCodecStates) {
@@ -325,11 +383,19 @@
codec = MediaCodec.createDecoderByType(mime);
}
- codec.configure(
- format,
- isVideo ? mSurfaceHolder.getSurface() : null,
- mCrypto,
- 0);
+ if (!mScrambled) {
+ codec.configure(
+ format,
+ isVideo ? mSurfaceHolder.getSurface() : null,
+ mCrypto,
+ 0);
+ } else {
+ codec.configure(
+ format,
+ isVideo ? mSurfaceHolder.getSurface() : null,
+ 0,
+ isVideo ? mDescrambler : null);
+ }
CodecState state;
if (isVideo) {
@@ -483,6 +549,16 @@
mCrypto = null;
}
+ if (mMediaCas != null) {
+ mMediaCas.release();
+ mMediaCas = null;
+ }
+
+ if (mDescrambler != null) {
+ mDescrambler.release();
+ mDescrambler = null;
+ }
+
mDurationUs = -1;
mState = STATE_IDLE;
}
diff --git a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
new file mode 100644
index 0000000..59c2e8e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import android.net.Uri;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.android.compatibility.common.util.MediaUtils;
+import com.google.android.collect.Lists;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.UUID;
+
+/**
+ * Tests MediaDrm NDK APIs. ClearKey system uses a subset of NDK APIs,
+ * this test only tests the APIs that are supported by ClearKey system.
+ */
+public class NativeClearKeySystemTest extends MediaPlayerTestBase {
+ private static final String TAG = NativeClearKeySystemTest.class.getSimpleName();
+
+ private static final int CONNECTION_RETRIES = 10;
+ private static final int VIDEO_WIDTH_CENC = 1280;
+ private static final int VIDEO_HEIGHT_CENC = 720;
+ private static final String ISO_BMFF_VIDEO_MIME_TYPE = "video/avc";
+ private static final String ISO_BMFF_AUDIO_MIME_TYPE = "audio/avc";
+ private static final Uri CENC_AUDIO_URL = Uri.parse(
+ "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/" +
+ "car_cenc-20120827-8c.mp4");
+ private static final Uri CENC_CLEARKEY_VIDEO_URL = Uri.parse(
+ "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/" +
+ "car_cenc-20120827-88.mp4");
+
+ private static final int UUID_BYTE_SIZE = 16;
+ private static final UUID CLEARKEY_SCHEME_UUID =
+ new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+ private static final UUID BAD_SCHEME_UUID =
+ new UUID(0xffffffffffffffffL, 0xffffffffffffffffL);
+ private MediaCodecClearKeyPlayer mMediaCodecPlayer;
+
+ static {
+ try {
+ System.loadLibrary("ctsmediadrm_jni");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+ e.printStackTrace();
+ }
+ try {
+ System.loadLibrary("mediandk");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+ e.printStackTrace();
+ }
+ }
+
+ public static class PlaybackParams {
+ public Surface surface;
+ public String mimeType;
+ public String audioUrl;
+ public String videoUrl;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (false == deviceHasMediaDrm()) {
+ tearDown();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private boolean deviceHasMediaDrm() {
+ // ClearKey is introduced after KitKat.
+ if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT) {
+ Log.i(TAG, "This test is designed to work after Android KitKat.");
+ return false;
+ }
+ return true;
+ }
+
+ private static final byte[] uuidByteArray(UUID uuid) {
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
+ buffer.putLong(uuid.getMostSignificantBits());
+ buffer.putLong(uuid.getLeastSignificantBits());
+ return buffer.array();
+ }
+
+ public void testIsCryptoSchemeSupported() throws Exception {
+ assertTrue(isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID)));
+ }
+
+ public void testIsCryptoSchemeNotSupported() throws Exception {
+ assertFalse(isCryptoSchemeSupportedNative(uuidByteArray(BAD_SCHEME_UUID)));
+ }
+
+ public void testPssh() throws Exception {
+ assertTrue(testPsshNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+ CENC_CLEARKEY_VIDEO_URL.toString()));
+ }
+
+ public void testGetPropertyString() throws Exception {
+ StringBuffer value = new StringBuffer();
+ testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value);
+ assertEquals("ClearKey CDM", value.toString());
+ }
+
+ public void testUnknownPropertyString() throws Exception {
+ try {
+ StringBuffer value = new StringBuffer();
+ testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+ "unknown-property", value);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'");
+ assertThat(e.getMessage(), containsString("get property string returns"));
+ }
+ }
+
+ /**
+ * Tests native clear key system playback.
+ */
+ private void testClearKeyPlayback(
+ String mimeType, /*String initDataType,*/ Uri audioUrl, Uri videoUrl,
+ int videoWidth, int videoHeight) throws Exception {
+
+ if (!isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID))) {
+ throw new Error("Crypto scheme is not supported.");
+ }
+
+ IConnectionStatus connectionStatus = new ConnectionStatus(mContext);
+ if (!connectionStatus.isAvailable()) {
+ throw new Error("Network is not available, reason: " +
+ connectionStatus.getNotConnectedReason());
+ }
+
+ // If device is not online, recheck the status a few times.
+ int retries = 0;
+ while (!connectionStatus.isConnected()) {
+ if (retries++ >= CONNECTION_RETRIES) {
+ throw new Error("Device is not online, reason: " +
+ connectionStatus.getNotConnectedReason());
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ connectionStatus.testConnection(videoUrl);
+
+ if (!MediaUtils.checkCodecsForPath(mContext, videoUrl.getPath())) {
+ Log.i(TAG, "Device does not support " +
+ videoWidth + "x" + videoHeight + " resolution for " + mimeType);
+ return; // skip
+ }
+
+ PlaybackParams params = new PlaybackParams();
+ params.surface = mActivity.getSurfaceHolder().getSurface();
+ params.mimeType = mimeType;
+ params.audioUrl = audioUrl.toString();
+ params.videoUrl = videoUrl.toString();
+
+ if (!testClearKeyPlaybackNative(
+ uuidByteArray(CLEARKEY_SCHEME_UUID), params)) {
+ Log.e(TAG, "Fails play back using native media drm APIs.");
+ }
+ params.surface.release();
+ }
+
+ private ArrayList<Integer> intVersion(String version) {
+ String versions[] = version.split("\\.");
+
+ ArrayList<Integer> versionNumbers = Lists.newArrayList();
+ for (String subVersion : versions) {
+ versionNumbers.add(Integer.parseInt(subVersion));
+ }
+ return versionNumbers;
+ }
+
+ private static native boolean isCryptoSchemeSupportedNative(final byte[] uuid);
+
+ private static native boolean testClearKeyPlaybackNative(final byte[] uuid,
+ PlaybackParams params);
+
+ private static native boolean testGetPropertyStringNative(final byte[] uuid,
+ final String name, StringBuffer value);
+
+ private static native boolean testPsshNative(final byte[] uuid, final String videoUrl);
+
+ public void testClearKeyPlaybackCenc() throws Exception {
+ testClearKeyPlayback(
+ ISO_BMFF_VIDEO_MIME_TYPE,
+ CENC_AUDIO_URL,
+ CENC_CLEARKEY_VIDEO_URL,
+ VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 5b989cc..e22d8a7 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -332,7 +332,8 @@
}
}
- public void testBuffering() throws Throwable {
+ // TODO: unhide this test when we sort out how to expose buffering control API.
+ private void doTestBuffering() throws Throwable {
final String name = "ringer.mp3";
mServer = new CtsTestServer(mContext);
try {
diff --git a/tests/tests/media/src/android/media/cts/VolumeShaperTest.java b/tests/tests/media/src/android/media/cts/VolumeShaperTest.java
index 8206ecb..fce8c80 100644
--- a/tests/tests/media/src/android/media/cts/VolumeShaperTest.java
+++ b/tests/tests/media/src/android/media/cts/VolumeShaperTest.java
@@ -45,7 +45,7 @@
.setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
.setCurve(new float[] { 0.f, 1.f } /* times */,
new float[] { 0.f, 0.f } /* volumes */)
- .setDurationMs((double)RAMP_TIME_MS)
+ .setDurationMillis((double)RAMP_TIME_MS)
.build();
// Duck configurations go from 1.f down to 0.2f (not full ramp down).
@@ -54,28 +54,28 @@
.setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
.setCurve(new float[] { 0.f, 1.f } /* times */,
new float[] { 1.f, 0.2f } /* volumes */)
- .setDurationMs((double)RAMP_TIME_MS)
+ .setDurationMillis((double)RAMP_TIME_MS)
.build();
// Ramp configurations go from 0.f up to 1.f
private static final VolumeShaper.Configuration LINEAR_RAMP =
new VolumeShaper.Configuration.Builder(VolumeShaper.Configuration.LINEAR_RAMP)
- .setDurationMs((double)RAMP_TIME_MS)
+ .setDurationMillis((double)RAMP_TIME_MS)
.build();
private static final VolumeShaper.Configuration CUBIC_RAMP =
new VolumeShaper.Configuration.Builder(VolumeShaper.Configuration.CUBIC_RAMP)
- .setDurationMs((double)RAMP_TIME_MS)
+ .setDurationMillis((double)RAMP_TIME_MS)
.build();
private static final VolumeShaper.Configuration SINE_RAMP =
new VolumeShaper.Configuration.Builder(VolumeShaper.Configuration.SINE_RAMP)
- .setDurationMs((double)RAMP_TIME_MS)
+ .setDurationMillis((double)RAMP_TIME_MS)
.build();
private static final VolumeShaper.Configuration SCURVE_RAMP =
new VolumeShaper.Configuration.Builder(VolumeShaper.Configuration.SCURVE_RAMP)
- .setDurationMs((double)RAMP_TIME_MS)
+ .setDurationMillis((double)RAMP_TIME_MS)
.build();
// internal use only
@@ -85,7 +85,7 @@
.setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_VOLUME_IN_DBFS)
.setCurve(new float[] { 0.f, 1.f } /* times */,
new float[] { -80.f, 0.f } /* volumes */)
- .setDurationMs((double)RAMP_TIME_MS)
+ .setDurationMillis((double)RAMP_TIME_MS)
.build();
private static final VolumeShaper.Configuration[] ALL_STANDARD_RAMPS = {
@@ -101,7 +101,7 @@
.setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_CUBIC_MONOTONIC)
.setCurve(new float[] { 0.f, 0.3f, 0.7f, 1.f } /* times */,
new float[] { 0.f, 0.5f, 0.5f, 1.f } /* volumes */)
- .setDurationMs((double)RAMP_TIME_MS)
+ .setDurationMillis((double)RAMP_TIME_MS)
.build();
private static final VolumeShaper.Configuration MONOTONIC_TEST_FAIL =
@@ -266,7 +266,7 @@
final VolumeShaper.Configuration config =
new VolumeShaper.Configuration.Builder()
.setCurve(ohOne, ohOne)
- .setDurationMs(-1.)
+ .setDurationMillis(-1.)
.build();
fail(TEST_NAME + " configuration builder should fail on invalid duration");
} catch (IllegalArgumentException e) {
@@ -291,7 +291,7 @@
assertEquals(TEST_NAME + " default interpolation should be cubic",
VolumeShaper.Configuration.INTERPOLATOR_TYPE_CUBIC, config.getInterpolatorType());
assertEquals(TEST_NAME + " default duration should be 1000 ms",
- 1000., config.getDurationMs());
+ 1000., config.getDurationMillis());
assertTrue(TEST_NAME + " times should be { 0.f, 1.f }",
Arrays.equals(ohOne, config.getTimes()));
assertTrue(TEST_NAME + " volumes should be { 0.f, 1.f }",
@@ -319,7 +319,7 @@
checkEqual(TEST_NAME, testRamp, ramp);
ramp = new VolumeShaper.Configuration.Builder(testRamp)
- .setDurationMs(10)
+ .setDurationMillis(10)
.build();
checkNotEqual(TEST_NAME, testRamp, ramp);
@@ -563,7 +563,7 @@
// we join several LINEAR_RAMPS together - this should effectively
// be one long LINEAR_RAMP.
volumeShaper.replace(new VolumeShaper.Configuration.Builder(LINEAR_RAMP)
- .setDurationMs((double)(duration - i))
+ .setDurationMillis((double)(duration - i))
.build(),
VolumeShaper.Operation.PLAY, true /* join */);
assertEquals(TEST_NAME + " linear ramp should continue on join",
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index ac7d703..0318610 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -787,86 +787,6 @@
insecure.isEmpty());
}
- private static final Set<File> CHAR_DEV_EXCEPTIONS = new HashSet<File>(
- Arrays.asList(
- // All exceptions should be alphabetical and associated with a bug number.
- new File("/dev/adsprpc-smd"), // b/11710243
- new File("/dev/alarm"), // b/9035217
- new File("/dev/ashmem"),
- new File("/dev/binder"),
- new File("/dev/card0"), // b/13159510
- new File("/dev/renderD128"),
- new File("/dev/renderD129"), // b/23798677
- new File("/dev/dri/card0"), // b/13159510
- new File("/dev/dri/renderD128"),
- new File("/dev/dri/renderD129"), // b/23798677
- new File("/dev/felica"), // b/11142586
- new File("/dev/felica_ant"), // b/11142586
- new File("/dev/felica_cen"), // b/11142586
- new File("/dev/felica_pon"), // b/11142586
- new File("/dev/felica_rfs"), // b/11142586
- new File("/dev/felica_rws"), // b/11142586
- new File("/dev/felica_uicc"), // b/11142586
- new File("/dev/full"),
- new File("/dev/galcore"),
- new File("/dev/genlock"), // b/9035217
- new File("/dev/graphics/galcore"),
- new File("/dev/ion"),
- new File("/dev/kgsl-2d0"), // b/11271533
- new File("/dev/kgsl-2d1"), // b/11271533
- new File("/dev/kgsl-3d0"), // b/9035217
- new File("/dev/log/events"), // b/9035217
- new File("/dev/log/main"), // b/9035217
- new File("/dev/log/radio"), // b/9035217
- new File("/dev/log/system"), // b/9035217
- new File("/dev/mali0"), // b/9106968
- new File("/dev/mali"), // b/11142586
- new File("/dev/mm_interlock"), // b/12955573
- new File("/dev/mm_isp"), // b/12955573
- new File("/dev/mm_v3d"), // b/12955573
- new File("/dev/msm_rotator"), // b/9035217
- new File("/dev/null"),
- new File("/dev/nvhost-as-gpu"),
- new File("/dev/nvhost-ctrl"), // b/9088251
- new File("/dev/nvhost-ctrl-gpu"),
- new File("/dev/nvhost-dbg-gpu"),
- new File("/dev/nvhost-gpu"),
- new File("/dev/nvhost-gr2d"), // b/9088251
- new File("/dev/nvhost-gr3d"), // b/9088251
- new File("/dev/nvhost-tsec"),
- new File("/dev/nvhost-prof-gpu"),
- new File("/dev/nvhost-vic"),
- new File("/dev/nvmap"), // b/9088251
- new File("/dev/pmsg0"), // b/31857082
- new File("/dev/ptmx"), // b/9088251
- new File("/dev/pvrsrvkm"), // b/9108170
- new File("/dev/pvr_sync"),
- new File("/dev/quadd"),
- new File("/dev/random"),
- new File("/dev/snfc_cen"), // b/11142586
- new File("/dev/snfc_hsel"), // b/11142586
- new File("/dev/snfc_intu_poll"), // b/11142586
- new File("/dev/snfc_rfs"), // b/11142586
- new File("/dev/tegra-throughput"),
- new File("/dev/tiler"), // b/9108170
- new File("/dev/tty"),
- new File("/dev/urandom"),
- new File("/dev/ump"), // b/11142586
- new File("/dev/xt_qtaguid"), // b/9088251
- new File("/dev/zero"),
- new File("/dev/fimg2d"), // b/10428016
- new File("/dev/mobicore-user") // b/10428016
- ));
-
- public void testAllCharacterDevicesAreSecure() throws Exception {
- Set<File> insecure = getAllInsecureDevicesInDirAndSubdir(new File("/dev"), FileUtils.S_IFCHR);
- Set<File> insecurePts = getAllInsecureDevicesInDirAndSubdir(new File("/dev/pts"), FileUtils.S_IFCHR);
- insecure.removeAll(CHAR_DEV_EXCEPTIONS);
- insecure.removeAll(insecurePts);
- assertTrue("Found insecure character devices: " + insecure.toString(),
- insecure.isEmpty());
- }
-
public void testDevRandomWorldReadableAndWritable() throws Exception {
File f = new File("/dev/random");
diff --git a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
index 54c2371..0197f5d 100644
--- a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
@@ -242,7 +242,11 @@
getContext().sendBroadcast(intent);
fail("SecurityException expected!");
} catch (SecurityException e) {}
-
+ try {
+ Intent intent = new Intent("android.provider.Telephony.SECRET_CODE");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
}
/**
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
index 198fe9f..eb394b3 100644
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -185,9 +185,6 @@
Log.d(LOG_TAG, "setUpClass()");
sInstrumentation = InstrumentationRegistry.getInstrumentation();
- assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_PRINTING));
-
sUiDevice = UiDevice.getInstance(sInstrumentation);
// Make sure we start with a clean slate.
@@ -240,6 +237,10 @@
public void setUp() throws Exception {
Log.d(LOG_TAG, "setUp()");
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_PRINTING));
+
// Initialize the latches.
Log.d(LOG_TAG, "init counters");
mCancelOperationCounter = new CallCounter();
@@ -289,16 +290,16 @@
clearPrintSpoolerData();
Log.d(LOG_TAG, "enable animations");
- if (sWindowAnimationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sWindowAnimationScaleBefore)) {
SystemUtil.runShellCommand(sInstrumentation,
"settings put global window_animation_scale " + sWindowAnimationScaleBefore);
}
- if (sTransitionAnimationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sTransitionAnimationScaleBefore)) {
SystemUtil.runShellCommand(sInstrumentation,
"settings put global transition_animation_scale " +
sTransitionAnimationScaleBefore);
}
- if (sAnimatiorDurationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sAnimatiorDurationScaleBefore)) {
SystemUtil.runShellCommand(sInstrumentation,
"settings put global animator_duration_scale " + sAnimatiorDurationScaleBefore);
}
diff --git a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
index 3b924a8..e431a6c 100644
--- a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
+++ b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
@@ -48,7 +48,7 @@
}
Intent intent = new Intent();
- intent.setAction("android.intent.action.stk.command");
+ intent.setAction("com.android.internal.stk.command");
intent.putExtra("STK CMD", "test");
ComponentName cn =
ComponentName.unflattenFromString("com.android.stk/com.android.stk.StkCmdReceiver");
@@ -56,11 +56,11 @@
try {
mContext.sendBroadcast(intent);
fail("Able to send broadcast which can be received by any app which has registered " +
- "broadcast for action 'android.intent.action.stk.command' since it is not " +
+ "broadcast for action 'com.android.internal.stk.command' since it is not " +
"protected with any permission. Device is vulnerable to CVE-2015-3843.");
} catch (SecurityException e) {
/* Pass the Test case: App should not be able to send broadcast using action
- * 'android.intent.action.stk.command' as it is protected by permission in
+ * 'com.android.internal.stk.command' as it is protected by permission in
* patched devices
*/
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
index 5ace9ff..ee1f901 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.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import java.util.ArrayList;
@@ -68,6 +69,23 @@
}
/**
+ * Tests {@link TelecomManager#getSelfManagedPhoneAccounts()} API to ensure it returns a list of
+ * the registered self-managed {@link android.telecom.PhoneAccount}s.
+ */
+ public void testTelecomManagerGetSelfManagedPhoneAccounts() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ List<PhoneAccountHandle> phoneAccountHandles =
+ mTelecomManager.getSelfManagedPhoneAccounts();
+
+ assertTrue(phoneAccountHandles.contains(TestUtils.TEST_SELF_MANAGED_HANDLE_1));
+ assertTrue(phoneAccountHandles.contains(TestUtils.TEST_SELF_MANAGED_HANDLE_2));
+ assertFalse(phoneAccountHandles.contains(TestUtils.TEST_PHONE_ACCOUNT_HANDLE));
+ }
+
+ /**
* Tests the ability to successfully register a self-managed
* {@link android.telecom.PhoneAccount}.
*/
@@ -348,14 +366,19 @@
waitOnAllHandlers(getInstrumentation());
}
- public void testEmergencyCallOngoing() throws Exception {
+ /**
+ * Disabled for now; there is not a reliable means of setting a phone number as a test emergency
+ * number.
+ * @throws Exception
+ */
+ public void DONOTtestEmergencyCallOngoing() throws Exception {
if (!mShouldTestTelecom) {
return;
}
+ // TODO: Need to find a reliable way to set a test emergency number.
// Set 555-1212 as a test emergency number.
- TestUtils.executeShellCommand(getInstrumentation(),
- "setprop ril.ecclist 5551212");
+ TestUtils.executeShellCommand(getInstrumentation(), "setprop ril.ecclist 5551212");
Bundle extras = new Bundle();
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
index 8ca7598..dfabe71 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
@@ -23,11 +23,15 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.Html;
+import android.text.Layout;
import android.text.SpanWatcher;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
+import android.text.method.SingleLineTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.text.style.AlignmentSpan;
import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
@@ -583,4 +587,27 @@
ParagraphStyle.class);
assertEquals(0, paragraphSpans.length);
}
+
+ @Test
+ public void testCopyConstructorDoesNotEnforceParagraphStyleConstraint() {
+ final SpannableStringBuilder original = new SpannableStringBuilder("\ntest data\nb");
+ final AlignmentSpan.Standard span = new AlignmentSpan.Standard(
+ Layout.Alignment.ALIGN_NORMAL);
+ original.setSpan(span, 1, original.length() - 1, Spanned.SPAN_PARAGRAPH);
+
+ // test that paragraph style is in the copied when it is valid
+ SpannableStringBuilder copied = new SpannableStringBuilder(original);
+ AlignmentSpan.Standard[] copiedSpans = copied.getSpans(0, copied.length(),
+ AlignmentSpan.Standard.class);
+
+ assertEquals(1, copiedSpans.length);
+
+ // test that paragraph style is in not in the copied when it is invalid
+ final TransformationMethod transformation = SingleLineTransformationMethod.getInstance();
+ final CharSequence transformed = transformation.getTransformation(original, null);
+ copied = new SpannableStringBuilder(transformed);
+ copiedSpans = copied.getSpans(0, copied.length(), AlignmentSpan.Standard.class);
+
+ assertEquals(0, copiedSpans.length);
+ }
}
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringTest.java b/tests/tests/text/src/android/text/cts/SpannableStringTest.java
index 81dbf9b..3771613 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringTest.java
@@ -16,19 +16,34 @@
package android.text.cts;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.fail;
+
import android.support.test.filters.SmallTest;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.Layout;
import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
import android.text.Spanned;
+import android.text.method.SingleLineTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.text.style.AlignmentSpan;
import android.text.style.LocaleSpan;
import android.text.style.QuoteSpan;
import android.text.style.UnderlineSpan;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.Locale;
-public class SpannableStringTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SpannableStringTest {
- @SmallTest
+ @Test
public void testConstructor() {
new SpannableString("test");
@@ -39,7 +54,7 @@
}
}
- @SmallTest
+ @Test
public void testValueOf() {
String text = "test valueOf";
SpannableString spannable = SpannableString.valueOf(text);
@@ -56,7 +71,7 @@
}
}
- @SmallTest
+ @Test
public void testSetSpan() {
String text = "hello, world";
SpannableString spannable = new SpannableString(text);
@@ -87,7 +102,7 @@
}
}
- @SmallTest
+ @Test
public void testRemoveSpan() {
SpannableString spannable = new SpannableString("hello, world");
@@ -111,7 +126,7 @@
assertEquals(0, spannable.getSpanFlags(underlineSpan));
}
- @SmallTest
+ @Test
public void testSubSequence() {
String text = "hello, world";
SpannableString spannable = new SpannableString(text);
@@ -135,7 +150,7 @@
}
}
- @SmallTest
+ @Test
public void testSubsequence_copiesSpans() {
SpannableString first = new SpannableString("t\nest data");
QuoteSpan quoteSpan = new QuoteSpan();
@@ -165,8 +180,7 @@
}
}
-
- @SmallTest
+ @Test
public void testCopyConstructor_copiesAllSpans() {
SpannableString first = new SpannableString("t\nest data");
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
@@ -189,7 +203,7 @@
}
}
- @SmallTest
+ @Test
public void testCopyGrowable() {
SpannableString first = new SpannableString("t\nest data");
final int N_SPANS = 127;
@@ -201,4 +215,27 @@
Object[] secondSpans = second.getSpans(0, second.length(), Object.class);
assertEquals(secondSpans.length, N_SPANS + 1);
}
+
+ @Test
+ public void testCopyConstructorDoesNotEnforceParagraphStyleConstraint() {
+ final SpannableStringBuilder original = new SpannableStringBuilder("\ntest data\nb");
+ final AlignmentSpan.Standard span = new AlignmentSpan.Standard(
+ Layout.Alignment.ALIGN_NORMAL);
+ original.setSpan(span, 1, original.length() - 1, Spanned.SPAN_PARAGRAPH);
+
+ // test that paragraph style is in the copied when it is valid
+ SpannableString copied = new SpannableString(original);
+ AlignmentSpan.Standard[] copiedSpans = copied.getSpans(0, copied.length(),
+ AlignmentSpan.Standard.class);
+
+ assertEquals(1, copiedSpans.length);
+
+ // test that paragraph style is in not in the copied when it is invalid
+ final TransformationMethod transformation = SingleLineTransformationMethod.getInstance();
+ final CharSequence transformed = transformation.getTransformation(original, null);
+ copied = new SpannableString(transformed);
+ copiedSpans = copied.getSpans(0, copied.length(), AlignmentSpan.Standard.class);
+
+ assertEquals(0, copiedSpans.length);
+ }
}
diff --git a/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
index d8342ca..16f632c 100644
--- a/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
@@ -111,16 +111,26 @@
@Test
public void testGetInputType() {
- final int expected = InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE;
+ // The "normal" input type that has been used consistently until Android O.
+ final int dateTimeType = InputType.TYPE_CLASS_DATETIME
+ | InputType.TYPE_DATETIME_VARIATION_DATE;
+ // Fallback for locales that need more characters.
+ final int textType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+ // Deprecated constructor that needs to preserve pre-existing behavior.
DateKeyListener dateKeyListener = new DateKeyListener();
- assertEquals(expected, dateKeyListener.getInputType());
+ assertEquals(dateTimeType, dateKeyListener.getInputType());
+ // TYPE_CLASS_DATETIME is fine for English locales.
dateKeyListener = new DateKeyListener(Locale.US);
- assertEquals(expected, dateKeyListener.getInputType());
+ assertEquals(dateTimeType, dateKeyListener.getInputType());
+ dateKeyListener = new DateKeyListener(Locale.UK);
+ assertEquals(dateTimeType, dateKeyListener.getInputType());
+ // Persian needs more characters then typically provided by datetime inputs, so it falls
+ // back on normal text.
dateKeyListener = new DateKeyListener(Locale.forLanguageTag("fa-IR"));
- assertEquals(expected, dateKeyListener.getInputType());
+ assertEquals(textType, dateKeyListener.getInputType());
}
/*
diff --git a/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
index 06bb68c..decd451 100644
--- a/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
@@ -114,17 +114,26 @@
@Test
public void testGetInputType() {
- final int expected = InputType.TYPE_CLASS_DATETIME
+ // The "normal" input type that has been used consistently until Android O.
+ final int dateTimeType = InputType.TYPE_CLASS_DATETIME
| InputType.TYPE_DATETIME_VARIATION_NORMAL;
+ // Fallback for locales that need more characters.
+ final int textType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+ // Deprecated constructor that needs to preserve pre-existing behavior.
DateTimeKeyListener listener = DateTimeKeyListener.getInstance();
- assertEquals(expected, listener.getInputType());
+ assertEquals(dateTimeType, listener.getInputType());
+ // TYPE_CLASS_DATETIME is fine for English locales.
listener = DateTimeKeyListener.getInstance(Locale.US);
- assertEquals(expected, listener.getInputType());
+ assertEquals(dateTimeType, listener.getInputType());
+ listener = DateTimeKeyListener.getInstance(Locale.UK);
+ assertEquals(dateTimeType, listener.getInputType());
+ // Persian needs more characters then typically provided by datetime inputs, so it falls
+ // back on normal text.
listener = DateTimeKeyListener.getInstance(Locale.forLanguageTag("fa-IR"));
- assertEquals(expected, listener.getInputType());
+ assertEquals(textType, listener.getInputType());
}
/*
diff --git a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
index 510d329..7854268 100644
--- a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
@@ -899,8 +899,9 @@
assertEquals(-1, acceptedChars.indexOf(usDecimalSeparator));
}
+ // Deprecated constructors that need to preserve pre-existing behavior.
@Test
- public void testGetInputType1() {
+ public void testGetInputType_deprecatedConstructors() {
DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(false, false);
int expected = InputType.TYPE_CLASS_NUMBER;
assertEquals(expected, digitsKeyListener.getInputType());
@@ -922,27 +923,57 @@
assertEquals(expected, digitsKeyListener.getInputType());
}
+ // Deprecated constructors that need to preserve pre-existing behavior.
@Test
- public void testGetInputType2() {
- final Locale irLocale = Locale.forLanguageTag("fa-IR");
- DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(irLocale, false, false);
+ public void testGetInputType_English() {
int expected = InputType.TYPE_CLASS_NUMBER;
+ DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(
+ Locale.US, false, false);
+ assertEquals(expected, digitsKeyListener.getInputType());
+ digitsKeyListener = DigitsKeyListener.getInstance(
+ Locale.UK, false, false);
assertEquals(expected, digitsKeyListener.getInputType());
- digitsKeyListener = DigitsKeyListener.getInstance(irLocale, true, false);
expected = InputType.TYPE_CLASS_NUMBER
| InputType.TYPE_NUMBER_FLAG_SIGNED;
+ digitsKeyListener = DigitsKeyListener.getInstance(Locale.US, true, false);
+ assertEquals(expected, digitsKeyListener.getInputType());
+ digitsKeyListener = DigitsKeyListener.getInstance(Locale.UK, true, false);
assertEquals(expected, digitsKeyListener.getInputType());
- digitsKeyListener = DigitsKeyListener.getInstance(irLocale, false, true);
expected = InputType.TYPE_CLASS_NUMBER
| InputType.TYPE_NUMBER_FLAG_DECIMAL;
+ digitsKeyListener = DigitsKeyListener.getInstance(Locale.US, false, true);
+ assertEquals(expected, digitsKeyListener.getInputType());
+ digitsKeyListener = DigitsKeyListener.getInstance(Locale.UK, false, true);
assertEquals(expected, digitsKeyListener.getInputType());
- digitsKeyListener = DigitsKeyListener.getInstance(irLocale, true, true);
expected = InputType.TYPE_CLASS_NUMBER
| InputType.TYPE_NUMBER_FLAG_SIGNED
| InputType.TYPE_NUMBER_FLAG_DECIMAL;
+ digitsKeyListener = DigitsKeyListener.getInstance(Locale.US, true, true);
+ assertEquals(expected, digitsKeyListener.getInputType());
+ digitsKeyListener = DigitsKeyListener.getInstance(Locale.UK, true, true);
+ assertEquals(expected, digitsKeyListener.getInputType());
+ }
+
+ // Persian needs more characters then typically provided by datetime inputs, so it falls
+ // back on normal text.
+ @Test
+ public void testGetInputType_Persian() {
+ final Locale irLocale = Locale.forLanguageTag("fa-IR");
+ final int expected = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+
+ DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(irLocale, false, false);
+ assertEquals(expected, digitsKeyListener.getInputType());
+
+ digitsKeyListener = DigitsKeyListener.getInstance(irLocale, true, false);
+ assertEquals(expected, digitsKeyListener.getInputType());
+
+ digitsKeyListener = DigitsKeyListener.getInstance(irLocale, false, true);
+ assertEquals(expected, digitsKeyListener.getInputType());
+
+ digitsKeyListener = DigitsKeyListener.getInstance(irLocale, true, true);
assertEquals(expected, digitsKeyListener.getInputType());
}
diff --git a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
index 3ed0ad4..bb4ffb6 100644
--- a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
@@ -17,13 +17,11 @@
package android.text.method.cts;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.reset;
@@ -221,23 +219,6 @@
assertSame(method0, method1);
}
- @Test
- public void testOnFocusChanged() {
- // lose focus
- reset(mMethod);
- assertTrue(mEditText.isFocused());
- CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, "DPAD_DOWN");
- assertFalse(mEditText.isFocused());
- verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
-
- // gain focus
- reset(mMethod);
- assertFalse(mEditText.isFocused());
- CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, "DPAD_UP");
- assertTrue(mEditText.isFocused());
- verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
- }
-
private void savePasswordPref() {
try {
mPasswordPrefBackUp = System.getInt(mActivity.getContentResolver(),
diff --git a/tests/tests/text/src/android/text/method/cts/SingleLineTransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/SingleLineTransformationMethodTest.java
index 5db4a6f..ad622d0 100644
--- a/tests/tests/text/src/android/text/method/cts/SingleLineTransformationMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/SingleLineTransformationMethodTest.java
@@ -25,7 +25,11 @@
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.text.Layout;
+import android.text.SpannableString;
+import android.text.Spanned;
import android.text.method.SingleLineTransformationMethod;
+import android.text.style.AlignmentSpan;
import android.util.TypedValue;
import android.widget.EditText;
@@ -72,6 +76,21 @@
// TODO cannot get transformed text from the view
}
+ @Test
+ public void testSubsequence_doesNotThrowExceptionWithParagraphSpans() {
+ final SingleLineTransformationMethod method = SingleLineTransformationMethod.getInstance();
+ final SpannableString original = new SpannableString("\ntest data\nb");
+ final AlignmentSpan.Standard span = new AlignmentSpan.Standard(
+ Layout.Alignment.ALIGN_NORMAL);
+ original.setSpan(span, 1, original.length() - 1, Spanned.SPAN_PARAGRAPH);
+
+ final CharSequence transformed = method.getTransformation(original, null);
+ // expectation: should not throw an exception
+ transformed.subSequence(0, transformed.length());
+ }
+
+
+
private static class MySingleLineTranformationMethod extends SingleLineTransformationMethod {
@Override
protected char[] getOriginal() {
diff --git a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
index 68d410d..a887a19 100644
--- a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
@@ -110,17 +110,26 @@
@Test
public void testGetInputType() {
- final int expected = InputType.TYPE_CLASS_DATETIME
+ // The "normal" input type that has been used consistently until Android O.
+ final int dateTimeType = InputType.TYPE_CLASS_DATETIME
| InputType.TYPE_DATETIME_VARIATION_TIME;
+ // Fallback for locales that need more characters.
+ final int textType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+ // Deprecated constructor that needs to preserve pre-existing behavior.
TimeKeyListener listener = TimeKeyListener.getInstance();
- assertEquals(expected, listener.getInputType());
+ assertEquals(dateTimeType, listener.getInputType());
+ // TYPE_CLASS_DATETIME is fine for English locales.
listener = TimeKeyListener.getInstance(Locale.US);
- assertEquals(expected, listener.getInputType());
+ assertEquals(dateTimeType, listener.getInputType());
+ listener = TimeKeyListener.getInstance(Locale.UK);
+ assertEquals(dateTimeType, listener.getInputType());
+ // Persian needs more characters then typically provided by datetime inputs, so it falls
+ // back on normal text.
listener = TimeKeyListener.getInstance(Locale.forLanguageTag("fa-IR"));
- assertEquals(expected, listener.getInputType());
+ assertEquals(textType, listener.getInputType());
}
/*
diff --git a/tests/tests/text/src/android/text/method/cts/TransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/TransformationMethodTest.java
new file mode 100644
index 0000000..d67fc59
--- /dev/null
+++ b/tests/tests/text/src/android/text/method/cts/TransformationMethodTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.method.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.returnsFirstArg;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.method.TransformationMethod;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test that {@link TransformationMethod} interface gets called.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class TransformationMethodTest {
+ private static final int EDIT_TXT_ID = 1;
+
+ private Instrumentation mInstrumentation;
+ private CtsActivity mActivity;
+ private TransformationMethod mMethod;
+ private EditText mEditText;
+ private Button mButton;
+
+ @Rule
+ public ActivityTestRule<CtsActivity> mActivityRule = new ActivityTestRule<>(CtsActivity.class);
+
+ @Before
+ public void setup() throws Throwable {
+ mActivity = mActivityRule.getActivity();
+ PollingCheck.waitFor(1000, mActivity::hasWindowFocus);
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mMethod = mock(TransformationMethod.class);
+ when(mMethod.getTransformation(any(), any())).then(returnsFirstArg());
+
+ mActivityRule.runOnUiThread(() -> {
+ mEditText = new EditTextNoIme(mActivity);
+ mEditText.setId(EDIT_TXT_ID);
+ mEditText.setTransformationMethod(mMethod);
+ mButton = new Button(mActivity);
+ mButton.setFocusableInTouchMode(true);
+ LinearLayout layout = new LinearLayout(mActivity);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(mEditText, new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ layout.addView(mButton, new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ mActivity.setContentView(layout);
+ mEditText.requestFocus();
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertTrue(mEditText.isFocused());
+ }
+
+ @Test
+ public void testGetTransformation() throws Throwable {
+ reset(mMethod);
+ when(mMethod.getTransformation(any(), any())).then(returnsFirstArg());
+ mActivityRule.runOnUiThread(() -> mEditText.setText("some text"));
+ mInstrumentation.waitForIdleSync();
+ verify(mMethod, atLeastOnce()).getTransformation(any(), any());
+ }
+
+ @Test
+ public void testOnFocusChanged() throws Throwable {
+ // lose focus
+ reset(mMethod);
+ assertTrue(mEditText.isFocused());
+ mActivityRule.runOnUiThread(() -> mButton.requestFocus());
+ mInstrumentation.waitForIdleSync();
+ verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
+
+ // gain focus
+ reset(mMethod);
+ assertFalse(mEditText.isFocused());
+ mActivityRule.runOnUiThread(() -> mEditText.requestFocus());
+ mInstrumentation.waitForIdleSync();
+ assertTrue(mEditText.isFocused());
+ verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
+ }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/ChangeBoundsTest.java b/tests/tests/transition/src/android/transition/cts/ChangeBoundsTest.java
index 0a14361..4b55057 100644
--- a/tests/tests/transition/src/android/transition/cts/ChangeBoundsTest.java
+++ b/tests/tests/transition/src/android/transition/cts/ChangeBoundsTest.java
@@ -17,19 +17,24 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import android.animation.Animator;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.transition.ChangeBounds;
import android.transition.Transition;
+import android.transition.TransitionValues;
import android.util.TypedValue;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.LinearInterpolator;
import org.junit.Before;
import org.junit.Test;
@@ -43,19 +48,22 @@
private static final int SMALL_OFFSET_DP = 2;
ChangeBounds mChangeBounds;
+ ValidateBoundsListener mBoundsChangeListener;
@Override
@Before
public void setup() {
super.setup();
resetChangeBoundsTransition();
+ mBoundsChangeListener = null;
}
private void resetChangeBoundsTransition() {
mListener = mock(Transition.TransitionListener.class);
- mChangeBounds = new ChangeBounds();
+ mChangeBounds = new MyChangeBounds();
mChangeBounds.setDuration(400);
mChangeBounds.addListener(mListener);
+ mChangeBounds.setInterpolator(new LinearInterpolator());
mTransition = mChangeBounds;
}
@@ -65,11 +73,10 @@
validateInScene1();
- startTransition(R.layout.scene6);
+ mBoundsChangeListener = new ValidateBoundsListener(true);
- // now delay for at least a few frames before checking intermediate values:
- Thread.sleep(150);
- validateNormalIntermediate();
+ startTransition(R.layout.scene6);
+ // The update listener will validate that it is changing throughout the animation
waitForEnd(400);
validateInScene6();
@@ -84,11 +91,11 @@
validateInScene1();
+ mBoundsChangeListener = new ValidateBoundsListener(true);
+
startTransition(R.layout.scene6);
- // now delay for at least a few frames before checking intermediate values:
- Thread.sleep(150);
- validateClippedIntermediate();
+ // The update listener will validate that it is changing throughout the animation
waitForEnd(400);
validateInScene6();
@@ -101,11 +108,10 @@
validateInScene6();
+ mBoundsChangeListener = new ValidateBoundsListener(false);
startTransition(R.layout.scene1);
- // now delay for at least a few frames before checking intermediate values:
- Thread.sleep(150);
- validateClippedIntermediate();
+ // The update listener will validate that it is changing throughout the animation
waitForEnd(400);
validateInScene1();
@@ -252,63 +258,6 @@
assertNull(belowSquare.getClipBounds());
}
- private void validateIntermediatePosition() {
- Resources resources = mActivity.getResources();
- float smallDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- SMALL_SQUARE_SIZE_DP, resources.getDisplayMetrics());
- float largeDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- LARGE_SQUARE_SIZE_DP, resources.getDisplayMetrics());
-
- View redSquare = mActivity.findViewById(R.id.redSquare);
- View greenSquare = mActivity.findViewById(R.id.greenSquare);
- assertTrue(redSquare.getTop() != 0);
- assertTrue(greenSquare.getTop() != 0);
- assertNotWithinAPixel(smallDim, redSquare.getTop());
- assertNotWithinAPixel(largeDim, redSquare.getTop());
- assertNotWithinAPixel(smallDim, greenSquare.getTop());
- assertNotWithinAPixel(largeDim, greenSquare.getTop());
- }
-
- private void validateClippedIntermediate() {
- validateIntermediatePosition();
- Resources resources = mActivity.getResources();
- float largeDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- LARGE_SQUARE_SIZE_DP, resources.getDisplayMetrics());
- View redSquare = mActivity.findViewById(R.id.redSquare);
- View greenSquare = mActivity.findViewById(R.id.greenSquare);
-
- assertWithinAPixel(largeDim, redSquare.getWidth());
- assertWithinAPixel(largeDim, redSquare.getHeight());
- assertWithinAPixel(largeDim, greenSquare.getWidth());
- assertWithinAPixel(largeDim, greenSquare.getHeight());
-
- assertNotNull(redSquare.getClipBounds());
- assertNotNull(greenSquare.getClipBounds());
- }
-
- private void validateNormalIntermediate() {
- validateIntermediatePosition();
- Resources resources = mActivity.getResources();
- float smallDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- SMALL_SQUARE_SIZE_DP, resources.getDisplayMetrics());
- float largeDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- LARGE_SQUARE_SIZE_DP, resources.getDisplayMetrics());
- View redSquare = mActivity.findViewById(R.id.redSquare);
- View greenSquare = mActivity.findViewById(R.id.greenSquare);
- assertNotWithinAPixel(smallDim, redSquare.getWidth());
- assertNotWithinAPixel(smallDim, redSquare.getHeight());
- assertNotWithinAPixel(largeDim, redSquare.getWidth());
- assertNotWithinAPixel(largeDim, redSquare.getHeight());
-
- assertNotWithinAPixel(smallDim, greenSquare.getWidth());
- assertNotWithinAPixel(smallDim, greenSquare.getHeight());
- assertNotWithinAPixel(largeDim, greenSquare.getWidth());
- assertNotWithinAPixel(largeDim, greenSquare.getHeight());
-
- assertNull(redSquare.getClipBounds());
- assertNull(greenSquare.getClipBounds());
- }
-
private static boolean isWithinAPixel(float expectedDim, int dim) {
return (Math.abs(dim - expectedDim) <= 1);
}
@@ -322,5 +271,114 @@
assertTrue("Expected dimension to not be within one pixel of "
+ expectedDim + ", but was " + dim, !isWithinAPixel(expectedDim, dim));
}
+
+ private class MyChangeBounds extends ChangeBounds {
+ private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
+ if (animator != null && mBoundsChangeListener != null) {
+ animator.addListener(mBoundsChangeListener);
+ Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ }
+ return animator;
+ }
+ }
+
+ private class ValidateBoundsListener implements ViewTreeObserver.OnDrawListener,
+ Animator.AnimatorListener {
+ final boolean mGrow;
+ final int mMin;
+ final int mMax;
+
+ final Point mRedDimensions = new Point(-1, -1);
+ final Point mGreenDimensions = new Point(-1, -1);
+
+ View mRedSquare;
+ View mGreenSquare;
+
+ boolean mDidChangeSize;
+
+ private ValidateBoundsListener(boolean grow) {
+ mGrow = grow;
+
+ Resources resources = mActivity.getResources();
+ mMin = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ SMALL_SQUARE_SIZE_DP, resources.getDisplayMetrics()));
+ mMax = (int) Math.ceil(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ LARGE_SQUARE_SIZE_DP, resources.getDisplayMetrics()));
+ }
+
+ public void validateView(View view, Point dimensions) {
+ final String name = view.getTransitionName();
+ final boolean clipped = mChangeBounds.getResizeClip();
+ assertEquals(clipped, view.getClipBounds() != null);
+
+ final int width;
+ final int height;
+ if (clipped) {
+ width = view.getClipBounds().width();
+ height = view.getClipBounds().height();
+ } else {
+ width = view.getWidth();
+ height = view.getHeight();
+ }
+ validateDim(name, "width", dimensions.x, width);
+ validateDim(name, "height", dimensions.y, height);
+ dimensions.set(width, height);
+ }
+
+ private void validateDim(String name, String dimen, int lastDim, int newDim) {
+ if (lastDim != -1) {
+ if (mGrow) {
+ assertTrue(name + " new " + dimen + " " + newDim
+ + " is less than previous " + lastDim,
+ newDim >= lastDim);
+ } else {
+ assertTrue(name + " new " + dimen + " " + newDim
+ + " is more than previous " + lastDim,
+ newDim <= lastDim);
+ }
+ if (newDim != lastDim) {
+ mDidChangeSize = true;
+ }
+ }
+ assertTrue(name + " " + dimen + " " + newDim + " must be <= " + mMax,
+ newDim <= mMax);
+ assertTrue(name + " " + dimen + " " + newDim + " must be >= " + mMin,
+ newDim >= mMin);
+ }
+
+ @Override
+ public void onDraw() {
+ if (mRedSquare == null) {
+ mRedSquare = mActivity.findViewById(R.id.redSquare);
+ mGreenSquare = mActivity.findViewById(R.id.greenSquare);
+ }
+ validateView(mRedSquare, mRedDimensions);
+ validateView(mGreenSquare, mGreenDimensions);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mActivity.getWindow().getDecorView().getViewTreeObserver().addOnDrawListener(this);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mActivity.getWindow().getDecorView().getViewTreeObserver().removeOnDrawListener(this);
+ assertTrue(mDidChangeSize);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ }
}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index e42b33f..0b28d05 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -159,6 +159,9 @@
TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
"US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
values.put(Programs.COLUMN_CONTENT_RATING, rating.flattenToString());
+ values.put(Programs.COLUMN_REVIEW_RATING_STYLE,
+ RecordedPrograms.REVIEW_RATING_STYLE_STARS);
+ values.put(Programs.COLUMN_REVIEW_RATING, "4.5");
return values;
}
@@ -256,6 +259,9 @@
values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2, 3);
values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3, 2);
values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4, 1);
+ values.put(RecordedPrograms.COLUMN_REVIEW_RATING_STYLE,
+ RecordedPrograms.REVIEW_RATING_STYLE_STARS);
+ values.put(RecordedPrograms.COLUMN_REVIEW_RATING, "4.5");
return values;
}
@@ -547,6 +553,8 @@
verifyStringColumn(cursor, expectedValues, Programs.COLUMN_THUMBNAIL_URI);
verifyBlobColumn(cursor, expectedValues, Programs.COLUMN_INTERNAL_PROVIDER_DATA);
verifyIntegerColumn(cursor, expectedValues, Programs.COLUMN_VERSION_NUMBER);
+ verifyStringColumn(cursor, expectedValues, Programs.COLUMN_REVIEW_RATING_STYLE);
+ verifyStringColumn(cursor, expectedValues, Programs.COLUMN_REVIEW_RATING);
}
}
@@ -991,6 +999,8 @@
verifyIntegerColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4);
verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_VERSION_NUMBER);
+ verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_REVIEW_RATING_STYLE);
+ verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_REVIEW_RATING);
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
index f66f5f4..c5aa1b3 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
@@ -81,7 +81,7 @@
new Point[] {
point(0, 0), point(48, 0), point(32, 40), point(0, 40), point(0, 56)
},
- new int[] { 0xff75fb4c, 0xff75fb4c, 0xff75fb4c, 0xffffffff, 0xffba7d26 }
+ new int[] { 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xffffffff, 0xff7f7f00 }
));
}
@@ -114,7 +114,7 @@
new Point[] {
point(0, 0), point(48, 0), point(32, 40), point(0, 40), point(0, 56)
},
- new int[] { 0xffc07f2c, 0xffc07f2c, 0xffc07f2c, 0xffffffff, 0xffe03f16 }
+ new int[] { 0xffff7f00, 0xffff7f00, 0xffff7f00, 0xffffffff, 0xffff3f00 }
));
}
diff --git a/tests/tests/view/src/android/view/cts/MenuItemTest.java b/tests/tests/view/src/android/view/cts/MenuItemTest.java
index fc0ae0b..c9d362c 100644
--- a/tests/tests/view/src/android/view/cts/MenuItemTest.java
+++ b/tests/tests/view/src/android/view/cts/MenuItemTest.java
@@ -60,7 +60,7 @@
}
@Test
- public void testAccessThumbTint() {
+ public void testAccessIconTint() {
// Note that this test is not marked as @UiThreadTest. Updating MenuItem does not
// immediately update the displayed content, and even though the getters are expected
// to immediately return the just-set value, using instrumentation to wait for the
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 03b8994..ca39aab 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -73,7 +73,6 @@
import android.util.Xml;
import android.view.ActionMode;
import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
@@ -1089,7 +1088,7 @@
assertTrue(view.hasCalledOnCreateContextMenu());
assertTrue(mMockParent.hasCreateContextMenu());
verify(listener, times(1)).onCreateContextMenu(
- eq(contextMenu), eq(view), any(ContextMenuInfo.class));
+ eq(contextMenu), eq(view), any());
}
@Test(expected=NullPointerException.class)
diff --git a/tests/tests/widget/res/layout/textview_layout.xml b/tests/tests/widget/res/layout/textview_layout.xml
index 70fbd73..93f4846 100644
--- a/tests/tests/widget/res/layout/textview_layout.xml
+++ b/tests/tests/widget/res/layout/textview_layout.xml
@@ -282,6 +282,13 @@
android:autoSizeStepGranularity="1px" />
<TextView
+ android:id="@+id/textview_autosize_basic"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/text_view_hello"
+ android:autoSizeTextType="uniform" />
+
+ <TextView
android:id="@+id/textview_fontresource_fontfamily"
android:text="@string/text_view_hello"
android:layout_width="wrap_content"
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index f7fc033..4e33156 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -270,7 +270,7 @@
verifyZeroInteractions(mockClickListener);
assertTrue(mAdapterView.performItemClick(null, 0, 0));
- verify(mockClickListener, times(1)).onItemClick(eq(mAdapterView), any(View.class),
+ verify(mockClickListener, times(1)).onItemClick(eq(mAdapterView), any(),
eq(0), eq(0L));
setArrayAdapter(mAdapterView);
diff --git a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
index 84c60c8..7595c98 100644
--- a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.database.DataSetObserver;
import android.support.test.annotation.UiThreadTest;
@@ -411,7 +412,7 @@
ArrayAdapter.createFromResource(null, R.array.string, R.layout.simple_spinner_item);
}
- @Test(expected=NullPointerException.class)
+ @Test(expected=Resources.NotFoundException.class)
public void testCreateFromResourceWithInvalidId() {
ArrayAdapter.createFromResource(mContext, INVALID_ID, R.layout.simple_spinner_item);
}
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java b/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
index 796073f..1482cae 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
@@ -164,7 +164,7 @@
mExpandableListView.setOnItemClickListener(mockOnItemClickListener);
assertTrue(mExpandableListView.performItemClick(null, 100, 99));
verify(mockOnItemClickListener, times(1)).onItemClick(eq(mExpandableListView),
- any(View.class), eq(100), eq(99L));
+ any(), eq(100), eq(99L));
}
@Test
@@ -309,7 +309,7 @@
mExpandableListView.performItemClick(null, 0, 0);
verify(mockOnGroupClickListener, times(1)).onGroupClick(eq(mExpandableListView),
- any(View.class), eq(0), eq(0L));
+ any(), eq(0), eq(0L));
}
@UiThreadTest
@@ -328,7 +328,7 @@
// click on the child list of the first group
mExpandableListView.performItemClick(null, 1, 0);
verify(mockOnChildClickListener, times(1)).onChildClick(eq(mExpandableListView),
- any(View.class), eq(0), eq(0), eq(0L));
+ any(), eq(0), eq(0), eq(0L));
}
@UiThreadTest
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
index 334a1f6..70f9661 100644
--- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -504,7 +504,7 @@
mInstrumentation.waitForIdleSync();
verify(mPopupWindowBuilder.mOnItemClickListener, times(1)).onItemClick(
- any(AdapterView.class), any(View.class), eq(1), eq(1L));
+ any(AdapterView.class), any(), eq(1), eq(1L));
// Also verify that the popup window has been dismissed
assertFalse(mPopupWindow.isShowing());
verify(mPopupWindowBuilder.mOnDismissListener, times(2)).onDismiss();
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 86f9877..3da2cb4 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -63,6 +63,7 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.fonts.FontVariationAxis;
+import android.icu.lang.UCharacter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -114,7 +115,6 @@
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -3038,7 +3038,7 @@
((ContextMenu) invocation.getArguments() [0]).add("menu item");
return null;
}).when(mockOnCreateContextMenuListener).onCreateContextMenu(
- any(ContextMenu.class), any(View.class), any(ContextMenuInfo.class));
+ any(ContextMenu.class), any(View.class), any());
mTextView.setOnLongClickListener(mockOnLongClickListener);
mTextView.setOnCreateContextMenuListener(mockOnCreateContextMenuListener);
@@ -3051,16 +3051,16 @@
assertTrue(mTextView.performLongClick());
verify(mockOnLongClickListener, times(1)).onLongClick(mTextView);
verify(mockOnCreateContextMenuListener, times(1)).onCreateContextMenu(
- any(ContextMenu.class), eq(mTextView), any(ContextMenuInfo.class));
+ any(ContextMenu.class), eq(mTextView), any());
reset(mockOnCreateContextMenuListener);
mTextView.setOnLongClickListener(null);
doNothing().when(mockOnCreateContextMenuListener).onCreateContextMenu(
- any(ContextMenu.class), any(View.class), any(ContextMenuInfo.class));
+ any(ContextMenu.class), any(View.class), any());
assertFalse(mTextView.performLongClick());
verifyNoMoreInteractions(mockOnLongClickListener);
verify(mockOnCreateContextMenuListener, times(1)).onCreateContextMenu(
- any(ContextMenu.class), eq(mTextView), any(ContextMenuInfo.class));
+ any(ContextMenu.class), eq(mTextView), any());
}
@UiThreadTest
@@ -3370,9 +3370,22 @@
// The default variation settings should be null.
assertNull(mTextView.getFontVariationSettings());
- final String[] nonEffectiveSettings = {
+ final String[] invalidFormatSettings = {
"invalid syntax",
"'aaa' 1.0", // tag is not 4 ascii chars
+ };
+ for (String settings : invalidFormatSettings) {
+ try {
+ mTextView.setFontVariationSettings(settings);
+ fail();
+ } catch (FontVariationAxis.InvalidFormatException e) {
+ // pass.
+ }
+ assertNull("Must not change settings for " + settings,
+ mTextView.getFontVariationSettings());
+ }
+
+ final String[] nonEffectiveSettings = {
"'bbbb' 1.0", // unsupported tag
"' ' 1.0", // unsupported tag
"'AAAA' 0.7", // unsupported tag (case sensitive)
@@ -4532,7 +4545,8 @@
assertEquals(InputType.TYPE_CLASS_NUMBER
| InputType.TYPE_NUMBER_FLAG_DECIMAL
| InputType.TYPE_NUMBER_FLAG_SIGNED, mTextView.getInputType());
- assertSame(mTextView.getKeyListener(), DigitsKeyListener.getInstance(true, true));
+ assertSame(mTextView.getKeyListener(),
+ DigitsKeyListener.getInstance(mTextView.getTextLocale(), true, true));
mTextView.setInputType(InputType.TYPE_CLASS_PHONE);
assertEquals(InputType.TYPE_CLASS_PHONE, mTextView.getInputType());
@@ -5392,28 +5406,75 @@
@UiThreadTest
@Test
- public void testAllCapsLocalization() {
- String testString = "abcdefghijklmnopqrstuvwxyz";
+ public void testAllCaps_Localization() {
+ final String testString = "abcdefghijklmnopqrstuvwxyz i\u0307\u0301 άέήίΐόύΰώάυ ή";
- // The capitalized characters of "i" on Turkish and Azerbaijani are different from English.
- Locale[] testLocales = {
- new Locale("az", "AZ"),
- new Locale("tr", "TR"),
- new Locale("en", "US"),
+ // Capital "i" in Turkish and Azerbaijani is different from English, Lithuanian has special
+ // rules for uppercasing dotted i with accents, and Greek has complex capitalization rules.
+ final Locale[] testLocales = {
+ new Locale("az", "AZ"), // Azerbaijani
+ new Locale("tr", "TR"), // Turkish
+ new Locale("lt", "LT"), // Lithuanian
+ new Locale("el", "GR"), // Greek
+ Locale.US,
};
- TextView tv = new TextView(mActivity);
+ final TextView tv = new TextView(mActivity);
tv.setAllCaps(true);
for (Locale locale: testLocales) {
tv.setTextLocale(locale);
assertEquals("Locale: " + locale.getDisplayName(),
- testString.toUpperCase(locale),
+ UCharacter.toUpperCase(locale, testString),
tv.getTransformationMethod().getTransformation(testString, tv).toString());
}
}
@UiThreadTest
@Test
+ public void testAllCaps_SpansArePreserved() {
+ final Locale greek = new Locale("el", "GR");
+ final String lowerString = "ι\u0301ριδα"; // ίριδα with first letter decomposed
+ final String upperString = "ΙΡΙΔΑ"; // uppercased
+ // expected lowercase to uppercase index map
+ final int[] indexMap = {0, 1, 1, 2, 3, 4, 5};
+ final int flags = Spanned.SPAN_INCLUSIVE_INCLUSIVE;
+
+ final TextView tv = new TextView(mActivity);
+ tv.setTextLocale(greek);
+ tv.setAllCaps(true);
+
+ final Spannable source = new SpannableString(lowerString);
+ source.setSpan(new Object(), 0, 1, flags);
+ source.setSpan(new Object(), 1, 2, flags);
+ source.setSpan(new Object(), 2, 3, flags);
+ source.setSpan(new Object(), 3, 4, flags);
+ source.setSpan(new Object(), 4, 5, flags);
+ source.setSpan(new Object(), 5, 6, flags);
+ source.setSpan(new Object(), 0, 2, flags);
+ source.setSpan(new Object(), 1, 3, flags);
+ source.setSpan(new Object(), 2, 4, flags);
+ source.setSpan(new Object(), 0, 6, flags);
+ final Object[] sourceSpans = source.getSpans(0, source.length(), Object.class);
+
+ final CharSequence transformed =
+ tv.getTransformationMethod().getTransformation(source, tv);
+ assertTrue(transformed instanceof Spanned);
+ final Spanned result = (Spanned) transformed;
+
+ assertEquals(upperString, transformed.toString());
+ final Object[] resultSpans = result.getSpans(0, result.length(), Object.class);
+ assertEquals(sourceSpans.length, resultSpans.length);
+ for (int i = 0; i < sourceSpans.length; i++) {
+ assertSame(sourceSpans[i], resultSpans[i]);
+ final Object span = sourceSpans[i];
+ assertEquals(indexMap[source.getSpanStart(span)], result.getSpanStart(span));
+ assertEquals(indexMap[source.getSpanEnd(span)], result.getSpanEnd(span));
+ assertEquals(source.getSpanFlags(span), result.getSpanFlags(span));
+ }
+ }
+
+ @UiThreadTest
+ @Test
public void testTextAlignmentDefault() {
TextView tv = new TextView(mActivity);
assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getRawTextAlignment());
@@ -6387,6 +6448,7 @@
final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
mActivityRule.runOnUiThread(() ->
textView.setEllipsize(newEllipsizeValue));
+ mInstrumentation.waitForIdleSync();
assertEquals(newEllipsizeValue, textView.getEllipsize());
assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType());
assertEquals(-1, textView.getAutoSizeMinTextSize());
@@ -6396,6 +6458,7 @@
mActivityRule.runOnUiThread(() ->
textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM));
+ mInstrumentation.waitForIdleSync();
assertEquals(newEllipsizeValue, textView.getEllipsize());
assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType());
// The auto-size defaults have been used.
@@ -6713,6 +6776,19 @@
}
@Test
+ public void testAutoSizeCallers_setHeightForOneLineText() throws Throwable {
+ final TextView autoSizeTextView = (TextView) mActivity.findViewById(
+ R.id.textview_autosize_basic);
+ assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, autoSizeTextView.getAutoSizeTextType());
+ final float initialTextSize = autoSizeTextView.getTextSize();
+ mActivityRule.runOnUiThread(() -> autoSizeTextView.setHeight(
+ autoSizeTextView.getHeight() * 3));
+ mInstrumentation.waitForIdleSync();
+
+ assertTrue(autoSizeTextView.getTextSize() > initialTextSize);
+ }
+
+ @Test
public void testAutoSizeUniform_obtainStyledAttributes() {
DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics();
TextView autoSizeTextViewUniform = (TextView) mActivity.findViewById(
diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed
index 86f2fe3..15249c3 100755
--- a/tools/cts-tradefed/etc/cts-tradefed
+++ b/tools/cts-tradefed/etc/cts-tradefed
@@ -121,5 +121,5 @@
JAR_PATH=${JAR_PATH}:$j
done
-java $RDBG_FLAG -XX:+HeapDumpOnOutOfMemoryError -cp ${JAR_PATH} -DCTS_ROOT=${CTS_ROOT} com.android.compatibility.common.tradefed.command.CompatibilityConsole "$@"
+java $RDBG_FLAG -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -cp ${JAR_PATH} -DCTS_ROOT=${CTS_ROOT} com.android.compatibility.common.tradefed.command.CompatibilityConsole "$@"
diff --git a/tools/cts-tradefed/res/config/cts-dev.xml b/tools/cts-tradefed/res/config/cts-dev.xml
index 97c3cdd..4ebda1b 100644
--- a/tools/cts-tradefed/res/config/cts-dev.xml
+++ b/tools/cts-tradefed/res/config/cts-dev.xml
@@ -20,6 +20,7 @@
<option name="log-level" value="verbose" />
<option name="skip-preconditions" value="true" />
<option name="skip-device-info" value="true" />
+ <option name="result-reporter:compress-logs" value="false" />
<option name="plan" value="cts-dev" />
<option name="compatibility:skip-all-system-status-check" value="true" />
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 4c0d1af..769336d 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -186,4 +186,9 @@
<option name="compatibility:exclude-filter" value="CtsSensorTestCases android.hardware.cts.SensorDirectReportTest#testGyroscopeHardwareBufferVeryFast" />
<option name="compatibility:exclude-filter" value="CtsSensorTestCases android.hardware.cts.SensorDirectReportTest#testMagneticFieldHardwareBufferVeryFast" />
+ <!-- b/37216168 -->
+ <option name="compatibility:exclude-filter" value="CtsNativeHardwareTestCases AHardwareBufferTest#AHardwareBuffer_SendAndRecv_Succeeds" />
+
+ <!-- b/37108688 -->
+ <option name="compatibility:exclude-filter" value="CtsHardwareTestCases android.hardware.cts.HardwareBufferTest#testCreate" />
</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-vendor-interface.xml b/tools/cts-tradefed/res/config/cts-vendor-interface.xml
index 5f38264..582cebf 100644
--- a/tools/cts-tradefed/res/config/cts-vendor-interface.xml
+++ b/tools/cts-tradefed/res/config/cts-vendor-interface.xml
@@ -48,6 +48,7 @@
<option name="compatibility:include-filter" value="CtsPrintTestCases" />
<option name="compatibility:include-filter" value="CtsProviderTestCases" />
<option name="compatibility:include-filter" value="CtsRsBlasTestCases" />
+ <option name="compatibility:include-filter" value="CtsSecurityHostTestCases" />
<option name="compatibility:include-filter" value="CtsSecurityTestCases" />
<option name="compatibility:include-filter" value="CtsSensorTestCases" />
<option name="compatibility:include-filter" value="CtsShortcutHostTestCases" />
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
index 31ee446..7e9c304 100644
--- a/tools/selinux/SELinuxNeverallowTestFrame.py
+++ b/tools/selinux/SELinuxNeverallowTestFrame.py
@@ -73,6 +73,10 @@
devicePolicyFile.deleteOnExit();
mDevice.pullFile("/sys/fs/selinux/policy", devicePolicyFile);
}
+
+ private boolean isFullTrebleDevice() throws Exception {
+ return android.security.cts.SELinuxHostTest.isFullTrebleDevice(mDevice);
+ }
"""
src_body = ""
src_footer = """}
@@ -82,6 +86,12 @@
@RestrictedBuildTest
public void testNeverallowRules() throws Exception {
String neverallowRule = "$NEVERALLOW_RULE_HERE$";
+ boolean fullTrebleOnly = $FULL_TREBLE_ONLY_BOOL_HERE$;
+
+ if ((fullTrebleOnly) && (!isFullTrebleDevice())) {
+ // This test applies only to Treble devices but this device isn't one
+ return;
+ }
/* run sepolicy-analyze neverallow check on policy file using given neverallow rules */
ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
diff --git a/tools/selinux/SELinuxNeverallowTestGen.py b/tools/selinux/SELinuxNeverallowTestGen.py
index bc775d6..e74ba78 100755
--- a/tools/selinux/SELinuxNeverallowTestGen.py
+++ b/tools/selinux/SELinuxNeverallowTestGen.py
@@ -4,35 +4,82 @@
import sys
import SELinuxNeverallowTestFrame
-usage = "Usage: ./gen_SELinux_CTS_neverallows.py <input policy file> <output cts java source>"
+usage = "Usage: ./SELinuxNeverallowTestGen.py <input policy file> <output cts java source>"
+
+
+class NeverallowRule:
+ statement = ''
+ treble_only = False
+
+ def __init__(self, statement):
+ self.statement = statement
+ self.treble_only = False
+
# extract_neverallow_rules - takes an intermediate policy file and pulls out the
# neverallow rules by taking all of the non-commented text between the 'neverallow'
# keyword and a terminating ';'
-# returns: a list of strings representing these rules
+# returns: a list of rules
def extract_neverallow_rules(policy_file):
with open(policy_file, 'r') as in_file:
policy_str = in_file.read()
+
+ # full-Treble only tests are inside sections delimited by BEGIN_TREBLE_ONLY
+ # and END_TREBLE_ONLY comments.
+
+ # uncomment TREBLE_ONLY section delimiter lines
+ remaining = re.sub(
+ r'^\s*#\s*(BEGIN_TREBLE_ONLY|END_TREBLE_ONLY)',
+ r'\1',
+ policy_str,
+ flags = re.M)
# remove comments
- no_comments = re.sub(r'#.+?$', r'', policy_str, flags = re.M)
+ remaining = re.sub(r'#.+?$', r'', remaining, flags = re.M)
# match neverallow rules
- return re.findall(r'(^neverallow\s.+?;)', no_comments, flags = re.M |re.S);
+ lines = re.findall(
+ r'^\s*(neverallow\s.+?;|BEGIN_TREBLE_ONLY|END_TREBLE_ONLY)',
+ remaining,
+ flags = re.M |re.S)
+
+ # extract neverallow rules from the remaining lines
+ rules = list()
+ treble_only_depth = 0
+ for line in lines:
+ if line.startswith("BEGIN_TREBLE_ONLY"):
+ treble_only_depth += 1
+ continue
+ elif line.startswith("END_TREBLE_ONLY"):
+ if treble_only_depth < 1:
+ exit("ERROR: END_TREBLE_ONLY outside of TREBLE_ONLY section")
+ treble_only_depth -= 1
+ continue
+ rule = NeverallowRule(line)
+ rule.treble_only = (treble_only_depth > 0)
+ rules.append(rule)
+
+ if treble_only_depth != 0:
+ exit("ERROR: end of input while inside TREBLE_ONLY section")
+ return rules
# neverallow_rule_to_test - takes a neverallow statement and transforms it into
# the output necessary to form a cts unit test in a java source file.
# returns: a string representing a generic test method based on this rule.
-def neverallow_rule_to_test(neverallow_rule, test_num):
- squashed_neverallow = neverallow_rule.replace("\n", " ")
+def neverallow_rule_to_test(rule, test_num):
+ squashed_neverallow = rule.statement.replace("\n", " ")
method = SELinuxNeverallowTestFrame.src_method
method = method.replace("testNeverallowRules()",
"testNeverallowRules" + str(test_num) + "()")
- return method.replace("$NEVERALLOW_RULE_HERE$", squashed_neverallow)
+ method = method.replace("$NEVERALLOW_RULE_HERE$", squashed_neverallow)
+ method = method.replace(
+ "$FULL_TREBLE_ONLY_BOOL_HERE$",
+ "true" if rule.treble_only else "false")
+ return method
if __name__ == "__main__":
# check usage
if len(sys.argv) != 3:
print usage
- exit()
+ exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
diff --git a/tools/vm-tests-tf/Android.mk b/tools/vm-tests-tf/Android.mk
index 6e29a24..d5811c3 100644
--- a/tools/vm-tests-tf/Android.mk
+++ b/tools/vm-tests-tf/Android.mk
@@ -89,13 +89,13 @@
$(LOCAL_BUILT_MODULE): PRIVATE_CLASS_PATH := $(subst $(space),:,$(vmteststf_dep_jars)):$(HOST_JDK_TOOLS_JAR)
$(LOCAL_BUILT_MODULE): PRIVATE_JACK_VERSION := $(LOCAL_JACK_VERSION)
ifndef LOCAL_JACK_ENABLED
-$(LOCAL_BUILT_MODULE) : $(vmteststf_dep_jars) $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
+$(LOCAL_BUILT_MODULE) : $(vmteststf_dep_jars) $(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar
$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)
$(hide) mkdir -p $(PRIVATE_INTERMEDIATES_HOSTJUNIT_FILES)/dot/junit $(dir $(PRIVATE_INTERMEDIATES_DEXCORE_JAR))
# generated and compile the host side junit tests
@echo "Write generated Main_*.java files to $(PRIVATE_INTERMEDIATES_MAIN_FILES)"
$(hide) java -cp $(PRIVATE_CLASS_PATH) util.build.BuildDalvikSuite $(PRIVATE_SRC_FOLDER) $(PRIVATE_INTERMEDIATES) \
- $(HOST_OUT_JAVA_LIBRARIES)/cts-tf-dalvik-buildutil.jar:$(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar \
+ $(HOST_OUT_JAVA_LIBRARIES)/cts-tf-dalvik-buildutil.jar:$(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar \
$(PRIVATE_INTERMEDIATES_MAIN_FILES) $(PRIVATE_INTERMEDIATES_CLASSES) $(PRIVATE_INTERMEDIATES_HOSTJUNIT_FILES) $$RUN_VM_TESTS_RTO
@echo "Generate $(PRIVATE_INTERMEDIATES_DEXCORE_JAR)"
$(hide) jar -cf $(PRIVATE_INTERMEDIATES_DEXCORE_JAR).jar \