Merge "RVCVXCheck Passing Criterion Revision and Bug Fix" into mnc-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 4e1e008..db777b5 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -62,7 +62,9 @@
CtsKeySetSharedUserSigningBUpgradeB \
CtsKeySetSigningABadUpgradeB \
CtsKeySetSigningCBadAUpgradeAB \
- CtsKeySetSigningANoDefUpgradeB
+ CtsKeySetSigningANoDefUpgradeB \
+ CtsKeySetSigningAUpgradeEcA \
+ CtsKeySetSigningEcAUpgradeA
cts_support_packages := \
CtsAccelerationTestStubs \
@@ -165,10 +167,12 @@
CtsSignatureTestCases \
CtsSpeechTestCases \
CtsTelecomTestCases \
+ CtsTelecomTestCases2 \
CtsTelephonyTestCases \
CtsTextTestCases \
CtsTextureViewTestCases \
CtsThemeTestCases \
+ CtsTransitionTestCases \
CtsTvTestCases \
CtsUiAutomationTestCases \
CtsUiRenderingTestCases \
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 03f8ff9..5475a59 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -369,6 +369,28 @@
rgb.reshape(w*h*3)[:] = flt.reshape(w*h*3)[:]
return rgb.astype(numpy.float32) / 255.0
+def load_rgb_image(fname):
+ """Load a standard image file (JPG, PNG, etc.).
+
+ Args:
+ fname: The path of the file to load.
+
+ Returns:
+ RGB float-3 image array, with pixel values in [0.0, 1.0].
+ """
+ img = Image.open(fname)
+ w = img.size[0]
+ h = img.size[1]
+ a = numpy.array(img)
+ if len(a.shape) == 3 and a.shape[2] == 3:
+ # RGB
+ return a.reshape(h,w,3) / 255.0
+ elif len(a.shape) == 2 or len(a.shape) == 3 and a.shape[2] == 1:
+ # Greyscale; convert to RGB
+ return a.reshape(h*w).repeat(3).reshape(h,w,3) / 255.0
+ else:
+ raise its.error.Error('Unsupported image type')
+
def load_yuv420_to_rgb_image(yuv_fname,
w, h,
ccm_yuv_to_rgb=DEFAULT_YUV_TO_RGB_CCM,
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
index 9b43a74..5fd8f73 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
@@ -41,10 +41,12 @@
range_max = ev_compensation_range[1]
ev_per_step = its.objects.rational_to_float(
props['android.control.aeCompensationStep'])
- steps_per_ev = int(1.0 / ev_per_step)
- evs = range(range_min, range_max + 1, steps_per_ev)
+ steps_per_ev = int(round(1.0 / ev_per_step))
+ ev_steps = range(range_min, range_max + 1, steps_per_ev)
+ imid = len(ev_steps) / 2
+ ev_shifts = [pow(2, step * ev_per_step) for step in ev_steps]
lumas = []
- for ev in evs:
+ for ev in ev_steps:
# Re-converge 3A, and lock AE once converged. skip AF trigger as
# dark/bright scene could make AF convergence fail and this test
# doesn't care the image sharpness.
@@ -65,19 +67,16 @@
tile = its.image.get_image_patch(y, 0.45,0.45,0.1,0.1)
lumas.append(its.image.compute_image_means(tile)[0])
- luma_increase_per_step = pow(2, ev_per_step)
print "ev_step_size_in_stops", ev_per_step
- imid = len(lumas) / 2
- expected_lumas = [lumas[imid] / pow(luma_increase_per_step, i)
- for i in range(imid , 0, -1)] + \
- [lumas[imid] * pow(luma_increase_per_step, i-imid)
- for i in range(imid, len(evs))]
+ shift_mid = ev_shifts[imid]
+ luma_normal = lumas[imid] / shift_mid
+ expected_lumas = [luma_normal * ev_shift for ev_shift in ev_shifts]
- pylab.plot(evs, lumas, 'r')
- pylab.plot(evs, expected_lumas, 'b')
+ pylab.plot(ev_steps, lumas, 'r')
+ pylab.plot(ev_steps, expected_lumas, 'b')
matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
- luma_diffs = [expected_lumas[i] - lumas[i] for i in range(len(evs))]
+ luma_diffs = [expected_lumas[i] - lumas[i] for i in range(len(ev_steps))]
max_diff = max(abs(i) for i in luma_diffs)
avg_diff = abs(numpy.array(luma_diffs)).mean()
print "Max delta between modeled and measured lumas:", max_diff
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 98e8acd..ac9466d 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -971,6 +971,17 @@
<meta-data android:name="test_required_features" android:value="android.hardware.camera.any" />
</activity>
+ <activity android:name=".camera.flashlight.CameraFlashlightActivity"
+ android:label="@string/camera_flashlight_test"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_camera" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.camera.flash" />
+ </activity>
+
<activity android:name=".usb.UsbAccessoryTestActivity"
android:label="@string/usb_accessory_test"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -1438,6 +1449,17 @@
android:value="android.software.live_tv" />
</activity>
+ <activity android:name=".tv.TimeShiftTestActivity"
+ android:label="@string/tv_time_shift_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_tv" />
+ <meta-data android:name="test_required_features"
+ android:value="android.software.live_tv" />
+ </activity>
+
<activity android:name=".screenpinning.ScreenPinningTestActivity"
android:label="@string/screen_pinning_test">
<intent-filter>
@@ -1449,12 +1471,6 @@
android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
</activity>
- <activity android:name=".tv.MockTvInputSettingsActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- </activity>
-
<activity android:name=".tv.MockTvInputSetupActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/apps/CtsVerifier/res/layout/camera_flashlight.xml b/apps/CtsVerifier/res/layout/camera_flashlight.xml
new file mode 100644
index 0000000..2d4378c
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/camera_flashlight.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/flash_instruction_text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="2"
+ android:gravity="center"
+ android:text="@string/camera_flashlight_start_text" />
+
+ <Button
+ android:id="@+id/flash_instruction_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="100dp"
+ android:layout_marginRight="100dp"
+ android:text="@string/camera_flashlight_start_button" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginLeft="50dp"
+ android:layout_marginRight="50dp"
+ android:layout_marginBottom="50dp"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/flash_on_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:text="@string/camera_flashlight_on_button" />
+
+ <Button
+ android:id="@+id/flash_off_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:text="@string/camera_flashlight_off_button" />
+
+ </LinearLayout>
+
+ <include layout="@layout/pass_fail_buttons"/>
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/hifi_ultrasound_popup_instru.xml b/apps/CtsVerifier/res/layout/hifi_ultrasound_popup_instru.xml
new file mode 100644
index 0000000..42af6e9
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/hifi_ultrasound_popup_instru.xml
@@ -0,0 +1,21 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:background="@android:color/black"
+ android:padding="5dp"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/instru"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="5" />
+
+ <Button
+ android:id="@+id/ok"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/hifi_ultrasound_test_ok"
+ android:layout_weight="1" />
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index fa4203d..ad41891 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -287,46 +287,53 @@
<string name="empty"></string>
<!-- Strings for HifiUltrasoundTestActivity -->
- <string name="hifi_ultrasound_test">Hifi Ultrasound Test (microphone)</string>
+ <string name="hifi_ultrasound_test">Hifi Ultrasound Microphone Test</string>
<string name="hifi_ultrasound_test_info">
- This is a test for microphone near-ultrasound (18500Hz - 20000Hz) response.\n
+ This is a test for near-ultrasound (18500Hz - 20000Hz) microphone response.\n
This test requires two devices.\n</string>
<string name="hifi_ultrasound_test_play">PLAY</string>
<string name="hifi_ultrasound_test_record">RECORD</string>
<string name="hifi_ultrasound_test_plot">PLOT</string>
<string name="hifi_ultrasound_test_dismiss">DISMISS</string>
+ <string name="hifi_ultrasound_test_ok">OK</string>
<string name="hifi_ultrasound_test_instruction1">
- Set the volume of the reference device at 70% and hold it with one hand.\n
+ Open Hifi Ultrasound Microphone Test on the test device and the reference device.\n
+ Set the media volume of the reference device at 70% and hold it with one hand.\n
Hold the testing device with the other hand\n
Press the RECORD button on the testing device, then the PLAY button on the reference device within one second.\n
- After the test, report result on the testing device.\n</string>
+ After the test, report result on the testing (recording) device.\n</string>
<string name="hifi_ultrasound_test_pass">PASS</string>
<string name="hifi_ultrasound_test_fail">FAIL</string>
<string name="hifi_ultrasound_test_default_false_string">false</string>
- <string name="hifi_ultrasound_test_mic_prop">persist.audio.mic.ultrasound</string>
- <string name="hifi_ultrasound_test_spkr_prop">persist.audio.spkr.ultrasound</string>
<string name="hifi_ultrasound_test_mic_no_support">
Device does not support near-ultrasound recording.\n
- Please click pass if this is the testing device.\n</string>
+ All new phones and tablets MUST support near-ultrasound recording.\n
+ Report FAIL if this is a new device, report PASS if this is an updating device.\n</string>
<string name="hifi_ultrasound_test_spkr_no_support">
Device does not support near-ultrasound playback.\n
If this is your reference device, please use a different reference device.\n</string>
- <string name="hifi_ultrasound_speaker_test">Hifi Ultrasound Test (speaker)</string>
+ <string name="hifi_ultrasound_speaker_test">Hifi Ultrasound Speaker Test</string>
<string name="hifi_ultrasound_speaker_test_info">
- This is a test for speaker near-ultrasound (18500Hz - 20000Hz) response.\n
+ This is a test for near-ultrasound (18500Hz - 20000Hz) speaker response.\n
This test requires two devices.\n</string>
<string name="hifi_ultrasound_speaker_test_instruction1">
- Set the volume of the testing device at 70% and hold it with one hand.\n
+ Open Hifi Ultrasound Speaker Test on the test device and the reference device.\n
+ Set the media volume of the testing device at 70% and hold it with one hand.\n
Hold the reference device with the other hand\n
Press the RECORD button on the reference device, then the PLAY button on the testing device within one second.\n
- After the test, report result on the testing device.\n</string>
+ After the test, report result on the testing (playback) device.\n</string>
<string name="hifi_ultrasound_speaker_test_mic_no_support">
Device does not support near-ultrasound recording.\n
If this is your reference device, please use a different reference device.\n</string>
<string name="hifi_ultrasound_speaker_test_spkr_no_support">
Device does not support near-ultrasound playback.\n
- Please click pass if this is the testing device.\n</string>
+ All new phones and tablets MUST support near-ultrasound playback.\n
+ Report FAIL if this is a new device, report PASS if this is an updating device.\n</string>
+ <string name="hifi_ultrasound_speaker_test_test_side">
+ Please wait for the result on the reference device then report here.</string>
+ <string name="hifi_ultrasound_speaker_test_reference_side">
+ Please report on the testing device.\n</string>
<!-- Strings for Location tests -->
<string name="location_gps_test">GPS Test</string>
@@ -835,6 +842,25 @@
<string name="its_test_passed">All Camera ITS tests passed. Pass button enabled!</string>
<string name="its_test_failed">Some Camera ITS tests failed.</string>
+ <!-- Strings for the Camera Flashlight test activity -->
+ <string name="camera_flashlight_test">Camera Flashlight</string>
+ <string name="camera_flashlight_info">
+ This test checks the flashlight functionality. It will turn on and off the flashlight of
+ each camera device that has a flash unit. Follow the instructions on screen and observe the
+ flashlight status changing.
+ </string>
+ <string name="camera_flashlight_start_button">Start</string>
+ <string name="camera_flashlight_next_button">Next</string>
+ <string name="camera_flashlight_done_button">Done</string>
+ <string name="camera_flashlight_on_button">On</string>
+ <string name="camera_flashlight_off_button">Off</string>
+ <string name="camera_flashlight_start_text">Press Start to start flashlight test.</string>
+ <string name="camera_flashlight_question_text">Is Camera %1$s flashlight on or off?</string>
+ <string name="camera_flashlight_next_text">Ok. Press next.</string>
+ <string name="camera_flashlight_failed_text">Test failed. Press Done or Fail button.</string>
+ <string name="camera_flashlight_passed_text">All tests passed. Press Done or Pass button.
+ </string>
+
<!-- Strings for StreamingVideoActivity -->
<string name="streaming_video">Streaming Video Quality Verifier</string>
<string name="streaming_video_info">This is a test for assessing the quality of streaming videos. Play each stream and verify that the video is smooth and in sync with the audio, and that there are no quality problems.</string>
@@ -1590,6 +1616,41 @@
The Spanish audio track should be selected.
</string>
+ <string name="tv_time_shift_test">TV app time shift test</string>
+ <string name="tv_time_shift_test_info">
+ This test verifies that the TV app invokes proper time shift APIs in the framwork.
+ </string>
+ <string name="tv_time_shift_test_pause_resume">
+ Press the \"Launch TV app\" button. Verify that the playback control is available.
+ Pause the playback and then resume it.
+ </string>
+ <string name="tv_time_shift_test_verify_resume_after_pause">
+ The playback should be resumed after pause.
+ </string>
+ <string name="tv_time_shift_test_verify_position_tracking">
+ The playback position tracking should be activated.
+ </string>
+ <string name="tv_time_shift_test_speed_rate">
+ Press the \"Launch TV app\" button. Verify that the playback control is available.
+ Rewind the playback and in a few seconds fast-forward it.
+ </string>
+ <string name="tv_time_shift_test_verify_rewind">
+ The playback should be rewinded.
+ </string>
+ <string name="tv_time_shift_test_verify_fast_forward">
+ The playback should be fast-forwarded.
+ </string>
+ <string name="tv_time_shift_test_seek">
+ Press the \"Launch TV app\" button. Verify that the playback control is available.
+ Seek to previous and then seek to next.
+ </string>
+ <string name="tv_time_shift_test_verify_seek_to_previous">
+ The playback position should be moved to the previous position.
+ </string>
+ <string name="tv_time_shift_test_verify_seek_to_next">
+ The playback position should be moved to the next position.
+ </string>
+
<string name="overlay_view_text">Overlay View Dummy Text</string>
<string name="fake_rating">Fake</string>
diff --git a/apps/CtsVerifier/res/xml/mock_tv_input_service.xml b/apps/CtsVerifier/res/xml/mock_tv_input_service.xml
index 1a2cf86..d9cb867 100644
--- a/apps/CtsVerifier/res/xml/mock_tv_input_service.xml
+++ b/apps/CtsVerifier/res/xml/mock_tv_input_service.xml
@@ -15,5 +15,4 @@
-->
<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
- android:setupActivity="com.android.cts.verifier.tv.MockTvInputSetupActivity"
- android:settingsActivity="com.android.cts.verifier.tv.MockTvInputSettingsActivity" />
+ android:setupActivity="com.android.cts.verifier.tv.MockTvInputSetupActivity" />
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
index fa5ad81..dc81e19 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
@@ -43,272 +43,310 @@
public class HifiUltrasoundSpeakerTestActivity extends PassFailButtons.Activity {
- public enum Status {
- START, RECORDING, DONE, PLAYER
- }
+ public enum Status {
+ START, RECORDING, DONE, PLAYER
+ }
- private static final String TAG = "HifiUltrasoundTestActivity";
+ private static final String TAG = "HifiUltrasoundTestActivity";
- private Status status = Status.START;
- private boolean onPlotScreen = false;
- private TextView info;
- private Button playerButton;
- private Button recorderButton;
- private AudioTrack audioTrack;
- private LayoutInflater layoutInflater;
- private View popupView;
- private PopupWindow popupWindow;
- private boolean micSupport = true;
- private boolean spkrSupport = true;
+ private Status status = Status.START;
+ private boolean onPlotScreen = false;
+ private boolean onInstruScreen = false;
+ private TextView info;
+ private Button playerButton;
+ private Button recorderButton;
+ private AudioTrack audioTrack;
+ private LayoutInflater layoutInflater;
+ private View popupView;
+ private View instruView;
+ private PopupWindow popupWindow;
+ private PopupWindow instruWindow;
+ private boolean micSupport = true;
+ private boolean spkrSupport = true;
- @Override
- public void onBackPressed () {
- if (onPlotScreen) {
- popupWindow.dismiss();
- onPlotScreen = false;
- recorderButton.setEnabled(true);
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.hifi_ultrasound);
- setInfoResources(R.string.hifi_ultrasound_speaker_test,
- R.string.hifi_ultrasound_speaker_test_info, -1);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- info = (TextView) findViewById(R.id.info_text);
- info.setMovementMethod(new ScrollingMovementMethod());
- info.setText(R.string.hifi_ultrasound_speaker_test_instruction1);
-
- AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- String micSupportString = audioManager.getProperty(
- getResources().getString(R.string.hifi_ultrasound_test_mic_prop));
- String spkrSupportString = audioManager.getProperty(
- getResources().getString(R.string.hifi_ultrasound_test_spkr_prop));
-
- if (micSupportString == null) {
- micSupportString = "null";
- }
- if (spkrSupportString == null) {
- spkrSupportString = "null";
- }
- if (micSupportString.equalsIgnoreCase(getResources().getString(
- R.string.hifi_ultrasound_test_default_false_string))) {
- micSupport = false;
- getPassButton().setEnabled(true);
- info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_mic_no_support));
- }
- if (spkrSupportString.equalsIgnoreCase(getResources().getString(
- R.string.hifi_ultrasound_test_default_false_string))) {
- spkrSupport = false;
- info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_spkr_no_support));
- }
-
- layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
- LAYOUT_INFLATER_SERVICE);
- popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
- popupWindow = new PopupWindow(
- popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-
- final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
- final int recordRate = audioRecorder.getSampleRate();
-
- recorderButton = (Button) findViewById(R.id.recorder_button);
+ @Override
+ public void onBackPressed () {
+ if (onPlotScreen) {
+ popupWindow.dismiss();
+ onPlotScreen = false;
+ recorderButton.setEnabled(true);
+ } else if (onInstruScreen) {
+ instruWindow.dismiss();
+ onInstruScreen = false;
+ if (status == Status.PLAYER) {
+ playerButton.setEnabled(spkrSupport);
+ } else {
recorderButton.setEnabled(micSupport);
- recorderButton.setOnClickListener(new View.OnClickListener() {
- private WavAnalyzerTask wavAnalyzerTask = null;
- private void stopRecording() {
- audioRecorder.stop();
- wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
- wavAnalyzerTask.execute();
- status = Status.DONE;
- }
- @Override
- public void onClick(View v) {
- switch (status) {
- case START:
- info.append("Recording at " + recordRate + "Hz using ");
- final int source = audioRecorder.getAudioSource();
- switch (source) {
- case 1:
- info.append("MIC");
- break;
- case 6:
- info.append("VOICE_RECOGNITION");
- break;
- default:
- info.append("UNEXPECTED " + source);
- break;
- }
- info.append("\n");
- status = Status.RECORDING;
- playerButton.setEnabled(false);
- recorderButton.setEnabled(false);
- audioRecorder.start();
+ }
+ if (status == Status.PLAYER) {
+ getPassButton().setEnabled(true);
+ }
+ } else {
+ super.onBackPressed();
+ }
+ }
- final View finalV = v;
- new Thread() {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.hifi_ultrasound);
+ setInfoResources(R.string.hifi_ultrasound_speaker_test,
+ R.string.hifi_ultrasound_speaker_test_info, -1);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ info = (TextView) findViewById(R.id.info_text);
+ info.setMovementMethod(new ScrollingMovementMethod());
+ info.setText(R.string.hifi_ultrasound_speaker_test_instruction1);
+
+ AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ String micSupportString = audioManager.getProperty(
+ AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND);
+ String spkrSupportString = audioManager.getProperty(
+ AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND);
+ Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupportString);
+ Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupportString);
+
+ if (micSupportString == null) {
+ micSupportString = "null";
+ }
+ if (spkrSupportString == null) {
+ spkrSupportString = "null";
+ }
+ if (micSupportString.equalsIgnoreCase(getResources().getString(
+ R.string.hifi_ultrasound_test_default_false_string))) {
+ micSupport = false;
+ getPassButton().setEnabled(true);
+ info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_mic_no_support));
+ }
+ if (spkrSupportString.equalsIgnoreCase(getResources().getString(
+ R.string.hifi_ultrasound_test_default_false_string))) {
+ spkrSupport = false;
+ info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_spkr_no_support));
+ }
+
+ layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
+ LAYOUT_INFLATER_SERVICE);
+ popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
+ popupWindow = new PopupWindow(
+ popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ instruView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup_instru, null);
+ instruWindow = new PopupWindow(
+ instruView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+
+ final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
+ final int recordRate = audioRecorder.getSampleRate();
+
+ recorderButton = (Button) findViewById(R.id.recorder_button);
+ recorderButton.setEnabled(micSupport);
+ recorderButton.setOnClickListener(new View.OnClickListener() {
+ private WavAnalyzerTask wavAnalyzerTask = null;
+ private void stopRecording() {
+ audioRecorder.stop();
+ wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
+ wavAnalyzerTask.execute();
+ status = Status.DONE;
+ }
+ @Override
+ public void onClick(View v) {
+ switch (status) {
+ case START:
+ info.append("Recording at " + recordRate + "Hz using ");
+ final int source = audioRecorder.getAudioSource();
+ switch (source) {
+ case 1:
+ info.append("MIC");
+ break;
+ case 6:
+ info.append("VOICE_RECOGNITION");
+ break;
+ default:
+ info.append("UNEXPECTED " + source);
+ break;
+ }
+ info.append("\n");
+ status = Status.RECORDING;
+ playerButton.setEnabled(false);
+ recorderButton.setEnabled(false);
+ audioRecorder.start();
+
+ final View finalV = v;
+ new Thread() {
+ @Override
+ public void run() {
+ Double recordingDuration_millis = new Double(1000 * (2.5
+ + Common.PREFIX_LENGTH_S
+ + Common.PAUSE_BEFORE_PREFIX_DURATION_S
+ + Common.PAUSE_AFTER_PREFIX_DURATION_S
+ + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
+ * Common.REPETITIONS));
+ Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
+ try {
+ Thread.sleep(recordingDuration_millis.intValue());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ runOnUiThread(new Runnable() {
@Override
public void run() {
- Double recordingDuration_millis = new Double(1000 * (2.5
- + Common.PREFIX_LENGTH_S
- + Common.PAUSE_BEFORE_PREFIX_DURATION_S
- + Common.PAUSE_AFTER_PREFIX_DURATION_S
- + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
- * Common.REPETITIONS));
- Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
- try {
- Thread.sleep(recordingDuration_millis.intValue());
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- stopRecording();
- }
- });
+ stopRecording();
}
- }.start();
+ });
+ }
+ }.start();
- break;
+ break;
- case DONE:
- plotResponse(wavAnalyzerTask);
- break;
+ case DONE:
+ plotResponse(wavAnalyzerTask);
+ break;
- default: break;
- }
- }
- });
+ default: break;
+ }
+ }
+ });
- playerButton = (Button) findViewById(R.id.player_button);
- playerButton.setEnabled(spkrSupport);
- playerButton.setOnClickListener(new View.OnClickListener() {
+ playerButton = (Button) findViewById(R.id.player_button);
+ playerButton.setEnabled(spkrSupport);
+ playerButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ recorderButton.setEnabled(false);
+ status = Status.PLAYER;
+ play();
+
+ Button okButton = (Button)instruView.findViewById(R.id.ok);
+ okButton.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
- recorderButton.setEnabled(false);
- status = Status.PLAYER;
- play();
- getPassButton().setEnabled(true);
+ instruWindow.dismiss();
+ onInstruScreen = false;
+ if (status == Status.PLAYER) {
+ playerButton.setEnabled(spkrSupport);
+ } else {
+ recorderButton.setEnabled(micSupport);
+ }
+ getPassButton().setEnabled(true);
}
});
+ TextView instruction = (TextView)instruView.findViewById(R.id.instru);
+ instruction.setText(R.string.hifi_ultrasound_speaker_test_test_side);
+ instruWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
+ recorderButton.setEnabled(false);
+ playerButton.setEnabled(false);
+ onInstruScreen = true;
+ }
+ });
+ }
+
+ private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
+ Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
+ dismissButton.setOnClickListener(new Button.OnClickListener(){
+ @Override
+ public void onClick(View v) {
+ popupWindow.dismiss();
+ onPlotScreen = false;
+ recorderButton.setEnabled(true);
+ }});
+ popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
+ onPlotScreen = true;
+
+ recorderButton.setEnabled(false);
+
+ XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
+ plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
+
+ Double[] frequencies = new Double[Common.PIP_NUM];
+ for (int i = 0; i < Common.PIP_NUM; i++) {
+ frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
}
+ if (wavAnalyzerTask != null) {
- private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
- Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
- dismissButton.setOnClickListener(new Button.OnClickListener(){
- @Override
- public void onClick(View v) {
- popupWindow.dismiss();
- onPlotScreen = false;
- recorderButton.setEnabled(true);
- }});
- popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
- onPlotScreen = true;
-
- recorderButton.setEnabled(false);
-
- XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
- plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
-
- Double[] frequencies = new Double[Common.PIP_NUM];
- for (int i = 0; i < Common.PIP_NUM; i++) {
- frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
- }
-
- if (wavAnalyzerTask != null) {
-
- double[][] power = wavAnalyzerTask.getPower();
- for(int i = 0; i < Common.REPETITIONS; i++) {
- Double[] powerWrap = new Double[Common.PIP_NUM];
- for (int j = 0; j < Common.PIP_NUM; j++) {
- powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
- }
- XYSeries series = new SimpleXYSeries(
- Arrays.asList(frequencies),
- Arrays.asList(powerWrap),
- "");
- LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
- seriesFormat.configure(getApplicationContext(),
- R.xml.ultrasound_line_formatter_trials);
- seriesFormat.setPointLabelFormatter(null);
- plot.addSeries(series, seriesFormat);
+ double[][] power = wavAnalyzerTask.getPower();
+ for(int i = 0; i < Common.REPETITIONS; i++) {
+ Double[] powerWrap = new Double[Common.PIP_NUM];
+ for (int j = 0; j < Common.PIP_NUM; j++) {
+ powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
}
-
- double[] noiseDB = wavAnalyzerTask.getNoiseDB();
- Double[] noiseDBWrap = new Double[Common.PIP_NUM];
- for (int i = 0; i < Common.PIP_NUM; i++) {
- noiseDBWrap[i] = new Double(noiseDB[i]);
- }
-
- XYSeries noiseSeries = new SimpleXYSeries(
- Arrays.asList(frequencies),
- Arrays.asList(noiseDBWrap),
- "background noise");
- LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
- noiseSeriesFormat.configure(getApplicationContext(),
- R.xml.ultrasound_line_formatter_noise);
- noiseSeriesFormat.setPointLabelFormatter(null);
- plot.addSeries(noiseSeries, noiseSeriesFormat);
-
- double[] dB = wavAnalyzerTask.getDB();
- Double[] dBWrap = new Double[Common.PIP_NUM];
- for (int i = 0; i < Common.PIP_NUM; i++) {
- dBWrap[i] = new Double(dB[i]);
- }
-
XYSeries series = new SimpleXYSeries(
Arrays.asList(frequencies),
- Arrays.asList(dBWrap),
- "median");
+ Arrays.asList(powerWrap),
+ "");
LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
seriesFormat.configure(getApplicationContext(),
- R.xml.ultrasound_line_formatter_median);
+ R.xml.ultrasound_line_formatter_trials);
seriesFormat.setPointLabelFormatter(null);
plot.addSeries(series, seriesFormat);
-
- Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
- Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
- XYSeries passSeries = new SimpleXYSeries(
- Arrays.asList(passX), Arrays.asList(passY), "passing");
- LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
- passSeriesFormat.configure(getApplicationContext(),
- R.xml.ultrasound_line_formatter_pass);
- passSeriesFormat.setPointLabelFormatter(null);
- plot.addSeries(passSeries, passSeriesFormat);
}
- }
- /**
- * Plays the generated pips.
- */
- private void play() {
- play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
- }
-
- /**
- * Plays the sound data.
- */
- private void play(byte[] data, int sampleRate) {
- if (audioTrack != null) {
- audioTrack.stop();
- audioTrack.release();
+ double[] noiseDB = wavAnalyzerTask.getNoiseDB();
+ Double[] noiseDBWrap = new Double[Common.PIP_NUM];
+ for (int i = 0; i < Common.PIP_NUM; i++) {
+ noiseDBWrap[i] = new Double(noiseDB[i]);
}
- audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
- sampleRate, AudioFormat.CHANNEL_OUT_MONO,
- AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
- sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
- AudioTrack.MODE_STATIC);
- audioTrack.write(data, 0, data.length);
- audioTrack.play();
+
+ XYSeries noiseSeries = new SimpleXYSeries(
+ Arrays.asList(frequencies),
+ Arrays.asList(noiseDBWrap),
+ "background noise");
+ LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
+ noiseSeriesFormat.configure(getApplicationContext(),
+ R.xml.ultrasound_line_formatter_noise);
+ noiseSeriesFormat.setPointLabelFormatter(null);
+ plot.addSeries(noiseSeries, noiseSeriesFormat);
+
+ double[] dB = wavAnalyzerTask.getDB();
+ Double[] dBWrap = new Double[Common.PIP_NUM];
+ for (int i = 0; i < Common.PIP_NUM; i++) {
+ dBWrap[i] = new Double(dB[i]);
+ }
+
+ XYSeries series = new SimpleXYSeries(
+ Arrays.asList(frequencies),
+ Arrays.asList(dBWrap),
+ "median");
+ LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
+ seriesFormat.configure(getApplicationContext(),
+ R.xml.ultrasound_line_formatter_median);
+ seriesFormat.setPointLabelFormatter(null);
+ plot.addSeries(series, seriesFormat);
+
+ Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
+ Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
+ XYSeries passSeries = new SimpleXYSeries(
+ Arrays.asList(passX), Arrays.asList(passY), "passing");
+ LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
+ passSeriesFormat.configure(getApplicationContext(),
+ R.xml.ultrasound_line_formatter_pass);
+ passSeriesFormat.setPointLabelFormatter(null);
+ plot.addSeries(passSeries, passSeriesFormat);
}
+ }
+
+ /**
+ * Plays the generated pips.
+ */
+ private void play() {
+ play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
+ }
+
+ /**
+ * Plays the sound data.
+ */
+ private void play(byte[] data, int sampleRate) {
+ if (audioTrack != null) {
+ audioTrack.stop();
+ audioTrack.release();
+ }
+ audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
+ sampleRate, AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
+ sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
+ AudioTrack.MODE_STATIC);
+ audioTrack.write(data, 0, data.length);
+ audioTrack.play();
+ }
/**
* AsyncTask class for the analyzing.
@@ -354,6 +392,26 @@
info.append(result);
recorderButton.setEnabled(true);
recorderButton.setText(R.string.hifi_ultrasound_test_plot);
+
+ Button okButton = (Button)instruView.findViewById(R.id.ok);
+ okButton.setOnClickListener(new Button.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ instruWindow.dismiss();
+ onInstruScreen = false;
+ if (status == HifiUltrasoundSpeakerTestActivity.Status.PLAYER) {
+ playerButton.setEnabled(spkrSupport);
+ } else {
+ recorderButton.setEnabled(micSupport);
+ }
+ }
+ });
+ TextView instruction = (TextView) instruView.findViewById(R.id.instru);
+ instruction.setText(R.string.hifi_ultrasound_speaker_test_reference_side);
+ instruWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
+ recorderButton.setEnabled(false);
+ playerButton.setEnabled(false);
+ onInstruScreen = true;
}
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java
index 690e109..85b3e37 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java
@@ -43,270 +43,271 @@
public class HifiUltrasoundTestActivity extends PassFailButtons.Activity {
- public enum Status {
- START, RECORDING, DONE, PLAYER
+ public enum Status {
+ START, RECORDING, DONE, PLAYER
+ }
+
+ private static final String TAG = "HifiUltrasoundTestActivity";
+
+ private Status status = Status.START;
+ private boolean onPlotScreen = false;
+ private TextView info;
+ private Button playerButton;
+ private Button recorderButton;
+ private AudioTrack audioTrack;
+ private LayoutInflater layoutInflater;
+ private View popupView;
+ private PopupWindow popupWindow;
+ private boolean micSupport = true;
+ private boolean spkrSupport = true;
+
+ @Override
+ public void onBackPressed () {
+ if (onPlotScreen) {
+ popupWindow.dismiss();
+ onPlotScreen = false;
+ recorderButton.setEnabled(true);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.hifi_ultrasound);
+ setInfoResources(R.string.hifi_ultrasound_test, R.string.hifi_ultrasound_test_info, -1);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ info = (TextView) findViewById(R.id.info_text);
+ info.setMovementMethod(new ScrollingMovementMethod());
+ info.setText(R.string.hifi_ultrasound_test_instruction1);
+
+ AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ String micSupportString = audioManager.getProperty(
+ AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND);
+ String spkrSupportString = audioManager.getProperty(
+ AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND);
+ Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupportString);
+ Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupportString);
+
+ if (micSupportString == null) {
+ micSupportString = "null";
+ }
+ if (spkrSupportString == null) {
+ spkrSupportString = "null";
+ }
+ if (micSupportString.equalsIgnoreCase(getResources().getString(
+ R.string.hifi_ultrasound_test_default_false_string))) {
+ micSupport = false;
+ getPassButton().setEnabled(true);
+ info.append(getResources().getString(R.string.hifi_ultrasound_test_mic_no_support));
+ }
+ if (spkrSupportString.equalsIgnoreCase(getResources().getString(
+ R.string.hifi_ultrasound_test_default_false_string))) {
+ spkrSupport = false;
+ info.append(getResources().getString(R.string.hifi_ultrasound_test_spkr_no_support));
}
- private static final String TAG = "HifiUltrasoundTestActivity";
+ layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
+ LAYOUT_INFLATER_SERVICE);
+ popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
+ popupWindow = new PopupWindow(
+ popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- private Status status = Status.START;
- private boolean onPlotScreen = false;
- private TextView info;
- private Button playerButton;
- private Button recorderButton;
- private AudioTrack audioTrack;
- private LayoutInflater layoutInflater;
- private View popupView;
- private PopupWindow popupWindow;
- private boolean micSupport = true;
- private boolean spkrSupport = true;
+ final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
+ final int recordRate = audioRecorder.getSampleRate();
- @Override
- public void onBackPressed () {
- if (onPlotScreen) {
- popupWindow.dismiss();
- onPlotScreen = false;
- recorderButton.setEnabled(true);
- } else {
- super.onBackPressed();
- }
- }
+ recorderButton = (Button) findViewById(R.id.recorder_button);
+ recorderButton.setEnabled(micSupport);
+ recorderButton.setOnClickListener(new View.OnClickListener() {
+ private WavAnalyzerTask wavAnalyzerTask = null;
+ private void stopRecording() {
+ audioRecorder.stop();
+ wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
+ wavAnalyzerTask.execute();
+ status = Status.DONE;
+ }
+ @Override
+ public void onClick(View v) {
+ switch (status) {
+ case START:
+ info.append("Recording at " + recordRate + "Hz using ");
+ final int source = audioRecorder.getAudioSource();
+ switch (source) {
+ case 1:
+ info.append("MIC");
+ break;
+ case 6:
+ info.append("VOICE_RECOGNITION");
+ break;
+ default:
+ info.append("UNEXPECTED " + source);
+ break;
+ }
+ info.append("\n");
+ status = Status.RECORDING;
+ playerButton.setEnabled(false);
+ recorderButton.setEnabled(false);
+ audioRecorder.start();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.hifi_ultrasound);
- setInfoResources(R.string.hifi_ultrasound_test, R.string.hifi_ultrasound_test_info, -1);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- info = (TextView) findViewById(R.id.info_text);
- info.setMovementMethod(new ScrollingMovementMethod());
- info.setText(R.string.hifi_ultrasound_test_instruction1);
-
- AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- String micSupportString = audioManager.getProperty(
- getResources().getString(R.string.hifi_ultrasound_test_mic_prop));
- String spkrSupportString = audioManager.getProperty(
- getResources().getString(R.string.hifi_ultrasound_test_spkr_prop));
-
- if (micSupportString == null) {
- micSupportString = "null";
- }
- if (spkrSupportString == null) {
- spkrSupportString = "null";
- }
- if (micSupportString.equalsIgnoreCase(getResources().getString(
- R.string.hifi_ultrasound_test_default_false_string))) {
- micSupport = false;
- getPassButton().setEnabled(true);
- info.append(getResources().getString(R.string.hifi_ultrasound_test_mic_no_support));
- }
- if (spkrSupportString.equalsIgnoreCase(getResources().getString(
- R.string.hifi_ultrasound_test_default_false_string))) {
- spkrSupport = false;
- info.append(getResources().getString(R.string.hifi_ultrasound_test_spkr_no_support));
- }
-
- layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
- LAYOUT_INFLATER_SERVICE);
- popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
- popupWindow = new PopupWindow(
- popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-
- final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
- final int recordRate = audioRecorder.getSampleRate();
-
- recorderButton = (Button) findViewById(R.id.recorder_button);
- recorderButton.setEnabled(micSupport);
- recorderButton.setOnClickListener(new View.OnClickListener() {
- private WavAnalyzerTask wavAnalyzerTask = null;
- private void stopRecording() {
- audioRecorder.stop();
- wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
- wavAnalyzerTask.execute();
- status = Status.DONE;
- }
- @Override
- public void onClick(View v) {
- switch (status) {
- case START:
- info.append("Recording at " + recordRate + "Hz using ");
- final int source = audioRecorder.getAudioSource();
- switch (source) {
- case 1:
- info.append("MIC");
- break;
- case 6:
- info.append("VOICE_RECOGNITION");
- break;
- default:
- info.append("UNEXPECTED " + source);
- break;
+ final View finalV = v;
+ new Thread() {
+ @Override
+ public void run() {
+ Double recordingDuration_millis = new Double(1000 * (2.5
+ + Common.PREFIX_LENGTH_S
+ + Common.PAUSE_BEFORE_PREFIX_DURATION_S
+ + Common.PAUSE_AFTER_PREFIX_DURATION_S
+ + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
+ * Common.REPETITIONS));
+ Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
+ try {
+ Thread.sleep(recordingDuration_millis.intValue());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
}
- info.append("\n");
- status = Status.RECORDING;
- playerButton.setEnabled(false);
- recorderButton.setEnabled(false);
- audioRecorder.start();
-
- final View finalV = v;
- new Thread() {
+ runOnUiThread(new Runnable() {
@Override
public void run() {
- Double recordingDuration_millis = new Double(1000 * (2.5
- + Common.PREFIX_LENGTH_S
- + Common.PAUSE_BEFORE_PREFIX_DURATION_S
- + Common.PAUSE_AFTER_PREFIX_DURATION_S
- + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
- * Common.REPETITIONS));
- Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
- try {
- Thread.sleep(recordingDuration_millis.intValue());
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- stopRecording();
- }
- });
+ stopRecording();
}
- }.start();
+ });
+ }
+ }.start();
- break;
+ break;
- case DONE:
- plotResponse(wavAnalyzerTask);
- break;
+ case DONE:
+ plotResponse(wavAnalyzerTask);
+ break;
- default: break;
- }
- }
- });
+ default: break;
+ }
+ }
+ });
- playerButton = (Button) findViewById(R.id.player_button);
- playerButton.setEnabled(spkrSupport);
- playerButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- recorderButton.setEnabled(false);
- status = Status.PLAYER;
- play();
- }
- });
+ playerButton = (Button) findViewById(R.id.player_button);
+ playerButton.setEnabled(spkrSupport);
+ playerButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ recorderButton.setEnabled(false);
+ status = Status.PLAYER;
+ play();
+ }
+ });
+ }
+
+ private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
+ Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
+ dismissButton.setOnClickListener(new Button.OnClickListener(){
+ @Override
+ public void onClick(View v) {
+ popupWindow.dismiss();
+ onPlotScreen = false;
+ recorderButton.setEnabled(true);
+ }});
+ popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
+ onPlotScreen = true;
+
+ recorderButton.setEnabled(false);
+
+ XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
+ plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
+
+ Double[] frequencies = new Double[Common.PIP_NUM];
+ for (int i = 0; i < Common.PIP_NUM; i++) {
+ frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
}
+ if (wavAnalyzerTask != null) {
- private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
- Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
- dismissButton.setOnClickListener(new Button.OnClickListener(){
- @Override
- public void onClick(View v) {
- popupWindow.dismiss();
- onPlotScreen = false;
- recorderButton.setEnabled(true);
- }});
- popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
- onPlotScreen = true;
-
- recorderButton.setEnabled(false);
-
- XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
- plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
-
- Double[] frequencies = new Double[Common.PIP_NUM];
- for (int i = 0; i < Common.PIP_NUM; i++) {
- frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
- }
-
- if (wavAnalyzerTask != null) {
-
- double[][] power = wavAnalyzerTask.getPower();
- for(int i = 0; i < Common.REPETITIONS; i++) {
- Double[] powerWrap = new Double[Common.PIP_NUM];
- for (int j = 0; j < Common.PIP_NUM; j++) {
- powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
- }
- XYSeries series = new SimpleXYSeries(
- Arrays.asList(frequencies),
- Arrays.asList(powerWrap),
- "");
- LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
- seriesFormat.configure(getApplicationContext(),
- R.xml.ultrasound_line_formatter_trials);
- seriesFormat.setPointLabelFormatter(null);
- plot.addSeries(series, seriesFormat);
+ double[][] power = wavAnalyzerTask.getPower();
+ for(int i = 0; i < Common.REPETITIONS; i++) {
+ Double[] powerWrap = new Double[Common.PIP_NUM];
+ for (int j = 0; j < Common.PIP_NUM; j++) {
+ powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
}
-
- double[] noiseDB = wavAnalyzerTask.getNoiseDB();
- Double[] noiseDBWrap = new Double[Common.PIP_NUM];
- for (int i = 0; i < Common.PIP_NUM; i++) {
- noiseDBWrap[i] = new Double(noiseDB[i]);
- }
-
- XYSeries noiseSeries = new SimpleXYSeries(
- Arrays.asList(frequencies),
- Arrays.asList(noiseDBWrap),
- "background noise");
- LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
- noiseSeriesFormat.configure(getApplicationContext(),
- R.xml.ultrasound_line_formatter_noise);
- noiseSeriesFormat.setPointLabelFormatter(null);
- plot.addSeries(noiseSeries, noiseSeriesFormat);
-
- double[] dB = wavAnalyzerTask.getDB();
- Double[] dBWrap = new Double[Common.PIP_NUM];
- for (int i = 0; i < Common.PIP_NUM; i++) {
- dBWrap[i] = new Double(dB[i]);
- }
-
XYSeries series = new SimpleXYSeries(
Arrays.asList(frequencies),
- Arrays.asList(dBWrap),
- "median");
+ Arrays.asList(powerWrap),
+ "");
LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
seriesFormat.configure(getApplicationContext(),
- R.xml.ultrasound_line_formatter_median);
+ R.xml.ultrasound_line_formatter_trials);
seriesFormat.setPointLabelFormatter(null);
plot.addSeries(series, seriesFormat);
-
- Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
- Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
- XYSeries passSeries = new SimpleXYSeries(
- Arrays.asList(passX), Arrays.asList(passY), "passing");
- LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
- passSeriesFormat.configure(getApplicationContext(),
- R.xml.ultrasound_line_formatter_pass);
- passSeriesFormat.setPointLabelFormatter(null);
- plot.addSeries(passSeries, passSeriesFormat);
}
- }
- /**
- * Plays the generated pips.
- */
- private void play() {
- play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
- }
-
- /**
- * Plays the sound data.
- */
- private void play(byte[] data, int sampleRate) {
- if (audioTrack != null) {
- audioTrack.stop();
- audioTrack.release();
+ double[] noiseDB = wavAnalyzerTask.getNoiseDB();
+ Double[] noiseDBWrap = new Double[Common.PIP_NUM];
+ for (int i = 0; i < Common.PIP_NUM; i++) {
+ noiseDBWrap[i] = new Double(noiseDB[i]);
}
- audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
- sampleRate, AudioFormat.CHANNEL_OUT_MONO,
- AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
- sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
- AudioTrack.MODE_STATIC);
- audioTrack.write(data, 0, data.length);
- audioTrack.play();
+
+ XYSeries noiseSeries = new SimpleXYSeries(
+ Arrays.asList(frequencies),
+ Arrays.asList(noiseDBWrap),
+ "background noise");
+ LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
+ noiseSeriesFormat.configure(getApplicationContext(),
+ R.xml.ultrasound_line_formatter_noise);
+ noiseSeriesFormat.setPointLabelFormatter(null);
+ plot.addSeries(noiseSeries, noiseSeriesFormat);
+
+ double[] dB = wavAnalyzerTask.getDB();
+ Double[] dBWrap = new Double[Common.PIP_NUM];
+ for (int i = 0; i < Common.PIP_NUM; i++) {
+ dBWrap[i] = new Double(dB[i]);
+ }
+
+ XYSeries series = new SimpleXYSeries(
+ Arrays.asList(frequencies),
+ Arrays.asList(dBWrap),
+ "median");
+ LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
+ seriesFormat.configure(getApplicationContext(),
+ R.xml.ultrasound_line_formatter_median);
+ seriesFormat.setPointLabelFormatter(null);
+ plot.addSeries(series, seriesFormat);
+
+ Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
+ Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
+ XYSeries passSeries = new SimpleXYSeries(
+ Arrays.asList(passX), Arrays.asList(passY), "passing");
+ LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
+ passSeriesFormat.configure(getApplicationContext(),
+ R.xml.ultrasound_line_formatter_pass);
+ passSeriesFormat.setPointLabelFormatter(null);
+ plot.addSeries(passSeries, passSeriesFormat);
}
+ }
+
+ /**
+ * Plays the generated pips.
+ */
+ private void play() {
+ play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
+ }
+
+ /**
+ * Plays the sound data.
+ */
+ private void play(byte[] data, int sampleRate) {
+ if (audioTrack != null) {
+ audioTrack.stop();
+ audioTrack.release();
+ }
+ audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
+ sampleRate, AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
+ sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
+ AudioTrack.MODE_STATIC);
+ audioTrack.write(data, 0, data.length);
+ audioTrack.play();
+ }
/**
* AsyncTask class for the analyzing.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/flashlight/CameraFlashlightActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/flashlight/CameraFlashlightActivity.java
new file mode 100644
index 0000000..4d37b3ee
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/flashlight/CameraFlashlightActivity.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.camera.flashlight;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraCharacteristics;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.util.HashSet;
+import java.util.HashMap;
+
+/**
+ * This test checks the flashlight functionality by turning on and off the flashlight. After it
+ * turns on or off the flashlight, it asks for user input to verify the flashlight status. The
+ * test will pass when the user input is correct for all camera devices with a flash unit.
+ */
+public class CameraFlashlightActivity extends PassFailButtons.Activity {
+
+ private static final String TAG = "CameraFlashlight";
+
+ private CameraManager mCameraManager;
+ private TestState mTestState;
+ private final HashSet<String> mPendingCameraIds = new HashSet<>();
+ private String mCurrentCameraId;
+
+ private Button mInstructionButton;
+ private Button mOnButton;
+ private Button mOffButton;
+ private TextView mInstructionTextView;
+ private final HashSet<View> mAllButtons = new HashSet<>();
+ // TestState -> enabled buttons
+ private final HashMap<TestState, HashSet<View>> mStateButtonsMap = new HashMap<>();
+
+ private enum TestState {
+ NOT_STARTED,
+ TESTING_ON,
+ WAITING_ON_CALLBACK_ON,
+ RESPONDED_ON_CORRECTLY,
+ WAITING_ON_CALLBACK_OFF,
+ TESTING_OFF,
+ RESPONDED_OFF_CORRECTLY,
+ ALL_PASSED,
+ FAILED
+ }
+
+ private final View.OnClickListener mInstructionButtonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ switch (mTestState) {
+ case NOT_STARTED:
+ // Start testing turning on the first camera's flashlight.
+ // Fall through.
+ case RESPONDED_OFF_CORRECTLY:
+ // Current camera passed. Start testing turning on next camera's flashlight.
+ if (mPendingCameraIds.size() == 0) {
+ // Passed
+ mTestState = TestState.ALL_PASSED;
+ updateButtonsAndInstructionLocked();
+ return;
+ }
+
+ mCurrentCameraId = (String)mPendingCameraIds.toArray()[0];
+ mPendingCameraIds.remove(mCurrentCameraId);
+
+ try {
+ mCameraManager.setTorchMode(mCurrentCameraId, true);
+ mTestState = TestState.WAITING_ON_CALLBACK_ON;
+ } catch (Exception e) {
+ e.printStackTrace();
+ mTestState = TestState.FAILED;
+ }
+ break;
+
+ case RESPONDED_ON_CORRECTLY:
+ // Flashlight is on and user responded correctly.
+ // Turning off the flashlight.
+ try {
+ mCameraManager.setTorchMode(mCurrentCameraId, false);
+ mTestState = TestState.WAITING_ON_CALLBACK_OFF;
+ } catch (Exception e) {
+ e.printStackTrace();
+ mTestState = TestState.FAILED;
+ }
+ break;
+
+ case FAILED:
+ // The test failed, report failure.
+ if (mCurrentCameraId != null) {
+ try {
+ mCameraManager.setTorchMode(mCurrentCameraId, false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "Test failed but cannot turn off the torch");
+ }
+ }
+ setTestResultAndFinish(false);
+ break;
+
+ case ALL_PASSED:
+ // The test passed, report pass.
+ setTestResultAndFinish(true);
+ break;
+ }
+
+ updateButtonsAndInstructionLocked();
+ }
+ };
+
+ private final View.OnClickListener mOnButtonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Check if user responded correctly.
+ if (mTestState == TestState.TESTING_ON) {
+ mTestState = TestState.RESPONDED_ON_CORRECTLY;
+ } else {
+ mTestState = TestState.FAILED;
+ }
+ updateButtonsAndInstructionLocked();
+ }
+ };
+
+ private final View.OnClickListener mOffButtonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Check if user responded correctly.
+ if (mTestState == TestState.TESTING_OFF) {
+ mTestState = TestState.RESPONDED_OFF_CORRECTLY;
+ } else {
+ mTestState = TestState.FAILED;
+ }
+ updateButtonsAndInstructionLocked();
+ }
+ };
+
+ private final CameraManager.TorchCallback mTorchCallback = new CameraManager.TorchCallback() {
+ @Override
+ public void onTorchModeChanged(String cameraId, boolean enabled) {
+ if (!cameraId.equals(mCurrentCameraId)) {
+ return;
+ }
+
+ // Move to next state after receiving the expected callback.
+ if (mTestState == TestState.WAITING_ON_CALLBACK_ON && enabled) {
+ mTestState = TestState.TESTING_ON;
+ } else if (mTestState == TestState.WAITING_ON_CALLBACK_OFF && !enabled) {
+ mTestState = TestState.TESTING_OFF;
+ }
+ updateButtonsAndInstructionLocked();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // initialize state -> buttons map
+ for (TestState state : TestState.values()) {
+ mStateButtonsMap.put(state, new HashSet<View>());
+ }
+
+ mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
+
+ try {
+ String[] cameraIds = mCameraManager.getCameraIdList();
+ for (String id : cameraIds) {
+ CameraCharacteristics info = mCameraManager.getCameraCharacteristics(id);
+ if (info.get(CameraCharacteristics.FLASH_INFO_AVAILABLE).booleanValue() ==
+ true) {
+ mPendingCameraIds.add(id);
+ }
+ }
+ mCameraManager.registerTorchCallback(mTorchCallback, new Handler());
+ } catch (Exception e) {
+ e.printStackTrace();
+ mTestState = TestState.FAILED;
+ updateButtonsAndInstructionLocked();
+ return;
+ }
+
+ // Setup the UI.
+ setContentView(R.layout.camera_flashlight);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.camera_flashlight_test, R.string.camera_flashlight_info, -1);
+
+ mInstructionTextView = (TextView) findViewById(R.id.flash_instruction_text);
+
+ // Get the buttons and attach the listener.
+ mInstructionButton = (Button) findViewById(R.id.flash_instruction_button);
+ mInstructionButton.setOnClickListener(mInstructionButtonListener);
+ mStateButtonsMap.get(TestState.NOT_STARTED).add(mInstructionButton);
+ mStateButtonsMap.get(TestState.RESPONDED_ON_CORRECTLY).add(mInstructionButton);
+ mStateButtonsMap.get(TestState.RESPONDED_OFF_CORRECTLY).add(mInstructionButton);
+ mStateButtonsMap.get(TestState.ALL_PASSED).add(mInstructionButton);
+ mStateButtonsMap.get(TestState.FAILED).add(mInstructionButton);
+ mAllButtons.add(mInstructionButton);
+
+ mOnButton = (Button) findViewById(R.id.flash_on_button);
+ mOnButton.setOnClickListener(mOnButtonListener);
+ mStateButtonsMap.get(TestState.TESTING_ON).add(mOnButton);
+ mStateButtonsMap.get(TestState.TESTING_OFF).add(mOnButton);
+ mAllButtons.add(mOnButton);
+
+ mOffButton = (Button) findViewById(R.id.flash_off_button);
+ mOffButton.setOnClickListener(mOffButtonListener);
+ mStateButtonsMap.get(TestState.TESTING_ON).add(mOffButton);
+ mStateButtonsMap.get(TestState.TESTING_OFF).add(mOffButton);
+ mAllButtons.add(mOffButton);
+
+ View passButton = getPassButton();
+ mStateButtonsMap.get(TestState.ALL_PASSED).add(passButton);
+ mAllButtons.add(passButton);
+
+ mTestState = TestState.NOT_STARTED;
+ updateButtonsAndInstructionLocked();
+ }
+
+
+ private void updateButtonsAndInstructionLocked() {
+ for (View v : mAllButtons) {
+ v.setEnabled(false);
+ }
+
+ // Only enable the buttons for this state.
+ HashSet<View> views = mStateButtonsMap.get(mTestState);
+ for (View v : views) {
+ v.setEnabled(true);
+ }
+
+ switch (mTestState) {
+ case TESTING_ON:
+ case TESTING_OFF:
+ mInstructionTextView.setText(String.format(
+ getString(R.string.camera_flashlight_question_text), mCurrentCameraId));
+ break;
+ case RESPONDED_ON_CORRECTLY:
+ case RESPONDED_OFF_CORRECTLY:
+ mInstructionTextView.setText(R.string.camera_flashlight_next_text);
+ mInstructionButton.setText(R.string.camera_flashlight_next_button);
+ break;
+ case FAILED:
+ mInstructionTextView.setText(R.string.camera_flashlight_failed_text);
+ mInstructionButton.setText(R.string.camera_flashlight_done_button);
+ break;
+ case ALL_PASSED:
+ mInstructionTextView.setText(R.string.camera_flashlight_passed_text);
+ mInstructionButton.setText(R.string.camera_flashlight_done_button);
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
index 8b989e1..959e98f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
@@ -145,7 +145,7 @@
mTargetDistanceCm = getTargetDistance();
mReportedFovDegrees = PhotoCaptureActivity.getReportedFovDegrees();
- mFovDegrees = mReportedFovDegrees > 80 ? 60 : mReportedFovDegrees;
+ mFovDegrees = mReportedFovDegrees > 120 ? 60 : mReportedFovDegrees;
mFovMaxDegrees = mFovDegrees + FOV_ADJUSTMENT_RANGE / 2;
mFovMinDegrees = mFovDegrees - FOV_ADJUSTMENT_RANGE / 2;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
index 57d0c8f..9abfa9a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsSerializer.java
@@ -151,6 +151,19 @@
cfgArray.put(obj);
}
}
+ sizes = map.getHighResolutionOutputSizes(fmts[fi]);
+ if (sizes != null) {
+ for (int si = 0; si < Array.getLength(sizes); si++) {
+ JSONObject obj = new JSONObject();
+ obj.put("format", fmts[fi]);
+ obj.put("width",sizes[si].getWidth());
+ obj.put("height", sizes[si].getHeight());
+ obj.put("input", false);
+ obj.put("minFrameDuration",
+ map.getOutputMinFrameDuration(fmts[fi],sizes[si]));
+ cfgArray.put(obj);
+ }
+ }
}
}
mapObj.put("availableStreamConfigurations", cfgArray);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 85b8a18..5b034fe 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -1077,10 +1077,10 @@
"Zero stream configs available for requested format: %s",
sformat));
}
- width = sizes[0].getWidth();
+ width = ItsUtils.getMaxSize(sizes).getWidth();
}
if (height <= 0) {
- height = sizes[0].getHeight();
+ height = ItsUtils.getMaxSize(sizes).getHeight();
}
outputSizes[i] = new Size(width, height);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
index fddee4d..6fd050b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
@@ -128,10 +128,24 @@
if (configMap == null) {
throw new ItsException("Failed to get stream config");
}
- return configMap.getOutputSizes(format);
+ Size[] normalSizes = configMap.getOutputSizes(format);
+ Size[] slowSizes = configMap.getHighResolutionOutputSizes(format);
+ Size[] allSizes = null;
+ if (normalSizes != null && slowSizes != null) {
+ allSizes = new Size[normalSizes.length + slowSizes.length];
+ System.arraycopy(normalSizes, 0, allSizes, 0,
+ normalSizes.length);
+ System.arraycopy(slowSizes, 0, allSizes, normalSizes.length,
+ slowSizes.length);
+ } else if (normalSizes != null) {
+ allSizes = normalSizes;
+ } else if (slowSizes != null) {
+ allSizes = slowSizes;
+ }
+ return allSizes;
}
- private static Size getMaxSize(Size[] sizes) {
+ public static Size getMaxSize(Size[] sizes) {
if (sizes == null || sizes.length == 0) {
throw new IllegalArgumentException("sizes was empty");
}
@@ -252,4 +266,3 @@
}
}
}
-
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
index e7d1d79..f875684 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
@@ -16,9 +16,9 @@
package com.android.cts.verifier.tv;
-import com.android.cts.verifier.R;
-
+import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -27,20 +27,29 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
+import android.media.PlaybackParams;
import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputService;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
import android.os.Bundle;
-import android.view.Surface;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
import android.view.LayoutInflater;
+import android.view.Surface;
import android.view.View;
import android.widget.TextView;
+import com.android.cts.verifier.R;
+
import java.util.ArrayList;
import java.util.List;
+@SuppressLint("NewApi")
public class MockTvInputService extends TvInputService {
private static final String TAG = "MockTvInputService";
@@ -48,6 +57,7 @@
private static final String SELECT_TRACK_TYPE = "type";
private static final String SELECT_TRACK_ID = "id";
private static final String CAPTION_ENABLED = "enabled";
+ private static final String PAUSE_CALLED = "pause_called";
private static Object sLock = new Object();
private static Callback sTuneCallback = null;
@@ -56,6 +66,14 @@
private static Callback sUnblockContentCallback = null;
private static Callback sSelectTrackCallback = null;
private static Callback sSetCaptionEnabledCallback = null;
+ // Callbacks for time shift.
+ private static Callback sResumeAfterPauseCallback = null;
+ private static Callback sPositionTrackingCallback = null;
+ private static Callback sRewindCallback = null;
+ private static Callback sFastForwardCallback = null;
+ private static Callback sSeekToPreviousCallback = null;
+ private static Callback sSeekToNextCallback = null;
+
private static TvContentRating sRating = null;
static final TvTrackInfo sEngAudioTrack =
@@ -142,6 +160,47 @@
}
}
+ static void expectResumeAfterPause(View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sResumeAfterPauseCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static void expectPositionTracking(View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sPositionTrackingCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static void expectRewind(View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sRewindCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static void expectFastForward(View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sFastForwardCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static void expectSeekToPrevious(View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sSeekToPreviousCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static void expectSeekToNext(View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sSeekToNextCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static String getInputId(Context context) {
+ return TvContract.buildInputId(new ComponentName(context,
+ MockTvInputService.class.getName()));
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -165,10 +224,45 @@
}
private static class MockSessionImpl extends Session {
+ private static final int MSG_SEEK = 1000;
+ private static final int SEEK_DELAY_MS = 300;
+
private final Context mContext;
private Surface mSurface = null;
private List<TvTrackInfo> mTracks = new ArrayList<>();
+ private long mRecordStartTimeMs;
+ private long mPausedTimeMs;
+ // The time in milliseconds when the current position is lastly updated.
+ private long mLastCurrentPositionUpdateTimeMs;
+ // The current playback position.
+ private long mCurrentPositionMs;
+ // The current playback speed rate.
+ private float mSpeed;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_SEEK) {
+ // Actually, this input doesn't play any videos, it just shows the image.
+ // So we should simulate the playback here by changing the current playback
+ // position periodically in order to test the time shift.
+ // If the playback is paused, the current playback position doesn't need to be
+ // changed.
+ if (mPausedTimeMs == 0) {
+ long currentTimeMs = System.currentTimeMillis();
+ mCurrentPositionMs += (long) ((currentTimeMs
+ - mLastCurrentPositionUpdateTimeMs) * mSpeed);
+ mCurrentPositionMs = Math.max(mRecordStartTimeMs,
+ Math.min(mCurrentPositionMs, currentTimeMs));
+ mLastCurrentPositionUpdateTimeMs = currentTimeMs;
+ }
+ sendEmptyMessageDelayed(MSG_SEEK, SEEK_DELAY_MS);
+ }
+ super.handleMessage(msg);
+ }
+ };
+
private MockSessionImpl(Context context) {
super(context);
mContext = context;
@@ -256,6 +350,12 @@
notifyTracksChanged(mTracks);
notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, sEngAudioTrack.getId());
notifyTrackSelected(TvTrackInfo.TYPE_SUBTITLE, null);
+ notifyTimeShiftStatusChanged(TvInputManager.TIME_SHIFT_STATUS_AVAILABLE);
+ mRecordStartTimeMs = mCurrentPositionMs = mLastCurrentPositionUpdateTimeMs
+ = System.currentTimeMillis();
+ mPausedTimeMs = 0;
+ mHandler.sendEmptyMessageDelayed(MSG_SEEK, SEEK_DELAY_MS);
+ mSpeed = 1;
return true;
}
@@ -298,6 +398,88 @@
}
}
}
+
+ @Override
+ public long onTimeShiftGetCurrentPosition() {
+ synchronized (sLock) {
+ if (sPositionTrackingCallback != null) {
+ sPositionTrackingCallback.post();
+ sPositionTrackingCallback = null;
+ }
+ }
+ Log.d(TAG, "currentPositionMs=" + mCurrentPositionMs);
+ return mCurrentPositionMs;
+ }
+
+ @Override
+ public long onTimeShiftGetStartPosition() {
+ return mRecordStartTimeMs;
+ }
+
+ @Override
+ public void onTimeShiftPause() {
+ synchronized (sLock) {
+ if (sResumeAfterPauseCallback != null) {
+ sResumeAfterPauseCallback.mBundle.putBoolean(PAUSE_CALLED, true);
+ }
+ }
+ mCurrentPositionMs = mPausedTimeMs = mLastCurrentPositionUpdateTimeMs
+ = System.currentTimeMillis();
+ }
+
+ @Override
+ public void onTimeShiftResume() {
+ synchronized (sLock) {
+ if (sResumeAfterPauseCallback != null
+ && sResumeAfterPauseCallback.mBundle.getBoolean(PAUSE_CALLED)) {
+ sResumeAfterPauseCallback.post();
+ sResumeAfterPauseCallback = null;
+ }
+ }
+ mSpeed = 1;
+ mPausedTimeMs = 0;
+ mLastCurrentPositionUpdateTimeMs = System.currentTimeMillis();
+ }
+
+ @Override
+ public void onTimeShiftSeekTo(long timeMs) {
+ synchronized (sLock) {
+ if (mCurrentPositionMs > timeMs) {
+ if (sSeekToPreviousCallback != null) {
+ sSeekToPreviousCallback.post();
+ sSeekToPreviousCallback = null;
+ }
+ } else if (mCurrentPositionMs < timeMs) {
+ if (sSeekToNextCallback != null) {
+ sSeekToNextCallback.post();
+ sSeekToNextCallback = null;
+ }
+ }
+ }
+ mLastCurrentPositionUpdateTimeMs = System.currentTimeMillis();
+ mCurrentPositionMs = Math.max(mRecordStartTimeMs,
+ Math.min(timeMs, mLastCurrentPositionUpdateTimeMs));
+ }
+
+ @Override
+ public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
+ synchronized(sLock) {
+ if (params != null) {
+ if (params.getSpeed() > 1) {
+ if (sFastForwardCallback != null) {
+ sFastForwardCallback.post();
+ sFastForwardCallback = null;
+ }
+ } else if (params.getSpeed() < 1) {
+ if (sRewindCallback != null) {
+ sRewindCallback.post();
+ sRewindCallback = null;
+ }
+ }
+ }
+ }
+ mSpeed = params.getSpeed();
+ }
}
private static class Callback {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java
index 66af4c6..a912cc6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java
@@ -17,7 +17,7 @@
package com.android.cts.verifier.tv;
import com.android.cts.verifier.R;
-
+import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.media.tv.TvContentRating;
@@ -36,6 +36,7 @@
/**
* Tests for verifying TV app behavior on multiple tracks and subtitle.
*/
+@SuppressLint("NewApi")
public class MultipleTracksTestActivity extends TvAppVerifierActivity
implements View.OnClickListener {
private static final String TAG = "MultipleTracksTestActivity";
@@ -113,7 +114,8 @@
}
if (mTvAppIntent == null) {
String[] projection = { TvContract.Channels._ID };
- try (Cursor cursor = getContentResolver().query(TvContract.Channels.CONTENT_URI,
+ try (Cursor cursor = getContentResolver().query(
+ TvContract.buildChannelsUriForInput(MockTvInputService.getInputId(this)),
projection, null, null, null)) {
if (cursor != null && cursor.moveToNext()) {
mTvAppIntent = new Intent(Intent.ACTION_VIEW,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/ParentalControlTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/ParentalControlTestActivity.java
index 284b485..c99da46 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/ParentalControlTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/ParentalControlTestActivity.java
@@ -17,7 +17,7 @@
package com.android.cts.verifier.tv;
import com.android.cts.verifier.R;
-
+import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.media.tv.TvContentRating;
@@ -35,6 +35,7 @@
/**
* Tests for verifying TV app behavior on parental control.
*/
+@SuppressLint("NewApi")
public class ParentalControlTestActivity extends TvAppVerifierActivity
implements View.OnClickListener {
private static final String TAG = "ParentalControlTestActivity";
@@ -110,7 +111,8 @@
}
if (mTvAppIntent == null) {
String[] projection = { TvContract.Channels._ID };
- try (Cursor cursor = getContentResolver().query(TvContract.Channels.CONTENT_URI,
+ try (Cursor cursor = getContentResolver().query(
+ TvContract.buildChannelsUriForInput(MockTvInputService.getInputId(this)),
projection, null, null, null)) {
if (cursor != null && cursor.moveToNext()) {
mTvAppIntent = new Intent(Intent.ACTION_VIEW,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TimeShiftTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TimeShiftTestActivity.java
new file mode 100644
index 0000000..5e4036c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TimeShiftTestActivity.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.media.tv.TvContract;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for verifying TV app behavior on time shift.
+ */
+public class TimeShiftTestActivity extends TvAppVerifierActivity
+ implements View.OnClickListener {
+ private static final long TIMEOUT_MS = TimeUnit.MINUTES.toMillis(5);
+ private static final boolean NOT_PASSED = false;
+ private static final boolean PASSED = true;
+
+ private View mPauseResumeItem;
+ private View mVerifyResumeAfterPauseItem;
+ private View mVerifyPositionTrackingItem;
+
+ private View mSetPlaybackParamsItem;
+ private View mVerifyRewindItem;
+ private View mVerifyFastForwardItem;
+
+ private View mSeekToItem;
+ private View mVerifySeekToPreviousItem;
+ private View mVerifySeekToNextItem;
+
+ private Intent mTvAppIntent = null;
+
+ @Override
+ public void onClick(View v) {
+ final View postTarget = getPostTarget();
+
+ if (containsButton(mPauseResumeItem, v)) {
+ mVerifyResumeAfterPauseItem.setTag(NOT_PASSED);
+ mVerifyPositionTrackingItem.setTag(NOT_PASSED);
+
+ final Runnable failCallback = new Runnable() {
+ @Override
+ public void run() {
+ setPassState(mVerifyResumeAfterPauseItem, false);
+ setPassState(mVerifyPositionTrackingItem, false);
+ }
+ };
+ postTarget.postDelayed(failCallback, TIMEOUT_MS);
+ MockTvInputService.expectResumeAfterPause(postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mPauseResumeItem, true);
+ setPassState(mVerifyResumeAfterPauseItem, true);
+ mVerifyResumeAfterPauseItem.setTag(PASSED);
+ if (isPassedState(mVerifyResumeAfterPauseItem)
+ && isPassedState(mVerifyPositionTrackingItem)) {
+ setButtonEnabled(mSetPlaybackParamsItem, true);
+ }
+ }
+ });
+ MockTvInputService.expectPositionTracking(postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mPauseResumeItem, true);
+ setPassState(mVerifyPositionTrackingItem, true);
+ mVerifyPositionTrackingItem.setTag(PASSED);
+ if (isPassedState(mVerifyResumeAfterPauseItem)
+ && isPassedState(mVerifyPositionTrackingItem)) {
+ setButtonEnabled(mSetPlaybackParamsItem, true);
+ }
+ }
+ });
+ } else if (containsButton(mSetPlaybackParamsItem, v)) {
+ mVerifyRewindItem.setTag(NOT_PASSED);
+ mVerifyFastForwardItem.setTag(NOT_PASSED);
+
+ final Runnable failCallback = new Runnable() {
+ @Override
+ public void run() {
+ setPassState(mVerifyRewindItem, false);
+ setPassState(mVerifyFastForwardItem, false);
+ }
+ };
+ postTarget.postDelayed(failCallback, TIMEOUT_MS);
+ MockTvInputService.expectRewind(postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mSetPlaybackParamsItem, true);
+ setPassState(mVerifyRewindItem, true);
+ mVerifyRewindItem.setTag(PASSED);
+ if (isPassedState(mVerifyRewindItem) && isPassedState(mVerifyFastForwardItem)) {
+ setButtonEnabled(mSeekToItem, true);
+ }
+ }
+ });
+ MockTvInputService.expectFastForward(postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mSetPlaybackParamsItem, true);
+ setPassState(mVerifyFastForwardItem, true);
+ mVerifyFastForwardItem.setTag(PASSED);
+ if (isPassedState(mVerifyRewindItem) && isPassedState(mVerifyFastForwardItem)) {
+ setButtonEnabled(mSeekToItem, true);
+ }
+ }
+ });
+ } else if (containsButton(mSeekToItem, v)) {
+ mVerifySeekToPreviousItem.setTag(NOT_PASSED);
+ mVerifySeekToNextItem.setTag(NOT_PASSED);
+
+ final Runnable failCallback = new Runnable() {
+ @Override
+ public void run() {
+ setPassState(mVerifySeekToPreviousItem, false);
+ setPassState(mVerifySeekToNextItem, false);
+ }
+ };
+ postTarget.postDelayed(failCallback, TIMEOUT_MS);
+ MockTvInputService.expectSeekToPrevious(postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mSeekToItem, true);
+ setPassState(mVerifySeekToPreviousItem, true);
+ mVerifySeekToPreviousItem.setTag(PASSED);
+ if (isPassedState(mVerifySeekToPreviousItem)
+ && isPassedState(mVerifySeekToNextItem)) {
+ getPassButton().setEnabled(true);
+ }
+ }
+ });
+ MockTvInputService.expectSeekToNext(postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mSeekToItem, true);
+ setPassState(mVerifySeekToNextItem, true);
+ mVerifySeekToNextItem.setTag(PASSED);
+ if (isPassedState(mVerifySeekToPreviousItem)
+ && isPassedState(mVerifySeekToNextItem)) {
+ getPassButton().setEnabled(true);
+ }
+ }
+ });
+ }
+ if (mTvAppIntent == null) {
+ String[] projection = { TvContract.Channels._ID };
+ try (Cursor cursor = getContentResolver().query(
+ TvContract.buildChannelsUriForInput(MockTvInputService.getInputId(this)),
+ projection, null, null, null)) {
+ if (cursor != null && cursor.moveToNext()) {
+ mTvAppIntent = new Intent(Intent.ACTION_VIEW,
+ TvContract.buildChannelUri(cursor.getLong(0)));
+ }
+ }
+ if (mTvAppIntent == null) {
+ Toast.makeText(this, R.string.tv_channel_not_found, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+ startActivity(mTvAppIntent);
+ }
+
+ @Override
+ protected void createTestItems() {
+ mPauseResumeItem = createUserItem(
+ R.string.tv_time_shift_test_pause_resume,
+ R.string.tv_launch_tv_app, this);
+ setButtonEnabled(mPauseResumeItem, true);
+ mVerifyResumeAfterPauseItem = createAutoItem(
+ R.string.tv_time_shift_test_verify_resume_after_pause);
+ mVerifyPositionTrackingItem = createAutoItem(
+ R.string.tv_time_shift_test_verify_position_tracking);
+ mSetPlaybackParamsItem = createUserItem(
+ R.string.tv_time_shift_test_speed_rate,
+ R.string.tv_launch_tv_app, this);
+ mVerifyRewindItem = createAutoItem(
+ R.string.tv_time_shift_test_verify_rewind);
+ mVerifyFastForwardItem = createAutoItem(
+ R.string.tv_time_shift_test_verify_fast_forward);
+ mSeekToItem = createUserItem(
+ R.string.tv_time_shift_test_seek,
+ R.string.tv_launch_tv_app, this);
+ mVerifySeekToPreviousItem = createAutoItem(
+ R.string.tv_time_shift_test_verify_seek_to_previous);
+ mVerifySeekToNextItem = createAutoItem(
+ R.string.tv_time_shift_test_verify_seek_to_next);
+ }
+
+ @Override
+ protected void setInfoResources() {
+ setInfoResources(R.string.tv_time_shift_test,
+ R.string.tv_time_shift_test_info, -1);
+ }
+
+ private boolean isPassedState(View view) {
+ return ((Boolean) view.getTag()) == PASSED;
+ }
+}
diff --git a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
index d0a3a37..efb53d5 100644
--- a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
+++ b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
@@ -22,9 +22,9 @@
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
-import vogar.Expectation;
import vogar.ExpectationStore;
import vogar.ModeId;
+import vogar.Result;
import java.io.File;
import java.io.FileOutputStream;
@@ -161,7 +161,7 @@
}
public static boolean isKnownFailure(ExpectationStore store, String fullname) {
- return store != null && store.get(fullname) != Expectation.SUCCESS;
+ return store != null && store.get(fullname).getResult() != Result.SUCCESS;
}
public static void main(String[] args) throws Exception {
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-dsa-a.pk8 b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-dsa-a.pk8
new file mode 100644
index 0000000..ac0b0c1
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-dsa-a.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.pk8 b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.pk8
new file mode 100644
index 0000000..ec27be1
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.x509.pem b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.x509.pem
new file mode 100644
index 0000000..183691d
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBejCCAR+gAwIBAgIJAMsY4Fz5jr/IMAoGCCqGSM49BAMCMBkxFzAVBgNVBAMM
+DnVuaXRfdGVzdF9lY19hMB4XDTE1MDYwMTIxNDU1M1oXDTQyMTAxNzIxNDU1M1ow
+GTEXMBUGA1UEAwwOdW5pdF90ZXN0X2VjX2EwWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAAR8Q+7lg4KSOs2Be0XhFwlFCsiCCIh3iX2t6fE+V/MD+QBT1265hIyBKEH/
+oAsTpLy8FdGKLC0x+TwuCedui0SBo1AwTjAdBgNVHQ4EFgQUX4h7gPTgwQXorm0H
+7R12wN2yNrwwHwYDVR0jBBgwFoAUX4h7gPTgwQXorm0H7R12wN2yNrwwDAYDVR0T
+BAUwAwEB/zAKBggqhkjOPQQDAgNJADBGAiEA5kHO4aK20dwt81mCABAywD7Y6V1O
+vqoff9yIx3USW8oCIQDTzo8tbHuPc+i3vBsb5Uo1+4BE/pcOe/je6PGlRHG8rg==
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java
index 7f3737d..9637a6c 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java
@@ -76,6 +76,10 @@
"CtsKeySetSigningCBadAUpgradeAB.apk";
private static final String A_SIGNED_NO_B_B_UPGRADE =
"CtsKeySetSigningANoDefUpgradeB.apk";
+ private static final String A_SIGNED_EC_A_UPGRADE =
+ "CtsKeySetSigningAUpgradeEcA.apk";
+ private static final String EC_A_SIGNED_A_UPGRADE =
+ "CtsKeySetSigningEcAUpgradeA.apk";
/* package which defines the KEYSET_PERM_NAME signature permission */
private static final String KEYSET_PERM_DEF_PKG =
@@ -486,4 +490,26 @@
assertNotNull("Installation of apk with upgrade key referring to a bad public key succeeded!",
installResult);
}
+
+ /*
+ * Check if an apk signed by RSA pub key can upgrade to apk signed by EC key.
+ */
+ public void testUpgradeKSRsaToEC() throws Exception {
+ String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_EC_A_UPGRADE,
+ EC_A_SIGNED_A_UPGRADE);
+ assertNull(String.format("failed to upgrade keyset app from one signed by RSA key"
+ + "to version signed by EC upgrade-key-set, Reason: %s", installResult),
+ installResult);
+ }
+
+ /*
+ * Check if an apk signed by EC pub key can upgrade to apk signed by RSA key.
+ */
+ public void testUpgradeKSECToRSA() throws Exception {
+ String installResult = testPackageUpgrade(KEYSET_PKG, EC_A_SIGNED_A_UPGRADE,
+ A_SIGNED_EC_A_UPGRADE);
+ assertNull(String.format("failed to upgrade keyset app from one signed by EC key"
+ + "to version signed by RSA upgrade-key-set, Reason: %s", installResult),
+ installResult);
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
index fb8993c..14f215c 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
@@ -239,13 +239,16 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- try {
- final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
- pipe[0]);
- doc.contents = readFullyNoClose(is);
- is.close();
- } catch (IOException e) {
- Log.w(TAG, "Failed to stream", e);
+ synchronized (doc) {
+ try {
+ final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
+ pipe[0]);
+ doc.contents = readFullyNoClose(is);
+ is.close();
+ doc.notifyAll();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to stream", e);
+ }
}
return null;
}
@@ -255,13 +258,20 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- try {
- final OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(
- pipe[1]);
- os.write(doc.contents);
- os.close();
- } catch (IOException e) {
- Log.w(TAG, "Failed to stream", e);
+ synchronized (doc) {
+ try {
+ final OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(
+ pipe[1]);
+ while (doc.contents == null) {
+ doc.wait();
+ }
+ os.write(doc.contents);
+ os.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to stream", e);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Interuppted", e);
+ }
}
return null;
}
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
index 4d441de..79d053b 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
@@ -37,6 +37,18 @@
include $(BUILD_CTS_SUPPORT_PACKAGE)
+#apks signed by cts-keyset-test-ec-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningEcAUpgradeA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
#apks signed by cts-keyset-test-a and cts-keyset-test-b
include $(CLEAR_VARS)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uEcA/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uEcA/Android.mk
new file mode 100644
index 0000000..3d0109a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uEcA/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed by cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeEcA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uEcA/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/uEcA/AndroidManifest.xml
new file mode 100644
index 0000000..a84704a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uEcA/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.keysets">
+ <application android:hasCode="false">
+ </application>
+ <key-sets>
+ <key-set android:name="EcA" >
+ <public-key android:name="keyEcA"
+ android:value="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfEPu5YOCkjrNgXtF4RcJRQrIggiId4l9renxPlfzA/kAU9duuYSMgShB/6ALE6S8vBXRiiwtMfk8LgnnbotEgQ=="/>
+ </key-set>
+ <upgrade-key-set android:name="EcA"/>
+ </key-sets>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
index e4f5134b..dcb5bbc 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
@@ -19,6 +19,9 @@
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Process;
import android.test.AndroidTestCase;
/**
@@ -32,6 +35,14 @@
public class BaseDeviceOwnerTest extends AndroidTestCase {
public static class BasicAdminReceiver extends DeviceAdminReceiver {
+ @Override
+ public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri,
+ String suggestedAlias) {
+ if (uid != Process.myUid() || uri == null) {
+ return null;
+ }
+ return uri.getQueryParameter("alias");
+ }
}
public static final String PACKAGE_NAME = BaseDeviceOwnerTest.class.getPackage().getName();
@@ -44,11 +55,15 @@
mDevicePolicyManager = (DevicePolicyManager)
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- assertTrue(mDevicePolicyManager.isAdminActive(getWho()));
- assertTrue(mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME));
+ assertDeviceOwner(mDevicePolicyManager);
}
- public static ComponentName getWho() {
+ static void assertDeviceOwner(DevicePolicyManager dpm) {
+ assertTrue(dpm.isAdminActive(getWho()));
+ assertTrue(dpm.isDeviceOwnerApp(PACKAGE_NAME));
+ }
+
+ protected static ComponentName getWho() {
return new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName());
}
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
index 23d108f..a608794 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
@@ -15,11 +15,21 @@
*/
package com.android.cts.deviceowner;
+import static com.android.cts.deviceowner.BaseDeviceOwnerTest.getWho;
import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1;
+import android.app.Activity;
import android.app.admin.DevicePolicyManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
+import android.security.KeyChainException;
+import android.test.ActivityInstrumentationTestCase2;
import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.Certificate;
@@ -28,12 +38,31 @@
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
-public class KeyManagementTest extends BaseDeviceOwnerTest {
+import android.content.ComponentName;
+import android.content.Context;
+
+public class KeyManagementTest extends
+ ActivityInstrumentationTestCase2<ExampleIntentReceivingActivity1> {
+
+ private static final int KEYCHAIN_TIMEOUT_MS = 8000;
+ private DevicePolicyManager mDevicePolicyManager;
+
+ public KeyManagementTest() {
+ super(ExampleIntentReceivingActivity1.class);
+ }
@Override
protected void setUp() throws Exception {
super.setUp();
+
+ // Confirm our DeviceOwner is set up
+ mDevicePolicyManager = (DevicePolicyManager)
+ getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ BaseDeviceOwnerTest.assertDeviceOwner(mDevicePolicyManager);
+
// Enable credential storage by setting a nonempty password.
assertTrue(mDevicePolicyManager.resetPassword("test", 0));
}
@@ -49,27 +78,32 @@
}
public void testCanInstallValidRsaKeypair()
- throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException {
+ throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException,
+ KeyChainException, InterruptedException, UnsupportedEncodingException {
final String alias = "com.android.test.valid-rsa-key-1";
final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA");
final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, cert, alias));
+
+ assertEquals(alias, new KeyChainAliasFuture(alias).get());
+ final PrivateKey retrievedKey = KeyChain.getPrivateKey(getActivity(), alias);
+ assertEquals(retrievedKey.getAlgorithm(), "RSA");
}
- public void testNullKeyParamsFailGracefully()
+ public void testNullKeyParamsFailPredictably()
throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException {
final String alias = "com.android.test.null-key-1";
final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA");
final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
try {
- assertFalse(mDevicePolicyManager.installKeyPair(getWho(), null, cert, alias));
- } catch (NullPointerException accept) {
- // Accept either false return value or NPE
+ mDevicePolicyManager.installKeyPair(getWho(), null, cert, alias);
+ fail("Exception should have been thrown for null PrivateKey");
+ } catch (NullPointerException expected) {
}
try {
- assertFalse(mDevicePolicyManager.installKeyPair(getWho(), privKey, null, alias));
- } catch (NullPointerException accept) {
- // Accept either false return value or NPE
+ mDevicePolicyManager.installKeyPair(getWho(), privKey, null, alias);
+ fail("Exception should have been thrown for null Certificate");
+ } catch (NullPointerException expected) {
}
}
@@ -79,21 +113,46 @@
final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA");
final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
try {
- assertFalse(mDevicePolicyManager.installKeyPair(null, privKey, cert, alias));
+ mDevicePolicyManager.installKeyPair(null, privKey, cert, alias);
fail("Exception should have been thrown for null ComponentName");
- } catch (SecurityException | NullPointerException expected) {
+ } catch (SecurityException expected) {
}
}
- PrivateKey getPrivateKey(final byte[] key, String type)
+ private static PrivateKey getPrivateKey(final byte[] key, String type)
throws NoSuchAlgorithmException, InvalidKeySpecException {
return KeyFactory.getInstance(type).generatePrivate(
new PKCS8EncodedKeySpec(key));
}
- Certificate getCertificate(byte[] cert) throws CertificateException {
+ private static Certificate getCertificate(byte[] cert) throws CertificateException {
return CertificateFactory.getInstance("X.509").generateCertificate(
new ByteArrayInputStream(cert));
}
+ private class KeyChainAliasFuture implements KeyChainAliasCallback {
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private String mChosenAlias = null;
+
+ @Override
+ public void alias(final String chosenAlias) {
+ mChosenAlias = chosenAlias;
+ mLatch.countDown();
+ }
+
+ public KeyChainAliasFuture(String alias) throws UnsupportedEncodingException {
+ /* Pass the alias as a GET to an imaginary server instead of explicitly asking for it,
+ * to make sure the DPC actually has to do some work to grant the cert.
+ */
+ final Uri uri =
+ Uri.parse("https://example.org/?alias=" + URLEncoder.encode(alias, "UTF-8"));
+ KeyChain.choosePrivateKeyAlias(getActivity(), this,
+ null /* keyTypes */, null /* issuers */, uri, null /* alias */);
+ }
+
+ public String get() throws InterruptedException {
+ assertTrue("Chooser timeout", mLatch.await(KEYCHAIN_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ return mChosenAlias;
+ }
+ };
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SilentPackageInstallerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
similarity index 71%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SilentPackageInstallerTest.java
rename to hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
index 8e63543..0eddbee 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SilentPackageInstallerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
@@ -33,29 +33,33 @@
/**
* This class tests silent package install and uninstall by a device owner.
*/
-public class SilentPackageInstallerTest extends BaseDeviceOwnerTest {
+public class PackageInstallTest extends BaseDeviceOwnerTest {
private static final String TEST_APP_LOCATION = "/data/local/tmp/CtsSimpleApp.apk";
private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
private static final int PACKAGE_INSTALLER_TIMEOUT_MS = 60000; // 60 seconds
private static final String ACTION_INSTALL_COMMIT =
"com.android.cts.deviceowner.INTENT_PACKAGE_INSTALL_COMMIT";
+ private static final int PACKAGE_INSTALLER_STATUS_UNDEFINED = -1000;
private PackageManager mPackageManager;
private PackageInstaller mPackageInstaller;
private PackageInstaller.Session mSession;
private boolean mCallbackReceived;
+ private int mCallbackStatus;
private final Object mPackageInstallerTimeoutLock = new Object();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- assertEquals(PackageInstaller.STATUS_SUCCESS, intent.getIntExtra(
- PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE));
- assertEquals(TEST_APP_PKG, intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME));
mContext.unregisterReceiver(this);
synchronized (mPackageInstallerTimeoutLock) {
+ mCallbackStatus = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PACKAGE_INSTALLER_STATUS_UNDEFINED);
+ if (mCallbackStatus == PackageInstaller.STATUS_SUCCESS) {
+ assertEquals(TEST_APP_PKG, intent.getStringExtra(
+ PackageInstaller.EXTRA_PACKAGE_NAME));
+ }
mCallbackReceived = true;
mPackageInstallerTimeoutLock.notify();
}
@@ -68,11 +72,14 @@
mPackageManager = mContext.getPackageManager();
mPackageInstaller = mPackageManager.getPackageInstaller();
assertNotNull(mPackageInstaller);
- mCallbackReceived = false;
+
+ // check that app is not already installed
+ assertFalse(isPackageInstalled(TEST_APP_PKG));
}
@Override
protected void tearDown() throws Exception {
+ mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
try {
mContext.unregisterReceiver(mBroadcastReceiver);
} catch (IllegalArgumentException e) {
@@ -85,10 +92,36 @@
}
public void testSilentInstallUninstall() throws Exception {
- // check that app is not already installed
- assertFalse(isPackageInstalled(TEST_APP_PKG));
-
// install the app
+ assertInstallPackage();
+
+ // uninstall the app again
+ assertTrue(tryUninstallPackage());
+ assertFalse(isPackageInstalled(TEST_APP_PKG));
+ }
+
+ public void testUninstallBlocked() throws Exception {
+ // install the app
+ assertInstallPackage();
+
+ mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, true);
+ assertTrue(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
+ assertTrue(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
+ assertFalse(tryUninstallPackage());
+ assertTrue(isPackageInstalled(TEST_APP_PKG));
+
+ mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
+ assertFalse(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
+ assertFalse(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
+ assertTrue(tryUninstallPackage());
+ assertFalse(isPackageInstalled(TEST_APP_PKG));
+ }
+
+ private void assertInstallPackage() throws Exception {
+ synchronized (mPackageInstallerTimeoutLock) {
+ mCallbackReceived = false;
+ mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
+ }
installPackage(TEST_APP_LOCATION);
synchronized (mPackageInstallerTimeoutLock) {
try {
@@ -96,20 +129,26 @@
} catch (InterruptedException e) {
}
assertTrue(mCallbackReceived);
+ assertEquals(PackageInstaller.STATUS_SUCCESS, mCallbackStatus);
}
assertTrue(isPackageInstalled(TEST_APP_PKG));
+ }
- // uninstall the app again
+ private boolean tryUninstallPackage() throws Exception {
+ assertTrue(isPackageInstalled(TEST_APP_PKG));
synchronized (mPackageInstallerTimeoutLock) {
mCallbackReceived = false;
- mPackageInstaller.uninstall(TEST_APP_PKG, getCommitCallback(0));
+ mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
+ }
+ mPackageInstaller.uninstall(TEST_APP_PKG, getCommitCallback(0));
+ synchronized (mPackageInstallerTimeoutLock) {
try {
mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS);
} catch (InterruptedException e) {
}
assertTrue(mCallbackReceived);
+ return mCallbackStatus == PackageInstaller.STATUS_SUCCESS;
}
- assertFalse(isPackageInstalled(TEST_APP_PKG));
}
private void installPackage(String packageLocation) throws Exception {
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/Android.mk b/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
index 4517ea2..5f645b6 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
+++ b/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
@@ -26,6 +26,8 @@
LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctstestrunner
+
LOCAL_SDK_VERSION := current
include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/AndroidManifest.xml b/hostsidetests/devicepolicy/app/LauncherTests/AndroidManifest.xml
index a21b8c2..5ded006 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/LauncherTests/AndroidManifest.xml
@@ -23,7 +23,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.test.InstrumentationTestRunner"
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.cts.launchertests"
android:label="Launcher Apps CTS Tests"/>
</manifest>
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
index 3d44ecd..3bd21d9 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
@@ -38,8 +38,8 @@
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
-import android.test.InstrumentationTestCase;
-import android.test.InstrumentationTestRunner;
+import android.support.test.InstrumentationRegistry;
+import android.test.AndroidTestCase;
import android.util.Pair;
import java.util.concurrent.Semaphore;
@@ -49,7 +49,7 @@
/**
* Tests for LauncherApps service
*/
-public class LauncherAppsTests extends InstrumentationTestCase {
+public class LauncherAppsTests extends AndroidTestCase {
public static final String SIMPLE_APP_PACKAGE = "com.android.cts.launcherapps.simpleapp";
@@ -69,7 +69,7 @@
private LauncherApps mLauncherApps;
private UserHandle mUser;
- private InstrumentationTestRunner mInstrumentation;
+ private Instrumentation mInstrumentation;
private Messenger mService;
private Connection mConnection;
private Result mResult;
@@ -78,8 +78,8 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mInstrumentation = (InstrumentationTestRunner) getInstrumentation();
- Bundle arguments = mInstrumentation.getArguments();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ Bundle arguments = InstrumentationRegistry.getArguments();
UserManager userManager = (UserManager) mInstrumentation.getContext().getSystemService(
Context.USER_SERVICE);
mUser = getUserHandleArgument(userManager, "testUser", arguments);
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 82f6d6d..31e4ad4 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
+ <uses-permission android:name="android.permission.CAMERA" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/device_admin.xml
index 8f39ed0..c3d5c98 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/device_admin.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/device_admin.xml
@@ -15,5 +15,6 @@
<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
<uses-policies>
<wipe-data />
+ <disable-camera />
</uses-policies>
</device-admin>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
index a6aff49..0ec3d1e 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
@@ -16,5 +16,6 @@
<uses-policies>
<reset-password />
<limit-password />
+ <disable-camera />
</uses-policies>
</device-admin>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CameraPolicyTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CameraPolicyTest.java
new file mode 100644
index 0000000..25e0b94
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CameraPolicyTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.managedprofile;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+
+
+public class CameraPolicyTest extends AndroidTestCase {
+
+ protected static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
+
+ private static final String PRIMARY_ADMIN_RECEIVER_TEST_CLASS =
+ MANAGED_PROFILE_PKG + ".PrimaryUserDeviceAdmin";
+
+ private static final String MANAGED_PROFILE_ADMIN_RECEIVER_TEST_CLASS =
+ MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
+
+ private DevicePolicyManager mDevicePolicyManager;
+
+ private CameraManager mCameraManager;
+
+ private ComponentName mPrimaryAdminComponent;
+
+ private ComponentName mManagedProfileAdminComponent;
+
+ private HandlerThread mBackgroundThread;
+
+ /**
+ * A {@link Handler} for running tasks in the background.
+ */
+ private Handler mBackgroundHandler;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDevicePolicyManager = (DevicePolicyManager) getContext()
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mCameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
+ mPrimaryAdminComponent = new ComponentName(MANAGED_PROFILE_PKG,
+ PRIMARY_ADMIN_RECEIVER_TEST_CLASS);
+ mManagedProfileAdminComponent = new ComponentName(MANAGED_PROFILE_PKG,
+ MANAGED_PROFILE_ADMIN_RECEIVER_TEST_CLASS);
+ startBackgroundThread();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ stopBackgroundThread();
+ super.tearDown();
+ }
+
+ public void testDisableCameraInManagedProfile() throws Exception {
+ mDevicePolicyManager.setCameraDisabled(mManagedProfileAdminComponent, true);
+ assertTrue(mDevicePolicyManager.getCameraDisabled(mManagedProfileAdminComponent));
+ assertTrue(mDevicePolicyManager.getCameraDisabled(null));
+ checkCanOpenCamera(false);
+ }
+
+ public void testEnableCameraInManagedProfile() throws Exception {
+ mDevicePolicyManager.setCameraDisabled(mManagedProfileAdminComponent, false);
+ assertFalse(mDevicePolicyManager.getCameraDisabled(mManagedProfileAdminComponent));
+ assertFalse(mDevicePolicyManager.getCameraDisabled(null));
+ checkCanOpenCamera(true);
+ }
+
+ public void testDisableCameraInPrimaryProfile() throws Exception {
+ mDevicePolicyManager.setCameraDisabled(mPrimaryAdminComponent, true);
+ assertTrue(mDevicePolicyManager.getCameraDisabled(mPrimaryAdminComponent));
+ assertTrue(mDevicePolicyManager.getCameraDisabled(null));
+ checkCanOpenCamera(false);
+ }
+
+ public void testEnableCameraInPrimaryProfile() throws Exception {
+ mDevicePolicyManager.setCameraDisabled(mPrimaryAdminComponent, false);
+ assertFalse(mDevicePolicyManager.getCameraDisabled(mPrimaryAdminComponent));
+ assertFalse(mDevicePolicyManager.getCameraDisabled(null));
+ checkCanOpenCamera(true);
+ }
+
+ public void testIsCameraEnabledInPrimaryProfile() throws Exception {
+ assertFalse(mDevicePolicyManager.getCameraDisabled(mPrimaryAdminComponent));
+ assertFalse(mDevicePolicyManager.getCameraDisabled(null));
+ checkCanOpenCamera(true);
+ }
+
+ public void testIsCameraEnabledInManagedProfile() throws Exception {
+ assertFalse(mDevicePolicyManager.getCameraDisabled(mManagedProfileAdminComponent));
+ assertFalse(mDevicePolicyManager.getCameraDisabled(null));
+ checkCanOpenCamera(true);
+ }
+
+ private void checkCanOpenCamera(boolean canOpen) {
+ boolean successToOpen = CameraUtils
+ .blockUntilOpenCamera(mCameraManager, mBackgroundHandler);
+ assertEquals(canOpen, successToOpen);
+ }
+
+ /**
+ * Starts a background thread and its {@link Handler}.
+ */
+ private void startBackgroundThread() {
+ mBackgroundThread = new HandlerThread("CameraBackground");
+ mBackgroundThread.start();
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+ }
+
+ /**
+ * Stops the background thread and its {@link Handler}.
+ */
+ private void stopBackgroundThread() {
+ mBackgroundThread.quitSafely();
+ try {
+ mBackgroundThread.join();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CameraUtils.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CameraUtils.java
new file mode 100644
index 0000000..516e244
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CameraUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.managedprofile;
+
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A util class to help open camera in a blocking way.
+ */
+class CameraUtils {
+
+ private static final String TAG = "CameraUtils";
+
+ /**
+ * @return true if success to open camera, false otherwise.
+ */
+ public static boolean blockUntilOpenCamera(CameraManager cameraManager, Handler handler) {
+ try {
+ String[] cameraIdList = cameraManager.getCameraIdList();
+ if (cameraIdList == null || cameraIdList.length == 0) {
+ return false;
+ }
+ String cameraId = cameraIdList[0];
+ CameraCallback callback = new CameraCallback();
+ cameraManager.openCamera(cameraId, callback, handler);
+ return callback.waitForResult();
+ } catch (Exception ex) {
+ // No matter what is going wrong, it means fail to open camera.
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
+ */
+ private static class CameraCallback extends CameraDevice.StateCallback {
+
+ private static final int OPEN_TIMEOUT_SECONDS = 5;
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+
+ private AtomicBoolean mResult = new AtomicBoolean(false);
+
+ @Override
+ public void onOpened(CameraDevice cameraDevice) {
+ Log.d(TAG, "open camera successfully");
+ mResult.set(true);
+ if (cameraDevice != null) {
+ cameraDevice.close();
+ }
+ mLatch.countDown();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice cameraDevice) {
+ Log.d(TAG, "disconnect camera");
+ mLatch.countDown();
+ }
+
+ @Override
+ public void onError(CameraDevice cameraDevice, int error) {
+ Log.e(TAG, "Fail to open camera, error code = " + error);
+ mLatch.countDown();
+ }
+
+ public boolean waitForResult() throws InterruptedException {
+ mLatch.await(OPEN_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ return mResult.get();
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java
index 2faf158..1cc2ee2 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java
@@ -30,6 +30,8 @@
public class PermissionsTest extends BaseManagedProfileTest {
private static final String SIMPLE_APP_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp";
+ private static final String SIMPLE_PRE_M_APP_PACKAGE_NAME =
+ "com.android.cts.launcherapps.simplepremapp";
private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
@Override
@@ -47,8 +49,8 @@
mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME,
DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
- assertTrue(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME) ==
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME),
DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_APP_PACKAGE_NAME),
PackageManager.PERMISSION_DENIED);
@@ -56,8 +58,8 @@
mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME,
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
- assertTrue(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME) ==
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME),
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
// Should stay denied
assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_APP_PACKAGE_NAME),
@@ -66,8 +68,8 @@
mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME,
DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
- assertTrue(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME) ==
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME),
DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_APP_PACKAGE_NAME),
PackageManager.PERMISSION_GRANTED);
@@ -75,8 +77,8 @@
mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME,
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
- assertTrue(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME) ==
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME),
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
// Should stay granted
assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_APP_PACKAGE_NAME),
@@ -97,4 +99,29 @@
assertEquals(mDevicePolicyManager.getPermissionPolicy(ADMIN_RECEIVER_COMPONENT),
DevicePolicyManager.PERMISSION_POLICY_PROMPT);
}
+
+ public void testPermissionGrantStatePreMApp() {
+ // These tests are to make sure that pre-M apps are not granted runtime permissions
+ // by a profile owner
+ PackageManager pm = mContext.getPackageManager();
+ assertFalse(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED));
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME),
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ // Install time permissions should always be granted
+ assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_PRE_M_APP_PACKAGE_NAME),
+ PackageManager.PERMISSION_GRANTED);
+
+ mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME),
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ // Install time permissions should always be granted
+ assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_PRE_M_APP_PACKAGE_NAME),
+ PackageManager.PERMISSION_GRANTED);
+ }
}
diff --git a/hostsidetests/devicepolicy/app/SimplePreMApp/Android.mk b/hostsidetests/devicepolicy/app/SimplePreMApp/Android.mk
new file mode 100644
index 0000000..7460552
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimplePreMApp/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This app is meant for testing device policy permission APIs on legacy apps (pre-M)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSimplePreMApp
+
+LOCAL_SDK_VERSION := 21
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/SimplePreMApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SimplePreMApp/AndroidManifest.xml
new file mode 100644
index 0000000..85962a1
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimplePreMApp/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.launcherapps.simplepremapp">
+
+ <uses-sdk android:targetSdkVersion="21"/>
+
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+
+ <application>
+ <activity android:name=".SimpleActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
+
diff --git a/hostsidetests/devicepolicy/app/SimplePreMApp/src/com/android/cts/launcherapps/simplepremapp/SimpleActivity.java b/hostsidetests/devicepolicy/app/SimplePreMApp/src/com/android/cts/launcherapps/simplepremapp/SimpleActivity.java
new file mode 100644
index 0000000..0dba719
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimplePreMApp/src/com/android/cts/launcherapps/simplepremapp/SimpleActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.launcherapps.simplepremapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.lang.Override;
+
+/**
+ * A simple activity to install for various users to test LauncherApps.
+ */
+public class SimpleActivity extends Activity {
+
+ private static final String TAG = "SimpleActivity";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ Log.i(TAG, "Created for user " + android.os.Process.myUserHandle());
+ }
+
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index bd53e91..ebdbcc7 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -95,12 +95,12 @@
executeDeviceOwnerTest("ScreenCaptureDisabledTest");
}
- public void testSilentPackageInstaller() throws Exception {
+ public void testPackageInstall() throws Exception {
final File apk = mCtsBuild.getTestApp(TEST_APP_APK);
try {
getDevice().uninstallPackage(TEST_APP_PKG);
assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
- executeDeviceOwnerTest("SilentPackageInstallerTest");
+ executeDeviceOwnerTest("PackageInstallTest");
} finally {
String command = "rm " + TEST_APP_LOCATION + apk.getName();
String commandOutput = getDevice().executeShellCommand(command);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 255c49e..3f0805c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -31,6 +31,7 @@
private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
private static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
+ private static final String SIMPLE_PRE_M_APP_APK = "CtsSimplePreMApp.apk";
private static final String INTENT_SENDER_PKG = "com.android.cts.intent.sender";
private static final String INTENT_SENDER_APK = "CtsIntentSenderApp.apk";
@@ -45,6 +46,7 @@
MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
+ private static final String FEATURE_CAMERA = "android.hardware.camera";
private int mUserId;
@Override
@@ -257,6 +259,52 @@
"testGetRemoteDevice", mUserId));
}
+ public void testCameraPolicy() throws Exception {
+ boolean hasCamera = hasDeviceFeature(FEATURE_CAMERA);
+ if (!mHasFeature || !hasCamera) {
+ return;
+ }
+ try {
+ setDeviceAdmin(MANAGED_PROFILE_PKG + "/.PrimaryUserDeviceAdmin");
+
+ // Disable managed profile camera.
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CameraPolicyTest",
+ "testDisableCameraInManagedProfile",
+ mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CameraPolicyTest",
+ "testIsCameraEnabledInPrimaryProfile",
+ 0));
+
+ // Enable managed profile camera.
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CameraPolicyTest",
+ "testEnableCameraInManagedProfile",
+ mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CameraPolicyTest",
+ "testIsCameraEnabledInPrimaryProfile",
+ 0));
+
+ // Disable primary profile camera.
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CameraPolicyTest",
+ "testDisableCameraInPrimaryProfile",
+ 0));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CameraPolicyTest",
+ "testIsCameraEnabledInManagedProfile",
+ mUserId));
+
+ // Enable primary profile camera.
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CameraPolicyTest",
+ "testEnableCameraInPrimaryProfile",
+ 0));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CameraPolicyTest",
+ "testIsCameraEnabledInManagedProfile",
+ mUserId));
+ } finally {
+ final String adminHelperClass = ".PrimaryUserAdminHelper";
+ assertTrue("Clear device admin failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ adminHelperClass, "testClearDeviceAdmin", 0 /* user 0 */));
+ }
+ }
+
public void testManagedContacts() throws Exception {
if (!mHasFeature) {
return;
@@ -417,11 +465,20 @@
if (!mHasFeature) {
return;
}
- installApp(SIMPLE_APP_APK);
+ installAppAsUser(SIMPLE_APP_APK, mUserId);
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
"testPermissionGrantState", mUserId));
}
+ public void testPermissionGrantPreMApp() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppAsUser(SIMPLE_PRE_M_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionGrantStatePreMApp", mUserId));
+ }
+
private void disableActivityForUser(String activityName, int userId)
throws DeviceNotAvailableException {
String command = "am start -W --user " + userId
diff --git a/hostsidetests/monkey/src/com/android/cts/monkey/SeedTest.java b/hostsidetests/monkey/src/com/android/cts/monkey/SeedTest.java
index a0016e2..d070972 100644
--- a/hostsidetests/monkey/src/com/android/cts/monkey/SeedTest.java
+++ b/hostsidetests/monkey/src/com/android/cts/monkey/SeedTest.java
@@ -16,6 +16,8 @@
package com.android.cts.monkey;
+import com.android.tradefed.log.LogUtil.CLog;
+
import java.util.Scanner;
public class SeedTest extends AbstractMonkeyTest {
@@ -24,11 +26,15 @@
String cmd1 = MONKEY_CMD + " -s 1337 -v -p " + PKGS[0] + " 500";
String out1 = mDevice.executeShellCommand(cmd1);
String out2 = mDevice.executeShellCommand(cmd1);
+ CLog.d("monkey output1: %s", out1);
+ CLog.d("monkey output2: %s", out2);
assertOutputs(out1, out2);
String cmd2 = MONKEY_CMD + " -s 3007 -v -p " + PKGS[0] + " 125";
String out3 = mDevice.executeShellCommand(cmd2);
String out4 = mDevice.executeShellCommand(cmd2);
+ CLog.d("monkey output3: %s", out3);
+ CLog.d("monkey output4: %s", out4);
assertOutputs(out3, out4);
}
diff --git a/libs/testserver/src/android/webkit/cts/CtsTestServer.java b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
index e39e435..de88f3b 100644
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -229,12 +229,30 @@
// request for shutdown and having the server's one thread
// sequentially call accept() and close().
URL url = new URL(mServerUri + SHUTDOWN_PREFIX);
- URLConnection connection = openConnection(url);
- connection.connect();
+ if (url.getProtocol().equalsIgnoreCase("http")) {
+ // Use Socket instead of HttpURLConnection when the server is in cleartext HTTP mode
+ // to avoid the request being blocked by NetworkSecurityPolicy.
+ Socket socket = null;
+ try {
+ socket = new Socket(url.getHost(), url.getPort());
+ socket.getOutputStream().write(
+ ("GET " + SHUTDOWN_PREFIX + " HTTP/1.0\r\n\r\n").getBytes("US-ASCII"));
+ socket.getOutputStream().flush();
+ } finally {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (Exception ignored) {}
+ }
+ }
+ } else {
+ URLConnection connection = openConnection(url);
+ connection.connect();
- // Read the input from the stream to send the request.
- InputStream is = connection.getInputStream();
- is.close();
+ // Read the input from the stream to send the request.
+ InputStream is = connection.getInputStream();
+ is.close();
+ }
// Block until the server thread is done shutting down.
mServerThread.join();
@@ -1014,7 +1032,7 @@
conn.receiveRequestEntity( (HttpEntityEnclosingRequest) request);
}
- mExecutorService.submit(new HandleResponseTask(conn, request));
+ mExecutorService.execute(new HandleResponseTask(conn, request));
} catch (IOException e) {
// normal during shutdown, ignore
Log.w(TAG, e);
@@ -1045,7 +1063,7 @@
return path.equals(SHUTDOWN_PREFIX);
}
- private class HandleResponseTask implements Callable<Void> {
+ private class HandleResponseTask implements Runnable {
private DefaultHttpServerConnection mConnection;
@@ -1058,12 +1076,15 @@
}
@Override
- public Void call() throws Exception {
- HttpResponse response = mServer.getResponse(mRequest);
- mConnection.sendResponseHeader(response);
- mConnection.sendResponseEntity(response);
- mConnection.close();
- return null;
+ public void run() {
+ try {
+ HttpResponse response = mServer.getResponse(mRequest);
+ mConnection.sendResponseHeader(response);
+ mConnection.sendResponseEntity(response);
+ mConnection.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Error handling request:", e);
+ }
}
}
}
diff --git a/libs/vogar-expect/src/vogar/Expectation.java b/libs/vogar-expect/src/vogar/Expectation.java
index f065f42..ddbc233f 100644
--- a/libs/vogar-expect/src/vogar/Expectation.java
+++ b/libs/vogar-expect/src/vogar/Expectation.java
@@ -16,7 +16,6 @@
package vogar;
-import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Pattern;
@@ -37,14 +36,6 @@
*/
public final class Expectation {
- /** The pattern to use when no expected output is specified */
- public static final Pattern MATCH_ALL_PATTERN
- = Pattern.compile(".*", Pattern.MULTILINE | Pattern.DOTALL);
-
- /** The expectation of a general successful run. */
- public static final Expectation SUCCESS = new Expectation(Result.SUCCESS, MATCH_ALL_PATTERN,
- Collections.<String>emptySet(), "", -1);
-
/** Justification for this expectation */
private final String description;
diff --git a/libs/vogar-expect/src/vogar/ExpectationStore.java b/libs/vogar-expect/src/vogar/ExpectationStore.java
index cfa20e9..090322d 100644
--- a/libs/vogar-expect/src/vogar/ExpectationStore.java
+++ b/libs/vogar-expect/src/vogar/ExpectationStore.java
@@ -26,6 +26,7 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -50,7 +51,17 @@
* expectation, the outcome expectation will be returned.
*/
public final class ExpectationStore {
+
+ /** The pattern to use when no expected output is specified */
+ private static final Pattern MATCH_ALL_PATTERN
+ = Pattern.compile(".*", Pattern.MULTILINE | Pattern.DOTALL);
+
+ /** The expectation of a general successful run. */
+ private static final Expectation SUCCESS = new Expectation(Result.SUCCESS, MATCH_ALL_PATTERN,
+ Collections.<String>emptySet(), "", -1);
+
private static final int PATTERN_FLAGS = Pattern.MULTILINE | Pattern.DOTALL;
+
private final Map<String, Expectation> outcomes = new LinkedHashMap<String, Expectation>();
private final Map<String, Expectation> failures = new LinkedHashMap<String, Expectation>();
@@ -62,7 +73,7 @@
*/
public Expectation get(String name) {
Expectation byName = getByNameOrPackage(name);
- return byName != null ? byName : Expectation.SUCCESS;
+ return byName != null ? byName : SUCCESS;
}
/**
@@ -87,7 +98,7 @@
}
Expectation byName = getByNameOrPackage(outcome.getName());
- return byName != null ? byName : Expectation.SUCCESS;
+ return byName != null ? byName : SUCCESS;
}
private Expectation getByNameOrPackage(String name) {
@@ -141,8 +152,8 @@
private void readExpectation(JsonReader reader, ModeId mode) throws IOException {
boolean isFailure = false;
- Result result = Result.SUCCESS;
- Pattern pattern = Expectation.MATCH_ALL_PATTERN;
+ Result result = Result.EXEC_FAILED;
+ Pattern pattern = MATCH_ALL_PATTERN;
Set<String> names = new LinkedHashSet<String>();
Set<String> tags = new LinkedHashSet<String>();
Set<ModeId> modes = null;
@@ -159,6 +170,12 @@
} else if (name.equals("names")) {
readStrings(reader, names);
} else if (name.equals("failure")) {
+ // isFailure is somewhat arbitrarily keyed on the existence of a "failure"
+ // element instead of looking at the "result" field. There are only about 5
+ // expectations in our entire expectation store that have this tag.
+ //
+ // TODO: Get rid of it and the "failures" map and just use the outcomes
+ // map for everything. Both uses seem useless.
isFailure = true;
names.add(reader.nextString());
} else if (name.equals("pattern")) {
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index ecb7050..86fe4af 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -151,13 +151,6 @@
bug: 17144778
},
{
- description: "Roboto font tests are not yet known good on all devices",
- names: [
- "android.uirendering.cts.testclasses.FontRenderingTests"
- ],
- bug: 17109280
-},
-{
description: "android.keystore tests will replace these tests",
names: [
"com.android.org.conscrypt.MacTest#test_getInstance_OpenSSL_ENGINE",
@@ -179,5 +172,12 @@
"android.hardware.camera2.cts.ImageReaderTest#testAllOutputYUVResolutions"
],
bug: 18689511
+},
+{
+ description: "The new prepare performance test is not yet passing on all devices",
+ names: [
+ "android.hardware.camera2.cts.SurfaceViewPreviewTest#testPreparePerformance"
+ ],
+ bug: 17989532
}
]
diff --git a/tests/netsecpolicy/Android.mk b/tests/netsecpolicy/Android.mk
new file mode 100644
index 0000000..137672e
--- /dev/null
+++ b/tests/netsecpolicy/Android.mk
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/netsecpolicy/usescleartexttraffic-false/Android.mk b/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
new file mode 100644
index 0000000..1af3b49
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, \
+ ../usescleartexttraffic-shared/src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficFalse
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml b/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
new file mode 100644
index 0000000..013821e
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.netsecpolicy.usescleartext.false.cts">
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <application android:usesCleartextTraffic="false">
+ </application>
+</manifest>
diff --git a/tests/netsecpolicy/usescleartexttraffic-shared/src/Dummy.java b/tests/netsecpolicy/usescleartexttraffic-shared/src/Dummy.java
new file mode 100644
index 0000000..2705a5f
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-shared/src/Dummy.java
@@ -0,0 +1,2 @@
+public class Dummy {
+}
diff --git a/tests/netsecpolicy/usescleartexttraffic-true/Android.mk b/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
new file mode 100644
index 0000000..5effc57
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, \
+ ../usescleartexttraffic-shared/src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficTrue
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml b/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
new file mode 100644
index 0000000..f50295e
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.netsecpolicy.usescleartext.true.cts">
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-library android:name="org.apache.http.legacy"/>
+
+ <application android:usesCleartextTraffic="true">
+ </application>
+</manifest>
diff --git a/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk b/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
new file mode 100644
index 0000000..685a16f
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, \
+ ../usescleartexttraffic-shared/src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficUnspecified
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java b/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
similarity index 60%
copy from apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java
copy to tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
index 4231db7..7e735c7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java
+++ b/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,12 +14,11 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- */
+ -->
-package com.android.cts.verifier.tv;
-
-import android.preference.PreferenceActivity;
-
-public class MockTvInputSettingsActivity extends PreferenceActivity {
-
-}
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.netsecpolicy.usescleartext.unspecified.cts">
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <application>
+ </application>
+</manifest>
diff --git a/tests/tests/accessibility/AndroidManifest.xml b/tests/tests/accessibility/AndroidManifest.xml
index 319fb49..b3bcbc8 100644
--- a/tests/tests/accessibility/AndroidManifest.xml
+++ b/tests/tests/accessibility/AndroidManifest.xml
@@ -17,18 +17,19 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.view.cts.accessibility">
+ package="android.view.cts.accessibility">
- <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
- <application android:theme="@android:style/Theme.Holo.NoActionBar" >
- <uses-library android:name="android.test.runner"/>
- </application>
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.view.cts.accessibility"
- android:label="Tests for the accessibility APIs.">
+ <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.view.cts.accessibility"
+ android:label="Tests for the accessibility APIs.">
<meta-data android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
+ android:value="com.android.cts.runner.CtsTestRunListener" />
</instrumentation>
</manifest>
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index 8be2b99..db4e54b 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -113,6 +113,8 @@
AccessibilityEvent.TYPE_VIEW_HOVER_EXIT));
assertEquals("TYPE_VIEW_LONG_CLICKED", AccessibilityEvent.eventTypeToString(
AccessibilityEvent.TYPE_VIEW_LONG_CLICKED));
+ assertEquals("TYPE_VIEW_CONTEXT_CLICKED", AccessibilityEvent.eventTypeToString(
+ AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED));
assertEquals("TYPE_VIEW_SCROLLED", AccessibilityEvent.eventTypeToString(
AccessibilityEvent.TYPE_VIEW_SCROLLED));
assertEquals("TYPE_VIEW_SELECTED", AccessibilityEvent.eventTypeToString(
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index f5e1d48..506c022 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -185,6 +185,7 @@
info.setFocusable(true);
info.setFocused(true);
info.setLongClickable(true);
+ info.setContextClickable(true);
info.setPassword(true);
info.setScrollable(true);
info.setSelected(true);
@@ -235,6 +236,8 @@
receivedInfo.isFocused());
assertSame("longClickable has incorrect value", expectedInfo.isLongClickable(),
receivedInfo.isLongClickable());
+ assertSame("contextClickable has incorrect value", expectedInfo.isContextClickable(),
+ receivedInfo.isContextClickable());
assertSame("password has incorrect value", expectedInfo.isPassword(),
receivedInfo.isPassword());
assertSame("scrollable has incorrect value", expectedInfo.isScrollable(),
@@ -281,6 +284,7 @@
assertFalse("focusable not properly recycled", info.isFocusable());
assertFalse("focused not properly recycled", info.isFocused());
assertFalse("longClickable not properly recycled", info.isLongClickable());
+ assertFalse("contextClickable not properly recycled", info.isContextClickable());
assertFalse("password not properly recycled", info.isPassword());
assertFalse("scrollable not properly recycled", info.isScrollable());
assertFalse("selected not properly recycled", info.isSelected());
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
new file mode 100644
index 0000000..53eb215
--- /dev/null
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility.cts;
+
+import android.app.UiAutomation;
+import android.os.ParcelFileDescriptor;
+import android.test.InstrumentationTestCase;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+
+/**
+ * Tests whether the CaptioningManager APIs are functional.
+ */
+public class CaptioningManagerTest extends InstrumentationTestCase {
+ private CaptioningManager mManager;
+ private UiAutomation mUiAutomation;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mManager = getInstrumentation().getTargetContext().getSystemService(
+ CaptioningManager.class);
+
+ assertNotNull("Obtained captioning manager", mManager);
+
+ mUiAutomation = getInstrumentation().getUiAutomation();
+ }
+
+ /**
+ * Tests whether a client can observe changes in caption properties.
+ */
+ public void testChangeListener() {
+ putSecureSetting("accessibility_captioning_enabled","0");
+ putSecureSetting("accessibility_captioning_preset", "1");
+ putSecureSetting("accessibility_captioning_locale", "en_US");
+ putSecureSetting("accessibility_captioning_font_scale", "1.0");
+
+ MockCaptioningChangeListener listener = new MockCaptioningChangeListener();
+ mManager.addCaptioningChangeListener(listener);
+
+ putSecureSetting("accessibility_captioning_enabled", "1");
+ assertTrue("Observed enabled change", listener.wasEnabledChangedCalled);
+
+ putSecureSetting("accessibility_captioning_preset", "-1");
+ assertTrue("Observed user style change", listener.wasUserStyleChangedCalled);
+
+ putSecureSetting("accessibility_captioning_locale", "ja_JP");
+ assertTrue("Observed locale change", listener.wasLocaleChangedCalled);
+
+ putSecureSetting("accessibility_captioning_font_scale", "2.0");
+ assertTrue("Observed font scale change", listener.wasFontScaleChangedCalled);
+
+ mManager.removeCaptioningChangeListener(listener);
+
+ listener.reset();
+
+ putSecureSetting("accessibility_captioning_enabled","0");
+ assertFalse("Did not observe enabled change", listener.wasEnabledChangedCalled);
+
+ try {
+ mManager.removeCaptioningChangeListener(listener);
+ } catch (Exception e) {
+ throw new AssertionError("Fails silently when removing listener twice", e);
+ }
+ }
+
+ public void testProperties() {
+ putSecureSetting("accessibility_captioning_font_scale", "2.0");
+ putSecureSetting("accessibility_captioning_locale", "ja_JP");
+ putSecureSetting("accessibility_captioning_enabled", "1");
+
+ assertEquals("Test runner set font scale to 2.0", 2.0f, mManager.getFontScale());
+ assertEquals("Test runner set locale to Japanese", Locale.JAPAN, mManager.getLocale());
+ assertEquals("Test runner set enabled to true", true, mManager.isEnabled());
+ }
+
+ public void testUserStyle() {
+ putSecureSetting("accessibility_captioning_preset", "-1");
+ putSecureSetting("accessibility_captioning_foreground_color", "511");
+ putSecureSetting("accessibility_captioning_background_color", "511");
+ putSecureSetting("accessibility_captioning_window_color", "511");
+ putSecureSetting("accessibility_captioning_edge_color", "511");
+ putSecureSetting("accessibility_captioning_edge_type", "-1");
+ deleteSecureSetting("accessibility_captioning_typeface");
+
+ CaptionStyle userStyle = mManager.getUserStyle();
+ assertNotNull("Default user style is not null", userStyle);
+ assertFalse("Default user style has no edge type", userStyle.hasEdgeType());
+ assertFalse("Default user style has no edge color", userStyle.hasEdgeColor());
+ assertFalse("Default user style has no foreground color", userStyle.hasForegroundColor());
+ assertFalse("Default user style has no background color", userStyle.hasBackgroundColor());
+ assertFalse("Default user style has no window color", userStyle.hasWindowColor());
+ assertNull("Default user style has no typeface", userStyle.getTypeface());
+ }
+
+ private void deleteSecureSetting(String name) {
+ execShellCommand("settings delete secure " + name);
+ }
+
+ private void putSecureSetting(String name, String value) {
+ execShellCommand("settings put secure " + name + " " + value);
+ }
+
+ private void execShellCommand(String cmd) {
+ ParcelFileDescriptor pfd = mUiAutomation.executeShellCommand(cmd);
+ InputStream is = new FileInputStream(pfd.getFileDescriptor());
+ try {
+ final byte[] buffer = new byte[8192];
+ while ((is.read(buffer)) != -1);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to exec: " + cmd);
+ }
+ }
+
+ private static class MockCaptioningChangeListener extends CaptioningChangeListener {
+ boolean wasEnabledChangedCalled = false;
+ boolean wasUserStyleChangedCalled = false;
+ boolean wasLocaleChangedCalled = false;
+ boolean wasFontScaleChangedCalled = false;
+
+ @Override
+ public void onEnabledChanged(boolean enabled) {
+ super.onEnabledChanged(enabled);
+ wasEnabledChangedCalled = true;
+ }
+
+ @Override
+ public void onUserStyleChanged(CaptionStyle userStyle) {
+ super.onUserStyleChanged(userStyle);
+ wasUserStyleChangedCalled = true;
+ }
+
+ @Override
+ public void onLocaleChanged(Locale locale) {
+ super.onLocaleChanged(locale);
+ wasLocaleChangedCalled = true;
+ }
+
+ @Override
+ public void onFontScaleChanged(float fontScale) {
+ super.onFontScaleChanged(fontScale);
+ wasFontScaleChangedCalled = true;
+ }
+
+ public void reset() {
+ wasEnabledChangedCalled = false;
+ wasUserStyleChangedCalled = false;
+ wasLocaleChangedCalled = false;
+ wasFontScaleChangedCalled = false;
+ }
+ }
+}
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index b11248a..71bf6cf 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -313,12 +313,14 @@
// create the notification to send
final int notificationId = 1;
- final Notification notification = new Notification();
- notification.icon = android.R.drawable.stat_notify_call_mute;
- notification.contentIntent = PendingIntent.getActivity(getActivity(), 0, new Intent(),
- PendingIntent.FLAG_CANCEL_CURRENT);
- notification.tickerText = message;
- notification.setLatestEventInfo(getActivity(), "", "", notification.contentIntent);
+ final Notification notification = new Notification.Builder(getActivity())
+ .setSmallIcon(android.R.drawable.stat_notify_call_mute)
+ .setContentIntent(PendingIntent.getActivity(getActivity(), 0, new Intent(),
+ PendingIntent.FLAG_CANCEL_CURRENT))
+ .setTicker(message)
+ .setContentTitle("")
+ .setContentText("")
+ .build();
// create and populate the expected event
final AccessibilityEvent expected = AccessibilityEvent.obtain();
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
index 0e84fb9..4350191 100644
--- a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
@@ -1452,27 +1452,14 @@
}
/**
- * Tests the setting of lastAuthenticatedTime on accountAuthenticated being
- * successful.
- */
- public void testLastAuthenticatedTimeAfterAccountAuthenticated() throws IOException,
- AuthenticatorException, OperationCanceledException {
- long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD);
- mockAuthenticator.callAccountAuthenticated();
- long accountAuthenticatedTime = getLastAuthenticatedTime(ACCOUNT);
- assertTrue(accountAuthenticatedTime > accountAddTime);
- }
-
- /**
- * Tests the setting of lastAuthenticatedTime on setPassword being
- * successful.
+ * LastAuthenticatedTime on setPassword should not be disturbed.
*/
public void testLastAuthenticatedTimeAfterSetPassword() throws IOException,
AuthenticatorException, OperationCanceledException {
long accountAddTime = addAccountAndReturnAccountAddedTime(ACCOUNT, ACCOUNT_PASSWORD);
mockAuthenticator.callSetPassword();
long setPasswordTime = getLastAuthenticatedTime(ACCOUNT);
- assertTrue(setPasswordTime > accountAddTime);
+ assertTrue(setPasswordTime == accountAddTime);
}
/**
@@ -1947,7 +1934,14 @@
private long addAccountAndReturnAccountAddedTime(Account account, String password)
throws OperationCanceledException, AuthenticatorException, IOException {
- addAccountExplicitly(account, password, null /* userData */);
+ addAccount(am,
+ ACCOUNT_TYPE,
+ AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES,
+ OPTIONS_BUNDLE,
+ mActivity,
+ null /* callback */,
+ null /* handler */);
return getLastAuthenticatedTime(account);
}
diff --git a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
index faebd53..c1b08de 100644
--- a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
+++ b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
@@ -151,6 +151,8 @@
this.mAuthTokenType = authTokenType;
this.mRequiredFeatures = requiredFeatures;
this.mOptionsAddAccount = options;
+ AccountManager am = AccountManager.get(mContext);
+ am.addAccountExplicitly(AccountManagerTest.ACCOUNT, "fakePassword", null);
return createResultBundle();
}
diff --git a/tests/tests/app/AndroidManifest.xml b/tests/tests/app/AndroidManifest.xml
index 8e17396..d05648c 100644
--- a/tests/tests/app/AndroidManifest.xml
+++ b/tests/tests/app/AndroidManifest.xml
@@ -18,6 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.app">
+ <uses-sdk android:minSdkVersion="11" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
<application>
diff --git a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
index a68d860..2c87b78 100644
--- a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -96,6 +96,82 @@
}
}
+ public void testDownloadManagerSupportsHttp() throws Exception {
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ long id = mDownloadManager.enqueue(new Request(getGoodUrl()));
+
+ assertEquals(1, getTotalNumberDownloads());
+
+ assertDownloadQueryableById(id);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+
+ assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ public void testDownloadManagerSupportsHttpWithExternalWebServer() throws Exception {
+ // As a result of testDownloadManagerSupportsHttpsWithExternalWebServer relying on an
+ // external resource https://www.example.com this test uses http://www.example.com to help
+ // disambiguate errors from testDownloadManagerSupportsHttpsWithExternalWebServer.
+
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ long id = mDownloadManager.enqueue(new Request(Uri.parse("http://www.example.com")));
+
+ assertEquals(1, getTotalNumberDownloads());
+
+ assertDownloadQueryableById(id);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+
+ assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ public void testDownloadManagerSupportsHttpsWithExternalWebServer() throws Exception {
+ // For HTTPS, DownloadManager trusts only SSL server certs issued by CAs trusted by the
+ // system. Unfortunately, this means that it cannot trust the mock web server's SSL cert.
+ // Until this is resolved (e.g., by making it possible to specify additional CA certs to
+ // trust for a particular download), this test relies on https://www.example.com being
+ // operational and reachable from the Android under test.
+
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ long id = mDownloadManager.enqueue(new Request(Uri.parse("https://www.example.com")));
+
+ assertEquals(1, getTotalNumberDownloads());
+
+ assertDownloadQueryableById(id);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+
+ assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
public void testMinimumDownload() throws Exception {
final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
try {
diff --git a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
index 23dac25..5781442 100644
--- a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -100,9 +100,6 @@
}
private void sendNotification(final int id, final int icon) {
- final Notification notification = new Notification(
- icon, "No intent", System.currentTimeMillis());
-
final Intent intent = new Intent(Intent.ACTION_MAIN, Threads.CONTENT_URI);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
@@ -110,8 +107,13 @@
intent.setAction(Intent.ACTION_MAIN);
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
- notification.setLatestEventInfo(mContext, "notify#" + id, "This is #" + id
- + "notification ", pendingIntent);
+ final Notification notification = new Notification.Builder(mContext)
+ .setSmallIcon(icon)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("notify#" + id)
+ .setContentText("This is #" + id + "notification ")
+ .setContentIntent(pendingIntent)
+ .build();
mNotificationManager.notify(id, notification);
StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
diff --git a/tests/tests/app/src/android/app/cts/NotificationTest.java b/tests/tests/app/src/android/app/cts/NotificationTest.java
index c2f62c3..614d7f2 100644
--- a/tests/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/tests/app/src/android/app/cts/NotificationTest.java
@@ -150,6 +150,22 @@
assertNull(result.sound);
}
+ public void testBuilder() {
+ final Intent intent = new Intent();
+ final PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ mNotification = new Notification.Builder(mContext)
+ .setSmallIcon(1)
+ .setContentTitle(CONTENT_TITLE)
+ .setContentText(CONTENT_TEXT)
+ .setContentIntent(contentIntent)
+ .build();
+ assertEquals(CONTENT_TEXT, mNotification.extras.getString(Notification.EXTRA_TEXT));
+ assertEquals(CONTENT_TITLE, mNotification.extras.getString(Notification.EXTRA_TITLE));
+ assertEquals(1, mNotification.icon);
+ assertEquals(contentIntent, mNotification.contentIntent);
+ assertNotNull(mNotification.contentView);
+ }
+
public void testSetLatestEventInfo() {
mNotification = new Notification();
mNotification.icon = 1;
diff --git a/tests/tests/content/res/values-v23/strings.xml b/tests/tests/content/res/values-v23/strings.xml
new file mode 100644
index 0000000..4ed1aaa
--- /dev/null
+++ b/tests/tests/content/res/values-v23/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="version_cur">v23cur</string>
+</resources>
diff --git a/tests/tests/graphics/res/drawable/rippledrawable_radius.xml b/tests/tests/graphics/res/drawable/rippledrawable_radius.xml
new file mode 100644
index 0000000..4701ef7
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/rippledrawable_radius.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:radius="10px"/>
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index 629e85e..80e0253 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -809,7 +809,6 @@
}
public void testReset() {
-
Paint p = new Paint();
ColorFilter c = new ColorFilter();
MaskFilter m = new MaskFilter();
@@ -845,7 +844,6 @@
assertEquals(null, p.getShader());
assertEquals(null, p.getTypeface());
assertEquals(null, p.getXfermode());
-
}
public void testSetLinearText() {
@@ -930,6 +928,20 @@
assertMeasureText(text, textChars, textSpan, 4, 7, widths[4] + widths[5] + widths[6]);
}
+ public void testMeasureTextContext() {
+ Paint p = new Paint();
+ // Arabic LAM, which is different width depending on context
+ String shortString = "\u0644";
+ String longString = "\u0644\u0644\u0644";
+ char[] longChars = longString.toCharArray();
+ SpannedString longSpanned = new SpannedString(longString);
+ float width = p.measureText(shortString);
+ // Verify that measurement of substring is consistent no matter what surrounds it.
+ assertMeasureText(longString, longChars, longSpanned, 0, 1, width);
+ assertMeasureText(longString, longChars, longSpanned, 1, 2, width);
+ assertMeasureText(longString, longChars, longSpanned, 2, 3, width);
+ }
+
public void testMeasureTextWithLongText() {
// This test is not compatible with 4.0.3
if ("4.0.3".equals(Build.VERSION.RELEASE)) {
@@ -1184,6 +1196,51 @@
}
}
+ public void testGetRunAdvance_nonzeroIndex() {
+ Paint p = new Paint();
+ final String text = "Android powers hundreds of millions of mobile " +
+ "devices in more than 190 countries around the world. It's" +
+ "the largest installed base of any mobile platform and" +
+ "growing fast—every day another million users power up their" +
+ "Android devices for the first time and start looking for" +
+ "apps, games, and other digital content.";
+ // Test offset index does not affect width.
+ final float widthAndroidFirst = p.getRunAdvance(
+ text, 0, 7, 0, text.length(), false, 7);
+ final float widthAndroidSecond = p.getRunAdvance(
+ text, 215, 222, 0, text.length(), false, 222);
+ assertTrue(Math.abs(widthAndroidFirst - widthAndroidSecond) < 1);
+ }
+
+ public void testGetRunAdvance_glyphDependingContext() {
+ Paint p = new Paint();
+ // Test the context change the character shape.
+ // First character should be isolated form because the context ends at index 1.
+ final float isolatedFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 1, true, 1);
+ // First character should be initial form because the context ends at index 2.
+ final float initialFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 2, true, 1);
+ assertTrue(isolatedFormWidth > initialFormWidth);
+ }
+
+ public void testGetRunAdvance_arabic() {
+ Paint p = new Paint();
+ // Test total width is equals to sum of each character's width.
+ // "What is Unicode?" in Arabic.
+ final String text =
+ "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" +
+ "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" +
+ "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" +
+ "\u062F\u061F";
+ final float totalWidth = p.getRunAdvance(
+ text, 0, text.length(), 0, text.length(), true, text.length());
+ float sumOfCharactersWidth = 0;
+ for (int i = 0; i < text.length(); i++) {
+ sumOfCharactersWidth += p.getRunAdvance(
+ text, i, i + 1, 0, text.length(), true, i + 1);
+ }
+ assertTrue(Math.abs(totalWidth - sumOfCharactersWidth) < 1);
+ }
+
public void testGetOffsetForAdvance() {
Paint p = new Paint();
{
@@ -1268,7 +1325,7 @@
try {
// start < context start
- p.getOffsetForAdvance(string, 1, string.length(), 0, string.length(), false, 0.0f);
+ p.getOffsetForAdvance(string, 0, string.length(), 1, string.length(), false, 0.0f);
fail("Should throw an IndexOutOfBoundsException.");
} catch (IndexOutOfBoundsException e) {
} catch (Exception e) {
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
index c895d0d..88e7acb 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -21,7 +21,6 @@
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
@@ -135,6 +134,22 @@
assertTrue(bitmapDrawable.getPaint().isFilterBitmap());
}
+ public void testIsFilterBitmap() {
+ InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
+
+ assertTrue(bitmapDrawable.isFilterBitmap());
+
+ bitmapDrawable.setFilterBitmap(false);
+ assertFalse(bitmapDrawable.isFilterBitmap());
+ assertEquals(bitmapDrawable.isFilterBitmap(), bitmapDrawable.getPaint().isFilterBitmap());
+
+
+ bitmapDrawable.setFilterBitmap(true);
+ assertTrue(bitmapDrawable.isFilterBitmap());
+ assertEquals(bitmapDrawable.isFilterBitmap(), bitmapDrawable.getPaint().isFilterBitmap());
+ }
+
public void testSetDither() {
InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
index 036c756..79d2a1d 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
@@ -20,7 +20,6 @@
import java.util.Arrays;
-import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -185,6 +184,44 @@
assertTrue(dr.hasSetDitherCalled());
}
+ public void testSetHotspotBounds() {
+ Rect bounds = new Rect(10, 15, 100, 150);
+ assertConstantStateNotSet();
+ assertNull(mDrawableContainer.getCurrent());
+
+ mDrawableContainer.setConstantState(mDrawableContainerState);
+
+ MockDrawable dr = new MockDrawable();
+ addAndSelectDrawable(dr);
+
+ dr.reset();
+ mDrawableContainer.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ Rect outRect = new Rect();
+ mDrawableContainer.getHotspotBounds(outRect);
+ assertEquals(bounds, outRect);
+
+ dr.reset();
+ }
+
+ public void testGetHotspotBounds() {
+ Rect bounds = new Rect(10, 15, 100, 150);
+ assertConstantStateNotSet();
+ assertNull(mDrawableContainer.getCurrent());
+
+ mDrawableContainer.setConstantState(mDrawableContainerState);
+
+ MockDrawable dr = new MockDrawable();
+ addAndSelectDrawable(dr);
+
+ dr.reset();
+ mDrawableContainer.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ Rect outRect = new Rect();
+ mDrawableContainer.getHotspotBounds(outRect);
+ assertEquals(bounds, outRect);
+
+ dr.reset();
+ }
+
public void testSetColorFilter() {
assertConstantStateNotSet();
assertNull(mDrawableContainer.getCurrent());
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
index a48372e..a91353f 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
@@ -16,6 +16,7 @@
package android.graphics.drawable.cts;
+import android.view.View;
import com.android.cts.graphics.R;
import org.xmlpull.v1.XmlPullParser;
@@ -545,6 +546,47 @@
mockDrawable.setDither(false);
}
+ public void testSetHotspotBounds() {
+ MockDrawable mockDrawable = new MockDrawable();
+
+ // setHotspotBounds is a non-operation function.
+ mockDrawable.setHotspotBounds(10, 15, 100, 150);
+ }
+
+ public void testGetHotspotBounds() {
+ MockDrawable mockDrawable = new MockDrawable();
+
+ // getHotspotBounds doesn't do anything interesting in the Drawable superclass
+ mockDrawable.getHotspotBounds(new Rect());
+ }
+
+ public void testSetLayoutDirection() {
+ MockDrawable mockDrawable = new MockDrawable();
+
+ mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+ assertEquals(View.LAYOUT_DIRECTION_LTR, mockDrawable.getLayoutDirection());
+
+ mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ assertEquals(View.LAYOUT_DIRECTION_RTL, mockDrawable.getLayoutDirection());
+ }
+
+ public void testGetLayoutDirection() {
+ MockDrawable mockDrawable = new MockDrawable();
+
+ mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+ assertEquals(View.LAYOUT_DIRECTION_LTR, mockDrawable.getLayoutDirection());
+
+ mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ assertEquals(View.LAYOUT_DIRECTION_RTL, mockDrawable.getLayoutDirection());
+ }
+
+ public void testOnLayoutDirectionChanged() {
+ MockDrawable mockDrawable = new MockDrawable();
+
+ // onLayoutDirectionChanged is a non-operation function.
+ mockDrawable.onLayoutDirectionChanged(View.LAYOUT_DIRECTION_LTR);
+ }
+
public void testSetFilterBitmap() {
MockDrawable mockDrawable = new MockDrawable();
@@ -552,6 +594,13 @@
mockDrawable.setFilterBitmap(false);
}
+ public void testIsFilterBitmap() {
+ MockDrawable mockDrawable = new MockDrawable();
+
+ // setFilterBitmap is a non-operation function.
+ mockDrawable.isFilterBitmap();
+ }
+
public void testUnscheduleSelf() {
MockDrawable mockDrawable = new MockDrawable();
MockCallback mockCallback = new MockCallback();
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
new file mode 100644
index 0000000..40680c1
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable.cts;
+
+import android.graphics.drawable.DrawableWrapper;
+import com.android.cts.graphics.R;
+
+
+import java.util.Arrays;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Drawable.ConstantState;
+import android.test.AndroidTestCase;
+import android.util.StateSet;
+
+public class DrawableWrapperTest extends AndroidTestCase {
+
+ static class MyWrapper extends DrawableWrapper {
+ public MyWrapper(Drawable dr) {
+ super(dr);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testConstructor() {
+ Drawable d = new BitmapDrawable();
+ MyWrapper wrapper = new MyWrapper(d);
+ assertSame(d, wrapper.getDrawable());
+
+ new MyWrapper(null);
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetDrawable() {
+ Drawable d = new BitmapDrawable();
+ MyWrapper wrapper = new MyWrapper(d);
+ assertSame(d, wrapper.getDrawable());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetDrawable() {
+ Drawable d = new BitmapDrawable();
+ MyWrapper wrapper = new MyWrapper(null);
+ assertSame(null, wrapper.getDrawable());
+
+ wrapper.setDrawable(d);
+ assertSame(d, wrapper.getDrawable());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testInvalidateDrawable() {
+ MyWrapper wrapper = new MyWrapper(new BitmapDrawable());
+
+ MockCallback cb = new MockCallback();
+ wrapper.setCallback(cb);
+ wrapper.invalidateDrawable(null);
+ assertTrue(cb.hasCalledInvalidate());
+
+ cb.reset();
+ wrapper.invalidateDrawable(new BitmapDrawable());
+ assertTrue(cb.hasCalledInvalidate());
+
+ cb.reset();
+ wrapper.setCallback(null);
+ wrapper.invalidateDrawable(null);
+ assertFalse(cb.hasCalledInvalidate());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testScheduleDrawable() {
+ MyWrapper wrapper = new MyWrapper(new BitmapDrawable());
+
+ MockCallback cb = new MockCallback();
+ wrapper.setCallback(cb);
+ wrapper.scheduleDrawable(null, null, 0);
+ assertTrue(cb.hasCalledSchedule());
+
+ cb.reset();
+ wrapper.scheduleDrawable(new BitmapDrawable(), new Runnable() {
+ public void run() {
+ }
+ }, 1000L);
+ assertTrue(cb.hasCalledSchedule());
+
+ cb.reset();
+ wrapper.setCallback(null);
+ wrapper.scheduleDrawable(null, null, 0);
+ assertFalse(cb.hasCalledSchedule());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testUnscheduleDrawable() {
+ MyWrapper wrapper = new MyWrapper(new BitmapDrawable());
+
+ MockCallback cb = new MockCallback();
+ wrapper.setCallback(cb);
+ wrapper.unscheduleDrawable(null, null);
+ assertTrue(cb.hasCalledUnschedule());
+
+ cb.reset();
+ wrapper.unscheduleDrawable(new BitmapDrawable(), new Runnable() {
+ public void run() {
+ }
+ });
+ assertTrue(cb.hasCalledUnschedule());
+
+ cb.reset();
+ wrapper.setCallback(null);
+ wrapper.unscheduleDrawable(null, null);
+ assertFalse(cb.hasCalledUnschedule());
+ }
+
+ private static class MockCallback implements Drawable.Callback {
+ private boolean mCalledInvalidate;
+ private boolean mCalledSchedule;
+ private boolean mCalledUnschedule;
+
+ public void invalidateDrawable(Drawable who) {
+ mCalledInvalidate = true;
+ }
+
+ public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ mCalledSchedule = true;
+ }
+
+ public void unscheduleDrawable(Drawable who, Runnable what) {
+ mCalledUnschedule = true;
+ }
+
+ public boolean hasCalledInvalidate() {
+ return mCalledInvalidate;
+ }
+
+ public boolean hasCalledSchedule() {
+ return mCalledSchedule;
+ }
+
+ public boolean hasCalledUnschedule() {
+ return mCalledUnschedule;
+ }
+
+ public int getResolvedLayoutDirection(Drawable who) {
+ return 0;
+ }
+
+ public void reset() {
+ mCalledInvalidate = false;
+ mCalledSchedule = false;
+ mCalledUnschedule = false;
+ }
+ }
+
+ public void testDraw() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+ wrapper.draw(new Canvas());
+ assertTrue(mockDrawable.hasCalledDraw());
+
+ mockDrawable.reset();
+ wrapper.draw(null);
+ assertTrue(mockDrawable.hasCalledDraw());
+ }
+
+ public void testGetChangingConfigurations() {
+ final int SUPER_CONFIG = 1;
+ final int CONTAINED_DRAWABLE_CONFIG = 2;
+
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+ assertEquals(0, wrapper.getChangingConfigurations());
+
+ mockDrawable.setChangingConfigurations(CONTAINED_DRAWABLE_CONFIG);
+ assertEquals(CONTAINED_DRAWABLE_CONFIG, wrapper.getChangingConfigurations());
+
+ wrapper.setChangingConfigurations(SUPER_CONFIG);
+ assertEquals(SUPER_CONFIG | CONTAINED_DRAWABLE_CONFIG,
+ wrapper.getChangingConfigurations());
+ }
+
+ public void testGetPadding() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+ // this method will call contained drawable's getPadding method.
+ wrapper.getPadding(new Rect());
+ assertTrue(mockDrawable.hasCalledGetPadding());
+
+ // input null as param
+ try {
+ wrapper.getPadding(null);
+ fail("Should throw NullPointerException");
+ } catch (NullPointerException e) {
+ }
+ }
+
+ public void testSetVisible() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+ assertTrue(wrapper.isVisible());
+
+ assertTrue(wrapper.setVisible(false, false));
+ assertFalse(wrapper.isVisible());
+ assertTrue(mockDrawable.hasCalledSetVisible());
+
+ mockDrawable.reset();
+ assertFalse(wrapper.setVisible(false, false));
+ assertFalse(wrapper.isVisible());
+ assertTrue(mockDrawable.hasCalledSetVisible());
+
+ mockDrawable.reset();
+ assertTrue(wrapper.setVisible(true, false));
+ assertTrue(wrapper.isVisible());
+ assertTrue(mockDrawable.hasCalledSetVisible());
+ }
+
+ public void testSetAlpha() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+ // this method will call contained drawable's setAlpha method.
+ wrapper.setAlpha(100);
+ assertTrue(mockDrawable.hasCalledSetAlpha());
+
+ mockDrawable.reset();
+ wrapper.setAlpha(Integer.MAX_VALUE);
+ assertTrue(mockDrawable.hasCalledSetAlpha());
+
+ mockDrawable.reset();
+ wrapper.setAlpha(-1);
+ assertTrue(mockDrawable.hasCalledSetAlpha());
+ }
+
+ public void testSetColorFilter() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+ // this method will call contained drawable's setColorFilter method.
+ wrapper.setColorFilter(new ColorFilter());
+ assertTrue(mockDrawable.hasCalledSetColorFilter());
+
+ mockDrawable.reset();
+ wrapper.setColorFilter(null);
+ assertTrue(mockDrawable.hasCalledSetColorFilter());
+ }
+
+ public void testGetOpacity() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+ // This method will call contained drawable's getOpacity method.
+ wrapper.setLevel(1);
+ wrapper.getOpacity();
+ assertTrue(mockDrawable.hasCalledGetOpacity());
+ }
+
+ public void testIsStateful() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+ // this method will call contained drawable's isStateful method.
+ wrapper.isStateful();
+ assertTrue(mockDrawable.hasCalledIsStateful());
+ }
+
+ public void testOnStateChange() {
+ Drawable d = new MockDrawable();
+ MockDrawableWrapper wrapper = new MockDrawableWrapper(d);
+ assertEquals("initial child state is empty", d.getState(), StateSet.WILD_CARD);
+
+ int[] state = new int[] {1, 2, 3};
+ assertFalse("child did not change", wrapper.onStateChange(state));
+ assertEquals("child state did not change", d.getState(), StateSet.WILD_CARD);
+
+ d = mContext.getDrawable(R.drawable.statelistdrawable);
+ wrapper = new MockDrawableWrapper(d);
+ assertEquals("initial child state is empty", d.getState(), StateSet.WILD_CARD);
+ wrapper.onStateChange(state);
+ assertTrue("child state changed", Arrays.equals(state, d.getState()));
+
+ // input null as param
+ wrapper.onStateChange(null);
+ // expected, no Exception thrown out, test success
+ }
+
+ public void testOnLevelChange() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MockDrawableWrapper mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
+
+ assertEquals(0, mockDrawable.getLevel());
+ assertFalse(mockDrawableWrapper.onLevelChange(0));
+ assertFalse(mockDrawable.hasCalledOnLevelChange());
+
+ assertFalse(mockDrawableWrapper.onLevelChange(1000));
+ assertTrue(mockDrawable.hasCalledOnLevelChange());
+ assertEquals(1000, mockDrawable.getLevel());
+
+ mockDrawable.reset();
+ mockDrawableWrapper.reset();
+ assertFalse(mockDrawableWrapper.onLevelChange(Integer.MIN_VALUE));
+ assertTrue(mockDrawable.hasCalledOnLevelChange());
+ }
+
+ public void testOnBoundsChange() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MockDrawableWrapper mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
+ Rect bounds = new Rect(2, 2, 26, 32);
+ mockDrawable.setBounds(bounds);
+ mockDrawableWrapper.onBoundsChange(bounds);
+
+ mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
+ mockDrawable.setBounds(bounds);
+ mockDrawableWrapper.onBoundsChange(bounds);
+ assertEquals(bounds.left, mockDrawable.getBounds().left);
+ assertEquals(bounds.top, mockDrawable.getBounds().top);
+ assertEquals(bounds.right, mockDrawable.getBounds().right);
+ assertEquals(bounds.bottom, mockDrawable.getBounds().bottom);
+
+ bounds = mockDrawable.getBounds();
+ assertEquals(2, bounds.left);
+ assertEquals(2, bounds.top);
+ assertEquals(26, bounds.right);
+ assertEquals(32, bounds.bottom);
+
+ // input null as param
+ try {
+ mockDrawableWrapper.onBoundsChange(null);
+ fail("There should be a NullPointerException thrown out.");
+ } catch (NullPointerException e) {
+ // expected, test success
+ }
+
+ }
+
+ public void testGetIntrinsicWidth() {
+ MockDrawable mockDrawable = new MockDrawable();
+ MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+ // this method will call contained drawable's getIntrinsicWidth method.
+ wrapper.getIntrinsicWidth();
+ assertTrue(mockDrawable.hasCalledGetIntrinsicWidth());
+ }
+
+ public void testGetIntrinsicHeight() {
+ MockDrawable mockDrawable = new MockDrawable();
+ DrawableWrapper wrapper = new MyWrapper(mockDrawable);
+
+ // this method will call contained drawable's getIntrinsicHeight method.
+ wrapper.getIntrinsicHeight();
+ assertTrue(mockDrawable.hasCalledGetIntrinsicHeight());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetConstantState() {
+ DrawableWrapper wrapper = new MyWrapper(new BitmapDrawable());
+ ConstantState constantState = wrapper.getConstantState();
+ }
+
+ private static class MockDrawable extends Drawable {
+ private boolean mCalledDraw = false;
+ private boolean mCalledGetPadding = false;
+ private boolean mCalledSetVisible = false;
+ private boolean mCalledSetAlpha = false;
+ private boolean mCalledGetOpacity = false;
+ private boolean mCalledSetColorFilter = false;
+ private boolean mCalledIsStateful = false;
+ private boolean mCalledGetIntrinsicWidth = false;
+ private boolean mCalledGetIntrinsicHeight = false;
+ private boolean mCalledSetState = false;
+ private boolean mCalledOnLevelChange = false;
+
+ @Override
+ public void draw(Canvas canvas) {
+ mCalledDraw = true;
+ }
+
+ @Override
+ public int getOpacity() {
+ mCalledGetOpacity = true;
+ return 0;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mCalledSetAlpha = true;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ mCalledSetColorFilter = true;
+ }
+
+ @Override
+ public boolean getPadding(Rect padding) {
+ mCalledGetPadding = true;
+ return super.getPadding(padding);
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ mCalledSetVisible = true;
+ return super.setVisible(visible, restart);
+ }
+
+ @Override
+ public boolean isStateful() {
+ mCalledIsStateful = true;
+ return super.isStateful();
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ mCalledGetIntrinsicWidth = true;
+ return super.getIntrinsicWidth();
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ mCalledGetIntrinsicHeight = true;
+ return super.getIntrinsicHeight();
+
+ }
+
+ @Override
+ public boolean setState(final int[] stateSet) {
+ mCalledSetState = true;
+ return super.setState(stateSet);
+ }
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ mCalledOnLevelChange = true;
+ return super.onLevelChange(level);
+ }
+
+ public boolean hasCalledDraw() {
+ return mCalledDraw;
+ }
+
+ public boolean hasCalledGetPadding() {
+ return mCalledGetPadding;
+ }
+
+ public boolean hasCalledSetVisible() {
+ return mCalledSetVisible;
+ }
+
+ public boolean hasCalledSetAlpha() {
+ return mCalledSetAlpha;
+ }
+
+ public boolean hasCalledGetOpacity() {
+ return mCalledGetOpacity;
+ }
+
+ public boolean hasCalledSetColorFilter() {
+ return mCalledSetColorFilter;
+ }
+
+ public boolean hasCalledIsStateful() {
+ return mCalledIsStateful;
+ }
+
+ public boolean hasCalledGetIntrinsicWidth() {
+ return mCalledGetIntrinsicWidth;
+ }
+
+ public boolean hasCalledGetIntrinsicHeight() {
+ return mCalledGetIntrinsicHeight;
+ }
+
+ public boolean hasCalledSetState() {
+ return mCalledSetState;
+ }
+
+ public boolean hasCalledOnLevelChange() {
+ return mCalledOnLevelChange;
+ }
+
+ public void reset() {
+ mCalledDraw = false;
+ mCalledGetPadding = false;
+ mCalledSetVisible = false;
+ mCalledSetAlpha = false;
+ mCalledGetOpacity = false;
+ mCalledSetColorFilter = false;
+ mCalledIsStateful = false;
+ mCalledGetIntrinsicWidth = false;
+ mCalledGetIntrinsicHeight = false;
+ mCalledSetState = false;
+ mCalledOnLevelChange = false;
+ }
+ }
+
+ private static class MockDrawableWrapper extends DrawableWrapper {
+ private boolean mCalledOnBoundsChange = false;
+
+ MockDrawableWrapper() {
+ super(null);
+ }
+
+ public MockDrawableWrapper(Drawable drawable) {
+ super(drawable);
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ return super.onStateChange(state);
+ }
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ return super.onLevelChange(level);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ mCalledOnBoundsChange = true;
+ super.onBoundsChange(bounds);
+ }
+
+ public boolean hasCalledOnBoundsChange() {
+ return mCalledOnBoundsChange;
+ }
+
+ public void reset() {
+ mCalledOnBoundsChange = false;
+ }
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
index e930671..a2f9ddf 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
@@ -16,6 +16,7 @@
package android.graphics.drawable.cts;
+import android.view.Gravity;
import com.android.cts.graphics.R;
import org.xmlpull.v1.XmlPullParserException;
@@ -501,6 +502,32 @@
assertTrue(mockDrawable2.hasCalledSetDither());
}
+ public void testSetHotspotBounds() {
+ Rect bounds = new Rect(10, 15, 100, 150);
+ MockDrawable mockDrawable1 = new MockDrawable();
+ MockDrawable mockDrawable2 = new MockDrawable();
+ Drawable[] array = new Drawable[] { mockDrawable1, mockDrawable2 };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ Rect outRect = new Rect();
+ layerDrawable.getHotspotBounds(outRect);
+ assertTrue(bounds.equals(outRect));
+ }
+
+ public void testGetHotspotBounds() {
+ Rect bounds = new Rect(10, 15, 100, 150);
+ MockDrawable mockDrawable1 = new MockDrawable();
+ MockDrawable mockDrawable2 = new MockDrawable();
+ Drawable[] array = new Drawable[] { mockDrawable1, mockDrawable2 };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ Rect outRect = new Rect();
+ layerDrawable.getHotspotBounds(outRect);
+ assertTrue(bounds.equals(outRect));
+ }
+
public void testSetAlpha() {
MockDrawable mockDrawable1 = new MockDrawable();
MockDrawable mockDrawable2 = new MockDrawable();
@@ -760,6 +787,611 @@
assertEquals(1, constantState.getChangingConfigurations());
}
+ @SuppressWarnings("deprecation")
+ public void testAddLayer() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable(), new ColorDrawable(Color.BLUE) };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ BitmapDrawable newDrawable = new BitmapDrawable();
+ int index = layerDrawable.addLayer(newDrawable);
+
+ final int numLayers = layerDrawable.getNumberOfLayers();
+ assertEquals(index, numLayers - 1);
+ assertEquals(newDrawable, layerDrawable.getDrawable(index));
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetDrawable() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable(), new ColorDrawable(Color.BLUE) };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ final int numLayers = layerDrawable.getNumberOfLayers();
+ assertEquals(array[0], layerDrawable.getDrawable(0));
+ assertEquals(array[1], layerDrawable.getDrawable(1));
+ try {
+ assertEquals(null, layerDrawable.getDrawable(2));
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testFindIndexByLayerId() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable(), new ColorDrawable(Color.BLUE) };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setId(0, 10);
+ layerDrawable.setId(1, 20);
+
+ assertEquals(0, layerDrawable.findIndexByLayerId(10));
+ assertEquals(1, layerDrawable.findIndexByLayerId(20));
+ assertEquals(-1, layerDrawable.findIndexByLayerId(30));
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetDrawable() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ BitmapDrawable newBitmapDrawable = new BitmapDrawable();
+ ColorDrawable newColorDrawable = new ColorDrawable(Color.GREEN);
+ layerDrawable.setDrawable(0, newColorDrawable);
+ layerDrawable.setDrawable(1, newBitmapDrawable);
+
+ final int numLayers = layerDrawable.getNumberOfLayers();
+ assertEquals(2, numLayers);
+ assertEquals(newColorDrawable, layerDrawable.getDrawable(0));
+ assertEquals(newBitmapDrawable, layerDrawable.getDrawable(1));
+ try {
+ assertEquals(null, layerDrawable.getDrawable(2));
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLeftPadding() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable()};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ layerDrawable.setPadding(10, 11, 20, 21);
+
+ assertEquals(10, layerDrawable.getLeftPadding());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetTopPadding() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable()};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ layerDrawable.setPadding(10, 11, 20, 21);
+
+ assertEquals(11, layerDrawable.getTopPadding());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetRightPadding() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable()};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ layerDrawable.setPadding(10, 11, 20, 21);
+
+ assertEquals(20, layerDrawable.getRightPadding());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetBottomPadding() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable()};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ layerDrawable.setPadding(10, 11, 20, 21);
+
+ assertEquals(21, layerDrawable.getBottomPadding());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetStartPadding() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable()};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ layerDrawable.setPadding(10, 11, 20, 21);
+
+ assertEquals(-1, layerDrawable.getStartPadding());
+ layerDrawable.setPaddingRelative(10, 11, 20, 21);
+ assertEquals(10, layerDrawable.getStartPadding());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetEndPadding() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable()};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ layerDrawable.setPadding(10, 11, 20, 21);
+
+ assertEquals(-1, layerDrawable.getEndPadding());
+ layerDrawable.setPaddingRelative(10, 11, 20, 21);
+ assertEquals(20, layerDrawable.getEndPadding());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetPadding() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable()};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ layerDrawable.setPadding(10, 11, 20, 21);
+
+ assertEquals(10, layerDrawable.getLeftPadding());
+ assertEquals(11, layerDrawable.getTopPadding());
+ assertEquals(20, layerDrawable.getRightPadding());
+ assertEquals(21, layerDrawable.getBottomPadding());
+ assertEquals(-1, layerDrawable.getStartPadding());
+ assertEquals(-1, layerDrawable.getEndPadding());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetPaddingRelative() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable()};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+ layerDrawable.setPaddingRelative(10, 11, 20, 21);
+
+ assertEquals(10, layerDrawable.getStartPadding());
+ assertEquals(11, layerDrawable.getTopPadding());
+ assertEquals(20, layerDrawable.getEndPadding());
+ assertEquals(21, layerDrawable.getBottomPadding());
+ assertEquals(-1, layerDrawable.getLeftPadding());
+ assertEquals(-1, layerDrawable.getRightPadding());
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerGravity() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setLayerGravity(0, Gravity.CENTER);
+ layerDrawable.setLayerGravity(1, Gravity.NO_GRAVITY);
+
+ try {
+ layerDrawable.setLayerGravity(2, Gravity.TOP);
+ fail("Should throw ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ assertEquals(Gravity.CENTER, layerDrawable.getLayerGravity(0));
+ assertEquals(Gravity.NO_GRAVITY, layerDrawable.getLayerGravity(1));
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerGravity() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setLayerGravity(0, Gravity.CENTER);
+ layerDrawable.setLayerGravity(1, Gravity.NO_GRAVITY);
+
+ assertEquals(Gravity.CENTER, layerDrawable.getLayerGravity(0));
+ assertEquals(Gravity.NO_GRAVITY, layerDrawable.getLayerGravity(1));
+ try {
+ layerDrawable.getLayerGravity(2);
+ fail("Should throw ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerWidth() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setLayerWidth(0, 100);
+ layerDrawable.setLayerWidth(1, 200);
+
+ try {
+ layerDrawable.setLayerWidth(2, 300);
+ fail("Should throw ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ assertEquals(100, layerDrawable.getLayerWidth(0));
+ assertEquals(200, layerDrawable.getLayerWidth(1));
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerWidth() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setLayerWidth(0, 100);
+ layerDrawable.setLayerWidth(1, 200);
+
+ assertEquals(100, layerDrawable.getLayerWidth(0));
+ assertEquals(200, layerDrawable.getLayerWidth(1));
+ try {
+ layerDrawable.getLayerWidth(2);
+ fail("Should throw ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerHeight() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setLayerHeight(0, 100);
+ layerDrawable.setLayerHeight(1, 200);
+
+ try {
+ layerDrawable.setLayerHeight(2, 300);
+ fail("Should throw ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ assertEquals(100, layerDrawable.getLayerHeight(0));
+ assertEquals(200, layerDrawable.getLayerHeight(1));
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerHeight() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setLayerHeight(0, 100);
+ layerDrawable.setLayerHeight(1, 200);
+
+ assertEquals(100, layerDrawable.getLayerHeight(0));
+ assertEquals(200, layerDrawable.getLayerHeight(1));
+ try {
+ layerDrawable.getLayerHeight(2);
+ fail("Should throw ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerSize() {
+ Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ layerDrawable.setLayerSize(0, 100, 200);
+ layerDrawable.setLayerSize(1, 300, 400);
+
+ try {
+ layerDrawable.setLayerSize(2, 500, 600);
+ fail("Should throw ArrayIndexOutOfBoundsException");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ assertEquals(100, layerDrawable.getLayerWidth(0));
+ assertEquals(200, layerDrawable.getLayerHeight(0));
+ assertEquals(300, layerDrawable.getLayerWidth(1));
+ assertEquals(400, layerDrawable.getLayerHeight(1));
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerInsetRelative() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable(), new ColorDrawable(Color.BLUE) };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int start = 10;
+ int top = 20;
+ int end = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+ layerDrawable.getIntrinsicWidth());
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+ layerDrawable.getIntrinsicHeight());
+ assertEquals(10, layerDrawable.getLayerInsetStart(0));
+ assertEquals(20, layerDrawable.getLayerInsetTop(0));
+ assertEquals(30, layerDrawable.getLayerInsetEnd(0));
+ assertEquals(40, layerDrawable.getLayerInsetBottom(0));
+ assertEquals(0, layerDrawable.getLayerInsetLeft(0));
+ assertEquals(0, layerDrawable.getLayerInsetRight(0));
+
+ // set bigger inset for layer 1
+ start += 10;
+ top += 10;
+ end += 10;
+ bottom += 10;
+ layerDrawable.setLayerInsetRelative(1, start, top, end, bottom);
+ assertEquals(layerDrawable.getDrawable(1).getIntrinsicWidth() + start + end,
+ layerDrawable.getIntrinsicWidth());
+ assertEquals(layerDrawable.getDrawable(1).getIntrinsicHeight() + top + bottom,
+ layerDrawable.getIntrinsicHeight());
+
+
+ try {
+ layerDrawable.setLayerInsetRelative(-1, start, top, end, bottom);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerInsetLeft() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int left = 10;
+ int top = 20;
+ int right = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInset(0, left, top, right, bottom);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + left + right,
+ layerDrawable.getIntrinsicWidth());
+ left += 5;
+ layerDrawable.setLayerInsetLeft(0, left);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + left + right,
+ layerDrawable.getIntrinsicWidth());
+ assertEquals(left, layerDrawable.getLayerInsetLeft(0));
+
+ try {
+ layerDrawable.setLayerInsetLeft(1, left);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerInsetLeft() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int left = 10;
+ int top = 20;
+ int right = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInset(0, left, top, right, bottom);
+ assertEquals(left, layerDrawable.getLayerInsetLeft(0));
+ left += 5;
+ layerDrawable.setLayerInsetLeft(0, left);
+ assertEquals(left, layerDrawable.getLayerInsetLeft(0));
+
+ try {
+ layerDrawable.getLayerInsetLeft(1);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerInsetTop() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int left = 10;
+ int top = 20;
+ int right = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInset(0, left, top, right, bottom);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+ layerDrawable.getIntrinsicHeight());
+ top += 5;
+ layerDrawable.setLayerInsetTop(0, top);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+ layerDrawable.getIntrinsicHeight());
+ assertEquals(top, layerDrawable.getLayerInsetTop(0));
+
+ try {
+ layerDrawable.setLayerInsetTop(1, top);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerInsetTop() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int left = 10;
+ int top = 20;
+ int right = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInset(0, left, top, right, bottom);
+ assertEquals(top, layerDrawable.getLayerInsetTop(0));
+ top += 5;
+ layerDrawable.setLayerInsetTop(0, top);
+ assertEquals(top, layerDrawable.getLayerInsetTop(0));
+
+ try {
+ layerDrawable.getLayerInsetTop(1);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerInsetRight() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int left = 10;
+ int top = 20;
+ int right = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInset(0, left, top, right, bottom);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + left + right,
+ layerDrawable.getIntrinsicWidth());
+ right += 5;
+ layerDrawable.setLayerInsetRight(0, right);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + left + right,
+ layerDrawable.getIntrinsicWidth());
+ assertEquals(right, layerDrawable.getLayerInsetRight(0));
+
+ try {
+ layerDrawable.setLayerInsetRight(1, right);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerInsetRight() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int left = 10;
+ int top = 20;
+ int right = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInset(0, left, top, right, bottom);
+ assertEquals(right, layerDrawable.getLayerInsetRight(0));
+ right += 5;
+ layerDrawable.setLayerInsetRight(0, right);
+ assertEquals(right, layerDrawable.getLayerInsetRight(0));
+
+ try {
+ layerDrawable.getLayerInsetRight(1);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerInsetBottom() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int left = 10;
+ int top = 20;
+ int right = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInset(0, left, top, right, bottom);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+ layerDrawable.getIntrinsicHeight());
+ bottom += 5;
+ layerDrawable.setLayerInsetBottom(0, bottom);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+ layerDrawable.getIntrinsicHeight());
+ assertEquals(bottom, layerDrawable.getLayerInsetBottom(0));
+
+ try {
+ layerDrawable.setLayerInsetBottom(1, bottom);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerInsetBottom() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int left = 10;
+ int top = 20;
+ int right = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInset(0, left, top, right, bottom);
+ assertEquals(bottom, layerDrawable.getLayerInsetBottom(0));
+ bottom += 5;
+ layerDrawable.setLayerInsetBottom(0, bottom);
+ assertEquals(bottom, layerDrawable.getLayerInsetBottom(0));
+
+ try {
+ layerDrawable.getLayerInsetBottom(1);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerInsetStart() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int start = 10;
+ int top = 20;
+ int end = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+ layerDrawable.getIntrinsicWidth());
+ start += 5;
+ layerDrawable.setLayerInsetStart(0, start);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+ layerDrawable.getIntrinsicWidth());
+ assertEquals(start, layerDrawable.getLayerInsetStart(0));
+
+ try {
+ layerDrawable.setLayerInset(1, start, top, end, bottom);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerInsetStart() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int start = 10;
+ int top = 20;
+ int end = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+ assertEquals(start, layerDrawable.getLayerInsetStart(0));
+ start += 5;
+ layerDrawable.setLayerInsetStart(0, start);
+ assertEquals(start, layerDrawable.getLayerInsetStart(0));
+
+ try {
+ layerDrawable.getLayerInsetStart(1);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testSetLayerInsetEnd() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int start = 10;
+ int top = 20;
+ int end = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+ assertEquals(end, layerDrawable.getLayerInsetEnd(0));
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+ layerDrawable.getIntrinsicWidth());
+ end += 5;
+ layerDrawable.setLayerInsetEnd(0, end);
+ assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+ layerDrawable.getIntrinsicWidth());
+ assertEquals(end, layerDrawable.getLayerInsetEnd(0));
+
+ try {
+ layerDrawable.setLayerInsetEnd(1, end);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGetLayerInsetEnd() {
+ Drawable[] array = new Drawable[] { new BitmapDrawable() };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ // set inset for layer 0
+ int start = 10;
+ int top = 20;
+ int end = 30;
+ int bottom = 40;
+ layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+ assertEquals(end, layerDrawable.getLayerInsetEnd(0));
+ end += 5;
+ layerDrawable.setLayerInsetEnd(0, end);
+ assertEquals(end, layerDrawable.getLayerInsetEnd(0));
+
+ try {
+ layerDrawable.getLayerInsetEnd(1);
+ fail("Should throw IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ }
+
+
+
private static class MockDrawable extends Drawable {
private boolean mCalledSetDither = false;
private boolean mCalledSetAlpha = false;
@@ -776,6 +1408,8 @@
private int mOpacity = PixelFormat.OPAQUE;
+ private boolean mDither = false;
+
Rect mPadding = null;
public MockDrawable() {
@@ -816,6 +1450,7 @@
@Override
public void setDither(boolean dither) {
+ mDither = dither;
mCalledSetDither = true;
}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
index 39ed55c..720397c 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
@@ -21,7 +21,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
@@ -37,9 +36,7 @@
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.NinePatchDrawable;
-import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.Drawable.ConstantState;
-import android.graphics.drawable.shapes.RectShape;
import android.test.InstrumentationTestCase;
import android.util.AttributeSet;
import android.util.Xml;
@@ -200,6 +197,27 @@
assertTrue(mNinePatchDrawable.getPaint().isDither());
}
+ public void testSetFilterBitmap() {
+ mNinePatchDrawable.setFilterBitmap(false);
+ assertFalse(mNinePatchDrawable.getPaint().isFilterBitmap());
+
+ mNinePatchDrawable.setFilterBitmap(true);
+ assertTrue(mNinePatchDrawable.getPaint().isFilterBitmap());
+ }
+
+ public void testIsFilterBitmap() {
+ mNinePatchDrawable.setFilterBitmap(false);
+ assertFalse(mNinePatchDrawable.isFilterBitmap());
+ assertEquals(mNinePatchDrawable.isFilterBitmap(),
+ mNinePatchDrawable.getPaint().isFilterBitmap());
+
+
+ mNinePatchDrawable.setFilterBitmap(true);
+ assertTrue(mNinePatchDrawable.isFilterBitmap());
+ assertEquals(mNinePatchDrawable.isFilterBitmap(),
+ mNinePatchDrawable.getPaint().isFilterBitmap());
+ }
+
public void testGetPaint() {
Paint paint = mNinePatchDrawable.getPaint();
assertNotNull(paint);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/RippleDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/RippleDrawableTest.java
new file mode 100644
index 0000000..b04433c
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/RippleDrawableTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable.cts;
+
+import com.android.cts.graphics.R;
+
+import android.content.res.ColorStateList;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.Color;
+import android.test.AndroidTestCase;
+
+public class RippleDrawableTest extends AndroidTestCase {
+ public void testConstructor() {
+ new RippleDrawable(ColorStateList.valueOf(Color.RED), null, null);
+ }
+
+ public void testAccessRadius() {
+ RippleDrawable drawable =
+ new RippleDrawable(ColorStateList.valueOf(Color.RED), null, null);
+ assertEquals(RippleDrawable.RADIUS_AUTO, drawable.getRadius());
+ drawable.setRadius(10);
+ assertEquals(10, drawable.getRadius());
+ }
+
+ public void testRadiusAttr() {
+ RippleDrawable drawable =
+ (RippleDrawable) getContext().getDrawable(R.drawable.rippledrawable_radius);
+ assertEquals(10, drawable.getRadius());
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
index b77139a..5b3234e 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
@@ -16,7 +16,6 @@
package android.graphics.drawable.cts;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
diff --git a/tests/tests/hardware/AndroidManifest.xml b/tests/tests/hardware/AndroidManifest.xml
index 7b15b61..1d1e3a8 100644
--- a/tests/tests/hardware/AndroidManifest.xml
+++ b/tests/tests/hardware/AndroidManifest.xml
@@ -71,6 +71,8 @@
android:process=":camera2ActivityProcess">
</activity>
+ <activity android:name="android.hardware.input.cts.InputCtsActivity"
+ android:label="InputCtsActivity" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/hardware/res/raw/gamepad_press_a.json b/tests/tests/hardware/res/raw/gamepad_press_a.json
new file mode 100644
index 0000000..ff3ca4f
--- /dev/null
+++ b/tests/tests/hardware/res/raw/gamepad_press_a.json
@@ -0,0 +1,39 @@
+{
+ "id": 1,
+ "command": "register",
+ "name": "Odie (Test)",
+ "vid": 0x18d1,
+ "pid": 0x2c40,
+ "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x09, 0x0a, 0x01, 0x00,
+ 0x0a, 0x02, 0x00, 0x0a, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x0a, 0x07, 0x00, 0x0a, 0x08, 0x00,
+ 0x0a, 0x0e, 0x00, 0x0a, 0x0f, 0x00, 0x0a, 0x0d, 0x00, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x0a,
+ 0x23, 0x02, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0b, 0x81, 0x02, 0x75, 0x01, 0x95,
+ 0x01, 0x81, 0x03, 0x05, 0x01, 0x75, 0x04, 0x95, 0x01, 0x25, 0x07, 0x46, 0x3b, 0x01, 0x66,
+ 0x14, 0x00, 0x09, 0x39, 0x81, 0x42, 0x66, 0x00, 0x00, 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30,
+ 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x05, 0x02, 0x09, 0xc5, 0x09, 0xc4, 0x15, 0x00, 0x26,
+ 0xff, 0x00, 0x35, 0x00, 0x46, 0xff, 0x00, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0xc0, 0x85,
+ 0x02, 0x05, 0x08, 0x0a, 0x01, 0x00, 0x0a, 0x02, 0x00, 0x0a, 0x03, 0x00, 0x0a, 0x04, 0x00,
+ 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x04, 0x91, 0x02, 0x75, 0x04, 0x95, 0x01, 0x91,
+ 0x03, 0xc0, 0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x85, 0x03, 0x05, 0x01, 0x09, 0x06, 0xa1,
+ 0x02, 0x05, 0x06, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81,
+ 0x02, 0x06, 0xbc, 0xff, 0x0a, 0xad, 0xbd, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0xc0, 0xc0],
+ "report": [0x01, 0x00, 0x80, 0x90, 0x80, 0x7f, 0x73, 0x00, 0x00]
+}
+
+{
+ "id": 1,
+ "command": "report",
+ "report": [0x01, 0x01, 0x80, 0x90, 0x80, 0x7f, 0x73, 0x00, 0x00]
+}
+
+{
+ "id": 1,
+ "command": "delay",
+ "duration": 10
+}
+
+{
+ "id": 1,
+ "command": "report",
+ "report": [0x01, 0x00, 0x80, 0x90, 0x80, 0x7f, 0x73, 0x00, 0x00]
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
index 5a66fa7..fd8c487 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -56,6 +56,12 @@
continue;
}
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ Log.i(TAG, "Legacy camera doesn't report min frame duration" +
+ ". Skip the test");
+ continue;
+ }
+
yuvBurstTestByCamera(id);
} finally {
closeDevice();
@@ -108,13 +114,11 @@
}
}
- if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
- assertTrue(String.format("Cam %s: Target FPS range of (%d, %d) must be supported",
- cameraId, minBurstFps, minBurstFps), foundConstantMaxYUVRange);
- assertTrue(String.format(
- "Cam %s: Target FPS range of (x, %d) where x <= 15 must be supported",
- cameraId, minBurstFps), foundYUVStreamingRange);
- }
+ assertTrue(String.format("Cam %s: Target FPS range of (%d, %d) must be supported",
+ cameraId, minBurstFps, minBurstFps), foundConstantMaxYUVRange);
+ assertTrue(String.format(
+ "Cam %s: Target FPS range of (x, %d) where x <= 15 must be supported",
+ cameraId, minBurstFps), foundYUVStreamingRange);
assertTrue(String.format("Cam %s: No target FPS range found with minimum FPS above " +
" 1/minFrameDuration (%d fps, duration %d ns) for full-resolution YUV",
cameraId, minBurstFps, minStillFrameDuration),
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java b/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
index 936883e..1881774 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.os.Bundle;
import android.os.ConditionVariable;
+import android.os.SystemClock;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -57,7 +58,7 @@
int waitTimeMs = timeOutMs;
boolean changeSucceeded = false;
while (!changeSucceeded && waitTimeMs > 0) {
- long startTimeMs = System.currentTimeMillis();
+ long startTimeMs = SystemClock.elapsedRealtime();
changeSucceeded = surfaceChangedDone.block(waitTimeMs);
if (!changeSucceeded) {
Log.e(TAG, "Wait for surface change timed out after " + timeOutMs + " ms");
@@ -72,7 +73,7 @@
// again.
changeSucceeded = false;
}
- waitTimeMs -= (System.currentTimeMillis() - startTimeMs);
+ waitTimeMs -= (SystemClock.elapsedRealtime() - startTimeMs);
}
// Couldn't get expected surface size change.
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
index be80eea..189d3cd 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -36,6 +36,7 @@
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.hardware.camera2.params.MeteringRectangle;
+import android.media.ImageReader;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
@@ -50,9 +51,15 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
import java.util.Set;
+import android.util.Size;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -596,6 +603,98 @@
}
}
+ /**
+ * Verify creating sessions back to back.
+ */
+ public void testCreateSessions() throws Exception {
+ for (int i = 0; i < mCameraIds.length; i++) {
+ try {
+ openDevice(mCameraIds[i], mCameraMockListener);
+ waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+ testCreateSessionsByCamera(mCameraIds[i]);
+ }
+ finally {
+ closeDevice(mCameraIds[i], mCameraMockListener);
+ }
+ }
+ }
+
+ /**
+ * Verify creating sessions back to back and only the last one is valid for
+ * submitting requests.
+ */
+ private void testCreateSessionsByCamera(String cameraId) throws Exception {
+ final int NUM_SESSIONS = 3;
+ final int SESSION_TIMEOUT_MS = 1000;
+ final int CAPTURE_TIMEOUT_MS = 3000;
+
+ if (VERBOSE) {
+ Log.v(TAG, "Testing creating sessions for camera " + cameraId);
+ }
+
+ Size yuvSize = getSortedSizesForFormat(cameraId, mCameraManager, ImageFormat.YUV_420_888,
+ /*bound*/null).get(0);
+ Size jpegSize = getSortedSizesForFormat(cameraId, mCameraManager, ImageFormat.JPEG,
+ /*bound*/null).get(0);
+
+ // Create a list of image readers. JPEG for last one and YUV for the rest.
+ List<ImageReader> imageReaders = new ArrayList<>();
+ List<CameraCaptureSession> allSessions = new ArrayList<>();
+
+ try {
+ for (int i = 0; i < NUM_SESSIONS - 1; i++) {
+ imageReaders.add(ImageReader.newInstance(yuvSize.getWidth(), yuvSize.getHeight(),
+ ImageFormat.YUV_420_888, /*maxImages*/1));
+ }
+ imageReaders.add(ImageReader.newInstance(jpegSize.getWidth(), jpegSize.getHeight(),
+ ImageFormat.JPEG, /*maxImages*/1));
+
+ // Create multiple sessions back to back.
+ MultipleSessionCallback sessionListener =
+ new MultipleSessionCallback(/*failOnConfigureFailed*/true);
+ for (int i = 0; i < NUM_SESSIONS; i++) {
+ List<Surface> outputs = new ArrayList<>();
+ outputs.add(imageReaders.get(i).getSurface());
+ mCamera.createCaptureSession(outputs, sessionListener, mHandler);
+ }
+
+ // Verify we get onConfigured() for all sessions.
+ allSessions = sessionListener.getAllSessions(NUM_SESSIONS,
+ SESSION_TIMEOUT_MS * NUM_SESSIONS);
+ assertEquals(String.format("Got %d sessions but configured %d sessions",
+ allSessions.size(), NUM_SESSIONS), allSessions.size(), NUM_SESSIONS);
+
+ // Verify all sessions except the last one are closed.
+ for (int i = 0; i < NUM_SESSIONS - 1; i++) {
+ sessionListener.waitForSessionClose(allSessions.get(i), SESSION_TIMEOUT_MS);
+ }
+
+ // Verify we can capture a frame with the last session.
+ CameraCaptureSession session = allSessions.get(allSessions.size() - 1);
+ SimpleCaptureCallback captureListener = new SimpleCaptureCallback();
+ ImageReader reader = imageReaders.get(imageReaders.size() - 1);
+ SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+ reader.setOnImageAvailableListener(imageListener, mHandler);
+
+ CaptureRequest.Builder builder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+ builder.addTarget(reader.getSurface());
+ CaptureRequest request = builder.build();
+
+ session.capture(request, captureListener, mHandler);
+ captureListener.getCaptureResultForRequest(request, CAPTURE_TIMEOUT_MS);
+ imageListener.getImage(CAPTURE_TIMEOUT_MS).close();
+ } finally {
+ for (ImageReader reader : imageReaders) {
+ reader.close();
+ }
+ for (CameraCaptureSession session : allSessions) {
+ session.close();
+ }
+ }
+ }
+
private void prepareTestByCamera() throws Exception {
final int PREPARE_TIMEOUT_MS = 10000;
@@ -632,7 +731,10 @@
Surface output3Surface = new Surface(output3);
try {
mSession.prepare(output3Surface);
- fail("Preparing surface not part of session must throw IllegalArgumentException");
+ // Legacy camera prepare always succeed
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ fail("Preparing surface not part of session must throw IllegalArgumentException");
+ }
} catch (IllegalArgumentException e) {
// expected
}
@@ -653,7 +755,10 @@
try {
mSession.prepare(output1Surface);
- fail("Preparing already-used surface must throw IllegalArgumentException");
+ // Legacy camera prepare always succeed
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ fail("Preparing already-used surface must throw IllegalArgumentException");
+ }
} catch (IllegalArgumentException e) {
// expected
}
@@ -671,7 +776,11 @@
try {
mSession.prepare(output1Surface);
- fail("Preparing surface used in previous session must throw IllegalArgumentException");
+ // Legacy camera prepare always succeed
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ fail("Preparing surface used in previous session must throw " +
+ "IllegalArgumentException");
+ }
} catch (IllegalArgumentException e) {
// expected
}
@@ -688,7 +797,10 @@
try {
mSession.prepare(output3Surface);
- fail("Preparing already-used surface must throw IllegalArgumentException");
+ // Legacy camera prepare always succeed
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ fail("Preparing already-used surface must throw IllegalArgumentException");
+ }
} catch (IllegalArgumentException e) {
// expected
}
@@ -710,7 +822,11 @@
try {
mSession.prepare(output1Surface);
- fail("Preparing surface used in previous session must throw IllegalArgumentException");
+ // Legacy camera prepare always succeed
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ fail("Preparing surface used in previous session must throw " +
+ "IllegalArgumentException");
+ }
} catch (IllegalArgumentException e) {
// expected
}
@@ -1529,4 +1645,97 @@
mSessionMockListener = null;
mSessionWaiter = null;
}
+
+ /**
+ * A camera capture session listener that keeps all the configured and closed sessions.
+ */
+ private class MultipleSessionCallback extends CameraCaptureSession.StateCallback {
+ public static final int SESSION_CONFIGURED = 0;
+ public static final int SESSION_CLOSED = 1;
+
+ final List<CameraCaptureSession> mSessions = new ArrayList<>();
+ final Map<CameraCaptureSession, Integer> mSessionStates = new HashMap<>();
+ CameraCaptureSession mCurrentConfiguredSession = null;
+
+ final ReentrantLock mLock = new ReentrantLock();
+ final Condition mNewStateCond = mLock.newCondition();
+
+ final boolean mFailOnConfigureFailed;
+
+ /**
+ * If failOnConfigureFailed is true, it calls fail() when onConfigureFailed() is invoked
+ * for any session.
+ */
+ public MultipleSessionCallback(boolean failOnConfigureFailed) {
+ mFailOnConfigureFailed = failOnConfigureFailed;
+ }
+
+ @Override
+ public void onClosed(CameraCaptureSession session) {
+ mLock.lock();
+ mSessionStates.put(session, SESSION_CLOSED);
+ mNewStateCond.signal();
+ mLock.unlock();
+ }
+
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ mLock.lock();
+ mSessions.add(session);
+ mSessionStates.put(session, SESSION_CONFIGURED);
+ mNewStateCond.signal();
+ mLock.unlock();
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ if (mFailOnConfigureFailed) {
+ fail("Configuring a session failed");
+ }
+ }
+
+ /**
+ * Get a number of sessions that have been configured.
+ */
+ public List<CameraCaptureSession> getAllSessions(int numSessions, int timeoutMs)
+ throws Exception {
+ long remainingTime = timeoutMs;
+ mLock.lock();
+ try {
+ while (mSessions.size() < numSessions) {
+ long startTime = SystemClock.elapsedRealtime();
+ boolean ret = mNewStateCond.await(remainingTime, TimeUnit.MILLISECONDS);
+ remainingTime -= (SystemClock.elapsedRealtime() - startTime);
+ ret &= remainingTime > 0;
+
+ assertTrue("Get " + numSessions + " sessions timed out after " + timeoutMs +
+ "ms", ret);
+ }
+
+ return mSessions;
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ /**
+ * Wait until a previously-configured sessoin is closed or it times out.
+ */
+ public void waitForSessionClose(CameraCaptureSession session, int timeoutMs) throws Exception {
+ long remainingTime = timeoutMs;
+ mLock.lock();
+ try {
+ while (mSessionStates.get(session).equals(SESSION_CLOSED) == false) {
+ long startTime = SystemClock.elapsedRealtime();
+ boolean ret = mNewStateCond.await(remainingTime, TimeUnit.MILLISECONDS);
+ remainingTime -= (SystemClock.elapsedRealtime() - startTime);
+ ret &= remainingTime > 0;
+
+ assertTrue("Wait for session close timed out after " + timeoutMs + "ms", ret);
+ }
+ } finally {
+ mLock.unlock();
+ }
+ }
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index afd71f5..70e7e68 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -739,6 +739,15 @@
BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener);
camera.createReprocessableCaptureSession(inputConfiguration, outputSurfaces,
sessionListener, handler);
+
+ Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
+ BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
+ int state = sessionListener.getStateWaiter().waitForAnyOfStates(
+ Arrays.asList(sessionStates), SESSION_CONFIGURE_TIMEOUT_MS);
+
+ assertTrue("Creating a reprocessable session failed.",
+ state == BlockingSessionCallback.SESSION_READY);
+
CameraCaptureSession session =
sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
assertTrue("Camera session should be a reprocessable session", session.isReprocessable());
@@ -765,6 +774,10 @@
* Check if image size and format match given size and format.
*/
public static void checkImage(Image image, int width, int height, int format) {
+ // Image reader will wrap YV12/NV21 image by YUV_420_888
+ if (format == ImageFormat.NV21 || format == ImageFormat.YV12) {
+ format = ImageFormat.YUV_420_888;
+ }
assertNotNull("Input image is invalid", image);
assertEquals("Format doesn't match", format, image.getFormat());
assertEquals("Width doesn't match", width, image.getWidth());
@@ -938,6 +951,44 @@
Size[] availableSizes = configMap.getOutputSizes(format);
assertArrayNotEmpty(availableSizes, "availableSizes should not be empty for format: "
+ format);
+ Size[] highResAvailableSizes = configMap.getHighResolutionOutputSizes(format);
+ if (highResAvailableSizes != null && highResAvailableSizes.length > 0) {
+ Size[] allSizes = new Size[availableSizes.length + highResAvailableSizes.length];
+ System.arraycopy(availableSizes, 0, allSizes, 0,
+ availableSizes.length);
+ System.arraycopy(highResAvailableSizes, 0, allSizes, availableSizes.length,
+ highResAvailableSizes.length);
+ availableSizes = allSizes;
+ }
+ if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes));
+ return availableSizes;
+ }
+
+ /**
+ * Get the available output sizes for the given class.
+ *
+ */
+ public static Size[] getSupportedSizeForClass(Class klass, String cameraId,
+ CameraManager cameraManager) throws CameraAccessException {
+ CameraCharacteristics properties = cameraManager.getCameraCharacteristics(cameraId);
+ assertNotNull("Can't get camera characteristics!", properties);
+ if (VERBOSE) {
+ Log.v(TAG, "get camera characteristics for camera: " + cameraId);
+ }
+ StreamConfigurationMap configMap =
+ properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ Size[] availableSizes = configMap.getOutputSizes(klass);
+ assertArrayNotEmpty(availableSizes, "availableSizes should not be empty for class: "
+ + klass);
+ Size[] highResAvailableSizes = configMap.getHighResolutionOutputSizes(ImageFormat.PRIVATE);
+ if (highResAvailableSizes != null && highResAvailableSizes.length > 0) {
+ Size[] allSizes = new Size[availableSizes.length + highResAvailableSizes.length];
+ System.arraycopy(availableSizes, 0, allSizes, 0,
+ availableSizes.length);
+ System.arraycopy(highResAvailableSizes, 0, allSizes, availableSizes.length,
+ highResAvailableSizes.length);
+ availableSizes = allSizes;
+ }
if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes));
return availableSizes;
}
@@ -961,10 +1012,9 @@
*/
static public List<Size> getSupportedPreviewSizes(String cameraId,
CameraManager cameraManager, Size bound) throws CameraAccessException {
- CameraCharacteristics props = cameraManager.getCameraCharacteristics(cameraId);
- StreamConfigurationMap config =
- props.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- Size[] rawSizes = config.getOutputSizes(android.view.SurfaceHolder.class);
+
+ Size[] rawSizes = getSupportedSizeForClass(android.view.SurfaceHolder.class, cameraId,
+ cameraManager);
assertArrayNotEmpty(rawSizes,
"Available sizes for SurfaceHolder class should not be empty");
if (VERBOSE) {
@@ -1050,10 +1100,9 @@
*/
static public List<Size> getSupportedVideoSizes(String cameraId,
CameraManager cameraManager, Size bound) throws CameraAccessException {
- CameraCharacteristics props = cameraManager.getCameraCharacteristics(cameraId);
- StreamConfigurationMap config =
- props.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- Size[] rawSizes = config.getOutputSizes(android.media.MediaRecorder.class);
+
+ Size[] rawSizes = getSupportedSizeForClass(android.media.MediaRecorder.class,
+ cameraId, cameraManager);
assertArrayNotEmpty(rawSizes,
"Available sizes for MediaRecorder class should not be empty");
if (VERBOSE) {
@@ -1668,7 +1717,9 @@
// Validate capture result vs. request
Size resultThumbnailSize = captureResult.get(CaptureResult.JPEG_THUMBNAIL_SIZE);
int orientationTested = expectedExifData.jpegOrientation;
- if ((orientationTested == 90 || orientationTested == 270)) {
+ // Legacy shim always doesn't rotate thumbnail size
+ if ((orientationTested == 90 || orientationTested == 270) &&
+ staticInfo.isHardwareLevelLimitedOrBetter()) {
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
/*defaultValue*/-1);
if (exifOrientation == ExifInterface.ORIENTATION_UNDEFINED) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
index 2430dd0..df3f151 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -519,17 +519,23 @@
waiverKeys.add(CaptureResult.SHADING_MODE);
}
- //Keys not required if manual sensor control is not supported
+ //Keys not required if neither MANUAL_SENSOR nor READ_SENSOR_SETTINGS is supported
+ if (!mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) &&
+ !mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
+ waiverKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
+ waiverKeys.add(CaptureResult.SENSOR_SENSITIVITY);
+ waiverKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
+ waiverKeys.add(CaptureResult.LENS_APERTURE);
+ }
+
if (!mStaticInfo.isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
- waiverKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
waiverKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
- waiverKeys.add(CaptureResult.SENSOR_SENSITIVITY);
waiverKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
waiverKeys.add(CaptureResult.LENS_FOCUS_RANGE);
- waiverKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
waiverKeys.add(CaptureResult.LENS_STATE);
- waiverKeys.add(CaptureResult.LENS_APERTURE);
waiverKeys.add(CaptureResult.LENS_FILTER_DENSITY);
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
index 8184226..0da0ce7 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -115,19 +115,7 @@
continue;
}
- Size[] targetCaptureSizes =
- mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
- StaticMetadata.StreamDirection.Output);
-
- assertTrue("No capture sizes available for RAW format!",
- targetCaptureSizes.length != 0);
- Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
- Size activeArraySize = new Size(activeArray.width(), activeArray.height());
- assertTrue("Missing ActiveArraySize", activeArray.width() > 0 &&
- activeArray.height() > 0);
- // TODO: Allow PixelArraySize also.
- assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
- targetCaptureSizes, activeArraySize);
+ Size activeArraySize = mStaticInfo.getRawDimensChecked();
// Create capture image reader
CameraTestUtils.SimpleImageReaderListener captureListener
@@ -202,19 +190,7 @@
continue;
}
- Size[] targetCaptureSizes =
- mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
- StaticMetadata.StreamDirection.Output);
-
- assertTrue("No capture sizes available for RAW format!",
- targetCaptureSizes.length != 0);
- Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
- Size activeArraySize = new Size(activeArray.width(), activeArray.height());
- assertTrue("Missing ActiveArraySize", activeArray.width() > 0 &&
- activeArray.height() > 0);
- // TODO: Allow PixelArraySize also.
- assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
- targetCaptureSizes, activeArraySize);
+ Size activeArraySize = mStaticInfo.getRawDimensChecked();
Size[] targetPreviewSizes =
mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888,
@@ -322,19 +298,7 @@
continue;
}
- Size[] targetCaptureSizes =
- mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
- StaticMetadata.StreamDirection.Output);
-
- assertTrue("No capture sizes available for RAW format!",
- targetCaptureSizes.length != 0);
- Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
- Size activeArraySize = new Size(activeArray.width(), activeArray.height());
- assertTrue("Active array has invalid size!", activeArray.width() > 0 &&
- activeArray.height() > 0);
- // TODO: Allow PixelArraySize also.
- assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
- targetCaptureSizes, activeArraySize);
+ Size activeArraySize = mStaticInfo.getRawDimensChecked();
// Get largest jpeg size
Size[] targetJpegSizes =
@@ -369,8 +333,8 @@
Bitmap.Config.ARGB_8888);
Size rawBitmapSize = new Size(rawBitmap.getWidth(), rawBitmap.getHeight());
- assertTrue("Raw bitmap size must be equal to active array size.",
- rawBitmapSize.equals(activeArraySize));
+ assertTrue("Raw bitmap size must be equal to either pre-correction active array" +
+ " size or pixel array size.", rawBitmapSize.equals(activeArraySize));
byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index c69053b..99f7091 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -247,11 +247,8 @@
expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES , LEGACY , NONE );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN , FULL , MANUAL_SENSOR, RAW );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1 , OPT , RAW );
- expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2 , OPT , RAW );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1 , OPT , RAW );
- expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2 , OPT , RAW );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1 , OPT , RAW );
- expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2 , OPT , RAW );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE , LEGACY , BC, RAW );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT , FULL , RAW );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE , FULL , MANUAL_SENSOR );
@@ -264,7 +261,6 @@
expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY , FULL , MANUAL_SENSOR );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1 , OPT , RAW );
- expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2 , OPT , RAW );
expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES , LIMITED , MANUAL_POSTPROC, RAW );
expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES , OPT , RAW );
@@ -279,6 +275,14 @@
// TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
}
+ // Only check for these if the second reference illuminant is included
+ if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) {
+ expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2 , OPT , RAW );
+ expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2 , OPT , RAW );
+ expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2 , OPT , RAW );
+ expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2 , OPT , RAW );
+ }
+
counter++;
}
}
@@ -369,7 +373,7 @@
/**
* Test values for static metadata used by the BURST capability.
*/
- public void testStaticBurstCharacteristics() {
+ public void testStaticBurstCharacteristics() throws Exception {
int counter = 0;
final float SIZE_ERROR_MARGIN = 0.03f;
for (CameraCharacteristics c : mCharacteristics) {
@@ -391,7 +395,18 @@
// Ensure that max YUV size matches max JPEG size
Size maxYuvSize = CameraTestUtils.getMaxSize(
config.getOutputSizes(ImageFormat.YUV_420_888));
- Size maxJpegSize = CameraTestUtils.getMaxSize(config.getOutputSizes(ImageFormat.JPEG));
+ Size maxFastYuvSize = maxYuvSize;
+
+ Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
+ assertTrue("Null slow YUV size array not allowed with BURST_CAPTURE capability!",
+ slowYuvSizes != null);
+ if (slowYuvSizes.length > 0) {
+ Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
+ maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
+ }
+
+ Size maxJpegSize = CameraTestUtils.getMaxSize(CameraTestUtils.getSupportedSizeForFormat(
+ ImageFormat.JPEG, mIds[counter], mCameraManager));
boolean haveMaxYuv = maxYuvSize != null ?
(maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
@@ -409,19 +424,29 @@
boolean haveAwbLock = CameraTestUtils.getValueNotNull(
c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
- // Ensure that YUV output is fast enough - needs to be at least 20 fps
+ // Ensure that max YUV output is fast enough - needs to be at least 10 fps
long maxYuvRate =
config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
- final long MIN_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps
+ final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
+ boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
- boolean haveMaxYuvRate = maxYuvRate <= MIN_DURATION_BOUND_NS;
+ // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
+
+ long maxFastYuvRate =
+ config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize);
+ final long MIN_8MP_DURATION_BOUND_NS = 200000000; // 50 ms, 20 fps
+ boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS;
+
+ final int SIZE_8MP_BOUND = 8000000;
+ boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
+ SIZE_8MP_BOUND;
// Ensure that there's an FPS range that's fast enough to capture at above
- // minFrameDuration, for full-auto bursts
+ // minFrameDuration, for full-auto bursts at the fast resolutions
Range[] fpsRanges = CameraTestUtils.getValueNotNull(
c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
- float minYuvFps = 1.f / maxYuvRate;
+ float minYuvFps = 1.f / maxFastYuvRate;
boolean haveFastAeTargetFps = false;
for (Range<Integer> r : fpsRanges) {
@@ -450,11 +475,18 @@
mIds[counter]),
haveMaxYuv);
assertTrue(
- String.format("BURST-capable camera device %s YUV frame rate is too slow" +
+ String.format("BURST-capable camera device %s max-resolution " +
+ "YUV frame rate is too slow" +
"(%d ns min frame duration reported, less than %d ns expected)",
- mIds[counter], maxYuvRate, MIN_DURATION_BOUND_NS),
+ mIds[counter], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS),
haveMaxYuvRate);
assertTrue(
+ String.format("BURST-capable camera device %s >= 8MP YUV output " +
+ "frame rate is too slow" +
+ "(%d ns min frame duration reported, less than %d ns expected)",
+ mIds[counter], maxYuvRate, MIN_8MP_DURATION_BOUND_NS),
+ haveFastYuvRate);
+ assertTrue(
String.format("BURST-capable camera device %s does not list an AE target " +
" FPS range with min FPS >= %f, for full-AUTO bursts",
mIds[counter], minYuvFps),
@@ -549,6 +581,7 @@
// Verify camera can output the reprocess input formats and sizes.
Size[] inputSizes = configs.getInputSizes(input);
Size[] outputSizes = configs.getOutputSizes(input);
+ Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
assertTrue("no input size supported for format " + input,
inputSizes.length > 0);
assertTrue("no output size supported for format " + input,
@@ -556,7 +589,9 @@
for (Size inputSize : inputSizes) {
assertTrue("Camera must be able to output the supported reprocessing " +
- "input size", arrayContains(outputSizes, inputSize));
+ "input size",
+ arrayContains(outputSizes, inputSize) ||
+ arrayContains(highResOutputSizes, inputSize));
}
}
}
@@ -566,7 +601,7 @@
/**
* Cross-check StreamConfigurationMap output
*/
- public void testStreamConfigurationMap() {
+ public void testStreamConfigurationMap() throws Exception {
int counter = 0;
for (CameraCharacteristics c : mCharacteristics) {
Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]);
@@ -612,10 +647,19 @@
for (int format : supportedFormats) {
assertTrue("Format " + format + " fails cross check",
config.isOutputSupportedFor(format));
- Size[] supportedSizes = config.getOutputSizes(format);
+ List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
+ Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
+ if (arrayContains(actualCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
+ supportedSizes.addAll(
+ Arrays.asList(config.getHighResolutionOutputSizes(format)));
+ supportedSizes = CameraTestUtils.getAscendingOrderSizes(
+ supportedSizes, /*ascending*/true);
+ }
assertTrue("Supported format " + format + " has no sizes listed",
- supportedSizes.length > 0);
- for (Size size : supportedSizes) {
+ supportedSizes.size() > 0);
+ for (int i = 0; i < supportedSizes.size(); i++) {
+ Size size = supportedSizes.get(i);
if (VERBOSE) {
Log.v(TAG,
String.format("Testing camera %s, format %d, size %s",
@@ -628,6 +672,28 @@
assertTrue("YUV_420_888 may not have a non-zero stall duration",
stallDuration == 0);
break;
+ case ImageFormat.JPEG:
+ case ImageFormat.RAW_SENSOR:
+ final float TOLERANCE_FACTOR = 2.0f;
+ long prevDuration = 0;
+ if (i > 0) {
+ prevDuration = config.getOutputStallDuration(
+ format, supportedSizes.get(i - 1));
+ }
+ long nextDuration = Long.MAX_VALUE;
+ if (i < (supportedSizes.size() - 1)) {
+ nextDuration = config.getOutputStallDuration(
+ format, supportedSizes.get(i + 1));
+ }
+ long curStallDuration = config.getOutputStallDuration(format, size);
+ // Stall duration should be in a reasonable range: larger size should
+ // normally have larger stall duration.
+ mCollector.expectInRange("Stall duration (format " + format +
+ " and size " + size + ") is not in the right range",
+ curStallDuration,
+ (long) (prevDuration / TOLERANCE_FACTOR),
+ (long) (nextDuration * TOLERANCE_FACTOR));
+ break;
default:
assertTrue("Negative stall duration for format " + format,
stallDuration >= 0);
@@ -690,7 +756,8 @@
SurfaceTexture st = new SurfaceTexture(1);
Surface surf = new Surface(st);
- Size[] opaqueSizes = config.getOutputSizes(SurfaceTexture.class);
+ Size[] opaqueSizes = CameraTestUtils.getSupportedSizeForClass(SurfaceTexture.class,
+ mIds[counter], mCameraManager);
assertTrue("Opaque format has no sizes listed",
opaqueSizes.length > 0);
for (Size size : opaqueSizes) {
@@ -734,7 +801,8 @@
* Test high speed capability and cross-check the high speed sizes and fps ranges from
* the StreamConfigurationMap.
*/
- public void testConstrainedHighSpeedCapability() {
+ public void testConstrainedHighSpeedCapability() throws Exception {
+ int counter = 0;
for (CameraCharacteristics c : mCharacteristics) {
int[] capabilities = CameraTestUtils.getValueNotNull(
c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
@@ -745,7 +813,8 @@
c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
List<Size> highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
assertTrue("High speed sizes shouldn't be empty", highSpeedSizes.size() > 0);
- Size[] allSizes = config.getOutputSizes(ImageFormat.PRIVATE);
+ Size[] allSizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.PRIVATE,
+ mIds[counter], mCameraManager);
assertTrue("Normal size for PRIVATE format shouldn't be null or empty",
allSizes != null && allSizes.length > 0);
for (Size size: highSpeedSizes) {
@@ -805,6 +874,7 @@
}
}
}
+ counter++;
}
}
@@ -812,8 +882,15 @@
* Create an invalid size that's close to one of the good sizes in the list, but not one of them
*/
private Size findInvalidSize(Size[] goodSizes) {
- Size invalidSize = new Size(goodSizes[0].getWidth() + 1, goodSizes[0].getHeight());
- while(arrayContains(goodSizes, invalidSize)) {
+ return findInvalidSize(Arrays.asList(goodSizes));
+ }
+
+ /**
+ * Create an invalid size that's close to one of the good sizes in the list, but not one of them
+ */
+ private Size findInvalidSize(List<Size> goodSizes) {
+ Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight());
+ while(goodSizes.contains(invalidSize)) {
invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
}
return invalidSize;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index c10deac..8cba6fd 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -669,7 +669,7 @@
// TODO: Update this to use availableResultKeys once shim supports this.
if (mStaticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
Long exposureTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
Integer sensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
mCollector.expectInRange(
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index 1fa429d..7368376 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -522,6 +522,7 @@
if (useHighSpeedSession) {
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+ requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
requestBuilder.addTarget(mPreviewSurface);
requestBuilder.addTarget(mRecordingSurface);
slowMoRequests = mCamera.createConstrainedHighSpeedRequestList(requestBuilder.build());
@@ -762,12 +763,6 @@
CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
- if (!mSupportedVideoSizes.contains(videoSz)) {
- mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " +
- profileId + " must be one of the camera device supported video size!");
- continue;
- }
-
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
if (mStaticInfo.isHardwareLevelLegacy() &&
@@ -777,6 +772,12 @@
continue;
}
+ if (!mSupportedVideoSizes.contains(videoSz)) {
+ mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " +
+ profileId + " must be one of the camera device supported video size!");
+ continue;
+ }
+
// For LEGACY, find closest supported smaller or equal JPEG size to the current video
// size; if no size is smaller than the video, pick the smallest JPEG size. The assert
// for video size above guarantees that for LIMITED or FULL, we select videoSz here.
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index 3d5ceaa..a078ad4 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -17,7 +17,7 @@
package android.hardware.camera2.cts;
import static android.hardware.camera2.cts.CameraTestUtils.*;
-import static android.hardware.camera2.cts.RobustnessTest.MaxOutputSizes.*;
+import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.*;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
@@ -27,16 +27,20 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.media.CamcorderProfile;
import android.media.Image;
import android.media.ImageReader;
+import android.media.ImageWriter;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
@@ -144,7 +148,7 @@
* Each row of the table is a set of (format, max resolution) pairs, using the below consts
*/
- // Enum values are defined in MaxOutputSizes
+ // Enum values are defined in MaxStreamSizes
final int[][] LEGACY_COMBINATIONS = {
{PRIV, MAXIMUM}, // Simple preview, GPU video processing, or no-preview video recording
{JPEG, MAXIMUM}, // No-viewfinder still image capture
@@ -191,9 +195,164 @@
final int[][][] TABLES =
{ LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
- // Sanity check the tables
+ sanityCheckConfigurationTables(TABLES);
+
+ for (String id : mCameraIds) {
+ openDevice(id);
+
+ // Find the concrete max sizes for each format/resolution combination
+ MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id);
+
+ String streamConfigurationMapString =
+ mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).toString();
+ if (VERBOSE) {
+ Log.v(TAG, "StreamConfigurationMap: " + streamConfigurationMapString);
+ }
+
+ // Always run legacy-level tests
+
+ for (int[] config : LEGACY_COMBINATIONS) {
+ testOutputCombination(id, config, maxSizes);
+ }
+
+ // Then run higher-level tests if applicable
+
+ if (!mStaticInfo.isHardwareLevelLegacy()) {
+
+ // If not legacy, at least limited, so run limited-level tests
+
+ for (int[] config : LIMITED_COMBINATIONS) {
+ testOutputCombination(id, config, maxSizes);
+ }
+
+ // Check for BURST_CAPTURE, FULL and RAW and run those if appropriate
+
+ if (mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
+ for (int[] config : BURST_COMBINATIONS) {
+ testOutputCombination(id, config, maxSizes);
+ }
+ }
+
+ if (mStaticInfo.isHardwareLevelFull()) {
+ for (int[] config : FULL_COMBINATIONS) {
+ testOutputCombination(id, config, maxSizes);
+ }
+ }
+
+ if (mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+ for (int[] config : RAW_COMBINATIONS) {
+ testOutputCombination(id, config, maxSizes);
+ }
+ }
+ }
+
+ closeDevice(id);
+ }
+ }
+
+ /**
+ * Test for making sure the required reprocess input/output combinations for each hardware
+ * level and capability work as expected.
+ */
+ public void testMandatoryReprocessConfigurations() throws Exception {
+
+ /**
+ * For each stream combination, verify that
+ * 1. A reprocessable session can be created using the stream combination.
+ * 2. Reprocess capture requests targeting YUV and JPEG outputs are successful.
+ */
+ final int[][] LIMITED_COMBINATIONS = {
+ // Input Outputs
+ {PRIV, MAXIMUM, JPEG, MAXIMUM},
+ {YUV , MAXIMUM, JPEG, MAXIMUM},
+ {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM},
+ {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM},
+ {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM},
+ {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM},
+ {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
+ {YUV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
+ };
+
+ final int[][] FULL_COMBINATIONS = {
+ // Input Outputs
+ {YUV , MAXIMUM, PRIV, PREVIEW},
+ {YUV , MAXIMUM, YUV , PREVIEW},
+ {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , RECORD},
+ {YUV , MAXIMUM, PRIV, PREVIEW, YUV , RECORD},
+ {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , MAXIMUM},
+ {PRIV, MAXIMUM, YUV , PREVIEW, YUV , MAXIMUM},
+ {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , RECORD , JPEG, RECORD},
+ {YUV , MAXIMUM, PRIV, PREVIEW, YUV , RECORD , JPEG, RECORD},
+ {PRIV, MAXIMUM, YUV , PREVIEW, PRIV, PREVIEW, YUV , MAXIMUM},
+ {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
+ {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
+ };
+
+ final int[][] RAW_COMBINATIONS = {
+ // Input Outputs
+ {PRIV, MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM},
+ {YUV , MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM},
+ {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
+ {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
+ {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
+ {YUV , MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
+ {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+ {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+ {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+ {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+ };
+
+ final int[][][] TABLES =
+ { LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
+
+ sanityCheckConfigurationTables(TABLES);
+
+ for (String id : mCameraIds) {
+ CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
+ StaticMetadata staticInfo = new StaticMetadata(cc);
+ MaxStreamSizes maxSizes = new MaxStreamSizes(staticInfo, id);
+
+ // Skip the test for legacy devices.
+ if (staticInfo.isHardwareLevelLegacy()) {
+ continue;
+ }
+
+ openDevice(id);
+
+ try {
+ for (int[] config : LIMITED_COMBINATIONS) {
+ testReprocessStreamCombination(id, config, maxSizes, staticInfo);
+ }
+
+ // Check FULL devices
+ if (staticInfo.isHardwareLevelFull()) {
+ for (int[] config : FULL_COMBINATIONS) {
+ testReprocessStreamCombination(id, config, maxSizes, staticInfo);
+ }
+ }
+
+ // Check devices with RAW capability.
+ if (staticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+ for (int[] config : RAW_COMBINATIONS) {
+ testReprocessStreamCombination(id, config, maxSizes, staticInfo);
+ }
+ }
+ } finally {
+ closeDevice(id);
+ }
+ }
+ }
+
+ /**
+ * Sanity check the configuration tables.
+ */
+ private void sanityCheckConfigurationTables(final int[][][] tables) throws Exception {
int tableIdx = 0;
- for (int[][] table : TABLES) {
+ for (int[][] table : tables) {
int rowIdx = 0;
for (int[] row : table) {
assertTrue(String.format("Odd number of entries for table %d row %d: %s ",
@@ -214,73 +373,14 @@
}
tableIdx++;
}
-
- for (String id : mCameraIds) {
-
- // Find the concrete max sizes for each format/resolution combination
-
- CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
-
- MaxOutputSizes maxSizes = new MaxOutputSizes(cc, id);
-
- final StaticMetadata staticInfo = new StaticMetadata(cc);
- String streamConfigurationMapString =
- cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).toString();
- if (VERBOSE) {
- Log.v(TAG, "StreamConfigurationMap: " + streamConfigurationMapString);
- }
-
- openDevice(id);
-
- // Always run legacy-level tests
-
- for (int[] config : LEGACY_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
-
- // Then run higher-level tests if applicable
-
- if (!staticInfo.isHardwareLevelLegacy()) {
-
- // If not legacy, at least limited, so run limited-level tests
-
- for (int[] config : LIMITED_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
-
- // Check for BURST_CAPTURE, FULL and RAW and run those if appropriate
-
- if (staticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
- for (int[] config : BURST_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- }
-
- if (staticInfo.isHardwareLevelFull()) {
- for (int[] config : FULL_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- }
-
- if (staticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- for (int[] config : RAW_COMBINATIONS) {
- testOutputCombination(id, config, maxSizes);
- }
- }
- }
-
- closeDevice(id);
- }
}
/**
* Simple holder for resolutions to use for different camera outputs and size limits.
*/
- static class MaxOutputSizes {
+ static class MaxStreamSizes {
// Format shorthands
- static final int PRIV = -1;
+ static final int PRIV = ImageFormat.PRIVATE;
static final int JPEG = ImageFormat.JPEG;
static final int YUV = ImageFormat.YUV_420_888;
static final int RAW = ImageFormat.RAW_SENSOR;
@@ -292,15 +392,15 @@
static final int VGA = 3;
static final int RESOLUTION_COUNT = 4;
- public MaxOutputSizes(CameraCharacteristics cc, String cameraId) {
- StreamConfigurationMap configs =
- cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- Size[] privSizes = configs.getOutputSizes(SurfaceTexture.class);
- Size[] yuvSizes = configs.getOutputSizes(ImageFormat.YUV_420_888);
- Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
- Size[] rawSizes = configs.getOutputSizes(ImageFormat.RAW_SENSOR);
+ public MaxStreamSizes(StaticMetadata sm, String cameraId) {
+ Size[] privSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.PRIVATE,
+ StaticMetadata.StreamDirection.Output);
+ Size[] yuvSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888,
+ StaticMetadata.StreamDirection.Output);
+ Size[] jpegSizes = sm.getJpegOutputSizesChecked();
+ Size[] rawSizes = sm.getRawOutputSizesChecked();
- maxRawSize = (rawSizes != null) ? CameraTestUtils.getMaxSize(rawSizes) : null;
+ maxRawSize = (rawSizes.length != 0) ? CameraTestUtils.getMaxSize(rawSizes) : null;
maxPrivSizes[PREVIEW] = getMaxSize(privSizes, PREVIEW_SIZE_BOUND);
maxYuvSizes[PREVIEW] = getMaxSize(yuvSizes, PREVIEW_SIZE_BOUND);
@@ -314,6 +414,15 @@
maxYuvSizes[MAXIMUM] = CameraTestUtils.getMaxSize(yuvSizes);
maxJpegSizes[MAXIMUM] = CameraTestUtils.getMaxSize(jpegSizes);
+ StreamConfigurationMap configs = sm.getCharacteristics().get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ Size[] privInputSizes = configs.getInputSizes(ImageFormat.PRIVATE);
+ maxInputPrivSize = privInputSizes != null ?
+ CameraTestUtils.getMaxSize(privInputSizes) : null;
+ Size[] yuvInputSizes = configs.getInputSizes(ImageFormat.YUV_420_888);
+ maxInputYuvSize = yuvInputSizes != null ?
+ CameraTestUtils.getMaxSize(yuvInputSizes) : null;
+
// Must always be supported, add unconditionally
final Size vgaSize = new Size(640, 480);
maxPrivSizes[VGA] = vgaSize;
@@ -325,118 +434,253 @@
public final Size[] maxJpegSizes = new Size[RESOLUTION_COUNT];
public final Size[] maxYuvSizes = new Size[RESOLUTION_COUNT];
public final Size maxRawSize;
+ // TODO: support non maximum reprocess input.
+ public final Size maxInputPrivSize;
+ public final Size maxInputYuvSize;
static public String configToString(int[] config) {
StringBuilder b = new StringBuilder("{ ");
for (int i = 0; i < config.length; i += 2) {
int format = config[i];
int sizeLimit = config[i + 1];
- switch (format) {
- case PRIV:
- b.append("[PRIV, ");
- break;
- case JPEG:
- b.append("[JPEG, ");
- break;
- case YUV:
- b.append("[YUV, ");
- break;
- case RAW:
- b.append("[RAW, ");
- break;
- default:
- b.append("[UNK, ");
- break;
- }
- switch (sizeLimit) {
- case PREVIEW:
- b.append("PREVIEW] ");
- break;
- case RECORD:
- b.append("RECORD] ");
- break;
- case MAXIMUM:
- b.append("MAXIMUM] ");
- break;
- case VGA:
- b.append("VGA] ");
- break;
- default:
- b.append("UNK] ");
- break;
- }
+
+ appendFormatSize(b, format, sizeLimit);
+ b.append(" ");
}
b.append("}");
return b.toString();
}
+
+ static public String reprocessConfigToString(int[] reprocessConfig) {
+ // reprocessConfig[0..1] is the input configuration
+ StringBuilder b = new StringBuilder("Input: ");
+ appendFormatSize(b, reprocessConfig[0], reprocessConfig[1]);
+
+ // reprocessConfig[0..1] is also output configuration to be captured as reprocess input.
+ b.append(", Outputs: { ");
+ for (int i = 0; i < reprocessConfig.length; i += 2) {
+ int format = reprocessConfig[i];
+ int sizeLimit = reprocessConfig[i + 1];
+
+ appendFormatSize(b, format, sizeLimit);
+ b.append(" ");
+ }
+ b.append("}");
+ return b.toString();
+ }
+
+ static private void appendFormatSize(StringBuilder b, int format, int Size) {
+ switch (format) {
+ case PRIV:
+ b.append("[PRIV, ");
+ break;
+ case JPEG:
+ b.append("[JPEG, ");
+ break;
+ case YUV:
+ b.append("[YUV, ");
+ break;
+ case RAW:
+ b.append("[RAW, ");
+ break;
+ default:
+ b.append("[UNK, ");
+ break;
+ }
+
+ switch (Size) {
+ case PREVIEW:
+ b.append("PREVIEW]");
+ break;
+ case RECORD:
+ b.append("RECORD]");
+ break;
+ case MAXIMUM:
+ b.append("MAXIMUM]");
+ break;
+ case VGA:
+ b.append("VGA]");
+ break;
+ default:
+ b.append("UNK]");
+ break;
+ }
+ }
}
- private void testOutputCombination(String cameraId, int[] config, MaxOutputSizes maxSizes)
+ /**
+ * Return an InputConfiguration for a given reprocess configuration.
+ */
+ private InputConfiguration getInputConfig(int[] reprocessConfig, MaxStreamSizes maxSizes) {
+ int format;
+ Size size;
+
+ if (reprocessConfig[1] != MAXIMUM) {
+ throw new IllegalArgumentException("Test only supports MAXIMUM input");
+ }
+
+ switch (reprocessConfig[0]) {
+ case PRIV:
+ format = ImageFormat.PRIVATE;
+ size = maxSizes.maxInputPrivSize;
+ break;
+ case YUV:
+ format = ImageFormat.YUV_420_888;
+ size = maxSizes.maxInputYuvSize;
+ break;
+ default:
+ throw new IllegalArgumentException("Input format not supported: " +
+ reprocessConfig[0]);
+ }
+
+ return new InputConfiguration(size.getWidth(), size.getHeight(), format);
+ }
+
+ private void testReprocessStreamCombination(String cameraId, int[] reprocessConfig,
+ MaxStreamSizes maxSizes, StaticMetadata staticInfo) throws Exception {
+
+ Log.i(TAG, String.format("Testing Camera %s, reprocess config: %s", cameraId,
+ MaxStreamSizes.reprocessConfigToString(reprocessConfig)));
+
+ final int TIMEOUT_FOR_RESULT_MS = 3000;
+ final int NUM_REPROCESS_CAPTURES = 3;
+
+ List<SurfaceTexture> privTargets = new ArrayList<>();
+ List<ImageReader> jpegTargets = new ArrayList<>();
+ List<ImageReader> yuvTargets = new ArrayList<>();
+ List<ImageReader> rawTargets = new ArrayList<>();
+ List<Surface> outputSurfaces = new ArrayList<>();
+ ImageReader inputReader = null;
+ ImageWriter inputWriter = null;
+ SimpleImageReaderListener inputReaderListener = new SimpleImageReaderListener();
+ SimpleCaptureCallback inputCaptureListener = new SimpleCaptureCallback();
+ SimpleCaptureCallback reprocessOutputCaptureListener = new SimpleCaptureCallback();
+
+ boolean supportYuvReprocess = staticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
+ boolean supportOpaqueReprocess = staticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
+
+ // Skip the configuration if the format is not supported for reprocessing.
+ if ((reprocessConfig[0] == YUV && !supportYuvReprocess) ||
+ (reprocessConfig[0] == PRIV && !supportOpaqueReprocess)) {
+ return;
+ }
+
+ try {
+ // reprocessConfig[0:1] is input
+ InputConfiguration inputConfig = getInputConfig(
+ Arrays.copyOfRange(reprocessConfig, 0, 2), maxSizes);
+
+
+ inputReader = ImageReader.newInstance(inputConfig.getWidth(), inputConfig.getHeight(),
+ inputConfig.getFormat(), NUM_REPROCESS_CAPTURES);
+ inputReader.setOnImageAvailableListener(inputReaderListener, mHandler);
+ outputSurfaces.add(inputReader.getSurface());
+
+ // reprocessConfig[2..] are additional outputs
+ setupConfigurationTargets(
+ Arrays.copyOfRange(reprocessConfig, 2, reprocessConfig.length),
+ maxSizes, privTargets, jpegTargets, yuvTargets, rawTargets, outputSurfaces,
+ NUM_REPROCESS_CAPTURES);
+
+ // Verify we can create a reprocessable session with the input and all outputs.
+ BlockingSessionCallback sessionListener = new BlockingSessionCallback();
+ CameraCaptureSession session = configureReprocessableCameraSession(mCamera,
+ inputConfig, outputSurfaces, sessionListener, mHandler);
+ inputWriter = ImageWriter.newInstance(session.getInputSurface(),
+ NUM_REPROCESS_CAPTURES);
+
+ // Prepare a request for reprocess input
+ CaptureRequest.Builder builder = mCamera.createCaptureRequest(
+ CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
+ builder.addTarget(inputReader.getSurface());
+
+ for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) {
+ session.capture(builder.build(), inputCaptureListener, mHandler);
+ }
+
+ List<CaptureRequest> reprocessRequests = new ArrayList<>();
+ int numReprocessOutputs = 0;
+
+ for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) {
+ TotalCaptureResult result = inputCaptureListener.getTotalCaptureResult(
+ TIMEOUT_FOR_RESULT_MS);
+ builder = mCamera.createReprocessCaptureRequest(result);
+ inputWriter.queueInputImage(inputReaderListener.getImage(TIMEOUT_FOR_RESULT_MS));
+
+ // Test mandatory YUV and JPEG reprocess outputs.
+ for (ImageReader reader : jpegTargets) {
+ builder.addTarget(reader.getSurface());
+ numReprocessOutputs++;
+ }
+
+ for (ImageReader reader : yuvTargets) {
+ builder.addTarget(reader.getSurface());
+ numReprocessOutputs++;
+ }
+
+ reprocessRequests.add(builder.build());
+ }
+
+ session.captureBurst(reprocessRequests, reprocessOutputCaptureListener, mHandler);
+
+ for (int i = 0; i < numReprocessOutputs; i++) {
+ TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult(
+ TIMEOUT_FOR_RESULT_MS);
+ }
+ } catch (Throwable e) {
+ mCollector.addMessage(String.format("Reprocess stream combination %s failed due to: %s",
+ MaxStreamSizes.reprocessConfigToString(reprocessConfig), e.getMessage()));
+ } finally {
+ inputReaderListener.drain();
+ reprocessOutputCaptureListener.drain();
+
+ for (SurfaceTexture target : privTargets) {
+ target.release();
+ }
+
+ for (ImageReader target : jpegTargets) {
+ target.close();
+ }
+
+ for (ImageReader target : yuvTargets) {
+ target.close();
+ }
+
+ for (ImageReader target : rawTargets) {
+ target.close();
+ }
+
+ if (inputReader != null) {
+ inputReader.close();
+ }
+
+ if (inputWriter != null) {
+ inputWriter.close();
+ }
+ }
+ }
+
+ private void testOutputCombination(String cameraId, int[] config, MaxStreamSizes maxSizes)
throws Exception {
Log.i(TAG, String.format("Testing Camera %s, config %s",
- cameraId, MaxOutputSizes.configToString(config)));
+ cameraId, MaxStreamSizes.configToString(config)));
// Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS
final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000;
final int MIN_RESULT_COUNT = 3;
- ImageDropperListener imageDropperListener = new ImageDropperListener();
// Set up outputs
- List<Object> outputTargets = new ArrayList<>();
- List<Surface> outputSurfaces = new ArrayList<>();
+ List<Surface> outputSurfaces = new ArrayList<Surface>();
List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
List<ImageReader> rawTargets = new ArrayList<ImageReader>();
- for (int i = 0; i < config.length; i += 2) {
- int format = config[i];
- int sizeLimit = config[i + 1];
- switch (format) {
- case PRIV: {
- Size targetSize = maxSizes.maxPrivSizes[sizeLimit];
- SurfaceTexture target = new SurfaceTexture(/*random int*/1);
- target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
- outputTargets.add(target);
- outputSurfaces.add(new Surface(target));
- privTargets.add(target);
- break;
- }
- case JPEG: {
- Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
- ImageReader target = ImageReader.newInstance(
- targetSize.getWidth(), targetSize.getHeight(), JPEG, MIN_RESULT_COUNT);
- target.setOnImageAvailableListener(imageDropperListener, mHandler);
- outputTargets.add(target);
- outputSurfaces.add(target.getSurface());
- jpegTargets.add(target);
- break;
- }
- case YUV: {
- Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
- ImageReader target = ImageReader.newInstance(
- targetSize.getWidth(), targetSize.getHeight(), YUV, MIN_RESULT_COUNT);
- target.setOnImageAvailableListener(imageDropperListener, mHandler);
- outputTargets.add(target);
- outputSurfaces.add(target.getSurface());
- yuvTargets.add(target);
- break;
- }
- case RAW: {
- Size targetSize = maxSizes.maxRawSize;
- ImageReader target = ImageReader.newInstance(
- targetSize.getWidth(), targetSize.getHeight(), RAW, MIN_RESULT_COUNT);
- target.setOnImageAvailableListener(imageDropperListener, mHandler);
- outputTargets.add(target);
- outputSurfaces.add(target.getSurface());
- rawTargets.add(target);
- break;
- }
- default:
- fail("Unknown output format " + format);
- }
- }
+ setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
+ rawTargets, outputSurfaces, MIN_RESULT_COUNT);
boolean haveSession = false;
try {
@@ -469,17 +713,17 @@
} catch (Throwable e) {
mCollector.addMessage(String.format("Output combination %s failed due to: %s",
- MaxOutputSizes.configToString(config), e.getMessage()));
+ MaxStreamSizes.configToString(config), e.getMessage()));
}
if (haveSession) {
try {
Log.i(TAG, String.format("Done with camera %s, config %s, closing session",
- cameraId, MaxOutputSizes.configToString(config)));
+ cameraId, MaxStreamSizes.configToString(config)));
stopCapture(/*fast*/false);
} catch (Throwable e) {
mCollector.addMessage(
String.format("Closing down for output combination %s failed due to: %s",
- MaxOutputSizes.configToString(config), e.getMessage()));
+ MaxStreamSizes.configToString(config), e.getMessage()));
}
}
@@ -497,6 +741,59 @@
}
}
+ private void setupConfigurationTargets(int[] outputConfigs, MaxStreamSizes maxSizes,
+ List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
+ List<ImageReader> yuvTargets, List<ImageReader> rawTargets,
+ List<Surface> outputSurfaces, int numBuffers) {
+
+ ImageDropperListener imageDropperListener = new ImageDropperListener();
+
+ for (int i = 0; i < outputConfigs.length; i += 2) {
+ int format = outputConfigs[i];
+ int sizeLimit = outputConfigs[i + 1];
+
+ switch (format) {
+ case PRIV: {
+ Size targetSize = maxSizes.maxPrivSizes[sizeLimit];
+ SurfaceTexture target = new SurfaceTexture(/*random int*/1);
+ target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
+ outputSurfaces.add(new Surface(target));
+ privTargets.add(target);
+ break;
+ }
+ case JPEG: {
+ Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
+ ImageReader target = ImageReader.newInstance(
+ targetSize.getWidth(), targetSize.getHeight(), JPEG, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ outputSurfaces.add(target.getSurface());
+ jpegTargets.add(target);
+ break;
+ }
+ case YUV: {
+ Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
+ ImageReader target = ImageReader.newInstance(
+ targetSize.getWidth(), targetSize.getHeight(), YUV, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ outputSurfaces.add(target.getSurface());
+ yuvTargets.add(target);
+ break;
+ }
+ case RAW: {
+ Size targetSize = maxSizes.maxRawSize;
+ ImageReader target = ImageReader.newInstance(
+ targetSize.getWidth(), targetSize.getHeight(), RAW, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ outputSurfaces.add(target.getSurface());
+ rawTargets.add(target);
+ break;
+ }
+ default:
+ fail("Unknown output format " + format);
+ }
+ }
+ }
+
private static Size getMaxRecordingSize(String cameraId) {
int id = Integer.valueOf(cameraId);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
index 0dba61e..0bc74b3 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -86,10 +86,6 @@
mCollector.expectTrue("Full device must contain BURST_CAPTURE capability",
availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE));
- // Max resolution fps must be >= 20.
- mCollector.expectTrue("Full device must support at least 20fps for max resolution",
- getFpsForMaxSize(id) >= MIN_FPS_FOR_FULL_DEVICE);
-
// Need support per frame control
mCollector.expectTrue("Full device must support per frame control",
mStaticInfo.isPerFrameControlSupported());
@@ -102,7 +98,7 @@
}
// Max jpeg resolution must be very close to sensor resolution
- Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
+ Size[] jpegSizes = mStaticInfo.getJpegOutputSizesChecked();
Size maxJpegSize = CameraTestUtils.getMaxSize(jpegSizes);
mCollector.expectSizesAreSimilar(
"Active array size and max JPEG size should be similar",
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
index feb0028..ee4ddd9 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -18,7 +18,6 @@
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static android.hardware.camera2.cts.helpers.AssertHelpers.assertArrayContains;
-import static junit.framework.Assert.assertNotNull;
import android.graphics.ImageFormat;
import android.graphics.Point;
@@ -106,7 +105,7 @@
public void testTakePicture() throws Exception{
for (String id : mCameraIds) {
try {
- Log.i(TAG, "Testing touch for focus for Camera " + id);
+ Log.i(TAG, "Testing basic take picture for Camera " + id);
openDevice(id);
takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null);
@@ -336,6 +335,21 @@
}
}
+ public void testAePrecaptureTriggerCancelJpegCapture() throws Exception {
+ for (String id : mCameraIds) {
+ try {
+ Log.i(TAG, "Testing AE precapture cancel for jpeg capture for Camera " + id);
+ openDevice(id);
+
+ takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
+ /*addAeTriggerCancel*/true);
+ } finally {
+ closeDevice();
+ closeImageReader();
+ }
+ }
+ }
+
/**
* Start preview,take a picture and test preview is still running after snapshot
*/
@@ -393,6 +407,29 @@
private void takePictureTestByCamera(
MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
MeteringRectangle[] afRegions) throws Exception {
+ takePictureTestByCamera(aeRegions, awbRegions, afRegions,
+ /*addAeTriggerCancel*/false);
+ }
+
+ /**
+ * Take a picture for a given set of 3A regions for a particular camera.
+ * <p>
+ * Before take a still capture, it triggers an auto focus and lock it first,
+ * then wait for AWB to converge and lock it, then trigger a precapture
+ * metering sequence and wait for AE converged. After capture is received, the
+ * capture result and image are validated. If {@code addAeTriggerCancel} is true,
+ * a precapture trigger cancel will be inserted between two adjacent triggers, which
+ * should effective cancel the first trigger.
+ * </p>
+ *
+ * @param aeRegions AE regions for this capture
+ * @param awbRegions AWB regions for this capture
+ * @param afRegions AF regions for this capture
+ * @param addAeTriggerCancel If a AE precapture trigger cancel is sent after the trigger.
+ */
+ private void takePictureTestByCamera(
+ MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
+ MeteringRectangle[] afRegions, boolean addAeTriggerCancel) throws Exception {
boolean hasFocuser = mStaticInfo.hasFocuser();
@@ -498,6 +535,22 @@
previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
mSession.capture(previewRequest.build(), resultListener, mHandler);
+ if (addAeTriggerCancel) {
+ // Cancel the current precapture trigger, then send another trigger.
+ // The camera device should behave as if the first trigger is not sent.
+ // Wait one request to make the trigger start doing something before cancel.
+ waitForNumResults(resultListener, /*numResultsWait*/ 1);
+ previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
+ mSession.capture(previewRequest.build(), resultListener, mHandler);
+ waitForResultValue(resultListener, CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL,
+ NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+ // Issue another trigger
+ previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
+ mSession.capture(previewRequest.build(), resultListener, mHandler);
+ }
waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
// Validate the next result immediately for region and mode.
@@ -610,16 +663,7 @@
*/
private void rawCaptureTestByCamera() throws Exception {
Size maxPreviewSz = mOrderedPreviewSizes.get(0);
- Size[] rawSizes = mStaticInfo.getRawOutputSizesChecked();
-
- assertTrue("No capture sizes available for RAW format!",
- rawSizes.length != 0);
- Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
- Size size = new Size(activeArray.width(), activeArray.height());
- assertTrue("Missing ActiveArraySize", activeArray.width() > 0 &&
- activeArray.height() > 0);
- assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
- rawSizes, size);
+ Size size = mStaticInfo.getRawDimensChecked();
// Prepare raw capture and start preview.
CaptureRequest.Builder previewBuilder =
@@ -658,20 +702,12 @@
private void fullRawCaptureTestByCamera() throws Exception {
Size maxPreviewSz = mOrderedPreviewSizes.get(0);
Size maxStillSz = mOrderedStillSizes.get(0);
- Size[] rawSizes = mStaticInfo.getRawOutputSizesChecked();
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
SimpleImageReaderListener jpegListener = new SimpleImageReaderListener();
SimpleImageReaderListener rawListener = new SimpleImageReaderListener();
- assertTrue("No capture sizes available for RAW format!",
- rawSizes.length != 0);
- Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
- Size size = new Size(activeArray.width(), activeArray.height());
- assertTrue("Missing ActiveArraySize", activeArray.width() > 0 &&
- activeArray.height() > 0);
- assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
- rawSizes, size);
+ Size size = mStaticInfo.getRawDimensChecked();
if (VERBOSE) {
Log.v(TAG, "Testing multi capture with size " + size.toString()
@@ -931,7 +967,7 @@
long maxExposureValuePreview = -1;
long maxExposureValueStill = -1;
if (mStaticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
// Minimum exposure settings is mostly static while maximum exposure setting depends on
// frame rate range which in term depends on capture request.
minExposureValue = mStaticInfo.getSensitivityMinimumOrDefault() *
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 7d377d6..da9b0ce 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -191,8 +191,11 @@
// Lock AE if possible to improve stability
previewRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
- waitForResultValue(resultListener, CaptureResult.CONTROL_AE_STATE,
- CaptureResult.CONTROL_AE_STATE_LOCKED, MAX_RESULTS_TO_WAIT);
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ // Legacy mode doesn't output AE state
+ waitForResultValue(resultListener, CaptureResult.CONTROL_AE_STATE,
+ CaptureResult.CONTROL_AE_STATE_LOCKED, MAX_RESULTS_TO_WAIT);
+ }
}
// Measure frame rate for a bit
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 3890b33..e26cf7a 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -41,6 +41,8 @@
import java.util.List;
import java.util.Set;
+import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+
/**
* Helpers to get common static info out of the camera.
*
@@ -743,6 +745,26 @@
}
/**
+ * Get and check pre-correction active array size.
+ */
+ public Rect getPreCorrectedActiveArraySizeChecked() {
+ Key<Rect> key = CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE;
+ Rect activeArray = getValueFromKeyNonNull(key);
+
+ if (activeArray == null) {
+ return new Rect(0, 0, 0, 0);
+ }
+
+ Size pixelArraySize = getPixelArraySizeChecked();
+ checkTrueForKey(key, "values left/top are invalid", activeArray.left >= 0 && activeArray.top >= 0);
+ checkTrueForKey(key, "values width/height are invalid",
+ activeArray.width() <= pixelArraySize.getWidth() &&
+ activeArray.height() <= pixelArraySize.getHeight());
+
+ return activeArray;
+ }
+
+ /**
* Get and check active array size.
*/
public Rect getActiveArraySizeChecked() {
@@ -763,6 +785,29 @@
}
/**
+ * Get the dimensions to use for RAW16 buffers.
+ */
+ public Size getRawDimensChecked() throws Exception {
+ Size[] targetCaptureSizes = getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+ StaticMetadata.StreamDirection.Output);
+ Assert.assertTrue("No capture sizes available for RAW format!",
+ targetCaptureSizes.length != 0);
+ Rect activeArray = getPreCorrectedActiveArraySizeChecked();
+ Size preCorrectionActiveArraySize =
+ new Size(activeArray.width(), activeArray.height());
+ Size pixelArraySize = getPixelArraySizeChecked();
+ Assert.assertTrue("Missing pre-correction active array size", activeArray.width() > 0 &&
+ activeArray.height() > 0);
+ Assert.assertTrue("Missing pixel array size", pixelArraySize.getWidth() > 0 &&
+ pixelArraySize.getHeight() > 0);
+ Size[] allowedArraySizes = new Size[] { preCorrectionActiveArraySize,
+ pixelArraySize };
+ return assertArrayContainsAnyOf("Available sizes for RAW format" +
+ " must include either the pre-corrected active array size, or the full " +
+ "pixel array size", targetCaptureSizes, allowedArraySizes);
+ }
+
+ /**
* Get the sensitivity value and clamp to the range if needed.
*
* @param sensitivity Input sensitivity value to check.
@@ -1168,7 +1213,7 @@
*
* @return Empty size array if jpeg output is not supported
*/
- public Size[] getJpegOutputSizeChecked() {
+ public Size[] getJpegOutputSizesChecked() {
return getAvailableSizesForFormatChecked(ImageFormat.JPEG,
StreamDirection.Output);
}
@@ -1236,6 +1281,22 @@
* @return The sizes of the given format, empty array if no available size is found.
*/
public Size[] getAvailableSizesForFormatChecked(int format, StreamDirection direction) {
+ return getAvailableSizesForFormatChecked(format, direction,
+ /*fastSizes*/true, /*slowSizes*/true);
+ }
+
+ /**
+ * Get available sizes for given format and direction, and whether to limit to slow or fast
+ * resolutions.
+ *
+ * @param format The format for the requested size array.
+ * @param direction The stream direction, input or output.
+ * @param fastSizes whether to include getOutputSizes() sizes (generally faster)
+ * @param slowSizes whether to include getHighResolutionOutputSizes() sizes (generally slower)
+ * @return The sizes of the given format, empty array if no available size is found.
+ */
+ public Size[] getAvailableSizesForFormatChecked(int format, StreamDirection direction,
+ boolean fastSizes, boolean slowSizes) {
Key<StreamConfigurationMap> key =
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
StreamConfigurationMap config = getValueFromKeyNonNull(key);
@@ -1244,11 +1305,27 @@
return new Size[0];
}
- Size[] sizes;
+ Size[] sizes = null;
switch (direction) {
case Output:
- sizes = config.getOutputSizes(format);
+ Size[] fastSizeList = null;
+ Size[] slowSizeList = null;
+ if (fastSizes) {
+ fastSizeList = config.getOutputSizes(format);
+ }
+ if (slowSizes) {
+ slowSizeList = config.getHighResolutionOutputSizes(format);
+ }
+ if (fastSizeList != null && slowSizeList != null) {
+ sizes = new Size[slowSizeList.length + fastSizeList.length];
+ System.arraycopy(fastSizeList, 0, sizes, 0, fastSizeList.length);
+ System.arraycopy(slowSizeList, 0, sizes, fastSizeList.length, slowSizeList.length);
+ } else if (fastSizeList != null) {
+ sizes = fastSizeList;
+ } else if (slowSizeList != null) {
+ sizes = slowSizeList;
+ }
break;
case Input:
sizes = config.getInputSizes(format);
@@ -1369,7 +1446,8 @@
return minDurationMap;
}
- for (android.util.Size size : config.getOutputSizes(format)) {
+ for (android.util.Size size : getAvailableSizesForFormatChecked(format,
+ StreamDirection.Output)) {
long minFrameDuration = config.getOutputMinFrameDuration(format, size);
if (minFrameDuration != 0) {
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index 019bd21..ec9caaf 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -37,6 +37,7 @@
import android.os.ConditionVariable;
import android.os.Environment;
import android.os.Looper;
+import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.MoreAsserts;
import android.test.UiThreadTest;
@@ -1975,7 +1976,7 @@
// This method tests if the actual fps is between minimum and maximum.
// It also tests if the frame interval is too long.
public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
- long arrivalTime = System.currentTimeMillis();
+ long arrivalTime = SystemClock.elapsedRealtime();
camera.addCallbackBuffer(data);
if (firstFrameArrivalTime == 0) firstFrameArrivalTime = arrivalTime;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
index 7c4deb9..a90725f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
@@ -43,7 +43,7 @@
private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<TestSensorEvent>();
private final long mMaximumSynchronizationErrorNs;
- private final long mReportLatencyNs;
+ private final long mExpectedSyncLatencyNs;
/**
* Constructs an instance of {@link EventTimestampSynchronizationVerification}.
@@ -53,9 +53,9 @@
*/
public EventTimestampSynchronizationVerification(
long maximumSynchronizationErrorNs,
- long reportLatencyNs) {
+ long expectedSyncLatencyNs) {
mMaximumSynchronizationErrorNs = maximumSynchronizationErrorNs;
- mReportLatencyNs = reportLatencyNs;
+ mExpectedSyncLatencyNs = expectedSyncLatencyNs;
}
/**
@@ -73,10 +73,12 @@
if (fifoMaxEventCount > 0 && maximumExpectedSamplingPeriodUs != Integer.MAX_VALUE) {
long fifoBasedReportLatencyUs =
fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
- reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+ reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs) +
+ (long)(2.5 * maximumExpectedSamplingPeriodUs);
}
- long reportLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs);
- return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS, reportLatencyNs);
+ long expectedSyncLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs);
+ return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS,
+ expectedSyncLatencyNs);
}
@Override
@@ -84,7 +86,6 @@
StringBuilder errorMessageBuilder =
new StringBuilder(" event timestamp synchronization failures: ");
List<IndexedEvent> failures = verifyTimestampSynchronization(errorMessageBuilder);
-
int failuresCount = failures.size();
stats.addValue(SensorStats.EVENT_TIME_SYNCHRONIZATION_COUNT_KEY, failuresCount);
stats.addValue(
@@ -104,7 +105,7 @@
public EventTimestampSynchronizationVerification clone() {
return new EventTimestampSynchronizationVerification(
mMaximumSynchronizationErrorNs,
- mReportLatencyNs);
+ mExpectedSyncLatencyNs);
}
/**
@@ -133,7 +134,7 @@
long receivedTimestampNs = event.receivedTimestamp;
long upperThresholdNs = receivedTimestampNs;
long lowerThresholdNs = receivedTimestampNs - mMaximumSynchronizationErrorNs
- - mReportLatencyNs;
+ - mExpectedSyncLatencyNs;
if (eventTimestampNs < lowerThresholdNs || eventTimestampNs > upperThresholdNs) {
if (failures.size() < TRUNCATE_MESSAGE_LENGTH) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java b/tests/tests/hardware/src/android/hardware/input/cts/InputCallback.java
similarity index 67%
copy from apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java
copy to tests/tests/hardware/src/android/hardware/input/cts/InputCallback.java
index 4231db7..accdcaf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/InputCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.cts.verifier.tv;
+package android.hardware.input.cts;
-import android.preference.PreferenceActivity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
-public class MockTvInputSettingsActivity extends PreferenceActivity {
-
+public interface InputCallback {
+ public void onKeyEvent(KeyEvent ev);
+ public void onMotionEvent(MotionEvent ev);
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java b/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java
new file mode 100644
index 0000000..b16cadb
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input.cts;
+
+import android.app.Activity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InputCtsActivity extends Activity {
+ private InputCallback mInputCallback;
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+ if (mInputCallback != null) {
+ mInputCallback.onMotionEvent(ev);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (mInputCallback != null) {
+ mInputCallback.onMotionEvent(ev);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean dispatchTrackballEvent(MotionEvent ev) {
+ if (mInputCallback != null) {
+ mInputCallback.onMotionEvent(ev);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent ev) {
+ if (mInputCallback != null) {
+ mInputCallback.onKeyEvent(ev);
+ }
+ return true;
+ }
+
+ public void setInputCallback(InputCallback callback) {
+ mInputCallback = callback;
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/GamepadTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/GamepadTestCase.java
new file mode 100644
index 0000000..92fba12
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/GamepadTestCase.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input.cts.tests;
+
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.io.Writer;
+import java.util.List;
+
+import com.android.cts.hardware.R;
+
+public class GamepadTestCase extends InputTestCase {
+ private static final String TAG = "GamepadTests";
+
+ public void testButtonA() throws Exception {
+ sendHidCommands(R.raw.gamepad_press_a);
+ assertReceivedKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BUTTON_A);
+ assertReceivedKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BUTTON_A);
+ assertNoMoreEvents();
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
new file mode 100644
index 0000000..fba5f51
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input.cts.tests;
+
+import android.app.UiAutomation;
+import android.hardware.input.cts.InputCtsActivity;
+import android.hardware.input.cts.InputCallback;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.List;
+import java.util.UUID;
+
+public class InputTestCase extends ActivityInstrumentationTestCase2<InputCtsActivity> {
+ private static final String TAG = "InputTestCase";
+ private static final String HID_EXECUTABLE = "hid";
+ private static final int SHELL_UID = 2000;
+ private static final String[] KEY_ACTIONS = {"DOWN", "UP", "MULTIPLE"};
+
+ private File mFifo;
+ private Writer mWriter;
+
+ private BlockingQueue<KeyEvent> mKeys;
+ private BlockingQueue<MotionEvent> mMotions;
+ private InputListener mInputListener;
+
+ public InputTestCase() {
+ super(InputCtsActivity.class);
+ mKeys = new LinkedBlockingQueue<KeyEvent>();
+ mMotions = new LinkedBlockingQueue<MotionEvent>();
+ mInputListener = new InputListener();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mFifo = setupFifo();
+ clearKeys();
+ clearMotions();
+ getActivity().setInputCallback(mInputListener);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mFifo != null) {
+ mFifo.delete();
+ mFifo = null;
+ }
+ closeQuietly(mWriter);
+ mWriter = null;
+ super.tearDown();
+ }
+
+ /**
+ * Sends the HID commands designated by the given resource id.
+ * The commands must be in the format expected by the `hid` shell command.
+ *
+ * @param id The resource id from which to load the HID commands. This must be a "raw"
+ * resource.
+ */
+ public void sendHidCommands(int id) {
+ try {
+ Writer w = getWriter();
+ w.write(getEvents(id));
+ w.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Asserts that the application received a {@link android.view.KeyEvent} with the given action
+ * and keycode.
+ *
+ * If other KeyEvents are received by the application prior to the expected KeyEvent, or no
+ * KeyEvents are received within a reasonable amount of time, then this will throw an
+ * AssertionFailedError.
+ *
+ * @param action The action to expect on the next KeyEvent
+ * (e.g. {@link android.view.KeyEvent#ACTION_DOWN}).
+ * @param keyCode The expected key code of the next KeyEvent.
+ */
+ public void assertReceivedKeyEvent(int action, int keyCode) {
+ KeyEvent k = waitForKey();
+ if (k == null) {
+ fail("Timed out waiting for " + KeyEvent.keyCodeToString(keyCode)
+ + " with action " + KEY_ACTIONS[action]);
+ return;
+ }
+ assertEquals(action, k.getAction());
+ assertEquals(keyCode, k.getKeyCode());
+ }
+
+ /**
+ * Asserts that no more events have been received by the application.
+ *
+ * If any more events have been received by the application, this throws an
+ * AssertionFailedError.
+ */
+ public void assertNoMoreEvents() {
+ KeyEvent key;
+ MotionEvent motion;
+ if ((key = mKeys.poll()) != null) {
+ fail("Extraneous key events generated: " + key);
+ }
+ if ((motion = mMotions.poll()) != null) {
+ fail("Extraneous motion events generated: " + motion);
+ }
+ }
+
+ private KeyEvent waitForKey() {
+ try {
+ return mKeys.poll(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+
+ private void clearKeys() {
+ mKeys.clear();
+ }
+
+ private void clearMotions() {
+ mMotions.clear();
+ }
+
+ private File setupFifo() throws ErrnoException {
+ File dir = getActivity().getCacheDir();
+ String filename = dir.getAbsolutePath() + File.separator + UUID.randomUUID().toString();
+ Os.mkfifo(filename, 0666);
+ File f = new File(filename);
+ return f;
+ }
+
+ private Writer getWriter() throws IOException {
+ if (mWriter == null) {
+ UiAutomation ui = getInstrumentation().getUiAutomation();
+ ui.executeShellCommand("hid " + mFifo.getAbsolutePath());
+ mWriter = new FileWriter(mFifo);
+ }
+ return mWriter;
+ }
+
+ private String getEvents(int id) throws IOException {
+ InputStream is =
+ getInstrumentation().getTargetContext().getResources().openRawResource(id);
+ return readFully(is);
+ }
+
+
+ private static void closeQuietly(AutoCloseable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) { }
+ }
+ }
+
+ private static String readFully(InputStream is) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int read = 0;
+ byte[] buffer = new byte[1024];
+ while ((read = is.read(buffer)) >= 0) {
+ baos.write(buffer, 0, read);
+ }
+ return baos.toString();
+ }
+
+ private class InputListener implements InputCallback {
+ public void onKeyEvent(KeyEvent ev) {
+ boolean done = false;
+ do {
+ try {
+ mKeys.put(new KeyEvent(ev));
+ done = true;
+ } catch (InterruptedException ignore) { }
+ } while (!done);
+ }
+
+ public void onMotionEvent(MotionEvent ev) {
+ boolean done = false;
+ do {
+ try {
+ mMotions.put(MotionEvent.obtain(ev));
+ done = true;
+ } catch (InterruptedException ignore) { }
+ } while (!done);
+ }
+ }
+}
diff --git a/tests/tests/keystore/Android.mk b/tests/tests/keystore/Android.mk
index 0f2cd03..eaf5389 100644
--- a/tests/tests/keystore/Android.mk
+++ b/tests/tests/keystore/Android.mk
@@ -16,7 +16,7 @@
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner core-tests-support
diff --git a/tests/tests/keystore/assets/nist_cavp_aes_kat.zip b/tests/tests/keystore/assets/nist_cavp_aes_kat.zip
new file mode 100644
index 0000000..90083c8
--- /dev/null
+++ b/tests/tests/keystore/assets/nist_cavp_aes_kat.zip
Binary files differ
diff --git a/tests/tests/keystore/res/raw/ec_key1_cert.der b/tests/tests/keystore/res/raw/ec_key1_cert.der
new file mode 100644
index 0000000..77293bb
--- /dev/null
+++ b/tests/tests/keystore/res/raw/ec_key1_cert.der
Binary files differ
diff --git a/tests/tests/keystore/res/raw/ec_key1_pkcs8.der b/tests/tests/keystore/res/raw/ec_key1_pkcs8.der
new file mode 100644
index 0000000..0400ad6
--- /dev/null
+++ b/tests/tests/keystore/res/raw/ec_key1_pkcs8.der
Binary files differ
diff --git a/tests/tests/keystore/res/raw/rsa_key1_cert.der b/tests/tests/keystore/res/raw/rsa_key1_cert.der
new file mode 100644
index 0000000..09e5a30
--- /dev/null
+++ b/tests/tests/keystore/res/raw/rsa_key1_cert.der
Binary files differ
diff --git a/tests/tests/keystore/res/raw/rsa_key1_pkcs8.der b/tests/tests/keystore/res/raw/rsa_key1_pkcs8.der
new file mode 100644
index 0000000..b398ab1
--- /dev/null
+++ b/tests/tests/keystore/res/raw/rsa_key1_pkcs8.der
Binary files differ
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESCipherNistCavpKatTest.java b/tests/tests/keystore/src/android/keystore/cts/AESCipherNistCavpKatTest.java
new file mode 100644
index 0000000..520b54d
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESCipherNistCavpKatTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+public class AESCipherNistCavpKatTest extends AndroidTestCase {
+
+ private static final String TAG = AESCipherNistCavpKatTest.class.getSimpleName();
+
+ public void testSomething() throws Exception {
+ try (ZipInputStream zipIn = new ZipInputStream(
+ getContext().getResources().getAssets().open("nist_cavp_aes_kat.zip"))) {
+ ZipEntry zipEntry;
+ while ((zipEntry = zipIn.getNextEntry()) != null) {
+ String entryName = zipEntry.getName();
+ if (!entryName.endsWith(".rsp")) {
+ continue;
+ }
+
+ if (zipEntry.getSize() > 1024 * 1024) {
+ fail("Entry " + entryName + " too large: " + zipEntry.getSize() + " bytes");
+ }
+ byte[] entryContents = new byte[(int) zipEntry.getSize()];
+ readFully(zipIn, entryContents);
+ runTestsForKatFile(entryName, entryContents);
+ }
+ }
+ }
+
+ private void runTestsForKatFile(String fileName, byte[] contents) throws Exception {
+ if ((!fileName.endsWith(".rsp")) || (fileName.length() < 10)) {
+ Log.i(TAG, "Ignoring " + fileName + " -- not a KAT file");
+ return;
+ }
+ String mode = fileName.substring(0, 3);
+ if ("CFB".equals(mode)) {
+ mode = fileName.substring(0, 4);
+ }
+ try {
+ Cipher.getInstance("AES/" + mode + "/NoPadding", "AndroidKeyStoreBCWorkaround");
+ } catch (NoSuchAlgorithmException e) {
+ if (("CBC".equals(mode)) || ("ECB".equals(mode))) {
+ fail("Supported mode is apparently not supported: " + mode);
+ }
+ Log.i(TAG, "Skipping " + fileName
+ + " -- transformation not supported by AndroidKeyStore");
+ return;
+ }
+
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(
+ new ByteArrayInputStream(contents), "ISO-8859-1"));
+ String line;
+ int lineNumber = 0;
+ String section = null; // ENCRYPT or DECRYPT
+
+ boolean insideTestDefinition = false;
+ int testNumber = 0;
+ TestVector testVector = null;
+
+ while ((line = in.readLine()) != null) {
+ lineNumber++;
+ line = line.trim();
+ if (line.startsWith("#")) {
+ // Ignore comment lines
+ continue;
+ }
+
+ if (!insideTestDefinition) {
+ // Outside of a test definition
+ if (line.length() == 0) {
+ // Ignore empty lines
+ continue;
+ }
+ if ((line.startsWith("[")) && (line.endsWith("]"))) {
+ section = line.substring(1, line.length() - 1);
+ if ((!"DECRYPT".equals(section)) && (!"ENCRYPT".equals(section))) {
+ throw new IOException(lineNumber + ": Unexpected section: " + section);
+ }
+ continue;
+ }
+
+ // Check whether this is a NAME = VALUE line
+ int delimiterIndex = line.indexOf('=');
+ if (delimiterIndex == -1) {
+ throw new IOException(lineNumber + ": Unexpected line outside of test"
+ + " definition: " + line);
+ }
+ String name = line.substring(0, delimiterIndex).trim();
+ String value = line.substring(delimiterIndex + 1).trim();
+
+ if ("COUNT".equals(name)) {
+ testNumber = Integer.parseInt(value);
+ insideTestDefinition = true;
+ testVector = new TestVector();
+ } else {
+ throw new IOException(lineNumber + ": Unexpected line outside of test"
+ + " definition: " + line);
+ }
+ } else {
+ // Inside of a test definition
+ if (line.length() == 0) {
+ // End of test definition
+ boolean encrypt;
+ if ("ENCRYPT".equals(section)) {
+ encrypt = true;
+ } else if ("DECRYPT".equals(section)) {
+ encrypt = false;
+ } else {
+ throw new IOException("Unexpected test operation: " + section);
+ }
+ Log.d(TAG, "Running test #" + testNumber + ": AES/" + mode + " from " + fileName);
+ runKatTest(mode, encrypt, testVector);
+ insideTestDefinition = false;
+ testVector = null;
+ } else {
+ // Check whether this is a NAME = VALUE line
+ int delimiterIndex = line.indexOf('=');
+ if (delimiterIndex == -1) {
+ throw new IOException(lineNumber + ": Unexpected line inside test"
+ + " definition: " + line);
+ }
+ String name = line.substring(0, delimiterIndex).trim();
+ String value = line.substring(delimiterIndex + 1).trim();
+
+ if ("KEY".equals(name)) {
+ testVector.key = HexEncoding.decode(value);
+ } else if ("IV".equals(name)) {
+ testVector.iv = HexEncoding.decode(value);
+ } else if ("PLAINTEXT".equals(name)) {
+ testVector.plaintext = HexEncoding.decode(value);
+ } else if ("CIPHERTEXT".equals(name)) {
+ testVector.ciphertext = HexEncoding.decode(value);
+ } else {
+ throw new IOException(lineNumber + ": Unexpected line inside test"
+ + " definition: " + line);
+ }
+ }
+ }
+ }
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (Exception ignored) {}
+ }
+ }
+ }
+
+ private void runKatTest(String mode, boolean encrypt, TestVector testVector) throws Exception {
+ String keyAlias = AESCipherNistCavpKatTest.class.getName();
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ keyStore.setEntry(keyAlias,
+ new KeyStore.SecretKeyEntry(new SecretKeySpec(testVector.key, "AES")),
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(mode)
+ .setEncryptionPaddings("NoPadding")
+ .setRandomizedEncryptionRequired(false)
+ .build());
+ try {
+ SecretKey key = (SecretKey) keyStore.getKey(keyAlias, null);
+ assertNotNull(key);
+ Cipher cipher = Cipher.getInstance("AES/" + mode + "/NoPadding");
+
+ int opmode = (encrypt) ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
+ if (testVector.iv != null) {
+ cipher.init(opmode, key, new IvParameterSpec(testVector.iv));
+ } else {
+ cipher.init(opmode, key);
+ }
+
+ byte[] input = (encrypt) ? testVector.plaintext : testVector.ciphertext;
+ byte[] actualOutput = cipher.doFinal(input);
+ byte[] expectedOutput = (encrypt) ? testVector.ciphertext : testVector.plaintext;
+ if (!Arrays.equals(expectedOutput, actualOutput)) {
+ fail("Expected: " + HexEncoding.encode(expectedOutput)
+ + ", actual: " + HexEncoding.encode(actualOutput));
+ }
+ } finally {
+ keyStore.deleteEntry(keyAlias);
+ }
+ }
+
+ private static void readFully(InputStream in, byte[] buf) throws IOException {
+ int offset = 0;
+ int remaining = buf.length;
+ while (remaining > 0) {
+ int chunkSize = in.read(buf, offset, remaining);
+ if (chunkSize == -1) {
+ throw new EOFException("Premature EOF. Remainig: " + remaining);
+ }
+ offset += chunkSize;
+ remaining -= chunkSize;
+ }
+ }
+
+ private static class TestVector {
+ public byte[] key;
+ public byte[] iv;
+ public byte[] plaintext;
+ public byte[] ciphertext;
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java
deleted file mode 100644
index 1f6a5c0..0000000
--- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.keystore.cts;
-
-import android.security.KeyPairGeneratorSpec;
-import android.test.AndroidTestCase;
-
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.Principal;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.ECKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.EllipticCurve;
-import java.security.spec.RSAKeyGenParameterSpec;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509ExtendedKeyManager;
-import javax.security.auth.x500.X500Principal;
-
-import libcore.java.security.TestKeyStore;
-import libcore.javax.net.ssl.TestKeyManager;
-import libcore.javax.net.ssl.TestSSLContext;
-
-public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
- private KeyPairGenerator mGenerator;
-
- private KeyStore mKeyStore;
-
- private static final String TEST_ALIAS_1 = "test1";
-
- private static final String TEST_ALIAS_2 = "test2";
-
- private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
-
- private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
-
- private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
-
- private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
-
- private static final long NOW_MILLIS = System.currentTimeMillis();
-
- /* We have to round this off because X509v3 doesn't store milliseconds. */
- private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
-
- @SuppressWarnings("deprecation")
- private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
-
- @Override
- protected void setUp() throws Exception {
- mKeyStore = KeyStore.getInstance("AndroidKeyStore");
- mKeyStore.load(null, null);
-
- mGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
- }
-
- public void testKeyPairGenerator_Initialize_Params_Unencrypted_Success() throws Exception {
- mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setSubject(TEST_DN_1)
- .setSerialNumber(TEST_SERIAL_1)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build());
- }
-
- public void testKeyPairGenerator_Initialize_KeySize_Unencrypted_Failure() throws Exception {
- try {
- mGenerator.initialize(1024);
- fail("KeyPairGenerator should not support setting the key size");
- } catch (IllegalArgumentException success) {
- }
- }
-
- public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Unencrypted_Failure()
- throws Exception {
- try {
- mGenerator.initialize(1024, new SecureRandom());
- fail("KeyPairGenerator should not support setting the key size");
- } catch (IllegalArgumentException success) {
- }
- }
-
- public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Unencrypted_Failure()
- throws Exception {
- mGenerator.initialize(
- new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setSubject(TEST_DN_1)
- .setSerialNumber(TEST_SERIAL_1)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build(),
- new SecureRandom());
- }
-
- public void testKeyPairGenerator_GenerateKeyPair_EC_Unencrypted_Success() throws Exception {
- mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setKeyType("EC")
- .setSubject(TEST_DN_1)
- .setSerialNumber(TEST_SERIAL_1)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build());
-
- final KeyPair pair = mGenerator.generateKeyPair();
- assertNotNull("The KeyPair returned should not be null", pair);
-
- assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 256, null, TEST_DN_1, TEST_SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
- }
-
- public void testKeyPairGenerator_GenerateKeyPair_EC_P521_Unencrypted_Success() throws Exception {
- mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setKeyType("EC")
- .setKeySize(521)
- .setSubject(TEST_DN_1)
- .setSerialNumber(TEST_SERIAL_1)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build());
-
- final KeyPair pair = mGenerator.generateKeyPair();
- assertNotNull("The KeyPair returned should not be null", pair);
-
- assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 521, null, TEST_DN_1, TEST_SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
- }
-
- public void testKeyPairGenerator_GenerateKeyPair_RSA_Unencrypted_Success() throws Exception {
- mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setSubject(TEST_DN_1)
- .setSerialNumber(TEST_SERIAL_1)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build());
-
- final KeyPair pair = mGenerator.generateKeyPair();
- assertNotNull("The KeyPair returned should not be null", pair);
-
- assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
- }
-
- public void testKeyPairGenerator_GenerateKeyPair_Replaced_Unencrypted_Success()
- throws Exception {
- // Generate the first key
- {
- mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1).setSubject(TEST_DN_1).setSerialNumber(TEST_SERIAL_1)
- .setStartDate(NOW).setEndDate(NOW_PLUS_10_YEARS).build());
- final KeyPair pair1 = mGenerator.generateKeyPair();
- assertNotNull("The KeyPair returned should not be null", pair1);
- assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
- NOW, NOW_PLUS_10_YEARS);
- }
-
- // Replace the original key
- {
- mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1).setSubject(TEST_DN_2).setSerialNumber(TEST_SERIAL_2)
- .setStartDate(NOW).setEndDate(NOW_PLUS_10_YEARS).build());
- final KeyPair pair2 = mGenerator.generateKeyPair();
- assertNotNull("The KeyPair returned should not be null", pair2);
- assertKeyPairCorrect(pair2, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
- NOW, NOW_PLUS_10_YEARS);
- }
- }
-
- public void testKeyPairGenerator_GenerateKeyPair_No_Collision_Unencrypted_Success()
- throws Exception {
- // Generate the first key
- mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setSubject(TEST_DN_1)
- .setSerialNumber(TEST_SERIAL_1)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build());
- final KeyPair pair1 = mGenerator.generateKeyPair();
- assertNotNull("The KeyPair returned should not be null", pair1);
- assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
-
- // Generate the second key
- mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_2)
- .setSubject(TEST_DN_2)
- .setSerialNumber(TEST_SERIAL_2)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build());
- final KeyPair pair2 = mGenerator.generateKeyPair();
- assertNotNull("The KeyPair returned should not be null", pair2);
- assertKeyPairCorrect(pair2, TEST_ALIAS_2, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2, NOW,
- NOW_PLUS_10_YEARS);
-
- // Check the first key again
- assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
- NOW_PLUS_10_YEARS);
- }
-
- private void assertKeyPairCorrect(KeyPair pair, String alias, String keyType, int keySize,
- AlgorithmParameterSpec spec, X500Principal dn, BigInteger serial, Date start, Date end)
- throws Exception {
- final PublicKey pubKey = pair.getPublic();
- assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
- assertEquals(keyType, pubKey.getAlgorithm());
- assertEquals("Public keys should be in X.509 format", "X.509", pubKey.getFormat());
- assertNotNull("Public keys should be encodable", pubKey.getEncoded());
-
- if ("EC".equalsIgnoreCase(keyType)) {
- assertEquals("Curve should be what was specified during initialization", keySize,
- ((ECPublicKey) pubKey).getParams().getCurve().getField().getFieldSize());
- } else if ("RSA".equalsIgnoreCase(keyType)) {
- RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey;
- assertEquals("Modulus size should be what is specified during initialization",
- (keySize + 7) & ~7, (rsaPubKey.getModulus().bitLength() + 7) & ~7);
- if (spec != null) {
- RSAKeyGenParameterSpec params = (RSAKeyGenParameterSpec) spec;
- assertEquals((keySize + 7) & ~7, (params.getKeysize() + 7) & ~7);
- assertEquals(params.getPublicExponent(), rsaPubKey.getPublicExponent());
- }
- } else {
- fail("Unsupported key algorithm: " + keyType);
- }
-
- final PrivateKey privKey = pair.getPrivate();
- assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
- assertEquals(keyType, privKey.getAlgorithm());
- assertNull("getFormat() should return null", privKey.getFormat());
- assertNull("getEncoded() should return null", privKey.getEncoded());
-
- if ("EC".equalsIgnoreCase(keyType)) {
- assertTrue("EC private key must be instanceof ECKey: " + privKey.getClass().getName(),
- privKey instanceof ECKey);
- assertECParameterSpecEqualsIgnoreSeed(
- "Private and public key must have the same EC parameters",
- ((ECKey) pubKey).getParams(), ((ECKey) privKey).getParams());
- } else if ("RSA".equalsIgnoreCase(keyType)) {
- assertTrue("RSA private key must be instance of RSAKey: "
- + privKey.getClass().getName(),
- privKey instanceof RSAKey);
- assertEquals("Private and public key must have the same RSA modulus",
- ((RSAKey) pubKey).getModulus(), ((RSAKey) privKey).getModulus());
- }
-
- KeyStore.Entry entry = mKeyStore.getEntry(alias, null);
- assertNotNull("Entry should exist", entry);
-
- assertTrue("Entry should be a PrivateKeyEntry", entry instanceof KeyStore.PrivateKeyEntry);
- KeyStore.PrivateKeyEntry privEntry = (KeyStore.PrivateKeyEntry) entry;
-
- Certificate userCert = privEntry.getCertificate();
- assertTrue("Certificate should be in X.509 format", userCert instanceof X509Certificate);
-
- final X509Certificate x509userCert = (X509Certificate) userCert;
-
- assertEquals(
- "Public key used to sign certificate should have the same algorithm as in KeyPair",
- pubKey.getAlgorithm(), x509userCert.getPublicKey().getAlgorithm());
-
- assertEquals("PublicKey used to sign certificate should match one returned in KeyPair",
- pubKey, x509userCert.getPublicKey());
-
- assertEquals("The Subject DN should be the one passed into the params", dn,
- x509userCert.getSubjectDN());
-
- assertEquals("The Issuer DN should be the same as the Subject DN", dn,
- x509userCert.getIssuerDN());
-
- assertEquals("The Serial should be the one passed into the params", serial,
- x509userCert.getSerialNumber());
-
- assertDateEquals("The notBefore date should be the one passed into the params", start,
- x509userCert.getNotBefore());
-
- assertDateEquals("The notAfter date should be the one passed into the params", end,
- x509userCert.getNotAfter());
-
- // Assert that the cert's signature verifies using the public key from generated KeyPair
- x509userCert.verify(pubKey);
- // Assert that the cert's signature verifies using the public key from the cert itself.
- x509userCert.verify(x509userCert.getPublicKey());
-
- Certificate[] chain = privEntry.getCertificateChain();
- assertEquals("A list of CA certificates should not exist for the generated entry", 1,
- chain.length);
-
- assertUsableInSSLConnection(privKey, x509userCert);
-
- assertEquals("Retrieved key and generated key should be equal", privKey,
- privEntry.getPrivateKey());
- }
-
- private static void assertUsableInSSLConnection(PrivateKey privKey,
- X509Certificate x509userCert) throws Exception {
- // Set up both client and server to use the same private key + cert, and to trust that cert
- // when it's presented by peer. This exercises the use of the private key both in client
- // and server scenarios.
- TestKeyStore serverKeyStore = TestKeyStore.getServer();
- serverKeyStore.keyStore.setCertificateEntry("client-selfSigned", x509userCert);
- SSLContext serverContext = TestSSLContext.createSSLContext("TLS",
- new KeyManager[] {
- TestKeyManager.wrap(new MyKeyManager(privKey, x509userCert))
- },
- TestKeyStore.createTrustManagers(serverKeyStore.keyStore));
- SSLContext clientContext = serverContext;
-
- if ("EC".equalsIgnoreCase(privKey.getAlgorithm())) {
- // As opposed to RSA (see below) EC keys are used in the same way in all cipher suites.
- // Assert that the key works with the default list of cipher suites.
- assertSSLConnectionWithClientAuth(
- clientContext, serverContext, null, x509userCert, x509userCert);
- } else if ("RSA".equalsIgnoreCase(privKey.getAlgorithm())) {
- // RSA keys are used differently between Forward Secure and non-Forward Secure cipher
- // suites. For example, RSA key exchange requires the server to decrypt using its RSA
- // private key, whereas ECDHE_RSA key exchange requires the server to sign usnig its
- // RSA private key. We thus assert that the key works with Forward Secure cipher suites
- // and that it works with non-Forward Secure cipher suites.
- List<String> fsCipherSuites = new ArrayList<String>();
- List<String> nonFsCipherSuites = new ArrayList<String>();
- for (String cipherSuite : clientContext.getDefaultSSLParameters().getCipherSuites()) {
- if (cipherSuite.contains("_ECDHE_RSA_") || cipherSuite.contains("_DHE_RSA_")) {
- fsCipherSuites.add(cipherSuite);
- } else if (cipherSuite.contains("_RSA_WITH_")) {
- nonFsCipherSuites.add(cipherSuite);
- }
- }
- assertFalse("No FS RSA cipher suites enabled by default", fsCipherSuites.isEmpty());
- assertFalse("No non-FS RSA cipher suites enabled", nonFsCipherSuites.isEmpty());
-
- // Assert that the key works with RSA Forward Secure cipher suites.
- assertSSLConnectionWithClientAuth(
- clientContext, serverContext, fsCipherSuites.toArray(new String[0]),
- x509userCert, x509userCert);
- // Assert that the key works with RSA non-Forward Secure cipher suites.
- assertSSLConnectionWithClientAuth(
- clientContext, serverContext, nonFsCipherSuites.toArray(new String[0]),
- x509userCert, x509userCert);
- } else {
- fail("Unsupported key algorithm: " + privKey.getAlgorithm());
- }
- }
-
- private static void assertSSLConnectionWithClientAuth(
- SSLContext clientContext, SSLContext serverContext, String[] enabledCipherSuites,
- X509Certificate expectedClientCert, X509Certificate expectedServerCert)
- throws Exception {
- SSLServerSocket serverSocket = (SSLServerSocket) serverContext.getServerSocketFactory()
- .createServerSocket(0);
- InetAddress host = InetAddress.getLocalHost();
- int port = serverSocket.getLocalPort();
- SSLSocket client = (SSLSocket) clientContext.getSocketFactory().createSocket(host, port);
-
- final SSLSocket server = (SSLSocket) serverSocket.accept();
- ExecutorService executor = Executors.newSingleThreadExecutor();
- Future<Certificate[]> future = executor.submit(new Callable<Certificate[]>() {
- @Override
- public Certificate[] call() throws Exception {
- server.setNeedClientAuth(true);
- server.setWantClientAuth(true);
- server.startHandshake();
- return server.getSession().getPeerCertificates();
- }
- });
- executor.shutdown();
- if (enabledCipherSuites != null) {
- client.setEnabledCipherSuites(enabledCipherSuites);
- }
- client.startHandshake();
- Certificate[] usedServerCerts = client.getSession().getPeerCertificates();
- Certificate[] usedClientCerts = future.get();
- client.close();
- server.close();
-
- assertNotNull(usedServerCerts);
- assertEquals(1, usedServerCerts.length);
- assertEquals(expectedServerCert, usedServerCerts[0]);
-
- assertNotNull(usedClientCerts);
- assertEquals(1, usedClientCerts.length);
- assertEquals(expectedClientCert, usedClientCerts[0]);
- }
-
- private static class MyKeyManager extends X509ExtendedKeyManager {
- private final PrivateKey key;
- private final X509Certificate[] chain;
-
- public MyKeyManager(PrivateKey key, X509Certificate cert) {
- this.key = key;
- this.chain = new X509Certificate[] { cert };
- }
-
- @Override
- public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
- return "fake";
- }
-
- @Override
- public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
- return "fake";
- }
-
- @Override
- public X509Certificate[] getCertificateChain(String alias) {
- return chain;
- }
-
- @Override
- public String[] getClientAliases(String keyType, Principal[] issuers) {
- return new String[] { "fake" };
- }
-
- @Override
- public String[] getServerAliases(String keyType, Principal[] issuers) {
- return new String[] { "fake" };
- }
-
- @Override
- public PrivateKey getPrivateKey(String alias) {
- return key;
- }
- }
-
- private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {
- SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
-
- String result1 = formatter.format(date1);
- String result2 = formatter.format(date2);
-
- assertEquals(message, result1, result2);
- }
-
- private static void assertECParameterSpecEqualsIgnoreSeed(String message,
- ECParameterSpec expected, ECParameterSpec actual) {
- EllipticCurve expectedCurve = expected.getCurve();
- EllipticCurve actualCurve = actual.getCurve();
- assertEquals(message + ": curve field", expectedCurve.getField(), actualCurve.getField());
- assertEquals(message + ": curve A", expectedCurve.getA(), actualCurve.getA());
- assertEquals(message + ": curve B", expectedCurve.getB(), actualCurve.getB());
- assertEquals(message + ": order mismatch", expected.getOrder(), actual.getOrder());
- assertEquals(message + ": generator mismatch",
- expected.getGenerator(), actual.getGenerator());
- assertEquals(message + ": cofactor mismatch", expected.getCofactor(), actual.getCofactor());
- }
-}
diff --git a/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java b/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java
new file mode 100644
index 0000000..a93cc35
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java
@@ -0,0 +1,89 @@
+package android.keystore.cts;
+
+import java.security.SecureRandom;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * {@link SecureRandom} which counts how many bytes it has output.
+ */
+public class CountingSecureRandom extends SecureRandom {
+
+ private final SecureRandom mDelegate = new SecureRandom();
+ private final AtomicLong mOutputSizeBytes = new AtomicLong();
+
+ public long getOutputSizeBytes() {
+ return mOutputSizeBytes.get();
+ }
+
+ public void resetCounters() {
+ mOutputSizeBytes.set(0);
+ }
+
+ @Override
+ public byte[] generateSeed(int numBytes) {
+ if (numBytes > 0) {
+ mOutputSizeBytes.addAndGet(numBytes);
+ }
+ return mDelegate.generateSeed(numBytes);
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return mDelegate.getAlgorithm();
+ }
+
+ @Override
+ public synchronized void nextBytes(byte[] bytes) {
+ if ((bytes != null) && (bytes.length > 0)) {
+ mOutputSizeBytes.addAndGet(bytes.length);
+ }
+ mDelegate.nextBytes(bytes);
+ }
+
+ @Override
+ public synchronized void setSeed(byte[] seed) {
+ // Ignore seeding -- not needed in tests and may impact the quality of the output of the
+ // delegate SecureRandom by preventing it from self-seeding
+ }
+
+ @Override
+ public void setSeed(long seed) {
+ // Ignore seeding -- not needed in tests and may impact the quality of the output of the
+ // delegate SecureRandom by preventing it from self-seeding
+ }
+
+ @Override
+ public boolean nextBoolean() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public double nextDouble() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public float nextFloat() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized double nextGaussian() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int nextInt() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int nextInt(int n) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long nextLong() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/ECCurves.java b/tests/tests/keystore/src/android/keystore/cts/ECCurves.java
new file mode 100644
index 0000000..24184e6
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/ECCurves.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import java.math.BigInteger;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
+
+abstract class ECCurves {
+ private ECCurves() {}
+
+ // NIST EC curve parameters copied from "Standards for Efficient Cryptography 2 (SEC 2)".
+
+ static ECParameterSpec NIST_P_192_SPEC = createNistPCurveSpec(
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", 16),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", 16),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", 16),
+ new BigInteger("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", 16),
+ new BigInteger("188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", 16),
+ new BigInteger("07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", 16),
+ 1,
+ HexEncoding.decode("3045AE6FC8422F64ED579528D38120EAE12196D5"));
+
+ static ECParameterSpec NIST_P_224_SPEC = createNistPCurveSpec(
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", 16),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", 16),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", 16),
+ new BigInteger("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", 16),
+ new BigInteger("B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", 16),
+ new BigInteger("BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", 16),
+ 1,
+ HexEncoding.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"));
+
+ static ECParameterSpec NIST_P_256_SPEC = createNistPCurveSpec(
+ new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFF"
+ + "FFFFFFFF", 16),
+ new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2"
+ + "FC632551", 16),
+ new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFF"
+ + "FFFFFFFC", 16),
+ new BigInteger("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E"
+ + "27D2604B", 16),
+ new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945"
+ + "D898C296", 16),
+ new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB64068"
+ + "37BF51F5", 16),
+ 1,
+ HexEncoding.decode("C49D360886E704936A6678E1139D26B7819F7E90"));
+
+ static ECParameterSpec NIST_P_384_SPEC = createNistPCurveSpec(
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81"
+ + "F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
+ new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F"
+ + "5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16),
+ new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E0"
+ + "82542A385502F25DBF55296C3A545E3872760AB7", 16),
+ new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113"
+ + "B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16),
+ 1,
+ HexEncoding.decode("A335926AA319A27A1D00896A6773A4827ACDAC73"));
+
+ static ECParameterSpec NIST_P_521_SPEC = createNistPCurveSpec(
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFF", 16),
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8"
+ + "899C47AEBB6FB71E91386409", 16),
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFC", 16),
+ new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3"
+ + "B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF88"
+ + "3D2C34F1EF451FD46B503F00", 16),
+ new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521"
+ + "F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1"
+ + "856A429BF97E7E31C2E5BD66", 16),
+ new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B4468"
+ + "17AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086"
+ + "A272C24088BE94769FD16650", 16),
+ 1,
+ HexEncoding.decode("D09E8800291CB85396CC6717393284AAA0DA64BA"));
+
+ private static ECParameterSpec createNistPCurveSpec(
+ BigInteger p,
+ BigInteger order,
+ BigInteger a,
+ BigInteger b,
+ BigInteger gx,
+ BigInteger gy,
+ int cofactor,
+ byte[] seed) {
+ ECField field = new ECFieldFp(p);
+ EllipticCurve curve = new EllipticCurve(field, a, b, seed);
+ ECPoint generator = new ECPoint(gx, gy);
+ return new ECParameterSpec(
+ curve,
+ generator,
+ order,
+ cofactor);
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
new file mode 100644
index 0000000..2330209
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import junit.framework.TestCase;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Security;
+import java.security.Signature;
+
+public class ECDSASignatureTest extends TestCase {
+
+ public void testNONEwithECDSATruncatesInputToFieldSize() throws Exception {
+ assertNONEwithECDSATruncatesInputToFieldSize(224);
+ assertNONEwithECDSATruncatesInputToFieldSize(256);
+ assertNONEwithECDSATruncatesInputToFieldSize(384);
+ assertNONEwithECDSATruncatesInputToFieldSize(521);
+ }
+
+ private void assertNONEwithECDSATruncatesInputToFieldSize(int keySizeBits) throws Exception {
+ byte[] message = new byte[(keySizeBits * 3) / 8];
+ for (int i = 0; i < message.length; i++) {
+ message[i] = (byte) (i + 1);
+ }
+ KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ "test1",
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .setKeySize(keySizeBits)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+
+ Signature signature = Signature.getInstance("NONEwithECDSA");
+ signature.initSign(keyPair.getPrivate());
+ assertSame(Security.getProvider(SignatureTest.EXPECTED_PROVIDER_NAME),
+ signature.getProvider());
+ signature.update(message);
+ byte[] sigBytes = signature.sign();
+
+ signature = Signature.getInstance(signature.getAlgorithm(), signature.getProvider());
+ signature.initVerify(keyPair.getPublic());
+
+ // Verify the full-length message
+ signature.update(message);
+ assertTrue(signature.verify(sigBytes));
+
+ // Verify the message truncated to field size
+ signature.update(message, 0, (keySizeBits + 7) / 8);
+ assertTrue(signature.verify(sigBytes));
+
+ // Verify message truncated to one byte shorter than field size -- this should fail
+ signature.update(message, 0, (keySizeBits / 8) - 1);
+ assertFalse(signature.verify(sigBytes));
+ }
+
+ public void testNONEwithECDSASupportsMessagesShorterThanFieldSize() throws Exception {
+ assertNONEwithECDSASupportsMessagesShorterThanFieldSize(224);
+ assertNONEwithECDSASupportsMessagesShorterThanFieldSize(256);
+ assertNONEwithECDSASupportsMessagesShorterThanFieldSize(384);
+ assertNONEwithECDSASupportsMessagesShorterThanFieldSize(521);
+ }
+
+ private void assertNONEwithECDSASupportsMessagesShorterThanFieldSize(
+ int keySizeBits) throws Exception {
+ byte[] message = new byte[(keySizeBits * 3 / 4) / 8];
+ for (int i = 0; i < message.length; i++) {
+ message[i] = (byte) (i + 1);
+ }
+ KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ "test1",
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .setKeySize(keySizeBits)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+
+ Signature signature = Signature.getInstance("NONEwithECDSA");
+ signature.initSign(keyPair.getPrivate());
+ assertSame(Security.getProvider(SignatureTest.EXPECTED_PROVIDER_NAME),
+ signature.getProvider());
+ signature.update(message);
+ byte[] sigBytes = signature.sign();
+
+ signature = Signature.getInstance(signature.getAlgorithm(), signature.getProvider());
+ signature.initVerify(keyPair.getPublic());
+
+ // Verify the message
+ signature.update(message);
+ assertTrue(signature.verify(sigBytes));
+
+ // Assert that the message is left-padded with zero bits
+ byte[] fullLengthMessage = new byte[keySizeBits / 8];
+ System.arraycopy(message, 0,
+ fullLengthMessage, fullLengthMessage.length - message.length,
+ message.length);
+ signature.update(fullLengthMessage);
+ assertTrue(signature.verify(sigBytes));
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/HexEncoding.java b/tests/tests/keystore/src/android/keystore/cts/HexEncoding.java
new file mode 100644
index 0000000..9346f2d
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/HexEncoding.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Hexadecimal encoding where each byte is represented by two hexadecimal digits.
+ *
+ * @hide
+ */
+public class HexEncoding {
+
+ /** Hidden constructor to prevent instantiation. */
+ private HexEncoding() {}
+
+ private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+
+ /**
+ * Encodes the provided data as a hexadecimal string.
+ */
+ public static String encode(byte[] data) {
+ return encode(data, 0, data.length);
+ }
+
+ /**
+ * Encodes the provided data as a hexadecimal string.
+ */
+ public static String encode(byte[] data, int offset, int len) {
+ StringBuilder result = new StringBuilder(len * 2);
+ for (int i = 0; i < len; i++) {
+ byte b = data[offset + i];
+ result.append(HEX_DIGITS[(b >>> 4) & 0x0f]);
+ result.append(HEX_DIGITS[b & 0x0f]);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Encodes the provided data as a hexadecimal string.
+ */
+ public static String encode(ByteBuffer buf) {
+ return encode(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
+ }
+
+ /**
+ * Decodes the provided hexadecimal string into an array of bytes.
+ */
+ public static byte[] decode(String encoded) {
+ // IMPLEMENTATION NOTE: Special care is taken to permit odd number of hexadecimal digits.
+ int resultLengthBytes = (encoded.length() + 1) / 2;
+ byte[] result = new byte[resultLengthBytes];
+ int resultOffset = 0;
+ int encodedCharOffset = 0;
+ if ((encoded.length() % 2) != 0) {
+ // Odd number of digits -- the first digit is the lower 4 bits of the first result byte.
+ result[resultOffset++] = (byte) getHexadecimalDigitValue(encoded.charAt(encodedCharOffset));
+ encodedCharOffset++;
+ }
+ for (int len = encoded.length(); encodedCharOffset < len; encodedCharOffset += 2) {
+ result[resultOffset++] = (byte)
+ ((getHexadecimalDigitValue(encoded.charAt(encodedCharOffset)) << 4)
+ | getHexadecimalDigitValue(encoded.charAt(encodedCharOffset + 1)));
+ }
+ return result;
+ }
+
+ private static int getHexadecimalDigitValue(char c) {
+ if ((c >= 'a') && (c <= 'f')) {
+ return (c - 'a') + 0x0a;
+ } else if ((c >= 'A') && (c <= 'F')) {
+ return (c - 'A') + 0x0a;
+ } else if ((c >= '0') && (c <= '9')) {
+ return c - '0';
+ } else {
+ throw new IllegalArgumentException(
+ "Invalid hexadecimal digit at position : '" + c + "' (0x" + Integer.toHexString(c) + ")");
+ }
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyGenParameterSpecTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyGenParameterSpecTest.java
new file mode 100644
index 0000000..a3e5d96
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyGenParameterSpecTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.test.MoreAsserts;
+
+import junit.framework.TestCase;
+
+import java.math.BigInteger;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+public class KeyGenParameterSpecTest extends TestCase {
+
+ private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
+ private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
+ private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
+ private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
+
+ public void testDefaults() {
+ // Set only the mandatory parameters and assert values returned by getters.
+
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+ "arbitrary", KeyProperties.PURPOSE_ENCRYPT)
+ .build();
+
+ assertEquals("arbitrary", spec.getKeystoreAlias());
+ assertEquals(KeyProperties.PURPOSE_ENCRYPT, spec.getPurposes());
+ assertNull(null, spec.getAlgorithmParameterSpec());
+ MoreAsserts.assertEmpty(Arrays.asList(spec.getBlockModes()));
+ assertEquals(DEFAULT_CERT_NOT_BEFORE, spec.getCertificateNotBefore());
+ assertEquals(DEFAULT_CERT_NOT_AFTER, spec.getCertificateNotAfter());
+ assertEquals(DEFAULT_CERT_SERIAL_NUMBER, spec.getCertificateSerialNumber());
+ assertEquals(DEFAULT_CERT_SUBJECT, spec.getCertificateSubject());
+ assertFalse(spec.isDigestsSpecified());
+ try {
+ spec.getDigests();
+ fail();
+ } catch (IllegalStateException expected) {}
+ MoreAsserts.assertEmpty(Arrays.asList(spec.getEncryptionPaddings()));
+ assertEquals(-1, spec.getKeySize());
+ assertNull(spec.getKeyValidityStart());
+ assertNull(spec.getKeyValidityForOriginationEnd());
+ assertNull(spec.getKeyValidityForConsumptionEnd());
+ assertTrue(spec.isRandomizedEncryptionRequired());
+ MoreAsserts.assertEmpty(Arrays.asList(spec.getSignaturePaddings()));
+ assertFalse(spec.isUserAuthenticationRequired());
+ assertEquals(-1, spec.getUserAuthenticationValidityDurationSeconds());
+ }
+
+ public void testSettersReflectedInGetters() {
+ // Set all parameters to non-default values and then assert that getters reflect that.
+
+ Date certNotBeforeDate = new Date(System.currentTimeMillis());
+ Date certNotAfterDate = new Date(System.currentTimeMillis() + 12345678);
+ Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+ Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+ Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+ AlgorithmParameterSpec algSpecificParams = new ECGenParameterSpec("secp256r1");
+
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+ "arbitrary", KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+ .setAlgorithmParameterSpec(algSpecificParams)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC)
+ .setCertificateNotBefore(certNotBeforeDate)
+ .setCertificateNotAfter(certNotAfterDate)
+ .setCertificateSerialNumber(new BigInteger("13946146"))
+ .setCertificateSubject(new X500Principal("CN=test"))
+ .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setKeySize(1234)
+ .setKeyValidityStart(keyValidityStartDate)
+ .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+ .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+ .setRandomizedEncryptionRequired(false)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationValidityDurationSeconds(12345)
+ .build();
+
+ assertEquals("arbitrary", spec.getKeystoreAlias());
+ assertEquals(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT, spec.getPurposes());
+ assertSame(algSpecificParams, spec.getAlgorithmParameterSpec());
+ MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getBlockModes()),
+ KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC);
+ assertEquals(certNotBeforeDate, spec.getCertificateNotBefore());
+ assertEquals(certNotAfterDate, spec.getCertificateNotAfter());
+ assertEquals(new BigInteger("13946146"), spec.getCertificateSerialNumber());
+ assertEquals(new X500Principal("CN=test"), spec.getCertificateSubject());
+ assertTrue(spec.isDigestsSpecified());
+ MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getDigests()),
+ KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384);
+ MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getEncryptionPaddings()),
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP, KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ assertEquals(1234, spec.getKeySize());
+ assertEquals(keyValidityStartDate, spec.getKeyValidityStart());
+ assertEquals(keyValidityEndDateForOrigination, spec.getKeyValidityForOriginationEnd());
+ assertEquals(keyValidityEndDateForConsumption, spec.getKeyValidityForConsumptionEnd());
+ assertFalse(spec.isRandomizedEncryptionRequired());
+ MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getSignaturePaddings()),
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS, KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ assertTrue(spec.isUserAuthenticationRequired());
+ assertEquals(12345, spec.getUserAuthenticationValidityDurationSeconds());
+ }
+
+ public void testNullAliasNotPermitted() {
+ try {
+ new KeyGenParameterSpec.Builder(null, KeyProperties.PURPOSE_ENCRYPT);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public void testEmptyAliasNotPermitted() {
+ try {
+ new KeyGenParameterSpec.Builder("", KeyProperties.PURPOSE_ENCRYPT);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ public void testSetKeyValidityEndDateAppliesToBothEndDates() {
+ Date date = new Date(System.currentTimeMillis() + 555555);
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+ "ignore", KeyProperties.PURPOSE_VERIFY)
+ .setKeyValidityEnd(date)
+ .build();
+ assertEquals(date, spec.getKeyValidityForOriginationEnd());
+ assertEquals(date, spec.getKeyValidityForConsumptionEnd());
+ }
+
+ public void testSetUserAuthenticationValidityDurationSecondsValidityCheck() {
+ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder("alias", 0);
+ try {
+ builder.setUserAuthenticationValidityDurationSeconds(-2);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ try {
+ builder.setUserAuthenticationValidityDurationSeconds(-100);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ try {
+ builder.setUserAuthenticationValidityDurationSeconds(Integer.MIN_VALUE);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ builder.setUserAuthenticationValidityDurationSeconds(-1);
+ builder.setUserAuthenticationValidityDurationSeconds(0);
+ builder.setUserAuthenticationValidityDurationSeconds(1);
+ builder.setUserAuthenticationValidityDurationSeconds(Integer.MAX_VALUE);
+
+ try {
+ builder.setUserAuthenticationValidityDurationSeconds(-2);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ public void testImmutabilityViaSetterParams() {
+ // Assert that all mutable parameters provided to setters are copied to ensure that values
+ // returned by getters never change.
+ String[] blockModes =
+ new String[] {KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC};
+ String[] originalBlockModes = blockModes.clone();
+ Date certNotBeforeDate = new Date(System.currentTimeMillis());
+ Date originalCertNotBeforeDate = new Date(certNotBeforeDate.getTime());
+ Date certNotAfterDate = new Date(System.currentTimeMillis() + 12345678);
+ Date originalCertNotAfterDate = new Date(certNotAfterDate.getTime());
+ Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+ Date originalKeyValidityStartDate = new Date(keyValidityStartDate.getTime());
+ Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+ Date originalKeyValidityEndDateForOrigination =
+ new Date(keyValidityEndDateForOrigination.getTime());
+ Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+ Date originalKeyValidityEndDateForConsumption =
+ new Date(keyValidityEndDateForConsumption.getTime());
+ String[] digests = new String[] {KeyProperties.DIGEST_MD5, KeyProperties.DIGEST_SHA512};
+ String[] originalDigests = digests.clone();
+ String[] encryptionPaddings = new String[] {
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP, KeyProperties.ENCRYPTION_PADDING_PKCS7};
+ String[] originalEncryptionPaddings = encryptionPaddings.clone();
+ String[] signaturePaddings = new String[] {
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS, KeyProperties.SIGNATURE_PADDING_RSA_PKCS1};
+ String[] originalSignaturePaddings = signaturePaddings.clone();
+
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+ "arbitrary", KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(blockModes)
+ .setCertificateNotBefore(certNotBeforeDate)
+ .setCertificateNotAfter(certNotAfterDate)
+ .setDigests(digests)
+ .setEncryptionPaddings(encryptionPaddings)
+ .setKeyValidityStart(keyValidityStartDate)
+ .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+ .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+ .setSignaturePaddings(signaturePaddings)
+ .build();
+
+ assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+ blockModes[0] = null;
+ assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+
+ assertEquals(originalCertNotBeforeDate, spec.getCertificateNotBefore());
+ certNotBeforeDate.setTime(1234567890L);
+ assertEquals(originalCertNotBeforeDate, spec.getCertificateNotBefore());
+
+ assertEquals(originalCertNotAfterDate, spec.getCertificateNotAfter());
+ certNotAfterDate.setTime(1234567890L);
+ assertEquals(originalCertNotAfterDate, spec.getCertificateNotAfter());
+
+ assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+ digests[1] = null;
+ assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+
+ assertEquals(Arrays.asList(originalEncryptionPaddings),
+ Arrays.asList(spec.getEncryptionPaddings()));
+ encryptionPaddings[0] = null;
+ assertEquals(Arrays.asList(originalEncryptionPaddings),
+ Arrays.asList(spec.getEncryptionPaddings()));
+
+ assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+ keyValidityStartDate.setTime(1234567890L);
+ assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+
+ assertEquals(originalKeyValidityEndDateForOrigination,
+ spec.getKeyValidityForOriginationEnd());
+ keyValidityEndDateForOrigination.setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForOrigination,
+ spec.getKeyValidityForOriginationEnd());
+
+ assertEquals(originalKeyValidityEndDateForConsumption,
+ spec.getKeyValidityForConsumptionEnd());
+ keyValidityEndDateForConsumption.setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForConsumption,
+ spec.getKeyValidityForConsumptionEnd());
+
+ assertEquals(Arrays.asList(originalSignaturePaddings),
+ Arrays.asList(spec.getSignaturePaddings()));
+ signaturePaddings[1] = null;
+ assertEquals(Arrays.asList(originalSignaturePaddings),
+ Arrays.asList(spec.getSignaturePaddings()));
+ }
+
+ public void testImmutabilityViaGetterReturnValues() {
+ // Assert that none of the mutable return values from getters modify the state of the spec.
+
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+ "arbitrary", KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC)
+ .setCertificateNotBefore(new Date(System.currentTimeMillis()))
+ .setCertificateNotAfter(new Date(System.currentTimeMillis() + 12345678))
+ .setDigests(KeyProperties.DIGEST_MD5, KeyProperties.DIGEST_SHA512)
+ .setEncryptionPaddings(
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setKeyValidityStart(new Date(System.currentTimeMillis() - 2222222))
+ .setKeyValidityForOriginationEnd(new Date(System.currentTimeMillis() + 11111111))
+ .setKeyValidityForConsumptionEnd(new Date(System.currentTimeMillis() + 33333333))
+ .setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .build();
+
+ String[] originalBlockModes = spec.getBlockModes().clone();
+ spec.getBlockModes()[0] = null;
+ assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+
+ Date originalCertNotBeforeDate = (Date) spec.getCertificateNotBefore().clone();
+ spec.getCertificateNotBefore().setTime(1234567890L);
+ assertEquals(originalCertNotBeforeDate, spec.getCertificateNotBefore());
+
+ Date originalCertNotAfterDate = (Date) spec.getCertificateNotAfter().clone();
+ spec.getCertificateNotAfter().setTime(1234567890L);
+ assertEquals(originalCertNotAfterDate, spec.getCertificateNotAfter());
+
+ String[] originalDigests = spec.getDigests().clone();
+ spec.getDigests()[0] = null;
+ assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+
+ String[] originalEncryptionPaddings = spec.getEncryptionPaddings().clone();
+ spec.getEncryptionPaddings()[0] = null;
+ assertEquals(Arrays.asList(originalEncryptionPaddings),
+ Arrays.asList(spec.getEncryptionPaddings()));
+
+ Date originalKeyValidityStartDate = (Date) spec.getKeyValidityStart().clone();
+ spec.getKeyValidityStart().setTime(1234567890L);
+ assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+
+ Date originalKeyValidityEndDateForOrigination =
+ (Date) spec.getKeyValidityForOriginationEnd().clone();
+ spec.getKeyValidityForOriginationEnd().setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForOrigination,
+ spec.getKeyValidityForOriginationEnd());
+
+ Date originalKeyValidityEndDateForConsumption =
+ (Date) spec.getKeyValidityForConsumptionEnd().clone();
+ spec.getKeyValidityForConsumptionEnd().setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForConsumption,
+ spec.getKeyValidityForConsumptionEnd());
+
+ String[] originalSignaturePaddings = spec.getSignaturePaddings().clone();
+ spec.getSignaturePaddings()[0] = null;
+ assertEquals(Arrays.asList(originalSignaturePaddings),
+ Arrays.asList(spec.getSignaturePaddings()));
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyInfoTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyInfoTest.java
new file mode 100644
index 0000000..2b1d6fc
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyInfoTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+
+import junit.framework.TestCase;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.util.Arrays;
+import java.util.Date;
+
+public class KeyInfoTest extends TestCase {
+
+ public void testImmutabilityViaGetterReturnValues() throws Exception {
+ // Assert that none of the mutable return values from getters modify the state of the
+ // instance.
+
+ Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+ Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+ Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
+ keyPairGenerator.initialize(new KeyGenParameterSpec.Builder(
+ KeyInfoTest.class.getSimpleName(),
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+ .setKeySize(1024) // use smaller key size to speed the test up
+ .setKeyValidityStart(keyValidityStartDate)
+ .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+ .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS)
+ .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+ .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
+ .build());
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ PrivateKey key = keyPair.getPrivate();
+ KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
+ KeyInfo info = keyFactory.getKeySpec(key, KeyInfo.class);
+
+ Date originalKeyValidityStartDate = (Date) info.getKeyValidityStart().clone();
+ info.getKeyValidityStart().setTime(1234567890L);
+ assertEquals(originalKeyValidityStartDate, info.getKeyValidityStart());
+
+ Date originalKeyValidityEndDateForOrigination =
+ (Date) info.getKeyValidityForOriginationEnd().clone();
+ info.getKeyValidityForOriginationEnd().setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForOrigination,
+ info.getKeyValidityForOriginationEnd());
+
+ Date originalKeyValidityEndDateForConsumption =
+ (Date) info.getKeyValidityForConsumptionEnd().clone();
+ info.getKeyValidityForConsumptionEnd().setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForConsumption,
+ info.getKeyValidityForConsumptionEnd());
+
+ String[] originalEncryptionPaddings = info.getEncryptionPaddings().clone();
+ info.getEncryptionPaddings()[0] = null;
+ assertEquals(Arrays.asList(originalEncryptionPaddings),
+ Arrays.asList(info.getEncryptionPaddings()));
+
+ String[] originalSignaturePaddings = info.getSignaturePaddings().clone();
+ info.getSignaturePaddings()[0] = null;
+ assertEquals(Arrays.asList(originalSignaturePaddings),
+ Arrays.asList(info.getSignaturePaddings()));
+
+ String[] originalDigests = info.getDigests().clone();
+ info.getDigests()[0] = null;
+ assertEquals(Arrays.asList(originalDigests), Arrays.asList(info.getDigests()));
+
+ String[] originalBlockModes = info.getBlockModes().clone();
+ info.getBlockModes()[0] = null;
+ assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(info.getBlockModes()));
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
new file mode 100644
index 0000000..db7f539
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
@@ -0,0 +1,1186 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.KeyPairGeneratorSpec;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.security.auth.x500.X500Principal;
+
+import libcore.java.security.TestKeyStore;
+import libcore.javax.net.ssl.TestKeyManager;
+import libcore.javax.net.ssl.TestSSLContext;
+
+public class KeyPairGeneratorTest extends AndroidTestCase {
+ private KeyStore mKeyStore;
+
+ private CountingSecureRandom mRng;
+
+ private static final String TEST_ALIAS_1 = "test1";
+
+ private static final String TEST_ALIAS_2 = "test2";
+
+ private static final String TEST_ALIAS_3 = "test3";
+
+ private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
+
+ private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
+
+ private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
+
+ private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
+
+ private static final long NOW_MILLIS = System.currentTimeMillis();
+
+ /* We have to round this off because X509v3 doesn't store milliseconds. */
+ private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
+
+ @SuppressWarnings("deprecation")
+ private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
+
+ private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+
+ private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
+ private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
+ private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
+ private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mRng = new CountingSecureRandom();
+ mKeyStore = KeyStore.getInstance("AndroidKeyStore");
+ mKeyStore.load(null, null);
+ }
+
+ public void testInitialize_LegacySpec() throws Exception {
+ @SuppressWarnings("deprecation")
+ KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+ getRsaGenerator().initialize(spec);
+ getRsaGenerator().initialize(spec, new SecureRandom());
+
+ getEcGenerator().initialize(spec);
+ getEcGenerator().initialize(spec, new SecureRandom());
+ }
+
+ public void testInitialize_ModernSpec() throws Exception {
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .build();
+ getRsaGenerator().initialize(spec);
+ getRsaGenerator().initialize(spec, new SecureRandom());
+
+ getEcGenerator().initialize(spec);
+ getEcGenerator().initialize(spec, new SecureRandom());
+ }
+
+ public void testInitialize_KeySizeOnly() throws Exception {
+ try {
+ getRsaGenerator().initialize(1024);
+ fail("KeyPairGenerator should not support setting the key size");
+ } catch (IllegalArgumentException success) {
+ }
+
+ try {
+ getEcGenerator().initialize(256);
+ fail("KeyPairGenerator should not support setting the key size");
+ } catch (IllegalArgumentException success) {
+ }
+ }
+
+ public void testInitialize_KeySizeAndSecureRandomOnly()
+ throws Exception {
+ try {
+ getRsaGenerator().initialize(1024, new SecureRandom());
+ fail("KeyPairGenerator should not support setting the key size");
+ } catch (IllegalArgumentException success) {
+ }
+
+ try {
+ getEcGenerator().initialize(1024, new SecureRandom());
+ fail("KeyPairGenerator should not support setting the key size");
+ } catch (IllegalArgumentException success) {
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testGenerate_EC_LegacySpec() throws Exception {
+ // There are three legacy ways to generate an EC key pair using Android Keystore
+ // KeyPairGenerator:
+ // 1. Use an RSA KeyPairGenerator and specify EC as key type,
+ // 2. Use an EC KeyPairGenerator and specify EC as key type,
+ // 3. Use an EC KeyPairGenerator and leave the key type unspecified.
+ //
+ // We test all three.
+
+ // 1. Use an RSA KeyPairGenerator and specify EC as key type.
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setKeyType("EC")
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "EC",
+ 256,
+ TEST_DN_1,
+ TEST_SERIAL_1, NOW,
+ NOW_PLUS_10_YEARS);
+ assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_1);
+ assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_1);
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ ECCurves.NIST_P_256_SPEC, ((ECPublicKey) keyPair.getPublic()).getParams());
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(256, keyInfo.getKeySize());
+ assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+ assertOneOf(keyInfo.getOrigin(),
+ KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+ assertEquals(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY,
+ keyInfo.getPurposes());
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertEquals(null, keyInfo.getKeyValidityStart());
+ assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+ KeyProperties.DIGEST_NONE);
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+
+ // 2. Use an EC KeyPairGenerator and specify EC as key type.
+ generator = getEcGenerator();
+ generator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_2)
+ .setKeyType("EC")
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_2,
+ "EC",
+ 256,
+ TEST_DN_1,
+ TEST_SERIAL_1,
+ NOW,
+ NOW_PLUS_10_YEARS);
+ assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_2);
+ assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_2);
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ ECCurves.NIST_P_256_SPEC, ((ECPublicKey) keyPair.getPublic()).getParams());
+ keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(256, keyInfo.getKeySize());
+ assertEquals(TEST_ALIAS_2, keyInfo.getKeystoreAlias());
+ assertOneOf(keyInfo.getOrigin(),
+ KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+ assertEquals(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY,
+ keyInfo.getPurposes());
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertEquals(null, keyInfo.getKeyValidityStart());
+ assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+ KeyProperties.DIGEST_NONE);
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+
+ // 3. Use an EC KeyPairGenerator and leave the key type unspecified.
+ generator = getEcGenerator();
+ generator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_3)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_3,
+ "EC",
+ 256,
+ TEST_DN_1,
+ TEST_SERIAL_1,
+ NOW,
+ NOW_PLUS_10_YEARS);
+ assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_3);
+ assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_3);
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ ECCurves.NIST_P_256_SPEC, ((ECPublicKey) keyPair.getPublic()).getParams());
+ keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(256, keyInfo.getKeySize());
+ assertEquals(TEST_ALIAS_3, keyInfo.getKeystoreAlias());
+ assertOneOf(keyInfo.getOrigin(),
+ KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+ assertEquals(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY,
+ keyInfo.getPurposes());
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertEquals(null, keyInfo.getKeyValidityStart());
+ assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+ KeyProperties.DIGEST_NONE);
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+ }
+
+ public void testGenerate_RSA_LegacySpec() throws Exception {
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(TEST_SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "RSA",
+ 2048,
+ TEST_DN_1,
+ TEST_SERIAL_1,
+ NOW,
+ NOW_PLUS_10_YEARS);
+ assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_1);
+ assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_1);
+ assertEquals(RSAKeyGenParameterSpec.F4,
+ ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(2048, keyInfo.getKeySize());
+ assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+ assertOneOf(keyInfo.getOrigin(),
+ KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+ assertEquals(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
+ keyInfo.getPurposes());
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertEquals(null, keyInfo.getKeyValidityStart());
+ assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+ KeyProperties.DIGEST_NONE);
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getEncryptionPaddings()),
+ KeyProperties.ENCRYPTION_PADDING_NONE);
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ }
+
+ public void testGenerate_ReplacesOldEntryWithSameAlias()
+ throws Exception {
+ // Generate the first key
+ {
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN
+ | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT
+ | KeyProperties.PURPOSE_DECRYPT)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setCertificateSubject(TEST_DN_1)
+ .setCertificateSerialNumber(TEST_SERIAL_1)
+ .setCertificateNotBefore(NOW)
+ .setCertificateNotAfter(NOW_PLUS_10_YEARS)
+ .build());
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ generator.generateKeyPair(),
+ TEST_ALIAS_1,
+ "RSA",
+ 2048,
+ TEST_DN_1,
+ TEST_SERIAL_1,
+ NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ // Replace the original key
+ {
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN
+ | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT
+ | KeyProperties.PURPOSE_DECRYPT)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setCertificateSubject(TEST_DN_2)
+ .setCertificateSerialNumber(TEST_SERIAL_2)
+ .setCertificateNotBefore(NOW)
+ .setCertificateNotAfter(NOW_PLUS_10_YEARS)
+ .build());
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ generator.generateKeyPair(),
+ TEST_ALIAS_1,
+ "RSA",
+ 2048,
+ TEST_DN_2,
+ TEST_SERIAL_2,
+ NOW,
+ NOW_PLUS_10_YEARS);
+ }
+ }
+
+ public void testGenerate_DoesNotReplaceOtherEntries()
+ throws Exception {
+ // Generate the first key
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN
+ | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT
+ | KeyProperties.PURPOSE_DECRYPT)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setCertificateSubject(TEST_DN_1)
+ .setCertificateSerialNumber(TEST_SERIAL_1)
+ .setCertificateNotBefore(NOW)
+ .setCertificateNotAfter(NOW_PLUS_10_YEARS)
+ .build());
+ KeyPair keyPair1 = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair1,
+ TEST_ALIAS_1,
+ "RSA",
+ 2048,
+ TEST_DN_1,
+ TEST_SERIAL_1,
+ NOW,
+ NOW_PLUS_10_YEARS);
+
+ // Generate the second key
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_2,
+ KeyProperties.PURPOSE_SIGN
+ | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT
+ | KeyProperties.PURPOSE_DECRYPT)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setCertificateSubject(TEST_DN_2)
+ .setCertificateSerialNumber(TEST_SERIAL_2)
+ .setCertificateNotBefore(NOW)
+ .setCertificateNotAfter(NOW_PLUS_10_YEARS)
+ .build());
+ KeyPair keyPair2 = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair2,
+ TEST_ALIAS_2,
+ "RSA",
+ 2048,
+ TEST_DN_2,
+ TEST_SERIAL_2,
+ NOW,
+ NOW_PLUS_10_YEARS);
+
+ // Check the first key pair again
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair1,
+ TEST_ALIAS_1,
+ "RSA",
+ 2048,
+ TEST_DN_1,
+ TEST_SERIAL_1,
+ NOW,
+ NOW_PLUS_10_YEARS);
+ }
+
+ public void testGenerate_EC_ModernSpec_Defaults() throws Exception {
+ KeyPairGenerator generator = getEcGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "EC",
+ 256,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ ECCurves.NIST_P_256_SPEC, ((ECKey) keyPair.getPrivate()).getParams());
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(256, keyInfo.getKeySize());
+ assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+ assertOneOf(keyInfo.getOrigin(),
+ KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+ assertEquals(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY, keyInfo.getPurposes());
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertEquals(null, keyInfo.getKeyValidityStart());
+ assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getDigests()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+ }
+
+ public void testGenerate_RSA_ModernSpec_Defaults() throws Exception {
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "RSA",
+ 2048,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ assertEquals(RSAKeyGenParameterSpec.F4,
+ ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(2048, keyInfo.getKeySize());
+ assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+ assertOneOf(keyInfo.getOrigin(),
+ KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+ assertEquals(
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
+ keyInfo.getPurposes());
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertEquals(null, keyInfo.getKeyValidityStart());
+ assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getDigests()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+ }
+
+ public void testGenerate_EC_ModernSpec_AsCustomAsPossible() throws Exception {
+ KeyPairGenerator generator = getEcGenerator();
+ Date keyValidityStart = new Date(System.currentTimeMillis());
+ Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 1000000);
+ Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 10000000);
+
+ Date certNotBefore = new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 7);
+ Date certNotAfter = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7);
+ BigInteger certSerialNumber = new BigInteger("12345678");
+ X500Principal certSubject = new X500Principal("cn=hello");
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT)
+ .setKeySize(224)
+ .setDigests(KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512)
+ .setKeyValidityStart(keyValidityStart)
+ .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+ .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+ .setCertificateSerialNumber(certSerialNumber)
+ .setCertificateSubject(certSubject)
+ .setCertificateNotBefore(certNotBefore)
+ .setCertificateNotAfter(certNotAfter)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "EC",
+ 224,
+ certSubject,
+ certSerialNumber,
+ certNotBefore,
+ certNotAfter);
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ ECCurves.NIST_P_224_SPEC, ((ECKey) keyPair.getPrivate()).getParams());
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(224, keyInfo.getKeySize());
+ assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+ assertOneOf(keyInfo.getOrigin(),
+ KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+ assertEquals(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT,
+ keyInfo.getPurposes());
+ assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
+ assertEquals(keyValidityEndDateForOrigination, keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(keyValidityEndDateForConsumption, keyInfo.getKeyValidityForConsumptionEnd());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+ KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512);
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertEquals(-1, keyInfo.getUserAuthenticationValidityDurationSeconds());
+ }
+
+ public void testGenerate_RSA_ModernSpec_AsCustomAsPossible() throws Exception {
+ KeyPairGenerator generator = getRsaGenerator();
+ Date keyValidityStart = new Date(System.currentTimeMillis());
+ Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 1000000);
+ Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 10000000);
+
+ Date certNotBefore = new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 210);
+ Date certNotAfter = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 210);
+ BigInteger certSerialNumber = new BigInteger("1234567890");
+ X500Principal certSubject = new X500Principal("cn=hello2");
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT)
+ .setAlgorithmParameterSpec(
+ new RSAKeyGenParameterSpec(3072, RSAKeyGenParameterSpec.F0))
+ .setKeySize(3072)
+ .setDigests(KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
+ .setKeyValidityStart(keyValidityStart)
+ .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+ .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+ .setCertificateSerialNumber(certSerialNumber)
+ .setCertificateSubject(certSubject)
+ .setCertificateNotBefore(certNotBefore)
+ .setCertificateNotAfter(certNotAfter)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "RSA",
+ 3072,
+ certSubject,
+ certSerialNumber,
+ certNotBefore,
+ certNotAfter);
+ assertEquals(RSAKeyGenParameterSpec.F0,
+ ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(3072, keyInfo.getKeySize());
+ assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+ assertOneOf(keyInfo.getOrigin(),
+ KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+ assertEquals(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_ENCRYPT,
+ keyInfo.getPurposes());
+ assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
+ assertEquals(keyValidityEndDateForOrigination, keyInfo.getKeyValidityForOriginationEnd());
+ assertEquals(keyValidityEndDateForConsumption, keyInfo.getKeyValidityForConsumptionEnd());
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+ KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512);
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getSignaturePaddings()),
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getBlockModes()),
+ KeyProperties.BLOCK_MODE_ECB);
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getEncryptionPaddings()),
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
+ assertFalse(keyInfo.isUserAuthenticationRequired());
+ assertEquals(-1, keyInfo.getUserAuthenticationValidityDurationSeconds());
+ }
+
+ public void testGenerate_EC_ModernSpec_UsableForTLSPeerAuth() throws Exception {
+ KeyPairGenerator generator = getEcGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "EC",
+ 256,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+ KeyProperties.DIGEST_NONE);
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+ assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_1);
+ assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_1);
+ }
+
+ public void testGenerate_RSA_ModernSpec_UsableForTLSPeerAuth() throws Exception {
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN
+ | KeyProperties.PURPOSE_VERIFY
+ | KeyProperties.PURPOSE_DECRYPT)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "RSA",
+ 2048,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+ KeyProperties.DIGEST_NONE);
+ MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+ MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getEncryptionPaddings()),
+ KeyProperties.ENCRYPTION_PADDING_NONE);
+ assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_1);
+ assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_1);
+ }
+
+ // TODO: Test fingerprint-authorized and secure lock screen-authorized keys. These can't
+ // currently be tested here because CTS does not require that secure lock screen is set up and
+ // that at least one fingerprint is enrolled.
+
+ public void testGenerate_EC_ModernSpec_KeyNotYetValid() throws Exception {
+ KeyPairGenerator generator = getEcGenerator();
+ Date validityStart = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(256)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setKeyValidityStart(validityStart)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "EC",
+ 256,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(validityStart, keyInfo.getKeyValidityStart());
+ }
+
+ public void testGenerate_RSA_ModernSpec_KeyExpiredForOrigination() throws Exception {
+ KeyPairGenerator generator = getRsaGenerator();
+ Date originationEnd = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(1024)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .setKeyValidityForOriginationEnd(originationEnd)
+ .build());
+ KeyPair keyPair = generator.generateKeyPair();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "RSA",
+ 1024,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(originationEnd, keyInfo.getKeyValidityForOriginationEnd());
+ }
+
+ public void testGenerate_EC_ModernSpec_SupportedSizes() throws Exception {
+ assertKeyGenUsingECSizeOnlyUsesCorrectCurve(224, ECCurves.NIST_P_224_SPEC);
+ assertKeyGenUsingECSizeOnlyUsesCorrectCurve(256, ECCurves.NIST_P_256_SPEC);
+ assertKeyGenUsingECSizeOnlyUsesCorrectCurve(384, ECCurves.NIST_P_384_SPEC);
+ assertKeyGenUsingECSizeOnlyUsesCorrectCurve(521, ECCurves.NIST_P_521_SPEC);
+ }
+
+ public void testGenerate_EC_ModernSpec_UnsupportedSizesRejected() throws Exception {
+ for (int keySizeBits = 0; keySizeBits <= 1024; keySizeBits++) {
+ if ((keySizeBits == 224) || (keySizeBits == 256) || (keySizeBits == 384)
+ || (keySizeBits == 521)) {
+ // Skip supported sizes
+ continue;
+ }
+ KeyPairGenerator generator = getEcGenerator();
+
+ try {
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(keySizeBits)
+ .build());
+ fail("EC KeyPairGenerator initialized with unsupported key size: "
+ + keySizeBits + " bits");
+ } catch (InvalidAlgorithmParameterException expected) {
+ }
+ }
+ }
+
+ public void testGenerate_EC_ModernSpec_SupportedNamedCurves() throws Exception {
+ assertKeyGenUsingECNamedCurveSupported("P-224", ECCurves.NIST_P_224_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("p-224", ECCurves.NIST_P_224_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("secp224r1", ECCurves.NIST_P_224_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("SECP224R1", ECCurves.NIST_P_224_SPEC);
+
+ assertKeyGenUsingECNamedCurveSupported("P-256", ECCurves.NIST_P_256_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("p-256", ECCurves.NIST_P_256_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("secp256r1", ECCurves.NIST_P_256_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("SECP256R1", ECCurves.NIST_P_256_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("prime256v1", ECCurves.NIST_P_256_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("PRIME256V1", ECCurves.NIST_P_256_SPEC);
+
+ assertKeyGenUsingECNamedCurveSupported("P-384", ECCurves.NIST_P_384_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("p-384", ECCurves.NIST_P_384_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("secp384r1", ECCurves.NIST_P_384_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("SECP384R1", ECCurves.NIST_P_384_SPEC);
+
+ assertKeyGenUsingECNamedCurveSupported("P-521", ECCurves.NIST_P_521_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("p-521", ECCurves.NIST_P_521_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("secp521r1", ECCurves.NIST_P_521_SPEC);
+ assertKeyGenUsingECNamedCurveSupported("SECP521R1", ECCurves.NIST_P_521_SPEC);
+ }
+
+ public void testGenerate_RSA_ModernSpec_SupportedSizes() throws Exception {
+ assertKeyGenUsingRSASizeOnlySupported(512);
+ assertKeyGenUsingRSASizeOnlySupported(768);
+ assertKeyGenUsingRSASizeOnlySupported(1024);
+ assertKeyGenUsingRSASizeOnlySupported(2048);
+ assertKeyGenUsingRSASizeOnlySupported(3072);
+ assertKeyGenUsingRSASizeOnlySupported(4096);
+
+ // The above use F4. Check that F0 is supported as well, just in case somebody is crazy
+ // enough.
+ assertKeyGenUsingRSAKeyGenParameterSpecSupported(new RSAKeyGenParameterSpec(
+ 2048, RSAKeyGenParameterSpec.F0));
+ }
+
+ private void assertKeyGenUsingECSizeOnlyUsesCorrectCurve(
+ int keySizeBits, ECParameterSpec expectedParams) throws Exception {
+ KeyPairGenerator generator = getEcGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(keySizeBits)
+ .build(),
+ mRng);
+ mRng.resetCounters();
+ KeyPair keyPair = generator.generateKeyPair();
+ long consumedEntropyAmountBytes = mRng.getOutputSizeBytes();
+ int expectedKeySize = expectedParams.getCurve().getField().getFieldSize();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "EC",
+ expectedKeySize,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(expectedKeySize, keyInfo.getKeySize());
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ expectedParams,
+ ((ECKey) keyPair.getPublic()).getParams());
+ assertEquals(((keySizeBits + 7) / 8) * 8, consumedEntropyAmountBytes * 8);
+ }
+
+ private void assertKeyGenUsingECNamedCurveSupported(
+ String curveName, ECParameterSpec expectedParams) throws Exception {
+ KeyPairGenerator generator = getEcGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setAlgorithmParameterSpec(new ECGenParameterSpec(curveName))
+ .build(),
+ mRng);
+ mRng.resetCounters();
+ KeyPair keyPair = generator.generateKeyPair();
+ long consumedEntropyAmountBytes = mRng.getOutputSizeBytes();
+ int expectedKeySize = expectedParams.getCurve().getField().getFieldSize();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "EC",
+ expectedKeySize,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(expectedKeySize, keyInfo.getKeySize());
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ expectedParams,
+ ((ECKey) keyPair.getPublic()).getParams());
+ assertEquals(((expectedKeySize + 7) / 8) * 8, consumedEntropyAmountBytes * 8);
+ }
+
+ private void assertKeyGenUsingRSASizeOnlySupported(int keySizeBits) throws Exception {
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(keySizeBits)
+ .build(),
+ mRng);
+ mRng.resetCounters();
+ KeyPair keyPair = generator.generateKeyPair();
+ long consumedEntropyAmountBytes = mRng.getOutputSizeBytes();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "RSA",
+ keySizeBits,
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(keySizeBits, keyInfo.getKeySize());
+ assertEquals(((keySizeBits + 7) / 8) * 8, consumedEntropyAmountBytes * 8);
+ }
+
+ private void assertKeyGenUsingRSAKeyGenParameterSpecSupported(
+ RSAKeyGenParameterSpec spec) throws Exception {
+ KeyPairGenerator generator = getRsaGenerator();
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setAlgorithmParameterSpec(spec)
+ .build(),
+ mRng);
+ mRng.resetCounters();
+ KeyPair keyPair = generator.generateKeyPair();
+ long consumedEntropyAmountBytes = mRng.getOutputSizeBytes();
+ assertGeneratedKeyPairAndSelfSignedCertificate(
+ keyPair,
+ TEST_ALIAS_1,
+ "RSA",
+ spec.getKeysize(),
+ DEFAULT_CERT_SUBJECT,
+ DEFAULT_CERT_SERIAL_NUMBER,
+ DEFAULT_CERT_NOT_BEFORE,
+ DEFAULT_CERT_NOT_AFTER);
+ assertEquals(spec.getPublicExponent(),
+ ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+ assertEquals(spec.getKeysize(), keyInfo.getKeySize());
+ assertEquals(((spec.getKeysize() + 7) / 8) * 8, consumedEntropyAmountBytes * 8);
+ }
+
+ private static void assertSelfSignedCertificateSignatureVerifies(Certificate certificate) {
+ try {
+ certificate.verify(certificate.getPublicKey());
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to verify self-signed certificate signature", e);
+ }
+ }
+
+ private void assertGeneratedKeyPairAndSelfSignedCertificate(
+ KeyPair keyPair, String alias,
+ String expectedKeyAlgorithm,
+ int expectedKeySize,
+ X500Principal expectedCertSubject,
+ BigInteger expectedCertSerialNumber,
+ Date expectedCertNotBefore,
+ Date expectedCertNotAfter)
+ throws Exception {
+ assertNotNull(keyPair);
+ TestUtils.assertKeyPairSelfConsistent(keyPair);
+ TestUtils.assertKeySize(expectedKeySize, keyPair);
+ assertEquals(expectedKeyAlgorithm, keyPair.getPublic().getAlgorithm());
+ TestUtils.assertKeyStoreKeyPair(mKeyStore, alias, keyPair);
+
+ X509Certificate cert = (X509Certificate) mKeyStore.getCertificate(alias);
+ assertEquals(keyPair.getPublic(), cert.getPublicKey());
+ assertX509CertificateParameters(cert,
+ expectedCertSubject,
+ expectedCertSerialNumber,
+ expectedCertNotBefore,
+ expectedCertNotAfter);
+ // Assert that the certificate chain consists only of the above certificate
+ MoreAsserts.assertContentsInOrder(
+ Arrays.asList(mKeyStore.getCertificateChain(alias)), cert);
+ }
+
+ private void assertSelfSignedCertificateSignatureVerifies(String alias) throws Exception {
+ assertSelfSignedCertificateSignatureVerifies(mKeyStore.getCertificate(alias));
+ }
+
+ private void assertKeyPairAndCertificateUsableForTLSPeerAuthentication(String alias)
+ throws Exception {
+ assertUsableForTLSPeerAuthentication(
+ (PrivateKey) mKeyStore.getKey(alias, null),
+ mKeyStore.getCertificateChain(alias));
+ }
+
+ private static void assertX509CertificateParameters(
+ X509Certificate actualCert,
+ X500Principal expectedSubject, BigInteger expectedSerialNumber,
+ Date expectedNotBefore, Date expectedNotAfter) {
+ assertEquals(expectedSubject, actualCert.getSubjectDN());
+ assertEquals(expectedSubject, actualCert.getIssuerDN());
+ assertEquals(expectedSerialNumber, actualCert.getSerialNumber());
+ assertDateEquals(expectedNotBefore, actualCert.getNotBefore());
+ assertDateEquals(expectedNotAfter, actualCert.getNotAfter());
+ }
+
+ private static void assertUsableForTLSPeerAuthentication(
+ PrivateKey privateKey,
+ Certificate[] certificateChain) throws Exception {
+ // Set up both client and server to use the same private key + cert, and to trust that cert
+ // when it's presented by peer. This exercises the use of the private key both in client
+ // and server scenarios.
+ X509Certificate[] x509CertificateChain = new X509Certificate[certificateChain.length];
+ for (int i = 0; i < certificateChain.length; i++) {
+ x509CertificateChain[i] = (X509Certificate) certificateChain[i];
+ }
+ TestKeyStore serverKeyStore = TestKeyStore.getServer();
+ // Make the peer trust the root certificate in the chain. As opposed to making the peer
+ // trust the leaf certificate, this will ensure that the whole chain verifies.
+ serverKeyStore.keyStore.setCertificateEntry(
+ "trusted", certificateChain[certificateChain.length - 1]);
+ SSLContext serverContext = TestSSLContext.createSSLContext("TLS",
+ new KeyManager[] {
+ TestKeyManager.wrap(new MyKeyManager(privateKey, x509CertificateChain))
+ },
+ TestKeyStore.createTrustManagers(serverKeyStore.keyStore));
+ SSLContext clientContext = serverContext;
+
+ if ("EC".equalsIgnoreCase(privateKey.getAlgorithm())) {
+ // As opposed to RSA (see below) EC keys are used in the same way in all cipher suites.
+ // Assert that the key works with the default list of cipher suites.
+ assertSSLConnectionWithClientAuth(
+ clientContext, serverContext, null, x509CertificateChain, x509CertificateChain);
+ } else if ("RSA".equalsIgnoreCase(privateKey.getAlgorithm())) {
+ // RSA keys are used differently between Forward Secure and non-Forward Secure cipher
+ // suites. For example, RSA key exchange requires the server to decrypt using its RSA
+ // private key, whereas ECDHE_RSA key exchange requires the server to sign usnig its
+ // RSA private key. We thus assert that the key works with Forward Secure cipher suites
+ // and that it works with non-Forward Secure cipher suites.
+ List<String> fsCipherSuites = new ArrayList<String>();
+ List<String> nonFsCipherSuites = new ArrayList<String>();
+ for (String cipherSuite : clientContext.getDefaultSSLParameters().getCipherSuites()) {
+ if (cipherSuite.contains("_ECDHE_RSA_") || cipherSuite.contains("_DHE_RSA_")) {
+ fsCipherSuites.add(cipherSuite);
+ } else if (cipherSuite.contains("_RSA_WITH_")) {
+ nonFsCipherSuites.add(cipherSuite);
+ }
+ }
+ assertFalse("No FS RSA cipher suites enabled by default", fsCipherSuites.isEmpty());
+ assertFalse("No non-FS RSA cipher suites enabled", nonFsCipherSuites.isEmpty());
+
+ // Assert that the key works with RSA Forward Secure cipher suites.
+ assertSSLConnectionWithClientAuth(
+ clientContext, serverContext, fsCipherSuites.toArray(new String[0]),
+ x509CertificateChain, x509CertificateChain);
+ // Assert that the key works with RSA non-Forward Secure cipher suites.
+ assertSSLConnectionWithClientAuth(
+ clientContext, serverContext, nonFsCipherSuites.toArray(new String[0]),
+ x509CertificateChain, x509CertificateChain);
+ } else {
+ fail("Unsupported key algorithm: " + privateKey.getAlgorithm());
+ }
+ }
+
+ private static void assertSSLConnectionWithClientAuth(
+ SSLContext clientContext, SSLContext serverContext, String[] enabledCipherSuites,
+ X509Certificate[] expectedClientCertChain, X509Certificate[] expectedServerCertChain)
+ throws Exception {
+ SSLServerSocket serverSocket = (SSLServerSocket) serverContext.getServerSocketFactory()
+ .createServerSocket(0);
+ InetAddress host = InetAddress.getLocalHost();
+ int port = serverSocket.getLocalPort();
+ SSLSocket client = (SSLSocket) clientContext.getSocketFactory().createSocket(host, port);
+
+ final SSLSocket server = (SSLSocket) serverSocket.accept();
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Certificate[]> future = executor.submit(new Callable<Certificate[]>() {
+ @Override
+ public Certificate[] call() throws Exception {
+ server.setNeedClientAuth(true);
+ server.setWantClientAuth(true);
+ server.startHandshake();
+ return server.getSession().getPeerCertificates();
+ }
+ });
+ executor.shutdown();
+ if (enabledCipherSuites != null) {
+ client.setEnabledCipherSuites(enabledCipherSuites);
+ }
+ client.startHandshake();
+ Certificate[] usedServerCerts = client.getSession().getPeerCertificates();
+ Certificate[] usedClientCerts = future.get();
+ client.close();
+ server.close();
+
+ assertNotNull(usedServerCerts);
+ assertEquals(Arrays.asList(expectedServerCertChain), Arrays.asList(usedServerCerts));
+
+ assertNotNull(usedClientCerts);
+ assertEquals(Arrays.asList(expectedClientCertChain), Arrays.asList(usedClientCerts));
+ }
+
+ private static class MyKeyManager extends X509ExtendedKeyManager {
+ private final PrivateKey key;
+ private final X509Certificate[] chain;
+
+ public MyKeyManager(PrivateKey key, X509Certificate[] certChain) {
+ this.key = key;
+ this.chain = certChain;
+ }
+
+ @Override
+ public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
+ return "fake";
+ }
+
+ @Override
+ public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+ return "fake";
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String alias) {
+ return chain;
+ }
+
+ @Override
+ public String[] getClientAliases(String keyType, Principal[] issuers) {
+ return new String[] { "fake" };
+ }
+
+ @Override
+ public String[] getServerAliases(String keyType, Principal[] issuers) {
+ return new String[] { "fake" };
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ return key;
+ }
+ }
+
+
+ private static void assertDateEquals(Date date1, Date date2) {
+ assertDateEquals(null, date1, date2);
+ }
+
+ private static void assertDateEquals(String message, Date date1, Date date2) {
+ SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
+
+ String result1 = formatter.format(date1);
+ String result2 = formatter.format(date2);
+
+ assertEquals(message, result1, result2);
+ }
+
+ private KeyPairGenerator getRsaGenerator()
+ throws NoSuchAlgorithmException, NoSuchProviderException {
+ return KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
+ }
+
+ private KeyPairGenerator getEcGenerator()
+ throws NoSuchAlgorithmException, NoSuchProviderException {
+ return KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
+ }
+
+ private static void assertOneOf(int actual, int... expected) {
+ assertOneOf(null, actual, expected);
+ }
+
+ private static void assertOneOf(String message, int actual, int... expected) {
+ for (int expectedValue : expected) {
+ if (actual == expectedValue) {
+ return;
+ }
+ }
+ fail(((message != null) ? message + ". " : "")
+ + "Expected one of " + Arrays.asList(expected)
+ + ", actual: <" + actual + ">");
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java
new file mode 100644
index 0000000..ee24eed
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.test.MoreAsserts;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.Date;
+
+public class KeyProtectionTest extends TestCase {
+ public void testDefaults() {
+ // Set only the mandatory parameters and assert values returned by getters.
+
+ KeyProtection spec = new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
+ .build();
+
+ assertEquals(KeyProperties.PURPOSE_ENCRYPT, spec.getPurposes());
+ MoreAsserts.assertEmpty(Arrays.asList(spec.getBlockModes()));
+ assertFalse(spec.isDigestsSpecified());
+ try {
+ spec.getDigests();
+ fail();
+ } catch (IllegalStateException expected) {}
+ MoreAsserts.assertEmpty(Arrays.asList(spec.getEncryptionPaddings()));
+ assertNull(spec.getKeyValidityStart());
+ assertNull(spec.getKeyValidityForOriginationEnd());
+ assertNull(spec.getKeyValidityForConsumptionEnd());
+ assertTrue(spec.isRandomizedEncryptionRequired());
+ MoreAsserts.assertEmpty(Arrays.asList(spec.getSignaturePaddings()));
+ assertFalse(spec.isUserAuthenticationRequired());
+ assertEquals(-1, spec.getUserAuthenticationValidityDurationSeconds());
+ }
+
+ public void testSettersReflectedInGetters() {
+ // Set all parameters to non-default values and then assert that getters reflect that.
+
+ Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+ Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+ Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+
+ KeyProtection spec = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_VERIFY)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CTR)
+ .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setKeyValidityStart(keyValidityStartDate)
+ .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+ .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+ .setRandomizedEncryptionRequired(false)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationValidityDurationSeconds(123456)
+ .build();
+
+ assertEquals(
+ KeyProperties.PURPOSE_DECRYPT| KeyProperties.PURPOSE_VERIFY, spec.getPurposes());
+ MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getBlockModes()),
+ KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CTR);
+ assertTrue(spec.isDigestsSpecified());
+ MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getDigests()),
+ KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512);
+ MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getEncryptionPaddings()),
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ assertEquals(keyValidityStartDate, spec.getKeyValidityStart());
+ assertEquals(keyValidityEndDateForOrigination, spec.getKeyValidityForOriginationEnd());
+ assertEquals(keyValidityEndDateForConsumption, spec.getKeyValidityForConsumptionEnd());
+ assertFalse(spec.isRandomizedEncryptionRequired());
+ MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getSignaturePaddings()),
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+ assertTrue(spec.isUserAuthenticationRequired());
+ assertEquals(123456, spec.getUserAuthenticationValidityDurationSeconds());
+ }
+
+ public void testSetKeyValidityEndDateAppliesToBothEndDates() {
+ Date date = new Date(System.currentTimeMillis() + 555555);
+ KeyProtection spec = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setKeyValidityEnd(date)
+ .build();
+ assertEquals(date, spec.getKeyValidityForOriginationEnd());
+ assertEquals(date, spec.getKeyValidityForConsumptionEnd());
+ }
+
+ public void testSetUserAuthenticationValidityDurationSecondsValidityCheck() {
+ KeyProtection.Builder builder = new KeyProtection.Builder(0);
+ try {
+ builder.setUserAuthenticationValidityDurationSeconds(-2);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ try {
+ builder.setUserAuthenticationValidityDurationSeconds(-100);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ try {
+ builder.setUserAuthenticationValidityDurationSeconds(Integer.MIN_VALUE);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ builder.setUserAuthenticationValidityDurationSeconds(-1);
+ builder.setUserAuthenticationValidityDurationSeconds(0);
+ builder.setUserAuthenticationValidityDurationSeconds(1);
+ builder.setUserAuthenticationValidityDurationSeconds(Integer.MAX_VALUE);
+
+ try {
+ builder.setUserAuthenticationValidityDurationSeconds(-2);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ public void testImmutabilityViaSetterParams() {
+ // Assert that all mutable parameters provided to setters are copied to ensure that values
+ // returned by getters never change.
+ String[] blockModes =
+ new String[] {KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC};
+ String[] originalBlockModes = blockModes.clone();
+ Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+ Date originalKeyValidityStartDate = new Date(keyValidityStartDate.getTime());
+ Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+ Date originalKeyValidityEndDateForOrigination =
+ new Date(keyValidityEndDateForOrigination.getTime());
+ Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+ Date originalKeyValidityEndDateForConsumption =
+ new Date(keyValidityEndDateForConsumption.getTime());
+ String[] digests = new String[] {KeyProperties.DIGEST_MD5, KeyProperties.DIGEST_SHA512};
+ String[] originalDigests = digests.clone();
+ String[] encryptionPaddings = new String[] {
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP, KeyProperties.ENCRYPTION_PADDING_PKCS7};
+ String[] originalEncryptionPaddings = encryptionPaddings.clone();
+ String[] signaturePaddings = new String[] {
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS, KeyProperties.SIGNATURE_PADDING_RSA_PKCS1};
+ String[] originalSignaturePaddings = signaturePaddings.clone();
+
+ KeyProtection spec = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(blockModes)
+ .setDigests(digests)
+ .setEncryptionPaddings(encryptionPaddings)
+ .setKeyValidityStart(keyValidityStartDate)
+ .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+ .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+ .setSignaturePaddings(signaturePaddings)
+ .build();
+
+ assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+ blockModes[0] = null;
+ assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+
+ assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+ digests[1] = null;
+ assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+
+ assertEquals(Arrays.asList(originalEncryptionPaddings),
+ Arrays.asList(spec.getEncryptionPaddings()));
+ encryptionPaddings[0] = null;
+ assertEquals(Arrays.asList(originalEncryptionPaddings),
+ Arrays.asList(spec.getEncryptionPaddings()));
+
+ assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+ keyValidityStartDate.setTime(1234567890L);
+ assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+
+ assertEquals(originalKeyValidityEndDateForOrigination,
+ spec.getKeyValidityForOriginationEnd());
+ keyValidityEndDateForOrigination.setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForOrigination,
+ spec.getKeyValidityForOriginationEnd());
+
+ assertEquals(originalKeyValidityEndDateForConsumption,
+ spec.getKeyValidityForConsumptionEnd());
+ keyValidityEndDateForConsumption.setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForConsumption,
+ spec.getKeyValidityForConsumptionEnd());
+
+ assertEquals(Arrays.asList(originalSignaturePaddings),
+ Arrays.asList(spec.getSignaturePaddings()));
+ signaturePaddings[1] = null;
+ assertEquals(Arrays.asList(originalSignaturePaddings),
+ Arrays.asList(spec.getSignaturePaddings()));
+ }
+
+ public void testImmutabilityViaGetterReturnValues() {
+ // Assert that none of the mutable return values from getters modify the state of the spec.
+
+ KeyProtection spec = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC)
+ .setDigests(KeyProperties.DIGEST_MD5, KeyProperties.DIGEST_SHA512)
+ .setEncryptionPaddings(
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setKeyValidityStart(new Date(System.currentTimeMillis() - 2222222))
+ .setKeyValidityForOriginationEnd(new Date(System.currentTimeMillis() + 11111111))
+ .setKeyValidityForConsumptionEnd(new Date(System.currentTimeMillis() + 33333333))
+ .setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .build();
+
+ String[] originalBlockModes = spec.getBlockModes().clone();
+ spec.getBlockModes()[0] = null;
+ assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+
+ String[] originalDigests = spec.getDigests().clone();
+ spec.getDigests()[0] = null;
+ assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+
+ String[] originalEncryptionPaddings = spec.getEncryptionPaddings().clone();
+ spec.getEncryptionPaddings()[0] = null;
+ assertEquals(Arrays.asList(originalEncryptionPaddings),
+ Arrays.asList(spec.getEncryptionPaddings()));
+
+ Date originalKeyValidityStartDate = (Date) spec.getKeyValidityStart().clone();
+ spec.getKeyValidityStart().setTime(1234567890L);
+ assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+
+ Date originalKeyValidityEndDateForOrigination =
+ (Date) spec.getKeyValidityForOriginationEnd().clone();
+ spec.getKeyValidityForOriginationEnd().setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForOrigination,
+ spec.getKeyValidityForOriginationEnd());
+
+ Date originalKeyValidityEndDateForConsumption =
+ (Date) spec.getKeyValidityForConsumptionEnd().clone();
+ spec.getKeyValidityForConsumptionEnd().setTime(1234567890L);
+ assertEquals(originalKeyValidityEndDateForConsumption,
+ spec.getKeyValidityForConsumptionEnd());
+
+ String[] originalSignaturePaddings = spec.getSignaturePaddings().clone();
+ spec.getSignaturePaddings()[0] = null;
+ assertEquals(Arrays.asList(originalSignaturePaddings),
+ Arrays.asList(spec.getSignaturePaddings()));
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
new file mode 100644
index 0000000..9e8678b
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
@@ -0,0 +1,980 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import com.android.cts.keystore.R;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Provider.Service;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+public class SignatureTest extends AndroidTestCase {
+ static final String EXPECTED_PROVIDER_NAME = "AndroidKeyStoreBCWorkaround";
+
+ private static final String[] EXPECTED_SIGNATURE_ALGORITHMS = {
+ "NONEwithRSA",
+ "MD5withRSA",
+ "SHA1withRSA",
+ "SHA224withRSA",
+ "SHA256withRSA",
+ "SHA384withRSA",
+ "SHA512withRSA",
+ "SHA1withRSA/PSS",
+ "SHA224withRSA/PSS",
+ "SHA256withRSA/PSS",
+ "SHA384withRSA/PSS",
+ "SHA512withRSA/PSS",
+ "NONEwithECDSA",
+ "SHA1withECDSA",
+ "SHA224withECDSA",
+ "SHA256withECDSA",
+ "SHA384withECDSA",
+ "SHA512withECDSA"
+ };
+
+ private static final Map<String, String> SIG_ALG_TO_CANONICAL_NAME_CASE_INSENSITIVE =
+ new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+ static {
+ // For an unknown legacy reason, libcore's ProviderTest#test_Provider_getServices insists
+ // that a Service with algorithm "ECDSA" be exposed, despite the RI not exposing any such
+ // services. Thus, our provider has to expose the "ECDSA" service whose actual proper
+ // name is SHA1withECDSA.
+ SIG_ALG_TO_CANONICAL_NAME_CASE_INSENSITIVE.put("ECDSA", "SHA1withECDSA");
+ }
+
+ private static final byte[] SHORT_MSG_KAT_MESSAGE =
+ HexEncoding.decode("ec174729c4f5c570ba0de4c424cdcbf0362a7718039464");
+ private static final byte[] LONG_MSG_KAT_SEED = SHORT_MSG_KAT_MESSAGE;
+ private static final int LONG_MSG_KAT_SIZE_BYTES = 3 * 1024 * 1024 + 123;
+
+ private static final Map<String, byte[]> SHORT_MSG_KAT_SIGNATURES =
+ new TreeMap<String, byte[]>(String.CASE_INSENSITIVE_ORDER);
+ static {
+ // From RI
+ SHORT_MSG_KAT_SIGNATURES.put("NONEwithECDSA", HexEncoding.decode(
+ "304402201ea57c2fb571991639d103bfec658ee7f359b60664e400a5834cfc20d28b588902202433f5"
+ + "eb07d2b03bf8d238ea256ea399d0913a6cfcae2c3b00e2efd50aebc967"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA1withECDSA", HexEncoding.decode(
+ "30440220742d71a013564ab196789322b9231ac5ff26460c2d6b1ab8ccb45eec254cc8ba0220780a86"
+ + "5ddc2334fae23d563e3142b04660c2ab1b875c4ff8c557a1d1accc43e1"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA224withECDSA", HexEncoding.decode(
+ "304502200f74966078b34317daa69e487c3163dbb4e0391cd74191cc3e95b33fc60966e3022100ebdc"
+ + "be19c516d550609f73fb37557a406e397bc1725a1baba50cdfc3537bd377"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA256withECDSA", HexEncoding.decode(
+ "304402204443b560d888beeae729155b0d9410fef2ec78607d9166af6144346fba8ce45d02205b0727"
+ + "bfa630050f1395c8bcf46c614c14eb15f2a6abbd3bc7c0e83b41819281"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA384withECDSA", HexEncoding.decode(
+ "3045022025ade03446ce95aa525a51aedd16baf12a2b8b9c1f4c87224c38e48c84cbbbf8022100ad21"
+ + "8424c3671bc1513e1da7e7186dbc6bf67bec434c95863a48e79f53684971"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA512withECDSA", HexEncoding.decode(
+ "3045022100969e8fed2dc4ddcdf341368e057efe4e3a00eda66bbb127dec31bb0144c5334602201087"
+ + "2b7f9ab9c06a07053e0641e6adc18a87a1d7807550a19e872e78e5c7f0dd"));
+
+ // From RI
+ SHORT_MSG_KAT_SIGNATURES.put("NONEwithRSA", HexEncoding.decode(
+ "257d0704e514ead29a5c45576adb2d5a7d7738e6a83b5d6463a5306788015d14580fee340e78e00d39"
+ + "f56ae616083ac929e5daf9eeab40b908eb05d0cd1036d9e92799587df0d4c5304c9b27f913e1c891"
+ + "919eff0df3b5d9c0d8cc4cd843795840799cc0353192c3868b3f8cad96d04bb566ca53e1146aa2a3"
+ + "4b890ce917680bbdea1dee417a89630224d2ee79d66d38c7c77e50b45e1dd1b8b63eb98ecd60426b"
+ + "c9fb30917e78ae4dd7cbfa9475f9be53bf45e7032add52681553679252f4f74de77831c95ea69f30"
+ + "2f0fd28867de058728455e3537680c86a001236e70c7680c78b4dc98942d233b968635a0debccb41"
+ + "fbc17ece7172631f5ab6d578d70eb0"));
+ SHORT_MSG_KAT_SIGNATURES.put("MD5withRSA", HexEncoding.decode(
+ "1e1edefc9a6a4e61fcef0d4b202cc2b53ab9043b1a0b21117d122d4c5399182998ec66608e1ab13513"
+ + "08fbb23f92d5d970f7fb1a0691f8d1e682ff4f5e394ef2dfcbdc2de5c2c33372aec9a0c7fba982c5"
+ + "c0f1a5f65677d9294d54a2e613cc0e5feb919135e883827da0e1c222bf31336afa63a837c57c0b70"
+ + "70ceb8a24492a42afa6750cc9fe3a9586aa15507a65410db973a4b26734624d323dc700928717789"
+ + "fb1f970d57326eef49e012fcebcfbbfd18fb4d6feff03587d0f2b0a556fe467437a44a2283ea5027"
+ + "6efda4fd427365687960e0c289c5853a7b300ff081511a2f52899e6f6a569b30e07adfd52e9d9d7e"
+ + "ab33999da0da1dd16d4e88bd980fcd"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA1withRSA", HexEncoding.decode(
+ "280977b4ee18cbe27d3e452c9b90174f5d81dd518018ce52ff1cd1e8d4d0626afca85be14a43fa3b76"
+ + "a80e818b4bc10abc62180fa15619d78be98ccd8fa642ea05355aa84f2924e041c2b594b1cf31d42b"
+ + "f11c78dd3cbb6cc2cbfe151792985e6e5cf73c2e600a38f31e26e84c3e4a434f67a625fefe712d17"
+ + "b34125ea91d333cfe1c4ac914b6c411b08e64700885325e07510c4f49ef3648252736f17d3ae7705"
+ + "0054ceb07ab04b5ecaa5fc4556328bad4f97eba37f9bf079506e0eb136c9eadf9e466ccb18d65b4d"
+ + "ef2ba3ca5c2f33354a2040dfb646423f96ba43d1d8f3dcf91c0c2c14e7958159d2ac3ebc0b6b87e6"
+ + "6efbdda046ce8a766fecc3f905d6ff"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA224withRSA", HexEncoding.decode(
+ "490af9e685ef44da9528d9271d00e09a3e688012bf3f63fd924a06cb4db747a28cdf924c2d51620165"
+ + "33985abf4b91d64c17ff7e2b4f0de5a28375dddf556cd9e5dcebd112f766f07cb867e8d5710ce79a"
+ + "1c3d5244cbd16618b0fedc2b9015d51a98d453747fb320b97995ea9579adbc8bf6042b2f4252cef1"
+ + "787207fefaf4b9c7212fe0ff8b22ae12ffc888f0a1e6923455577e82b58608dabc2acba05be693ff"
+ + "ae7da263d6c83cb13d59a083f177578d11030f8974bdb301f6135ecd5ec18dd68dc453c5963e94b6"
+ + "2d89bcda0ff63ac7394030f79b59139e1d51573f0d4a1e85d22502c9b0d29412f7eb277fb42fa4db"
+ + "18875baffa7719b700e4830edbcd6f"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA256withRSA", HexEncoding.decode(
+ "1f1adcd6edbf4c50777c932db6e99a577853fbc9c71c692e921291c5aa73eb0155e30c8d4f3aff828f"
+ + "2040c84e10b1ba729ccc23899650451022fcd3574df5454b01112adec5f01565b578bbc7c32810c9"
+ + "407106054ad8f4f640b589ddef264d028ad906536d61c8053ef0dba8e10ca2e30a9dd6ccc9a9ec3e"
+ + "76c10d36029820865b2d01095987af4a29369ffc6f70fa7e1de2b8e28f41894df4225cf966454096"
+ + "7fb7ecff443948c8a0ee6a1be51e0f8e8887ff512dbdc4fc81636e69ae698000ce3899c2ec999b68"
+ + "691adfb53092380264b27d91bd64561fee9d2e622919cf6b472fd764dc2065ae6a67df2c7b5ec855"
+ + "099bdb6bb104ee41fa129c9da99745"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA384withRSA", HexEncoding.decode(
+ "3b5e8baa62803569642fa8c3255249709c9f1d69bd31f7b531d5071c07cd9bac29273097666d96b2e3"
+ + "2db13529b6414be5aee0c8a90c3f3b2a5c815f37fac16a3527fa45903f847416ed218eee2fef5b87"
+ + "5f0c97576f58b3467e83497c1cdeea44d0ea151e9c4d27b85eef75d612b1fc16731859738e95bdf1"
+ + "2f2098ebd501d8493c66585e8545fb13d736f7dbbb530fb06f6f157cd10c332ca498b379336efdaf"
+ + "a8f940552da2dbb047c33e87f699068eaadd6d47c92a299f35483ba3ae09f7e52a205f202c1af997"
+ + "c9bdc40f423b3767292c7fcea3eaf338a76f9db07a53d7f8d084bf1ceac38ec0509e442b1283cd8f"
+ + "013c4c7a9189fe4ef9ab00c80cb470"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA512withRSA", HexEncoding.decode(
+ "23eb7577d2ffefed10780c2a26f79f64abe08203e900db2333413f30bbc81f800857857c8b0e02c3e8"
+ + "8fe3cf5514130d6216ef7c4a86b1012594c7cb07a293159b92bf40291224386a84e607e0a8389c8a"
+ + "a0c45cc553037517c52f61fe0ea51dba184e890db7d9517760724c038018330c0a9450c280430e6f"
+ + "9e4cdd4545c3f6684485cd6e27203735ff4be76420071920b18c54d98c0e3eb7ae7d1f01f5171ace"
+ + "87885c6185f66d947d51a441a756bc953458f7d3a1714226899562478ebf91ab18d8e7556a966661"
+ + "31de37bc2e399b366877f53c1d88f93c989aeb64a43f0f6cbc2a29587230f7e3e90ea18868d79584"
+ + "3e62b49f5df78e355b437ec2f882f9"));
+
+ // From Bouncy Castle
+ SHORT_MSG_KAT_SIGNATURES.put("SHA1withRSA/PSS", HexEncoding.decode(
+ "17e483781695067a25bc7cb204429a8754af36032038460e1938c28cd058025b14d2cffe5d3da39e76"
+ + "6542014e5419f1d4c4d7d8e3ebcd2221dde04d24bbbad657f6782b7a0fada3c3ea595bc21054b0ab"
+ + "d1eb1ada86276ed31dbcce58be7407cbbb924d595fbf44f2bb6e3eab92296076e291439107e67912"
+ + "b4fac3a27ff84af7cd2db1385a8340b2e49c7c2ec96a6b657a1641da80799cb88734cca35a2b3a2c"
+ + "4af832a34ac8d3134ccc8b61150dc1b64391888a3a84bdb5184b48e8509e8ba726ba8847e4ca0640"
+ + "ce615e3adf5248ce08adb6484f6f29caf6c65308ec6351d97369ae005a7c762f76f0ddc0becc3e45"
+ + "529aa9c8391473e392c9a60c2d0834"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA224withRSA/PSS", HexEncoding.decode(
+ "3b7641a49e7766ed879f2b0e33ceb3d935678a7deffbd855a97abf00a65c981814ac54a71150b2ffea"
+ + "d5db83aa96d0939267b3c5e2fcf958e9c6fdf7d90908e6139f7f330a16dc625d8268ffd324659f6c"
+ + "e36798ef3b71a92f1d2237e3ce1e281aacc1d5370ae63c9b75e7134ad15cca1410746bf259ac4519"
+ + "c407877503900ec8f3b71edce727e9d0275c9cd48385f89ce76ed17a2bf246578f183bb6577d6942"
+ + "2056c7d9145528fc8ca036926a9fafe819f37c1a4a0a69b17f3d4b0a116106f94a5d2a5f8ce6981c"
+ + "d6e5c2f858dcb0823e725fffe6be14ca882c81fa993bebda549fcf983eb7f8a87eccd545951dcdc9"
+ + "d8055ae4f4067de997cfd89952c905"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA256withRSA/PSS", HexEncoding.decode(
+ "6f0f744fa8e813b4c7caa0c395c1aa8aee0d61e621b4daae305c759b5b5972311ad691f8867821efba"
+ + "d57995cc8ff38f33393293e94e1c484e94de4816b0fd986f5710a02d80e62461cc6f87f1f3742268"
+ + "c28a54870f290d136aa629cbe00a1bf243fab1674c04cd5910a786b2ac5e71d9c6f4c41daa4c584d"
+ + "46ba7ee768d2d2559be587a7b2009f3b7497d556a0da8a8ae80ce91152c81ffba62720d36b699d1f"
+ + "157137ff7ee7239fc4baf611d01582346e201900f7a4f2617cdf574653e124fb895c6cb76d4ed5a8"
+ + "aca97d1e408e8011eba649d5617bae8b27c1b946dcff7b29151d8632ad128f22907e8b83b9149e16"
+ + "fbb9e9b87600a2f90c1fd6dc164c52"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA384withRSA/PSS", HexEncoding.decode(
+ "8e57992362ad4b0487a707b2f8811d953f5aaf800978859981e7dcddad6f9f411fb162859115577c53"
+ + "7a3524e26bf069508185848d6e29e7da1f9660a49771533e43853e02232314afd2928a1ff1824345"
+ + "a5a90309a59d213ff6a4d04520f95a976342e6ac529ec6a6821157f4fee3bdae30d836d3ab44386d"
+ + "3914e6aacd6a6a63e1d63b4d9bfb93b343b6c1f28d60042ffbe1e46fb692a381456e84b3328dbcae"
+ + "ed6fc577cb1c5f86a38c5c34d439eeee7e798edc9f2bcd4fc217b1630e45b8df67def2c2cdb9fea0"
+ + "5d67aa6cce6e9a72e9a114e2e620a54c05755e32685ffc7e50487c3cd00888c09492fad8c461c338"
+ + "e7d099b275deaf184b7d6689385f7c"));
+ SHORT_MSG_KAT_SIGNATURES.put("SHA512withRSA/PSS", HexEncoding.decode(
+ "7a40f9f2797beda0702df0520c7138269295a0f0328aab4eba123ebf178ea4abc745ed42d3b175dc70"
+ + "c8dcc98f46f2234b392dbb3e939f30888715c4fbb47fbb5bb7c0557c140c579f48226710e5b3da0d"
+ + "9511337cde5626df586b4004100dd45490e5f8ae23307b5d1054c97e9ef58f9c385ca55b6db4f58d"
+ + "2e19bc8ca9d8c2b4922fb3325b6fb61fc40a359e9196aa9388845b136d2790d71410e20371dcf0a7"
+ + "0425ee1854c5c3d7de976b28de0ee9b1048ed99b2a957edc97466cc4c87e36224fd323605228f61a"
+ + "1aad30253b0698f9a358491138027d325d46bdfdf72171c57a2dab0a9cddaad8e170b8275c172e42"
+ + "33b29ed81c0f4de9fe9f0670106aad"));
+ }
+
+ private static final Map<String, byte[]> LONG_MSG_KAT_SIGNATURES =
+ new TreeMap<String, byte[]>(String.CASE_INSENSITIVE_ORDER);
+ static {
+ // From RI
+ LONG_MSG_KAT_SIGNATURES.put("NONEwithECDSA", HexEncoding.decode(
+ "304502206e4039608a66ce118821eeca3e2af7f530f51d1ce8089685a13f49010e3cd58b02210083a5"
+ + "fe62a171f1b1d775fad712128a223d6b63336e0248783652474221cb3193"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA1withECDSA", HexEncoding.decode(
+ "3044022075f09bb5c87d883c088ca2ad263bbe1754ab614f727465bc43695d3521eaccf80220460e4e"
+ + "32421e6f4398cd9b7fbb31a1d1f2961f26b9783620f6413f0e6f7efb84"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA224withECDSA", HexEncoding.decode(
+ "3045022100d6b24250b7d3cbd329913705f4990cfd1000f338f7332a44f07d7731bd8e1ff602200565"
+ + "0951e14d0d21c4344a449843ef65ac3a3f831dc7f304c0fa068c996f7d34"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA256withECDSA", HexEncoding.decode(
+ "30440220501946a2c373e8da19b36e3c7718e3f2f2f16395d5026ac4fbbc7b2d53f9f21a0220347d7a"
+ + "46685282f308bacd5fb25ae92b351228ea39082784789696580f27eed1"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA384withECDSA", HexEncoding.decode(
+ "30450220576836de4ab94a869e867b2360a71dc5a0b3351ea1c896b163206db7c3507dc2022100c1a6"
+ + "719052a175e023bca7f3b9bb7a379fc6b51864cb28a195076d2f3c79ed2e"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA512withECDSA", HexEncoding.decode(
+ "304402204ca46bac4e43e8694d1af38854c96024a4e9bcc55c6904c1f8fea0d1927f69f7022054662e"
+ + "84b4d16b9f7e8164f4896212dec3c7c1e7fd108f69b0dff5bc15399eeb"));
+
+ // From RI
+ LONG_MSG_KAT_SIGNATURES.put("MD5withRSA", HexEncoding.decode(
+ "7040f3e0d95f4d22719d26e5e684dbcd5ed52ab4a7c5aa51b938b2c060c79eb600f9c9771c2fcda7e3"
+ + "55e7c7b5e2ba9fe9a2a3621881c0fe51702781ffcde6ce7013218c04bb05988346c2bed99afb97a8"
+ + "113fb50697adf93791c9129e938040f91178e35d6f323cfa515ea6d2112e8cce1302201b51333794"
+ + "4a5c425cecc8181842ace89163d84784599ea688060ad0d61ac92b673feabe01ae5e6b85d8b5e8f0"
+ + "519aea3c29781e82df9153404d027d75df8370658898ed348acf4e13fd8f79c8a545881fbbf585e1"
+ + "c666be3805e808819e2cc730379f35a207f9e0e646c7ab6d598c75b1901f0c5ca7099e34f7f01579"
+ + "3b57dfb5c2a32e8423bfed6215f9d0"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA1withRSA", HexEncoding.decode(
+ "187d7689206c9dd03861009c6cb62c7752fd2bbc354f0bea4e76059fe582744c80027175112a3df4b6"
+ + "3b4a5626ed3051192e3c9b6d906497472f6df81171064b59114ff5d7c60f66943549634461cfadd8"
+ + "a033cba2b8781fb7936ea1ca0043da119856a21e533afa999f095cf87604bb33a14e8f82fab01998"
+ + "9ef3133e8069708670645ddd5cdc86bbe19fbf672b409fb6d7cae2f913814cd3dc8d5ae8e4037ccf"
+ + "4a3ef97db8c8a08516716258c4b767607c51dfb289d90af014d3cfc64dbadb2135ed59728b78fda0"
+ + "823fe7e68e84280c283d21ab660364a9bf035afa9a7262bade87057a63aa1d7e2c09bb9dd037bcbd"
+ + "7b98356793bc32be81623833c6ab62"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA224withRSA", HexEncoding.decode(
+ "31ff68ddfafcf3ff6e651c93649bf9cc06f4138493317606d8676a8676f9d9f3a1d5e418358f79d143"
+ + "a922a3cfc5e1ad6765dc829b556c9019a6d9389144cc6a7571011c024c0514891970508dac5f0d26"
+ + "f26b536cf3e4511b5e72cd9f60590b387d8a351a9f28839a1c5be5272cb75a9062aa313f3d095074"
+ + "6d0a21d4f8c9a94d3bb4715c3ef0207cf1335653161a8f78972329f3ec2fa5cfe05318221cb1f535"
+ + "8151dde5410f6c36f32287a2d5e76bf36134d7103fc6810a1bb8627de37d6b9efde347242d08b0b6"
+ + "2b1d73bacd243ccc8546536080b42a82b7162afeb4151315746a14b64e45226c9f5b35cf1577fc6b"
+ + "f5c882b71deb7f0e375db5c0196446"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA256withRSA", HexEncoding.decode(
+ "529c70877dedf3eb1abda98a2f2b0fc899e1edece70da79f8f8bbceb98de5c85263bef2ef8a7322624"
+ + "5ed2767045ea6965f35cb53e6ac1d6c62e8007a79962507d3e01c77d4e96674344438519adae67d9"
+ + "6357da5c4527969c939fd86f3b8685338c2be4bf6f1f85527b11fcaa4708f925e8bb9b877bda179b"
+ + "d1b45153ef22834cf593ecc5b6eca3deddbe5d05894e4e5707d71bc35ea879ccb6e8ffc32e0cdc5e"
+ + "88a30eef7a608d9ea80b5cefec2aa493a3b1354ad20e88ab1f8bfda3bd9961e10f0736d1bc090d57"
+ + "b93fbce3e6e2fc99e67c7b466188d1615b4150c206472e48a9253b7549cebf6c7cbb558b54e10b73"
+ + "c8b1747c18d1890a24d0a835ee710a"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA384withRSA", HexEncoding.decode(
+ "5dd3553bc594c541937dac9a8ac119407712da7564816bcdc0ca4e14bc6059b9f9bd72e99be8a3df3e"
+ + "0a3c4e8ed643db9ed528b43a396dba470ad3307815bd7c75fa5b08775a378cc4203341379087dcb3"
+ + "62a5e9f5c979744e4498a6aafd1b1a8069caf4ef437f2743754861fcc96d67a0f1dd3397bb65ede3"
+ + "18d2b3628eb2c3ec5db8a3e21fbbe2629f1030641e420963abc4da99e24dd497337c8149a52d97da"
+ + "7176c0767d72e18f8c9a49e6808509837f719fd16ba27b19a2b32bd19b9b14818e0b9be81062be77"
+ + "4fb1b3105a1528170822391915a5cd12b8e79aaab7943c34094da4c4f8d56f52177db953d3bf7846"
+ + "f0e5f22f2311054a1daba4fec6b589"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA512withRSA", HexEncoding.decode(
+ "971d6350337866fbcb48c49446c50cac1995b822cfec8f2a3e2c8206158a2ddfc8fc7b38f5174b3288"
+ + "91489e7b379829bac5e48cd41e9713ea7e2dc9c61cf90d255387d31818d2f161ec5c3a977b4ce121"
+ + "62fb11ede30d1e63c0fbba8a4094e6ad39e64176a033e7130bbed71a67ff1713b45f0bedeb1ee532"
+ + "15690f169452c061cd7d15e71cc754a2f233f5647af8373d2b583e98e4242c0a0581e0ce2b22e15a"
+ + "443b0ff23d516ed39664f8b8ab5ca98a44af500407941fae97f37cb1becbcbff453608cb94a176d9"
+ + "e702947fff80bc8d1e9bcdef2b7bbe681e15327cee50a72649aed0d730df7b3c9c31b165416d9c9f"
+ + "1fcb04edbf96514f5758b9e90ebc0e"));
+
+ // From Bouncy Castle
+ LONG_MSG_KAT_SIGNATURES.put("SHA1withRSA/PSS", HexEncoding.decode(
+ "54a2050b22f6182b65d790da80ea16bfbc34b0c7e564d1a3ce4450e9b7785d9eaa14814dee8699977a"
+ + "e8da5cfb3c55c9a623ca55abcc0ef4b3b515ce31d49a78db442f9db270d35a179baf71057fe8d6d2"
+ + "d3f7e4fd5f5c80e11dc059c72a1a0373f527d88089c230525f895ee19e45f5547572083418c9e542"
+ + "5ff44e407500d1c49159484f38e4b00523c2fa45b7b9b38a2c1ad676b36f02a06db6fca52bd79ba1"
+ + "94d5062f5035a12a1f026ac216789844a5da0caa4d481386a12ca635c06b877515ce3782d9189d87"
+ + "d1ff5ec6ec7c39437071c8db7d1c2702205da4cfb01805ca7fec5595dba2234602ca5347d30538cb"
+ + "4b5286c151609afcca890a6276d5e8"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA224withRSA/PSS", HexEncoding.decode(
+ "7e95c1e4f700ceaf9ce72fd3f9f245ba80f2e1341341c49521779c8a79004f9c534297441330b9df36"
+ + "bb23467eb560e5e5538612cecc27953336a0d4d8044d5a80f6bcef5299830215258c574d271ea6cd"
+ + "7117c2723189385435b0f06951ff3d6a700b23bc7ed3298cfb6aa65e8f540717d57f8b55290a4862"
+ + "034d9f73e8d9cb6ae7fa55a8b4c127535b5690122d6405cb0c9a313808327cfd4fb763eae875acd1"
+ + "b60e1920ecf1116102cc5f7d776ed88e666962f759258d6f5454c29cb99b8f9ccad07d209671b607"
+ + "014d19009e392bfb08247acf7f354458dc51196d84b492798dd829b7300c7591d42c58f21bd2c3d1"
+ + "e5ce0a0d3e0aa8aa4b090b6a619fc6"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA256withRSA/PSS", HexEncoding.decode(
+ "5a8c0ae593a6714207b3ad83398b38b93da18cfda73139ea9f02c88a989368ae6901357194a873dd2e"
+ + "8cd1bb86d1f81fbc8bf725538dc2ad60759f8caab6a98a6baa6014874a92d4b92ed72e73f2721ba2"
+ + "86e545924860d27210b53f9308c4fec622fdfca7dd6d51a5b092184114e9dcf57636cdabaca17b49"
+ + "70cd5e93ce12c30af6d09d6964c5ad173095ea000529620d94a25b4cc977deefd25cc810a7b11cd5"
+ + "e5b71c9276b0bd33c53db01304b359a4a88f3fe8bc3335669f7609b0f6da17e49ad87f38468fa2c6"
+ + "8134ba6df407207559355b6e486a745009931796ab0567c9bd61788073aa00113b324fa25bd32b4d"
+ + "3521e98e0b4905c6dce30d70387a2e"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA384withRSA/PSS", HexEncoding.decode(
+ "7913f13dc399adb07cd96c1bb484f999d047efcd96501c92477d2234a1da94db9c6fd65a8031bd3040"
+ + "82d90ef4a4f388e670795d144ef72a160583d4a2c805415542fa16ffd8760d2f28bdc82f63db0900"
+ + "a3554bc9175dafa1899249abd49591216ba2965a4862d0f59d8b8c8d1042ed7ac43a3a15650d578a"
+ + "2ea53696e462f757b326b7f0f7610fb9934aee7d954a45ca03ef66464a5611433e1224d05f783cd1"
+ + "935eff90015140cb35e15f2bbf491a0d6342ccef57e453f3462412c5ff4dfdc44527ea76c6b05b1d"
+ + "1330869aec1b2f41e7d975eba6b056e7c2f75dd73b1eff6d853b9507f410279b02f9244b656a1aca"
+ + "befcc5e1167df3a49c4a7d8479c30f"));
+ LONG_MSG_KAT_SIGNATURES.put("SHA512withRSA/PSS", HexEncoding.decode(
+ "43ffefe9c96014312679a2e3803eb7c58a2a4ab8bb659c12fec7fb574c82aed673e21ed86ac309cf6c"
+ + "e567e47b7c6c83dcd72e3ee946067c2004689420528174d028e3d32b2b306bcbcb6a9c8e8b83918f"
+ + "7415d792f9d6417769def3316ed61898443d3ffa4dc160e5b5ecf4a11a9dfed6b4a7aa65f0f2c653"
+ + "4f7e514aed73be441609ffca29207b4ced249058543fd6e13a02ef42babe2cdf4aaba66b42e9d47f"
+ + "c79b4ed54fbc28d9d732f2e468d43f0ca1de6fd5312fad2c4e3eaf3e9586bca6a8bab24b4dfab8b3"
+ + "9a4057c8ed27024b61b425036bea5e23689cd9db2450be47ec2c30bb6707740c70a53b3e7a1c7ecf"
+ + "f04e3de1460e60e9be7a42b1ddff0c"));
+ }
+
+ private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+
+ public void testAlgorithmList() {
+ // Assert that Android Keystore Provider exposes exactly the expected signature algorithms.
+ // We don't care whether the algorithms are exposed via aliases, as long as the canonical
+ // names of algorithms are accepted.
+ // If the Provider exposes extraneous algorithms, it'll be caught because it'll have to
+ // expose at least one Service for such an algorithm, and this Service's algorithm will
+ // not be in the expected set.
+
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ Set<Service> services = provider.getServices();
+ Set<String> actualSigAlgsLowerCase = new HashSet<String>();
+ Set<String> expectedSigAlgsLowerCase = new HashSet<String>(
+ Arrays.asList(TestUtils.toLowerCase(EXPECTED_SIGNATURE_ALGORITHMS)));
+ for (Service service : services) {
+ if ("Signature".equalsIgnoreCase(service.getType())) {
+ String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+ if (!expectedSigAlgsLowerCase.contains(algLowerCase)) {
+ // Unexpected algorithm -- check whether it's an alias for an expected one
+ String canonicalAlgorithm =
+ SIG_ALG_TO_CANONICAL_NAME_CASE_INSENSITIVE.get(algLowerCase);
+ if (canonicalAlgorithm != null) {
+ // Use the canonical name instead
+ algLowerCase = canonicalAlgorithm.toLowerCase();
+ }
+ }
+ actualSigAlgsLowerCase.add(algLowerCase);
+ }
+ }
+
+ TestUtils.assertContentsInAnyOrder(actualSigAlgsLowerCase,
+ expectedSigAlgsLowerCase.toArray(new String[0]));
+ }
+
+ public void testGeneratedSignatureVerifies() throws Exception {
+ Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ assertNotNull(provider);
+ for (String sigAlgorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+ try {
+ KeyPair keyPair = getKeyPairForSignatureAlgorithm(sigAlgorithm, keyPairs);
+
+ // Generate a signature
+ Signature signature = Signature.getInstance(sigAlgorithm);
+ signature.initSign(keyPair.getPrivate());
+ assertSame(provider, signature.getProvider());
+ byte[] message = "This is a test".getBytes("UTF-8");
+ signature.update(message);
+ byte[] sigBytes = signature.sign();
+
+ // Assert that it verifies using our own Provider
+ assertSignatureVerifiesOneShot(
+ sigAlgorithm, provider, keyPair.getPublic(), message, sigBytes);
+
+ // Assert that it verifies using whatever Provider is chosen by JCA by
+ // default for this signature algorithm and public key.
+ assertSignatureVerifiesOneShot(
+ sigAlgorithm, keyPair.getPublic(), message, sigBytes);
+ } catch (Exception e) {
+ throw new RuntimeException(sigAlgorithm + " failed", e);
+ }
+ }
+ }
+
+ public void testSmallMsgKat() throws Exception {
+ Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+ byte[] message = SHORT_MSG_KAT_MESSAGE;
+
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ assertNotNull(provider);
+ for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+ try {
+ byte[] goodSigBytes = SHORT_MSG_KAT_SIGNATURES.get(algorithm);
+ assertNotNull(goodSigBytes);
+ KeyPair keyPair = getKeyPairForSignatureAlgorithm(algorithm, keyPairs);
+ // Assert that AndroidKeyStore provider can verify the known good signature.
+ assertSignatureVerifiesOneShot(
+ algorithm, provider, keyPair.getPublic(), message, goodSigBytes);
+ assertSignatureVerifiesFedOneByteAtATime(
+ algorithm, provider, keyPair.getPublic(), message, goodSigBytes);
+ assertSignatureVerifiesFedUsingFixedSizeChunks(
+ algorithm, provider, keyPair.getPublic(), message, goodSigBytes, 3);
+
+ byte[] messageWithBitFlip = message.clone();
+ messageWithBitFlip[messageWithBitFlip.length / 2] ^= 1;
+ assertSignatureDoesNotVerifyOneShot(
+ algorithm, provider, keyPair.getPublic(), messageWithBitFlip, goodSigBytes);
+
+ byte[] goodSigWithBitFlip = goodSigBytes.clone();
+ goodSigWithBitFlip[goodSigWithBitFlip.length / 2] ^= 1;
+ assertSignatureDoesNotVerifyOneShot(
+ algorithm, provider, keyPair.getPublic(), message, goodSigWithBitFlip);
+
+ // Sign the message in one go
+ Signature signature = Signature.getInstance(algorithm);
+ signature.initSign(keyPair.getPrivate());
+ assertSame(provider, signature.getProvider());
+ signature.update(message);
+ byte[] generatedSigBytes = signature.sign();
+ boolean deterministicSignatureScheme =
+ algorithm.toLowerCase().endsWith("withrsa");
+ if (deterministicSignatureScheme) {
+ MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
+ } else {
+ if (Math.abs(goodSigBytes.length - generatedSigBytes.length) > 2) {
+ fail("Generated signature expected to be between "
+ + (goodSigBytes.length - 2) + " and "
+ + (goodSigBytes.length + 2) + " bytes long, but was: "
+ + generatedSigBytes.length + " bytes: "
+ + HexEncoding.encode(generatedSigBytes));
+ }
+
+ // Assert that the signature verifies using our own Provider
+ assertSignatureVerifiesOneShot(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
+ assertSignatureVerifiesFedOneByteAtATime(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
+ assertSignatureVerifiesFedUsingFixedSizeChunks(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes,
+ 3);
+
+ // Assert that the signature verifies using whatever Provider is chosen by JCA
+ // by default for this signature algorithm and public key.
+ assertSignatureVerifiesOneShot(
+ algorithm, keyPair.getPublic(), message, generatedSigBytes);
+ }
+
+ // Sign the message by feeding it into the Signature one byte at a time
+ signature = Signature.getInstance(signature.getAlgorithm());
+ signature.initSign(keyPair.getPrivate());
+ for (int i = 0; i < message.length; i++) {
+ signature.update(message[i]);
+ }
+ generatedSigBytes = signature.sign();
+ if (deterministicSignatureScheme) {
+ MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
+ } else {
+ if (Math.abs(goodSigBytes.length - generatedSigBytes.length) > 2) {
+ fail("Generated signature expected to be between "
+ + (goodSigBytes.length - 2) + " and "
+ + (goodSigBytes.length + 2) + " bytes long, but was: "
+ + generatedSigBytes.length + " bytes: "
+ + HexEncoding.encode(generatedSigBytes));
+ }
+ // Assert that the signature verifies using our own Provider
+ assertSignatureVerifiesOneShot(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
+ assertSignatureVerifiesFedOneByteAtATime(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
+ assertSignatureVerifiesFedUsingFixedSizeChunks(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes,
+ 3);
+
+ // Assert that the signature verifies using whatever Provider is chosen by JCA
+ // by default for this signature algorithm and public key.
+ assertSignatureVerifiesOneShot(
+ algorithm, keyPair.getPublic(), message, generatedSigBytes);
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ private static byte[] generateLargeKatMsg(byte[] seed, int msgSizeBytes) throws Exception {
+ byte[] result = new byte[msgSizeBytes];
+ MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ int resultOffset = 0;
+ int resultRemaining = msgSizeBytes;
+ while (resultRemaining > 0) {
+ seed = digest.digest(seed);
+ int chunkSize = Math.min(seed.length, resultRemaining);
+ System.arraycopy(seed, 0, result, resultOffset, chunkSize);
+ resultOffset += chunkSize;
+ resultRemaining -= chunkSize;
+ }
+ return result;
+ }
+
+ public void testLongMsgKat() throws Exception {
+ Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+ byte[] message = generateLargeKatMsg(LONG_MSG_KAT_SEED, LONG_MSG_KAT_SIZE_BYTES);
+
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ assertNotNull(provider);
+ for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+ KeyPair keyPair = getKeyPairForSignatureAlgorithm(algorithm, keyPairs);
+
+ try {
+ if (algorithm.toLowerCase(Locale.US).startsWith("nonewithrsa")) {
+ // This algorithm cannot accept large messages
+ Signature signature = Signature.getInstance(algorithm);
+ signature.initSign(keyPair.getPrivate());
+ assertSame(provider, signature.getProvider());
+ try {
+ signature.update(message);
+ signature.sign();
+ fail();
+ } catch (SignatureException expected) {}
+
+ // Bogus signature generated using SHA-256 digest -- shouldn't because the
+ // message is too long (and should not be digested/hashed) and because the
+ // signature uses the wrong digest/hash.
+ byte[] sigBytes = SHORT_MSG_KAT_SIGNATURES.get(
+ "SHA256" + algorithm.substring("NONE".length()));
+ assertNotNull(sigBytes);
+ signature = Signature.getInstance(algorithm);
+ signature.initVerify(keyPair.getPublic());
+ try {
+ signature.update(message);
+ signature.verify(sigBytes);
+ fail();
+ } catch (SignatureException expected) {}
+ continue;
+ }
+
+ byte[] goodSigBytes = LONG_MSG_KAT_SIGNATURES.get(algorithm);
+ assertNotNull(goodSigBytes);
+
+ // Assert that AndroidKeyStore provider can verify the known good signature.
+ assertSignatureVerifiesOneShot(
+ algorithm, provider, keyPair.getPublic(), message, goodSigBytes);
+ assertSignatureVerifiesFedUsingFixedSizeChunks(
+ algorithm, provider, keyPair.getPublic(), message, goodSigBytes, 718871);
+
+ // Sign the message in one go
+ Signature signature = Signature.getInstance(algorithm);
+ signature.initSign(keyPair.getPrivate());
+ assertSame(provider, signature.getProvider());
+ signature.update(message);
+ byte[] generatedSigBytes = signature.sign();
+ boolean deterministicSignatureScheme =
+ algorithm.toLowerCase().endsWith("withrsa");
+ if (deterministicSignatureScheme) {
+ MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
+ } else {
+ if (Math.abs(goodSigBytes.length - generatedSigBytes.length) > 2) {
+ fail("Generated signature expected to be between "
+ + (goodSigBytes.length - 2) + " and "
+ + (goodSigBytes.length + 2) + " bytes long, but was: "
+ + generatedSigBytes.length + " bytes: "
+ + HexEncoding.encode(generatedSigBytes));
+ }
+
+ // Assert that the signature verifies using our own Provider
+ assertSignatureVerifiesOneShot(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
+ assertSignatureVerifiesFedUsingFixedSizeChunks(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes,
+ 718871);
+
+ // Assert that the signature verifies using whatever Provider is chosen by JCA
+ // by default for this signature algorithm and public key.
+ assertSignatureVerifiesOneShot(
+ algorithm, keyPair.getPublic(), message, generatedSigBytes);
+ }
+
+ // Sign the message by feeding it into the Signature one byte at a time
+ signature = Signature.getInstance(signature.getAlgorithm());
+ generatedSigBytes = generateSignatureFedUsingFixedSizeChunks(
+ algorithm, provider, keyPair.getPrivate(), message, 444307);
+ signature.initSign(keyPair.getPrivate());
+ if (deterministicSignatureScheme) {
+ MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
+ } else {
+ if (Math.abs(goodSigBytes.length - generatedSigBytes.length) > 2) {
+ fail("Generated signature expected to be between "
+ + (goodSigBytes.length - 2) + " and "
+ + (goodSigBytes.length + 2) + " bytes long, but was: "
+ + generatedSigBytes.length + " bytes: "
+ + HexEncoding.encode(generatedSigBytes));
+ }
+ // Assert that the signature verifies using our own Provider
+ assertSignatureVerifiesOneShot(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
+ assertSignatureVerifiesFedUsingFixedSizeChunks(
+ algorithm, provider, keyPair.getPublic(), message, generatedSigBytes,
+ 718871);
+
+ // Assert that the signature verifies using whatever Provider is chosen by JCA
+ // by default for this signature algorithm and public key.
+ assertSignatureVerifiesOneShot(
+ algorithm, keyPair.getPublic(), message, generatedSigBytes);
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testInitVerifySucceedsDespiteMissingAuthorizations() throws Exception {
+ KeyProtection spec = new KeyProtection.Builder(0).build();
+ assertInitVerifySucceeds("SHA256withECDSA", spec);
+ assertInitVerifySucceeds("SHA256withRSA", spec);
+ assertInitVerifySucceeds("SHA256withRSA/PSS", spec);
+ }
+
+ public void testInitSignFailsWhenNotAuthorizedToSign() throws Exception {
+ KeyProtection.Builder good = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_NONE);
+ int badPurposes = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
+ | KeyProperties.PURPOSE_VERIFY;
+
+ assertInitSignSucceeds("SHA256withECDSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA256withECDSA",
+ TestUtils.buildUpon(good, badPurposes).build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ assertInitSignSucceeds("SHA256withRSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA256withRSA",
+ TestUtils.buildUpon(good, badPurposes).build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+ assertInitSignSucceeds("SHA256withRSA/PSS", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA256withRSA/PSS",
+ TestUtils.buildUpon(good, badPurposes).build());
+ }
+
+ public void testInitSignFailsWhenDigestNotAuthorized() throws Exception {
+ KeyProtection.Builder good = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_SHA384);
+ String badDigest = KeyProperties.DIGEST_SHA256;
+
+ assertInitSignSucceeds("SHA384withECDSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA384withECDSA",
+ TestUtils.buildUpon(good).setDigests(badDigest).build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ assertInitSignSucceeds("SHA384withRSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA384withRSA",
+ TestUtils.buildUpon(good).setDigests(badDigest).build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+ assertInitSignSucceeds("SHA384withRSA/PSS", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA384withRSA/PSS",
+ TestUtils.buildUpon(good).setDigests(badDigest).build());
+ }
+
+ public void testInitSignFailsWhenPaddingNotAuthorized() throws Exception {
+ KeyProtection.Builder good = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_SHA512);
+
+ // Does not apply to ECDSA because it doesn't any signature padding schemes
+ // assertInitSignThrowsInvalidKeyException("SHA256withECDSA", builder.build());
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ assertInitSignSucceeds("SHA512withRSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA512withRSA",
+ TestUtils.buildUpon(good)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
+ .build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+ assertInitSignSucceeds("SHA512withRSA/PSS", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA512withRSA/PSS",
+ TestUtils.buildUpon(good)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .build());
+ }
+
+ public void testInitSignFailsWhenKeyNotYetValid() throws Exception {
+ KeyProtection.Builder good = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_SHA224);
+ Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+ assertInitSignSucceeds("SHA224withECDSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA224withECDSA",
+ TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ assertInitSignSucceeds("SHA224withRSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA224withRSA",
+ TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+ assertInitSignSucceeds("SHA224withRSA/PSS", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS",
+ TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+ }
+
+ public void testInitSignFailsWhenKeyNoLongerValid() throws Exception {
+ KeyProtection.Builder good = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_SHA224);
+ Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+ assertInitSignSucceeds("SHA224withECDSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA224withECDSA",
+ TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ assertInitSignSucceeds("SHA224withRSA", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA224withRSA",
+ TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
+
+ good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+ assertInitSignSucceeds("SHA224withRSA/PSS", good.build());
+ assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS",
+ TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
+ }
+
+ private void assertInitVerifySucceeds(
+ String signatureAlgorithm,
+ KeyProtection keyProtection) throws Exception {
+ int[] resIds = getDefaultKeyAndCertResIds(signatureAlgorithm);
+ assertInitVerifySucceeds(
+ signatureAlgorithm,
+ resIds[0],
+ resIds[1],
+ keyProtection);
+ }
+
+ private void assertInitVerifySucceeds(
+ String signatureAlgorithm,
+ int privateKeyResId,
+ int certResId,
+ KeyProtection keyProtection) throws Exception {
+ PublicKey publicKey = TestUtils.importIntoAndroidKeyStore(
+ "test1",
+ TestUtils.getRawResPrivateKey(getContext(), privateKeyResId),
+ TestUtils.getRawResX509Certificate(getContext(), certResId),
+ keyProtection)
+ .getPublic();
+ Signature signature = Signature.getInstance(signatureAlgorithm);
+ signature.initVerify(publicKey);
+ }
+
+ private void assertInitSignSucceeds(
+ String signatureAlgorithm,
+ KeyProtection keyProtection) throws Exception {
+ int[] resIds = getDefaultKeyAndCertResIds(signatureAlgorithm);
+ assertInitSignSucceeds(
+ signatureAlgorithm,
+ resIds[0],
+ resIds[1],
+ keyProtection);
+ }
+
+ private void assertInitSignSucceeds(
+ String signatureAlgorithm,
+ int privateKeyResId,
+ int certResId,
+ KeyProtection keyProtection) throws Exception {
+ PrivateKey privateKey = TestUtils.importIntoAndroidKeyStore(
+ "test1",
+ TestUtils.getRawResPrivateKey(getContext(), privateKeyResId),
+ TestUtils.getRawResX509Certificate(getContext(), certResId),
+ keyProtection)
+ .getPrivate();
+ Signature signature = Signature.getInstance(signatureAlgorithm);
+ signature.initSign(privateKey);
+ }
+
+ private void assertInitSignThrowsInvalidKeyException(
+ String signatureAlgorithm,
+ KeyProtection keyProtection) throws Exception {
+ assertInitSignThrowsInvalidKeyException(null, signatureAlgorithm, keyProtection);
+ }
+
+ private void assertInitSignThrowsInvalidKeyException(
+ String message,
+ String signatureAlgorithm,
+ KeyProtection keyProtection) throws Exception {
+ int[] resIds = getDefaultKeyAndCertResIds(signatureAlgorithm);
+ assertInitSignThrowsInvalidKeyException(
+ message,
+ signatureAlgorithm,
+ resIds[0],
+ resIds[1],
+ keyProtection);
+ }
+
+ private void assertInitSignThrowsInvalidKeyException(
+ String message,
+ String signatureAlgorithm,
+ int privateKeyResId,
+ int certResId,
+ KeyProtection keyProtection) throws Exception {
+ PrivateKey privateKey = TestUtils.importIntoAndroidKeyStore(
+ "test1",
+ TestUtils.getRawResPrivateKey(getContext(), privateKeyResId),
+ TestUtils.getRawResX509Certificate(getContext(), certResId),
+ keyProtection)
+ .getPrivate();
+ Signature signature = Signature.getInstance(signatureAlgorithm);
+ try {
+ signature.initSign(privateKey);
+ fail(message);
+ } catch (InvalidKeyException expected) {}
+ }
+
+ static int[] getDefaultKeyAndCertResIds(String signatureAlgorithm) {
+ String sigAlgLowerCase = signatureAlgorithm.toLowerCase();
+ if (sigAlgLowerCase.contains("ecdsa")) {
+ return new int[] {R.raw.ec_key1_pkcs8, R.raw.ec_key1_cert};
+ } else if (sigAlgLowerCase.contains("rsa")) {
+ return new int[] {R.raw.rsa_key1_pkcs8, R.raw.rsa_key1_cert};
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown signature algorithm: " + signatureAlgorithm);
+ }
+ }
+
+ private static String getKeyAlgorithmForSignatureAlgorithm(String signatureAlgorithm) {
+ String algLowerCase = signatureAlgorithm.toLowerCase();
+ if (algLowerCase.contains("withecdsa")) {
+ return KeyProperties.KEY_ALGORITHM_EC;
+ } else if (algLowerCase.contains("withrsa")) {
+ return KeyProperties.KEY_ALGORITHM_RSA;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported signature algorithm: " + signatureAlgorithm);
+ }
+ }
+
+ private static KeyPair getKeyPairForSignatureAlgorithm(String signatureAlgorithm,
+ Iterable<KeyPair> keyPairs) {
+ return TestUtils.getKeyPairForKeyAlgorithm(
+ getKeyAlgorithmForSignatureAlgorithm(signatureAlgorithm), keyPairs);
+ }
+
+ private Collection<KeyPair> importDefaultKatKeyPairs() throws Exception {
+ return Arrays.asList(
+ TestUtils.importIntoAndroidKeyStore(
+ "testRsa",
+ TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key1_pkcs8),
+ TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key1_cert),
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS)
+ .build()),
+ TestUtils.importIntoAndroidKeyStore(
+ "testEc",
+ TestUtils.getRawResPrivateKey(getContext(), R.raw.ec_key1_pkcs8),
+ TestUtils.getRawResX509Certificate(getContext(), R.raw.ec_key1_cert),
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_NONE)
+ .build())
+ );
+ }
+
+ private void assertSignatureVerifiesOneShot(
+ String algorithm,
+ PublicKey publicKey,
+ byte[] message,
+ byte[] signature) throws Exception {
+ assertSignatureVerifiesOneShot(algorithm, null, publicKey, message, signature);
+ }
+
+ private void assertSignatureVerifiesOneShot(
+ String algorithm,
+ Provider provider,
+ PublicKey publicKey,
+ byte[] message,
+ byte[] signature) throws Exception {
+ Signature sig = (provider != null)
+ ? Signature.getInstance(algorithm, provider) : Signature.getInstance(algorithm);
+ sig.initVerify(publicKey);
+ sig.update(message);
+ if (!sig.verify(signature)) {
+ fail("Signature did not verify. algorithm: " + algorithm
+ + ", provider: " + sig.getProvider().getName()
+ + ", signature (" + signature.length + " bytes): "
+ + HexEncoding.encode(signature));
+ }
+ }
+
+ private void assertSignatureDoesNotVerifyOneShot(
+ String algorithm,
+ Provider provider,
+ PublicKey publicKey,
+ byte[] message,
+ byte[] signature) throws Exception {
+ Signature sig = (provider != null)
+ ? Signature.getInstance(algorithm, provider) : Signature.getInstance(algorithm);
+ sig.initVerify(publicKey);
+ sig.update(message);
+ if (sig.verify(signature)) {
+ fail("Signature verified unexpectedly. algorithm: " + algorithm
+ + ", provider: " + sig.getProvider().getName()
+ + ", signature (" + signature.length + " bytes): "
+ + HexEncoding.encode(signature));
+ }
+ }
+
+ private void assertSignatureVerifiesFedOneByteAtATime(
+ String algorithm,
+ Provider provider,
+ PublicKey publicKey,
+ byte[] message,
+ byte[] signature) throws Exception {
+ Signature sig = (provider != null)
+ ? Signature.getInstance(algorithm, provider) : Signature.getInstance(algorithm);
+ sig.initVerify(publicKey);
+ for (int i = 0; i < message.length; i++) {
+ sig.update(message[i]);
+ }
+ if (!sig.verify(signature)) {
+ fail("Signature did not verify. algorithm: " + algorithm
+ + ", provider: " + sig.getProvider().getName()
+ + ", signature (" + signature.length + " bytes): "
+ + HexEncoding.encode(signature));
+ }
+ }
+
+ private byte[] generateSignatureFedUsingFixedSizeChunks(
+ String algorithm,
+ Provider expectedProvider,
+ PrivateKey privateKey,
+ byte[] message,
+ int chunkSizeBytes) throws Exception {
+ Signature signature = Signature.getInstance(algorithm);
+ signature.initSign(privateKey);
+ assertSame(expectedProvider, signature.getProvider());
+ int messageRemaining = message.length;
+ int messageOffset = 0;
+ while (messageRemaining > 0) {
+ int actualChunkSizeBytes = Math.min(chunkSizeBytes, messageRemaining);
+ signature.update(message, messageOffset, actualChunkSizeBytes);
+ messageOffset += actualChunkSizeBytes;
+ messageRemaining -= actualChunkSizeBytes;
+ }
+ return signature.sign();
+ }
+ private void assertSignatureVerifiesFedUsingFixedSizeChunks(
+ String algorithm,
+ Provider provider,
+ PublicKey publicKey,
+ byte[] message,
+ byte[] signature,
+ int chunkSizeBytes) throws Exception {
+ Signature sig = (provider != null)
+ ? Signature.getInstance(algorithm, provider) : Signature.getInstance(algorithm);
+ sig.initVerify(publicKey);
+ int messageRemaining = message.length;
+ int messageOffset = 0;
+ while (messageRemaining > 0) {
+ int actualChunkSizeBytes = Math.min(chunkSizeBytes, messageRemaining);
+ sig.update(message, messageOffset, actualChunkSizeBytes);
+ messageOffset += actualChunkSizeBytes;
+ messageRemaining -= actualChunkSizeBytes;
+ }
+ if (!sig.verify(signature)) {
+ fail("Signature did not verify. algorithm: " + algorithm
+ + ", provider: " + sig.getProvider().getName()
+ + ", signature (" + signature.length + " bytes): "
+ + HexEncoding.encode(signature));
+ }
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
new file mode 100644
index 0000000..02fbd1e
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import android.content.Context;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.test.MoreAsserts;
+import junit.framework.Assert;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.EllipticCurve;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.SecretKeySpec;
+
+abstract class TestUtils extends Assert {
+ private TestUtils() {}
+
+ /**
+ * Asserts the the key algorithm and algorithm-specific parameters of the two keys in the
+ * provided pair match.
+ */
+ static void assertKeyPairSelfConsistent(KeyPair keyPair) {
+ assertKeyPairSelfConsistent(keyPair.getPublic(), keyPair.getPrivate());
+ }
+
+ /**
+ * Asserts the the key algorithm and public algorithm-specific parameters of the two provided
+ * keys match.
+ */
+ static void assertKeyPairSelfConsistent(PublicKey publicKey, PrivateKey privateKey) {
+ assertNotNull(publicKey);
+ assertNotNull(privateKey);
+ assertEquals(publicKey.getAlgorithm(), privateKey.getAlgorithm());
+ String keyAlgorithm = publicKey.getAlgorithm();
+ if ("EC".equalsIgnoreCase(keyAlgorithm)) {
+ assertTrue("EC public key must be instanceof ECKey: "
+ + publicKey.getClass().getName(),
+ publicKey instanceof ECKey);
+ assertTrue("EC private key must be instanceof ECKey: "
+ + privateKey.getClass().getName(),
+ privateKey instanceof ECKey);
+ assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ "Private key must have the same EC parameters as public key",
+ ((ECKey) publicKey).getParams(), ((ECKey) privateKey).getParams());
+ } else if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
+ assertTrue("RSA public key must be instance of RSAKey: "
+ + publicKey.getClass().getName(),
+ publicKey instanceof RSAKey);
+ assertTrue("RSA private key must be instance of RSAKey: "
+ + privateKey.getClass().getName(),
+ privateKey instanceof RSAKey);
+ assertEquals("Private and public key must have the same RSA modulus",
+ ((RSAKey) publicKey).getModulus(), ((RSAKey) privateKey).getModulus());
+ } else {
+ fail("Unsuported key algorithm: " + keyAlgorithm);
+ }
+ }
+
+ private static int getKeySizeBits(Key key) {
+ if (key instanceof ECKey) {
+ return ((ECKey) key).getParams().getCurve().getField().getFieldSize();
+ } else if (key instanceof RSAKey) {
+ return ((RSAKey) key).getModulus().bitLength();
+ } else {
+ throw new IllegalArgumentException("Unsupported key type: " + key.getClass());
+ }
+ }
+
+ static void assertKeySize(int expectedSizeBits, KeyPair keyPair) {
+ assertEquals(expectedSizeBits, getKeySizeBits(keyPair.getPrivate()));
+ assertEquals(expectedSizeBits, getKeySizeBits(keyPair.getPublic()));
+ }
+
+ /**
+ * Asserts that the provided key pair is an Android Keystore key pair stored under the provided
+ * alias.
+ */
+ static void assertKeyStoreKeyPair(KeyStore keyStore, String alias, KeyPair keyPair) {
+ assertKeyMaterialExportable(keyPair.getPublic());
+ assertKeyMaterialNotExportable(keyPair.getPrivate());
+ assertTransparentKey(keyPair.getPublic());
+ assertOpaqueKey(keyPair.getPrivate());
+
+ KeyStore.Entry entry;
+ Certificate cert;
+ try {
+ entry = keyStore.getEntry(alias, null);
+ cert = keyStore.getCertificate(alias);
+ } catch (KeyStoreException | UnrecoverableEntryException | NoSuchAlgorithmException e) {
+ throw new RuntimeException("Failed to load entry: " + alias, e);
+ }
+ assertNotNull(entry);
+
+ assertTrue(entry instanceof KeyStore.PrivateKeyEntry);
+ KeyStore.PrivateKeyEntry privEntry = (KeyStore.PrivateKeyEntry) entry;
+ assertEquals(cert, privEntry.getCertificate());
+ assertTrue("Certificate must be an X.509 certificate: " + cert.getClass(),
+ cert instanceof X509Certificate);
+ final X509Certificate x509Cert = (X509Certificate) cert;
+
+ PrivateKey keystorePrivateKey = privEntry.getPrivateKey();
+ PublicKey keystorePublicKey = cert.getPublicKey();
+ assertEquals(keyPair.getPrivate(), keystorePrivateKey);
+ assertEquals(keyPair.getPublic(), keystorePublicKey);
+
+ assertEquals(
+ "Public key used to sign certificate should have the same algorithm as in KeyPair",
+ keystorePublicKey.getAlgorithm(), x509Cert.getPublicKey().getAlgorithm());
+
+ Certificate[] chain = privEntry.getCertificateChain();
+ if (chain.length == 0) {
+ fail("Empty certificate chain");
+ return;
+ }
+ assertEquals(cert, chain[0]);
+ }
+
+
+ private static void assertKeyMaterialExportable(Key key) {
+ if (key instanceof PublicKey) {
+ assertEquals("X.509", key.getFormat());
+ } else if (key instanceof PrivateKey) {
+ assertEquals("PKCS#8", key.getFormat());
+ } else if (key instanceof SecretKey) {
+ assertEquals("RAW", key.getFormat());
+ } else {
+ fail("Unsupported key type: " + key.getClass().getName());
+ }
+ byte[] encodedForm = key.getEncoded();
+ assertNotNull(encodedForm);
+ if (encodedForm.length == 0) {
+ fail("Empty encoded form");
+ }
+ }
+
+ private static void assertKeyMaterialNotExportable(Key key) {
+ assertEquals(null, key.getFormat());
+ assertEquals(null, key.getEncoded());
+ }
+
+ private static void assertOpaqueKey(Key key) {
+ assertFalse(key.getClass().getName() + " is a transparent key", isTransparentKey(key));
+ }
+
+ private static void assertTransparentKey(Key key) {
+ assertTrue(key.getClass().getName() + " is not a transparent key", isTransparentKey(key));
+ }
+
+ private static boolean isTransparentKey(Key key) {
+ if (key instanceof PrivateKey) {
+ return (key instanceof ECPrivateKey) || (key instanceof RSAPrivateKey);
+ } else if (key instanceof PublicKey) {
+ return (key instanceof ECPublicKey) || (key instanceof RSAPublicKey);
+ } else if (key instanceof SecretKey) {
+ return (key instanceof SecretKeySpec);
+ } else {
+ throw new IllegalArgumentException("Unsupported key type: " + key.getClass().getName());
+ }
+ }
+
+ static void assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ ECParameterSpec expected, ECParameterSpec actual) {
+ assertECParameterSpecEqualsIgnoreSeedIfNotPresent(null, expected, actual);
+ }
+
+ static void assertECParameterSpecEqualsIgnoreSeedIfNotPresent(String message,
+ ECParameterSpec expected, ECParameterSpec actual) {
+ EllipticCurve expectedCurve = expected.getCurve();
+ EllipticCurve actualCurve = actual.getCurve();
+ String msgPrefix = (message != null) ? message + ": " : "";
+ assertEquals(msgPrefix + "curve field", expectedCurve.getField(), actualCurve.getField());
+ assertEquals(msgPrefix + "curve A", expectedCurve.getA(), actualCurve.getA());
+ assertEquals(msgPrefix + "curve B", expectedCurve.getB(), actualCurve.getB());
+ assertEquals(msgPrefix + "order", expected.getOrder(), actual.getOrder());
+ assertEquals(msgPrefix + "generator",
+ expected.getGenerator(), actual.getGenerator());
+ assertEquals(msgPrefix + "cofactor", expected.getCofactor(), actual.getCofactor());
+
+ // If present, the seed must be the same
+ byte[] expectedSeed = expectedCurve.getSeed();
+ byte[] actualSeed = expectedCurve.getSeed();
+ if ((expectedSeed != null) && (actualSeed != null)) {
+ MoreAsserts.assertEquals(expectedSeed, actualSeed);
+ }
+ }
+
+ static KeyInfo getKeyInfo(Key key) throws InvalidKeySpecException, NoSuchAlgorithmException,
+ NoSuchProviderException {
+ if ((key instanceof PrivateKey) || (key instanceof PublicKey)) {
+ return KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore")
+ .getKeySpec(key, KeyInfo.class);
+ } else if (key instanceof SecretKey) {
+ return (KeyInfo) SecretKeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore")
+ .getKeySpec((SecretKey) key, KeyInfo.class);
+ } else {
+ throw new IllegalArgumentException("Unexpected key type: " + key.getClass());
+ }
+ }
+
+ static <T> void assertContentsInAnyOrder(Iterable<T> actual, T... expected) {
+ assertContentsInAnyOrder(null, actual, expected);
+ }
+
+ static <T> void assertContentsInAnyOrder(String message, Iterable<T> actual, T... expected) {
+ Map<T, Integer> actualFreq = getFrequencyTable(actual);
+ Map<T, Integer> expectedFreq = getFrequencyTable(expected);
+ if (actualFreq.equals(expectedFreq)) {
+ return;
+ }
+
+ Map<T, Integer> extraneousFreq = new HashMap<T, Integer>();
+ for (Map.Entry<T, Integer> actualEntry : actualFreq.entrySet()) {
+ int actualCount = actualEntry.getValue();
+ Integer expectedCount = expectedFreq.get(actualEntry.getKey());
+ int diff = actualCount - ((expectedCount != null) ? expectedCount : 0);
+ if (diff > 0) {
+ extraneousFreq.put(actualEntry.getKey(), diff);
+ }
+ }
+
+ Map<T, Integer> missingFreq = new HashMap<T, Integer>();
+ for (Map.Entry<T, Integer> expectedEntry : expectedFreq.entrySet()) {
+ int expectedCount = expectedEntry.getValue();
+ Integer actualCount = actualFreq.get(expectedEntry.getKey());
+ int diff = expectedCount - ((actualCount != null) ? actualCount : 0);
+ if (diff > 0) {
+ missingFreq.put(expectedEntry.getKey(), diff);
+ }
+ }
+
+ List<T> extraneous = frequencyTableToValues(extraneousFreq);
+ List<T> missing = frequencyTableToValues(missingFreq);
+ StringBuilder result = new StringBuilder();
+ String delimiter = "";
+ if (message != null) {
+ result.append(message).append(".");
+ delimiter = " ";
+ }
+ if (!missing.isEmpty()) {
+ result.append(delimiter).append("missing: " + missing);
+ delimiter = ", ";
+ }
+ if (!extraneous.isEmpty()) {
+ result.append(delimiter).append("extraneous: " + extraneous);
+ }
+ fail(result.toString());
+ }
+
+ private static <T> Map<T, Integer> getFrequencyTable(Iterable<T> values) {
+ Map<T, Integer> result = new HashMap<T, Integer>();
+ for (T value : values) {
+ Integer count = result.get(value);
+ if (count == null) {
+ count = 1;
+ } else {
+ count++;
+ }
+ result.put(value, count);
+ }
+ return result;
+ }
+
+ private static <T> Map<T, Integer> getFrequencyTable(T... values) {
+ Map<T, Integer> result = new HashMap<T, Integer>();
+ for (T value : values) {
+ Integer count = result.get(value);
+ if (count == null) {
+ count = 1;
+ } else {
+ count++;
+ }
+ result.put(value, count);
+ }
+ return result;
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static <T> List<T> frequencyTableToValues(Map<T, Integer> table) {
+ if (table.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List<T> result = new ArrayList<T>();
+ boolean comparableValues = true;
+ for (Map.Entry<T, Integer> entry : table.entrySet()) {
+ T value = entry.getKey();
+ if (!(value instanceof Comparable)) {
+ comparableValues = false;
+ }
+ int frequency = entry.getValue();
+ for (int i = 0; i < frequency; i++) {
+ result.add(value);
+ }
+ }
+
+ if (comparableValues) {
+ sortAssumingComparable(result);
+ }
+ return result;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static void sortAssumingComparable(List<?> values) {
+ Collections.sort((List<Comparable>)values);
+ }
+
+ static String[] toLowerCase(String... values) {
+ if (values == null) {
+ return null;
+ }
+ String[] result = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ String value = values[i];
+ result[i] = (value != null) ? value.toLowerCase() : null;
+ }
+ return result;
+ }
+
+ static Certificate generateSelfSignedCert(String keyAlgorithm) throws Exception {
+ KeyPairGenerator generator =
+ KeyPairGenerator.getInstance(keyAlgorithm, "AndroidKeyStore");
+ generator.initialize(new KeyGenParameterSpec.Builder(
+ "test1",
+ KeyProperties.PURPOSE_SIGN)
+ .build());
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ return keyStore.getCertificate("test1");
+ }
+
+ static PrivateKey getRawResPrivateKey(Context context, int resId) throws Exception {
+ byte[] pkcs8EncodedForm;
+ try (InputStream in = context.getResources().openRawResource(resId)) {
+ pkcs8EncodedForm = drain(in);
+ }
+ PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pkcs8EncodedForm);
+
+ try {
+ return KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
+ } catch (InvalidKeySpecException e) {
+ try {
+ return KeyFactory.getInstance("RSA").generatePrivate(privateKeySpec);
+ } catch (InvalidKeySpecException e2) {
+ throw new InvalidKeySpecException("The key is neither EC nor RSA", e);
+ }
+ }
+ }
+
+ static X509Certificate getRawResX509Certificate(Context context, int resId) throws Exception {
+ try (InputStream in = context.getResources().openRawResource(resId)) {
+ return (X509Certificate) CertificateFactory.getInstance("X.509")
+ .generateCertificate(in);
+ }
+ }
+
+ static KeyPair importIntoAndroidKeyStore(
+ String alias,
+ PrivateKey privateKey,
+ Certificate certificate,
+ KeyProtection keyProtection) throws Exception {
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ keyStore.setEntry(alias,
+ new KeyStore.PrivateKeyEntry(privateKey, new Certificate[] {certificate}),
+ keyProtection);
+ return new KeyPair(
+ keyStore.getCertificate(alias).getPublicKey(),
+ (PrivateKey) keyStore.getKey(alias, null));
+ }
+
+ static byte[] drain(InputStream in) throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ byte[] buffer = new byte[16 * 1024];
+ int chunkSize;
+ while ((chunkSize = in.read(buffer)) != -1) {
+ result.write(buffer, 0, chunkSize);
+ }
+ return result.toByteArray();
+ }
+
+ static KeyProtection.Builder buildUpon(
+ KeyProtection.Builder builder) {
+ return buildUponInternal(builder, null);
+ }
+
+ static KeyProtection.Builder buildUpon(
+ KeyProtection.Builder builder, int newPurposes) {
+ return buildUponInternal(builder, newPurposes);
+ }
+
+ private static KeyProtection.Builder buildUponInternal(
+ KeyProtection.Builder builder, Integer newPurposes) {
+ KeyProtection spec = builder.build();
+ int purposes = (newPurposes == null) ? spec.getPurposes() : newPurposes;
+ KeyProtection.Builder result = new KeyProtection.Builder(purposes);
+ result.setBlockModes(spec.getBlockModes());
+ if (spec.isDigestsSpecified()) {
+ result.setDigests(spec.getDigests());
+ }
+ result.setEncryptionPaddings(spec.getEncryptionPaddings());
+ result.setSignaturePaddings(spec.getSignaturePaddings());
+ result.setKeyValidityStart(spec.getKeyValidityStart());
+ result.setKeyValidityForOriginationEnd(spec.getKeyValidityForOriginationEnd());
+ result.setKeyValidityForConsumptionEnd(spec.getKeyValidityForConsumptionEnd());
+ result.setRandomizedEncryptionRequired(spec.isRandomizedEncryptionRequired());
+ result.setUserAuthenticationRequired(spec.isUserAuthenticationRequired());
+ result.setUserAuthenticationValidityDurationSeconds(
+ spec.getUserAuthenticationValidityDurationSeconds());
+ return result;
+ }
+
+ static KeyPair getKeyPairForKeyAlgorithm(String keyAlgorithm, Iterable<KeyPair> keyPairs) {
+ for (KeyPair keyPair : keyPairs) {
+ if (keyAlgorithm.equalsIgnoreCase(keyPair.getPublic().getAlgorithm())) {
+ return keyPair;
+ }
+ }
+ throw new IllegalArgumentException("No KeyPair for key algorithm " + keyAlgorithm);
+ }
+}
diff --git a/tests/tests/media/res/raw/gb18030_5.mp3 b/tests/tests/media/res/raw/gb18030_5.mp3
deleted file mode 100644
index 70f76ce..0000000
--- a/tests/tests/media/res/raw/gb18030_5.mp3
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 0edbc61..34469b79 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -327,20 +327,42 @@
* Queue some frames with an EOS on the last one. Test that we have decoded as many
* frames as we queued. This tests the EOS handling of the codec to see if all queued
* (and out-of-order) frames are actually decoded and returned.
+ *
+ * Also test flushing prior to sending CSD, and immediately after sending CSD.
*/
class EarlyEosTest extends ActivityTest {
+ // using bitfields to create a directed state graph that terminates at FLUSH_NEVER
+ static final int FLUSH_BEFORE_CSD = (1 << 1);
+ static final int FLUSH_AFTER_CSD = (1 << 0);
+ static final int FLUSH_NEVER = 0;
+
public boolean isValid(Codec c) {
return getFormat(c) != null;
}
public void addTests(TestList tests, final Codec c) {
- for (int i = NUM_FRAMES / 2; i > 0; i--) {
+ int state = FLUSH_BEFORE_CSD;
+ for (int i = NUM_FRAMES / 2; i > 0; --i, state >>= 1) {
final int queuedFrames = i;
+ final int earlyFlushMode = state;
tests.add(
new Step("testing early EOS at " + queuedFrames, this, c) {
public void run() {
Decoder decoder = new Decoder(c.name);
try {
- decoder.configureAndStart(stepFormat(), stepSurface());
+ MediaFormat fmt = stepFormat();
+ MediaFormat configFmt = fmt;
+ if (earlyFlushMode == FLUSH_BEFORE_CSD) {
+ // flush before CSD requires not submitting CSD with configure
+ configFmt = Media.removeCSD(fmt);
+ }
+ decoder.configureAndStart(configFmt, stepSurface());
+ if (earlyFlushMode != FLUSH_NEVER) {
+ decoder.flush();
+ // We must always queue CSD after a flush that is potentially
+ // before we receive output format has changed. This should
+ // work even after we receive the format change.
+ decoder.queueCSD(fmt);
+ }
int decodedFrames = -decoder.queueInputBufferRange(
stepMedia(),
0 /* startFrame */,
@@ -817,6 +839,7 @@
class Decoder implements MediaCodec.OnFrameRenderedListener {
private final static String TAG = "AdaptiveDecoder";
final long kTimeOutUs = 5000;
+ final long kCSDTimeOutUs = 1000000;
MediaCodec mCodec;
ByteBuffer[] mInputBuffers;
ByteBuffer[] mOutputBuffers;
@@ -1029,6 +1052,31 @@
return queueInputBufferRange(media,frameStartIx,frameEndIx,sendEosAtEnd,waitForEos,0);
}
+ public void queueCSD(MediaFormat format) {
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ for (int csdIx = 0; ; ++csdIx) {
+ ByteBuffer csdBuf = format.getByteBuffer("csd-" + csdIx);
+ if (csdBuf == null) {
+ break;
+ }
+
+ int ix = mCodec.dequeueInputBuffer(kCSDTimeOutUs);
+ if (ix < 0) {
+ fail("Could not dequeue input buffer for CSD #" + csdIx);
+ return;
+ }
+
+ ByteBuffer buf = mInputBuffers[ix];
+ buf.clear();
+ buf.put((ByteBuffer)csdBuf.clear());
+ Log.v(TAG, "queue-CSD { [" + buf.position() + "]=" +
+ byteBufferToString(buf, 0, 16) + "} => #" + ix);
+ mCodec.queueInputBuffer(
+ ix, 0 /* offset */, buf.position(), 0 /* timeUs */,
+ MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+ }
+ }
+
public int queueInputBufferRange(
Media media, int frameStartIx, int frameEndIx, boolean sendEosAtEnd,
boolean waitForEos, long adjustTimeUs) {
@@ -1174,6 +1222,31 @@
return mFormat;
}
+ public static MediaFormat removeCSD(MediaFormat orig) {
+ MediaFormat copy = MediaFormat.createVideoFormat(
+ orig.getString(orig.KEY_MIME),
+ orig.getInteger(orig.KEY_WIDTH), orig.getInteger(orig.KEY_HEIGHT));
+ for (String k : new String[] {
+ orig.KEY_FRAME_RATE, orig.KEY_MAX_WIDTH, orig.KEY_MAX_HEIGHT,
+ orig.KEY_MAX_INPUT_SIZE
+ }) {
+ if (orig.containsKey(k)) {
+ try {
+ copy.setInteger(k, orig.getInteger(k));
+ } catch (ClassCastException e) {
+ try {
+ copy.setFloat(k, orig.getFloat(k));
+ } catch (ClassCastException e2) {
+ // Could not copy value. Don't fail here, as having non-standard
+ // value types for defined keys is permissible by the media API
+ // for optional keys.
+ }
+ }
+ }
+ }
+ return copy;
+ }
+
public MediaFormat getAdaptiveFormat(int width, int height) {
mAdaptiveFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, width);
mAdaptiveFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, height);
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 56d2b68..6ba1aeb 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -416,6 +416,39 @@
assertEquals(TEST_NAME + ": state", expectedState, observedState);
}
+ // Test AudioRecord to ensure we can build after a failure.
+ public void testAudioRecordBufferSize() throws Exception {
+ // constants for test
+ final String TEST_NAME = "testAudioRecordBufferSize";
+
+ // use builder with parameters that should fail
+ final int superBigBufferSize = 1 << 28;
+ try {
+ final AudioRecord record = new AudioRecord.Builder()
+ .setBufferSizeInBytes(superBigBufferSize)
+ .build();
+ record.release();
+ fail(TEST_NAME + ": should throw exception on failure");
+ } catch (UnsupportedOperationException e) {
+ ;
+ }
+
+ // we should be able to create again with minimum buffer size
+ final int verySmallBufferSize = 2 * 3 * 4; // frame size multiples
+ final AudioRecord record2 = new AudioRecord.Builder()
+ .setBufferSizeInBytes(verySmallBufferSize)
+ .build();
+
+ final int observedState2 = record2.getState();
+ final int observedBufferSize2 = record2.getBufferSizeInFrames();
+ record2.release();
+
+ // succeeds for minimum buffer size
+ assertEquals(TEST_NAME + ": state", AudioRecord.STATE_INITIALIZED, observedState2);
+ // should force the minimum size buffer which is > 0
+ assertTrue(TEST_NAME + ": buffer frame count", observedBufferSize2 > 0);
+ }
+
public void testSynchronizedRecord() throws Exception {
if (!hasMicrophone()) {
return;
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index dfb83eb..4c03183 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -2214,6 +2214,39 @@
track.release();
}
+ // Test AudioTrack to ensure we can build after a failure.
+ public void testAudioTrackBufferSize() throws Exception {
+ // constants for test
+ final String TEST_NAME = "testAudioTrackBufferSize";
+
+ // use builder with parameters that should fail
+ final int superBigBufferSize = 1 << 28;
+ try {
+ final AudioTrack track = new AudioTrack.Builder()
+ .setBufferSizeInBytes(superBigBufferSize)
+ .build();
+ track.release();
+ fail(TEST_NAME + ": should throw exception on failure");
+ } catch (UnsupportedOperationException e) {
+ ;
+ }
+
+ // we should be able to create again with minimum buffer size
+ final int verySmallBufferSize = 2 * 3 * 4; // frame size multiples
+ final AudioTrack track2 = new AudioTrack.Builder()
+ .setBufferSizeInBytes(verySmallBufferSize)
+ .build();
+
+ final int observedState2 = track2.getState();
+ final int observedBufferSize2 = track2.getBufferSizeInFrames();
+ track2.release();
+
+ // succeeds for minimum buffer size
+ assertEquals(TEST_NAME + ": state", AudioTrack.STATE_INITIALIZED, observedState2);
+ // should force the minimum size buffer which is > 0
+ assertTrue(TEST_NAME + ": buffer frame count", observedBufferSize2 > 0);
+ }
+
/* Do not run in JB-MR1. will be re-opened in the next platform release.
public void testResourceLeakage() throws Exception {
final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
index c19102b..5b4d1aa 100644
--- a/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
+++ b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
@@ -24,6 +24,9 @@
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.audiofx.LoudnessEnhancer;
+import java.util.UUID;
+import android.media.audiofx.Visualizer;
+import android.media.audiofx.Visualizer.MeasurementPeakRms;
import android.os.Looper;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -77,6 +80,113 @@
}
}
+ //Test case 2.0: test loudness gain change in audio
+ public void test2_0MeasureGainChange() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ AudioEffect vc = null;
+ Visualizer visualizer = null;
+ MediaPlayer mp = null;
+ try {
+ // this test will play a 1kHz sine wave with peaks at -40dB, and apply 6 db gain
+ // using loudness enhancement
+ mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
+ final int LOUDNESS_GAIN = 600;
+ final int MAX_MEASUREMENT_ERROR_MB = 200;
+ assertNotNull("null MediaPlayer", mp);
+
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+ 0,
+ mp.getAudioSessionId());
+ vc.setEnabled(true);
+
+ AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull("null AudioManager", am);
+ int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
+ int sessionId = mp.getAudioSessionId();
+
+ getLoudnessEnhancer(sessionId);
+ visualizer = new Visualizer(sessionId);
+
+ mp.setLooping(true);
+ mp.start();
+
+ visualizer.setEnabled(true);
+ assertTrue("visualizer not enabled", visualizer.getEnabled());
+ Thread.sleep(100);
+ int status = visualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+ assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
+ Visualizer.SUCCESS, status);
+ // make sure we're playing long enough so the measurement is valid
+ int currentPosition = mp.getCurrentPosition();
+ int maxTry = 100;
+ int tryCount = 0;
+ while (currentPosition < 200 && tryCount < maxTry) {
+ Thread.sleep(50);
+ currentPosition = mp.getCurrentPosition();
+ tryCount++;
+ }
+ assertTrue("MediaPlayer not ready", tryCount < maxTry);
+
+ MeasurementPeakRms measurement = new MeasurementPeakRms();
+ status = visualizer.getMeasurementPeakRms(measurement);
+
+ //run for a new set of 3 seconds, get new measurement
+ mLE.setTargetGain(LOUDNESS_GAIN);
+ assertEquals("target gain differs from value set", (float)LOUDNESS_GAIN,
+ mLE.getTargetGain());
+
+ mLE.setEnabled(true);
+
+ visualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+ currentPosition = mp.getCurrentPosition();
+ maxTry = 5;
+ tryCount = 0;
+ while (tryCount < maxTry) {
+ Thread.sleep(50);
+ tryCount++;
+ }
+
+ MeasurementPeakRms measurement2 = new MeasurementPeakRms();
+ status = visualizer.getMeasurementPeakRms(measurement2);
+
+ //compare both measurements
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
+ assertEquals("getMeasurementPeakRms() reports failure",
+ Visualizer.SUCCESS, status);
+ Log.i("VisTest", "peak="+measurement.mPeak+" rms="+measurement.mRms);
+ Log.i("VisTest", "peak2="+measurement2.mPeak+" rms2="+measurement2.mRms);
+
+ int deltaPeak = Math.abs(measurement2.mPeak - (measurement.mPeak + LOUDNESS_GAIN) );
+ assertTrue("peak deviation in mB = "+deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
+
+ } catch (IllegalStateException e) {
+ fail("method called in wrong state");
+ } catch (InterruptedException e) {
+ fail("sleep() interrupted");
+ } finally {
+ if (mp != null) {
+ mp.stop();
+ mp.release();
+ }
+
+ if (vc != null)
+ vc.release();
+
+ if (visualizer != null)
+ visualizer.release();
+
+ releaseLoudnessEnhancer();
+ }
+ }
+
//-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 90ea714..bb05ea0 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -21,6 +21,8 @@
import android.content.res.AssetFileDescriptor;
import android.cts.util.MediaUtils;
import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodec.CodecException;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
@@ -28,13 +30,20 @@
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel;
import android.opengl.GLES20;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.test.AndroidTestCase;
import android.util.Log;
import android.view.Surface;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* General MediaCodec tests.
@@ -457,6 +466,241 @@
}
}
+ public void testReleaseAfterFlush() throws IOException, InterruptedException {
+ CountDownLatch buffersExhausted = null;
+ CountDownLatch codecFlushed = null;
+ AtomicInteger numBuffers = null;
+
+ // sync flush from same thread
+ MediaCodec encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+ runReleaseAfterFlush(encoder, buffersExhausted, codecFlushed, numBuffers);
+
+ // sync flush from different thread
+ encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+ buffersExhausted = new CountDownLatch(1);
+ codecFlushed = new CountDownLatch(1);
+ numBuffers = new AtomicInteger();
+ Thread flushThread = new FlushThread(encoder, buffersExhausted, codecFlushed);
+ flushThread.start();
+ runReleaseAfterFlush(encoder, buffersExhausted, codecFlushed, numBuffers);
+ flushThread.join();
+
+ // async
+ // This value is calculated in getOutputBufferIndices by calling dequeueOutputBuffer
+ // with a fixed timeout until buffers are exhausted; it is possible that random timing
+ // in dequeueOutputBuffer can result in a smaller `nBuffs` than the max possible value.
+ int nBuffs = numBuffers.get();
+ HandlerThread callbackThread = new HandlerThread("ReleaseAfterFlushCallbackThread");
+ callbackThread.start();
+ Handler handler = new Handler(callbackThread.getLooper());
+
+ // async flush from same thread
+ encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+ buffersExhausted = null;
+ codecFlushed = null;
+ ReleaseAfterFlushCallback callback =
+ new ReleaseAfterFlushCallback(encoder, buffersExhausted, codecFlushed, nBuffs);
+ encoder.setCallback(callback, handler); // setCallback before configure, which is called in run
+ callback.run(); // drive input on main thread
+
+ // async flush from different thread
+ encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+ buffersExhausted = new CountDownLatch(1);
+ codecFlushed = new CountDownLatch(1);
+ callback = new ReleaseAfterFlushCallback(encoder, buffersExhausted, codecFlushed, nBuffs);
+ encoder.setCallback(callback, handler);
+ flushThread = new FlushThread(encoder, buffersExhausted, codecFlushed);
+ flushThread.start();
+ callback.run();
+ flushThread.join();
+
+ callbackThread.quitSafely();
+ callbackThread.join();
+ }
+
+ private static class FlushThread extends Thread {
+ final MediaCodec mEncoder;
+ final CountDownLatch mBuffersExhausted;
+ final CountDownLatch mCodecFlushed;
+
+ FlushThread(MediaCodec encoder, CountDownLatch buffersExhausted,
+ CountDownLatch codecFlushed) {
+ mEncoder = encoder;
+ mBuffersExhausted = buffersExhausted;
+ mCodecFlushed = codecFlushed;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mBuffersExhausted.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.w(TAG, "buffersExhausted wait interrupted; flushing immediately.", e);
+ }
+ mEncoder.flush();
+ mCodecFlushed.countDown();
+ }
+ }
+
+ private static class ReleaseAfterFlushCallback extends MediaCodec.Callback implements Runnable {
+ final MediaCodec mEncoder;
+ final CountDownLatch mBuffersExhausted, mCodecFlushed;
+ final int mNumBuffersBeforeFlush;
+
+ CountDownLatch mStopInput = new CountDownLatch(1);
+ List<Integer> mOutputBufferIndices = new ArrayList<>();
+
+ ReleaseAfterFlushCallback(MediaCodec encoder,
+ CountDownLatch buffersExhausted,
+ CountDownLatch codecFlushed,
+ int numBuffersBeforeFlush) {
+ mEncoder = encoder;
+ mBuffersExhausted = buffersExhausted;
+ mCodecFlushed = codecFlushed;
+ mNumBuffersBeforeFlush = numBuffersBeforeFlush;
+ }
+
+ @Override
+ public void onInputBufferAvailable(MediaCodec codec, int index) {
+ fail(codec + " onInputBufferAvailable " + index);
+ }
+
+ @Override
+ public void onOutputBufferAvailable(MediaCodec codec, int index, BufferInfo info) {
+ mOutputBufferIndices.add(index);
+ if (mOutputBufferIndices.size() == mNumBuffersBeforeFlush) {
+ releaseAfterFlush(codec, mOutputBufferIndices, mBuffersExhausted, mCodecFlushed);
+ mStopInput.countDown();
+ }
+ }
+
+ @Override
+ public void onError(MediaCodec codec, CodecException e) {
+ Log.e(TAG, codec + " onError", e);
+ fail(codec + " onError " + e.getMessage());
+ }
+
+ @Override
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ Log.v(TAG, codec + " onOutputFormatChanged " + format);
+ }
+
+ @Override
+ public void run() {
+ InputSurface inputSurface = null;
+ try {
+ inputSurface = initCodecAndSurface(mEncoder);
+ do {
+ GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ inputSurface.swapBuffers();
+ } while (!mStopInput.await(TIMEOUT_USEC, TimeUnit.MICROSECONDS));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.w(TAG, "mEncoder input frames interrupted/stopped", e);
+ } finally {
+ cleanupCodecAndSurface(mEncoder, inputSurface);
+ }
+ }
+ }
+
+ private static void runReleaseAfterFlush(
+ MediaCodec encoder,
+ CountDownLatch buffersExhausted,
+ CountDownLatch codecFlushed,
+ AtomicInteger numBuffers) {
+ InputSurface inputSurface = null;
+ try {
+ inputSurface = initCodecAndSurface(encoder);
+ List<Integer> outputBufferIndices = getOutputBufferIndices(encoder, inputSurface);
+ if (numBuffers != null) {
+ numBuffers.set(outputBufferIndices.size());
+ }
+ releaseAfterFlush(encoder, outputBufferIndices, buffersExhausted, codecFlushed);
+ } finally {
+ cleanupCodecAndSurface(encoder, inputSurface);
+ }
+ }
+
+ private static InputSurface initCodecAndSurface(MediaCodec encoder) {
+ InputSurface inputSurface;
+ CodecInfo info = getAvcSupportedFormatInfo();
+ MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, info.mMaxW, info.mMaxH);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, info.mBitRate);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, info.mFps);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ OutputSurface outputSurface = new OutputSurface(1, 1);
+ encoder.configure(format, outputSurface.getSurface(), null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ inputSurface = new InputSurface(encoder.createInputSurface());
+ inputSurface.makeCurrent();
+ encoder.start();
+ return inputSurface;
+ }
+
+ private static void cleanupCodecAndSurface(MediaCodec encoder, InputSurface inputSurface) {
+ if (encoder != null) {
+ encoder.stop();
+ encoder.release();
+ }
+
+ if (inputSurface != null) {
+ inputSurface.release();
+ }
+ }
+
+ private static List<Integer> getOutputBufferIndices(MediaCodec encoder, InputSurface inputSurface) {
+ boolean feedMoreFrames;
+ MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+ List<Integer> indices = new ArrayList<>();
+ do {
+ feedMoreFrames = indices.isEmpty();
+ GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ inputSurface.swapBuffers();
+ // dequeue buffers until not available
+ int index = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
+ while (index >= 0) {
+ feedMoreFrames = true;
+ indices.add(index);
+ index = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC_SHORT);
+ }
+ } while (feedMoreFrames);
+ assertFalse(indices.isEmpty());
+ return indices;
+ }
+
+ private static void releaseAfterFlush(
+ MediaCodec encoder,
+ List<Integer> outputBufferIndices,
+ CountDownLatch buffersExhausted,
+ CountDownLatch codecFlushed) {
+ if (buffersExhausted == null) {
+ // flush from same thread
+ encoder.flush();
+ } else {
+ assertNotNull(codecFlushed);
+ buffersExhausted.countDown();
+ try {
+ codecFlushed.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.w(TAG, "codecFlushed wait interrupted; releasing buffers immediately.", e);
+ }
+ }
+
+ for (int index : outputBufferIndices) {
+ try {
+ encoder.releaseOutputBuffer(index, true);
+ fail("MediaCodec releaseOutputBuffer after flush() does not throw exception");
+ } catch (MediaCodec.CodecException e) {
+ // Expected
+ }
+ }
+ }
+
/**
* Tests:
* <br> dequeueInputBuffer() fails when encoder configured with an input Surface
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index a31408c..54e6ef1 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -20,10 +20,13 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
+import android.cts.util.MediaUtils;
import android.media.MediaDataSource;
import android.media.MediaMetadataRetriever;
import android.test.AndroidTestCase;
+import java.io.IOException;
+
public class MediaMetadataRetrieverTest extends AndroidTestCase {
protected Resources mResources;
protected MediaMetadataRetriever mRetriever;
@@ -151,16 +154,20 @@
}
private void testThumbnail(int resId) {
+ if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")) {
+ MediaUtils.skipTest("no video codecs for resource");
+ return;
+ }
+
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ Resources resources = getContext().getResources();
+ AssetFileDescriptor afd = resources.openRawResourceFd(resId);
+
+ retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
try {
- Resources resources = getContext().getResources();
- AssetFileDescriptor afd = resources.openRawResourceFd(resId);
-
- retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-
afd.close();
- } catch (Exception e) {
+ } catch (IOException e) {
fail("Unable to open file");
}
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerTest.java b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
index 4b42690..af7cfaa 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
@@ -390,8 +390,6 @@
new String[] {"电视原声带", "格斗天王(限量精装版)(预购版)", null, "11.Open Arms.( cn808.net )", null} ),
new MediaScanEntry(R.raw.gb18030_4,
new String[] {"莫扎特", "黄金古典", "柏林爱乐乐团", "第25号交响曲", "莫扎特"} ),
- new MediaScanEntry(R.raw.gb18030_5,
- new String[] {"光良", "童话", "光良", "02.童话", "鍏夎壇"} ),
new MediaScanEntry(R.raw.gb18030_6,
new String[] {"张韶涵", "潘朵拉", "張韶涵", "隐形的翅膀", "王雅君"} ),
new MediaScanEntry(R.raw.gb18030_7, // this is actually utf-8
diff --git a/tests/tests/media/src/android/media/cts/MediaSyncTest.java b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
index 6f9e2a2..41d8d89 100644
--- a/tests/tests/media/src/android/media/cts/MediaSyncTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
@@ -503,7 +503,7 @@
// sync.getTolerance() is MediaSync's tolerance of the playback rate, whereas
// PLAYBACK_RATE_TOLERANCE_PERCENT / 100 is our test's tolerance.
// We need to add both to get an upperbound for allowable error.
- mediaDurationUs * (sync.getTolerance() + PLAYBACK_RATE_TOLERANCE_PERCENT / 100)
+ mediaDurationUs * (sync.getTolerance() + PLAYBACK_RATE_TOLERANCE_PERCENT / 100.)
+ TIME_MEASUREMENT_TOLERANCE_US);
}
diff --git a/tests/tests/midi/AndroidManifest.xml b/tests/tests/midi/AndroidManifest.xml
index 2cdd211..971f4fb 100755
--- a/tests/tests/midi/AndroidManifest.xml
+++ b/tests/tests/midi/AndroidManifest.xml
@@ -25,7 +25,8 @@
<application>
<uses-library android:name="android.test.runner" />
- <service android:name="MidiEchoTestService">
+ <service android:name="MidiEchoTestService"
+ android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
<intent-filter>
<action android:name="android.media.midi.MidiDeviceService" />
</intent-filter>
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
index a35e145..6524871 100644
--- a/tests/tests/net/Android.mk
+++ b/tests/tests/net/Android.mk
@@ -26,7 +26,8 @@
LOCAL_JAVA_LIBRARIES := voip-common conscrypt org.apache.http.legacy
-LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libnativedns_jni
+LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libnativedns_jni \
+ libnativemultinetwork_jni
# include CtsTestServer as a temporary hack to free net.cts from cts.stub.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/net/jni/Android.mk b/tests/tests/net/jni/Android.mk
index 75982de..ca82b30 100644
--- a/tests/tests/net/jni/Android.mk
+++ b/tests/tests/net/jni/Android.mk
@@ -28,3 +28,12 @@
LOCAL_SHARED_LIBRARIES := libnativehelper liblog
include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libnativemultinetwork_jni
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := NativeMultinetworkJni.c
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+LOCAL_SHARED_LIBRARIES := libandroid libnativehelper liblog
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/net/jni/NativeMultinetworkJni.c b/tests/tests/net/jni/NativeMultinetworkJni.c
new file mode 100644
index 0000000..9a5ab9c
--- /dev/null
+++ b/tests/tests/net/jni/NativeMultinetworkJni.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#define LOG_TAG "MultinetworkApiTest"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <jni.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <android/multinetwork.h>
+
+#define UNUSED(X) ((void) X)
+
+static const char kHostname[] = "connectivitycheck.android.com";
+
+
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runGetaddrinfoCheck(
+ JNIEnv* env, jclass class, jlong nethandle) {
+ UNUSED(env);
+ UNUSED(class);
+ net_handle_t handle = (net_handle_t) nethandle;
+ struct addrinfo *res = NULL;
+
+ errno = 0;
+ int rval = android_getaddrinfofornetwork(handle, kHostname, NULL, NULL, &res);
+ const int saved_errno = errno;
+ freeaddrinfo(res);
+
+ ALOGD("android_getaddrinfofornetwork(%llu, %s) returned rval=%d errno=%d",
+ handle, kHostname, rval, saved_errno);
+ return rval == 0 ? 0 : -saved_errno;
+}
+
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runSetprocnetwork(
+ JNIEnv* env, jclass class, jlong nethandle) {
+ UNUSED(env);
+ UNUSED(class);
+ net_handle_t handle = (net_handle_t) nethandle;
+
+ errno = 0;
+ int rval = android_setprocnetwork(handle);
+ const int saved_errno = errno;
+ ALOGD("android_setprocnetwork(%llu) returned rval=%d errno=%d",
+ handle, rval, saved_errno);
+ return rval == 0 ? 0 : -saved_errno;
+}
+
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runSetsocknetwork(
+ JNIEnv* env, jclass class, jlong nethandle) {
+ UNUSED(env);
+ UNUSED(class);
+ net_handle_t handle = (net_handle_t) nethandle;
+
+ errno = 0;
+ int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0) {
+ ALOGD("socket() failed, errno=%d", errno);
+ return -errno;
+ }
+
+ errno = 0;
+ int rval = android_setsocknetwork(handle, fd);
+ const int saved_errno = errno;
+ ALOGD("android_setprocnetwork(%llu, %d) returned rval=%d errno=%d",
+ handle, fd, rval, saved_errno);
+ close(fd);
+ return rval == 0 ? 0 : -saved_errno;
+}
+
+static const int kSockaddrStrLen = INET6_ADDRSTRLEN + strlen("[]:65535");
+
+void sockaddr_ntop(const struct sockaddr *sa, socklen_t salen, char *dst, const size_t size) {
+ char addrstr[INET6_ADDRSTRLEN];
+ char portstr[sizeof("65535")];
+ char buf[sizeof(addrstr) + sizeof(portstr) + sizeof("[]:")];
+ int ret = getnameinfo(sa, salen,
+ addrstr, sizeof(addrstr),
+ portstr, sizeof(portstr),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (ret == 0) {
+ snprintf(buf, sizeof(buf),
+ (sa->sa_family == AF_INET6) ? "[%s]:%s" : "%s:%s",
+ addrstr, portstr);
+ } else {
+ sprintf(buf, "???");
+ }
+
+ strlcpy(dst, buf, (strlen(buf) < size - 1) ? strlen(buf) : size - 1);
+}
+
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runDatagramCheck(
+ JNIEnv* env, jclass class, jlong nethandle) {
+ UNUSED(env);
+ UNUSED(class);
+ const struct addrinfo kHints = {
+ .ai_flags = AI_ADDRCONFIG,
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ };
+ struct addrinfo *res = NULL;
+ net_handle_t handle = (net_handle_t) nethandle;
+
+ // Quoth Ian Swett:
+ // "QUIC always uses 80 and 443, but only 443 is used for secure(HTTPS) traffic."
+ int rval = android_getaddrinfofornetwork(handle, kHostname, "80", &kHints, &res);
+ if (rval != 0) {
+ ALOGD("android_getaddrinfofornetwork(%llu, %s) returned rval=%d errno=%d",
+ handle, kHostname, rval, errno);
+ freeaddrinfo(res);
+ return -errno;
+ }
+
+ // Rely upon getaddrinfo sorting the best destination to the front.
+ int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (fd < 0) {
+ ALOGD("socket(%d, %d, %d) failed, errno=%d",
+ res->ai_family, res->ai_socktype, res->ai_protocol, errno);
+ freeaddrinfo(res);
+ return -errno;
+ }
+
+ rval = android_setsocknetwork(handle, fd);
+ ALOGD("android_setprocnetwork(%llu, %d) returned rval=%d errno=%d",
+ handle, fd, rval, errno);
+ if (rval != 0) {
+ close(fd);
+ freeaddrinfo(res);
+ return -errno;
+ }
+
+ char addrstr[kSockaddrStrLen];
+ sockaddr_ntop(res->ai_addr, res->ai_addrlen, addrstr, sizeof(addrstr));
+ ALOGD("Attempting connect() to %s...", addrstr);
+
+ rval = connect(fd, res->ai_addr, res->ai_addrlen);
+ if (rval != 0) {
+ close(fd);
+ freeaddrinfo(res);
+ return -errno;
+ }
+ freeaddrinfo(res);
+
+ struct sockaddr_storage src_addr;
+ socklen_t src_addrlen = sizeof(src_addr);
+ if (getsockname(fd, (struct sockaddr *)&src_addr, &src_addrlen) != 0) {
+ close(fd);
+ return -errno;
+ }
+ sockaddr_ntop((const struct sockaddr *)&src_addr, sizeof(src_addr), addrstr, sizeof(addrstr));
+ ALOGD("... from %s", addrstr);
+
+ // Don't let reads or writes block indefinitely.
+ const struct timeval timeo = { 5, 0 }; // 5 seconds
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
+
+ uint8_t quic_packet[] = {
+ 0x0c, // public flags: 64bit conn ID, 8bit sequence number
+ 0, 0, 0, 0, 0, 0, 0, 0, // 64bit connection ID
+ 0x01, // sequence number
+ 0x00, // private flags
+ 0x07, // type: regular frame type "PING"
+ };
+
+ arc4random_buf(quic_packet + 1, 8); // random connection ID
+
+ ssize_t sent = send(fd, quic_packet, sizeof(quic_packet), 0);
+ if (sent < (ssize_t)sizeof(quic_packet)) {
+ ALOGD("send(QUIC packet) returned sent=%zd, errno=%d", sent, errno);
+ close(fd);
+ return -errno;
+ }
+
+ uint8_t response[1500];
+ ssize_t rcvd = recv(fd, response, sizeof(response), 0);
+ if (rcvd < sent) {
+ ALOGD("recv() returned rcvd=%zd, errno=%d", rcvd, errno);
+ close(fd);
+ return -errno;
+ }
+
+ int conn_id_cmp = memcmp(quic_packet + 1, response + 1, 8);
+ if (conn_id_cmp != 0) {
+ ALOGD("sent and received connection IDs do not match");
+ close(fd);
+ return -EPROTO;
+ }
+
+ // TODO: log, and compare to the IP address encoded in the
+ // response, since this should be a public reset packet.
+
+ close(fd);
+ return 0;
+}
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
new file mode 100644
index 0000000..51ee50e
--- /dev/null
+++ b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkUtils;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+
+
+public class MultinetworkApiTest extends AndroidTestCase {
+
+ static {
+ System.loadLibrary("nativemultinetwork_jni");
+ }
+
+ private static final String TAG = "MultinetworkNativeApiTest";
+
+ /**
+ * @return 0 on success
+ */
+ private static native int runGetaddrinfoCheck(long networkHandle);
+ private static native int runSetprocnetwork(long networkHandle);
+ private static native int runSetsocknetwork(long networkHandle);
+ private static native int runDatagramCheck(long networkHandle);
+
+ private ConnectivityManager mCM;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ private Network[] getTestableNetworks() {
+ final ArrayList<Network> testableNetworks = new ArrayList<Network>();
+ for (Network network : mCM.getAllNetworks()) {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
+ if (nc != null
+ && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ testableNetworks.add(network);
+ }
+ }
+
+ assertTrue(
+ "This test requires that at least one network be connected. " +
+ "Please ensure that the device is connected to a network.",
+ testableNetworks.size() >= 1);
+ return testableNetworks.toArray(new Network[0]);
+ }
+
+ public void testGetaddrinfo() throws ErrnoException {
+ for (Network network : getTestableNetworks()) {
+ int errno = runGetaddrinfoCheck(network.getNetworkHandle());
+ if (errno != 0) {
+ throw new ErrnoException(
+ "getaddrinfo on " + mCM.getNetworkInfo(network), -errno);
+ }
+ }
+ }
+
+ public void testSetprocnetwork() throws ErrnoException {
+ // Hopefully no prior test in this process space has set a default network.
+ assertNull(mCM.getProcessDefaultNetwork());
+ assertEquals(0, NetworkUtils.getBoundNetworkForProcess());
+
+ for (Network network : getTestableNetworks()) {
+ mCM.setProcessDefaultNetwork(null);
+ assertNull(mCM.getProcessDefaultNetwork());
+
+ int errno = runSetprocnetwork(network.getNetworkHandle());
+ if (errno != 0) {
+ throw new ErrnoException(
+ "setprocnetwork on " + mCM.getNetworkInfo(network), -errno);
+ }
+ Network processDefault = mCM.getProcessDefaultNetwork();
+ assertNotNull(processDefault);
+ assertEquals(network, processDefault);
+ // TODO: open DatagramSockets, connect them to 192.0.2.1 and 2001:db8::,
+ // and ensure that the source address is in fact on this network as
+ // determined by mCM.getLinkProperties(network).
+
+ mCM.setProcessDefaultNetwork(null);
+ }
+
+ for (Network network : getTestableNetworks()) {
+ NetworkUtils.bindProcessToNetwork(0);
+ assertNull(mCM.getBoundNetworkForProcess());
+
+ int errno = runSetprocnetwork(network.getNetworkHandle());
+ if (errno != 0) {
+ throw new ErrnoException(
+ "setprocnetwork on " + mCM.getNetworkInfo(network), -errno);
+ }
+ assertEquals(network, new Network(mCM.getBoundNetworkForProcess()));
+ // TODO: open DatagramSockets, connect them to 192.0.2.1 and 2001:db8::,
+ // and ensure that the source address is in fact on this network as
+ // determined by mCM.getLinkProperties(network).
+
+ NetworkUtils.bindProcessToNetwork(0);
+ }
+ }
+
+ public void testSetsocknetwork() throws ErrnoException {
+ for (Network network : getTestableNetworks()) {
+ int errno = runSetsocknetwork(network.getNetworkHandle());
+ if (errno != 0) {
+ throw new ErrnoException(
+ "setsocknetwork on " + mCM.getNetworkInfo(network), -errno);
+ }
+ }
+ }
+
+ public void testNativeDatagramTransmission() throws ErrnoException {
+ for (Network network : getTestableNetworks()) {
+ int errno = runDatagramCheck(network.getNetworkHandle());
+ if (errno != 0) {
+ throw new ErrnoException(
+ "DatagramCheck on " + mCM.getNetworkInfo(network), -errno);
+ }
+ }
+ }
+
+ public void testNoSuchNetwork() {
+ final Network eNoNet = new Network(54321);
+ assertNull(mCM.getNetworkInfo(eNoNet));
+
+ final long eNoNetHandle = eNoNet.getNetworkHandle();
+ assertEquals(-OsConstants.ENONET, runSetsocknetwork(eNoNetHandle));
+ assertEquals(-OsConstants.ENONET, runSetprocnetwork(eNoNetHandle));
+ // TODO: correct test permissions so this call is not silently re-mapped
+ // to query on the default network.
+ // assertEquals(-OsConstants.ENONET, runGetaddrinfoCheck(eNoNetHandle));
+ }
+}
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkTest.java b/tests/tests/net/src/android/net/cts/MultinetworkSysctlTest.java
similarity index 96%
rename from tests/tests/net/src/android/net/cts/MultinetworkTest.java
rename to tests/tests/net/src/android/net/cts/MultinetworkSysctlTest.java
index 256c030..c091a13 100644
--- a/tests/tests/net/src/android/net/cts/MultinetworkTest.java
+++ b/tests/tests/net/src/android/net/cts/MultinetworkSysctlTest.java
@@ -27,9 +27,9 @@
import java.io.IOException;
/**
- * Tests for multinetwork functionality.
+ * Tests for multinetwork sysctl functionality.
*/
-public class MultinetworkTest extends AndroidTestCase {
+public class MultinetworkSysctlTest extends AndroidTestCase {
// Global sysctls. Must be present and set to 1.
private static final String[] GLOBAL_SYSCTLS = {
diff --git a/tests/tests/net/src/android/net/http/cts/ApacheHttpClientTest.java b/tests/tests/net/src/android/net/http/cts/ApacheHttpClientTest.java
index 7d9189f..2e5306d 100644
--- a/tests/tests/net/src/android/net/http/cts/ApacheHttpClientTest.java
+++ b/tests/tests/net/src/android/net/http/cts/ApacheHttpClientTest.java
@@ -21,56 +21,27 @@
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.State;
import android.net.Uri;
-import android.net.wifi.WifiManager;
import android.test.AndroidTestCase;
-import android.util.Log;
import android.webkit.cts.CtsTestServer;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
public class ApacheHttpClientTest extends AndroidTestCase {
- private static final String TAG = ApacheHttpClientTest.class.getSimpleName();
-
private static final int NUM_DOWNLOADS = 20;
private static final int SMALL_DOWNLOAD_SIZE = 100 * 1024;
private CtsTestServer mWebServer;
- private WifiManager mWifiManager;
-
- private ConnectivityManager mConnectivityManager;
-
- private boolean mHasTelephony;
-
- private boolean mHasWifi;
-
@Override
protected void setUp() throws Exception {
super.setUp();
mWebServer = new CtsTestServer(mContext);
- mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
- mConnectivityManager = (ConnectivityManager)
- mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-
- PackageManager packageManager = mContext.getPackageManager();
- mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
- mHasWifi = packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI);
}
@Override
@@ -79,25 +50,8 @@
mWebServer.shutdown();
}
- public void testExecute_withMobile() throws Exception {
- if (mHasTelephony) {
- disconnectWifiToConnectToMobile();
- }
-
+ public void testExecute() throws Exception {
downloadMultipleFiles();
-
- if (mHasWifi) {
- connectToWifi();
- }
- }
-
- public void testExecute_withWifi() throws Exception {
- if (mHasWifi) {
- if (!mWifiManager.isWifiEnabled()) {
- connectToWifi();
- }
- downloadMultipleFiles();
- }
}
private void downloadMultipleFiles() throws ClientProtocolException, IOException {
@@ -134,77 +88,6 @@
numBytes += bytesRead;
}
}
- assertEquals(message, SMALL_DOWNLOAD_SIZE, numBytes);
- }
-
- private void connectToWifi() throws InterruptedException {
- if (!mWifiManager.isWifiEnabled()) {
- ConnectivityActionReceiver receiver =
- new ConnectivityActionReceiver(ConnectivityManager.TYPE_WIFI, State.CONNECTED);
- IntentFilter filter = new IntentFilter();
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiver(receiver, filter);
-
- assertTrue(mWifiManager.setWifiEnabled(true));
- assertTrue("Wifi must be configured to connect to an access point for this test.",
- receiver.waitForStateChange());
-
- mContext.unregisterReceiver(receiver);
- }
- }
-
- private void disconnectWifiToConnectToMobile() throws InterruptedException {
- if (mHasWifi && mWifiManager.isWifiEnabled()) {
- ConnectivityActionReceiver connectMobileReceiver =
- new ConnectivityActionReceiver(ConnectivityManager.TYPE_MOBILE,
- State.CONNECTED);
- ConnectivityActionReceiver disconnectWifiReceiver =
- new ConnectivityActionReceiver(ConnectivityManager.TYPE_WIFI,
- State.DISCONNECTED);
- IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiver(connectMobileReceiver, filter);
- mContext.registerReceiver(disconnectWifiReceiver, filter);
-
- assertTrue(mWifiManager.setWifiEnabled(false));
- assertTrue(disconnectWifiReceiver.waitForStateChange());
- assertTrue(connectMobileReceiver.waitForStateChange());
-
- mContext.unregisterReceiver(connectMobileReceiver);
- mContext.unregisterReceiver(disconnectWifiReceiver);
- }
- }
-
- /** Receiver that captures the last connectivity change's network type and state. */
- private class ConnectivityActionReceiver extends BroadcastReceiver {
-
- private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
-
- private final int mNetworkType;
-
- private final State mExpectedState;
-
- ConnectivityActionReceiver(int networkType, State expectedState) {
- mNetworkType = networkType;
- mExpectedState = expectedState;
- }
-
- public void onReceive(Context context, Intent intent) {
- NetworkInfo networkInfo = intent.getExtras()
- .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
- int networkType = networkInfo.getType();
- State networkState = networkInfo.getState();
- Log.i(TAG, "Network type: " + networkType + " State: " + networkInfo.getState());
- if (networkType == mNetworkType && networkInfo.getState() == mExpectedState) {
- mReceiveLatch.countDown();
- }
- }
-
- public boolean waitForStateChange() throws InterruptedException {
- return mReceiveLatch.await(30, TimeUnit.SECONDS) || hasExpectedState();
- }
-
- private boolean hasExpectedState() {
- return mExpectedState == mConnectivityManager.getNetworkInfo(mNetworkType).getState();
- }
+ assertEquals(message, expectedNumBytes, numBytes);
}
}
diff --git a/tests/tests/netsecpolicy/Android.mk b/tests/tests/netsecpolicy/Android.mk
new file mode 100644
index 0000000..137672e
--- /dev/null
+++ b/tests/tests/netsecpolicy/Android.mk
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/netsecpolicy/src/android/security/NetworkSecurityPolicyTestBase.java b/tests/tests/netsecpolicy/src/android/security/NetworkSecurityPolicyTestBase.java
new file mode 100644
index 0000000..0ab07ae
--- /dev/null
+++ b/tests/tests/netsecpolicy/src/android/security/NetworkSecurityPolicyTestBase.java
@@ -0,0 +1,380 @@
+package android.security;
+
+import android.app.DownloadManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.database.Cursor;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.net.http.AndroidHttpClient;
+import android.test.AndroidTestCase;
+import android.webkit.cts.CtsTestServer;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.UnknownServiceException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+abstract class NetworkSecurityPolicyTestBase extends AndroidTestCase {
+ private CtsTestServer mHttpOnlyWebServer;
+
+ private final boolean mCleartextTrafficExpectedToBePermitted;
+
+ NetworkSecurityPolicyTestBase(boolean cleartextTrafficExpectedToBePermitted) {
+ mCleartextTrafficExpectedToBePermitted = cleartextTrafficExpectedToBePermitted;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mHttpOnlyWebServer = new CtsTestServer(mContext, false);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ mHttpOnlyWebServer.shutdown();
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ public void testNetworkSecurityPolicy() {
+ assertEquals(mCleartextTrafficExpectedToBePermitted,
+ NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
+ }
+
+ public void testApplicationInfoFlag() {
+ ApplicationInfo appInfo = getContext().getApplicationInfo();
+ int expectedValue = (mCleartextTrafficExpectedToBePermitted)
+ ? ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC : 0;
+ assertEquals(expectedValue, appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC);
+ }
+
+ public void testDefaultHttpURLConnection() throws Exception {
+ if (mCleartextTrafficExpectedToBePermitted) {
+ assertCleartextHttpURLConnectionSucceeds();
+ } else {
+ assertCleartextHttpURLConnectionBlocked();
+ }
+ }
+
+ private void assertCleartextHttpURLConnectionSucceeds() throws Exception {
+ URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
+ HttpURLConnection conn = null;
+ try {
+ mHttpOnlyWebServer.resetRequestState();
+ conn = (HttpURLConnection) url.openConnection();
+ conn.setConnectTimeout(5000);
+ conn.setReadTimeout(5000);
+ assertEquals(200, conn.getResponseCode());
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
+ assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+ }
+
+ private void assertCleartextHttpURLConnectionBlocked() throws Exception {
+ URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
+ HttpURLConnection conn = null;
+ try {
+ mHttpOnlyWebServer.resetRequestState();
+ conn = (HttpURLConnection) url.openConnection();
+ conn.setConnectTimeout(5000);
+ conn.setReadTimeout(5000);
+ conn.getResponseCode();
+ fail();
+ } catch (UnknownServiceException e) {
+ if ((e.getMessage() == null) || (!e.getMessage().toLowerCase().contains("cleartext"))) {
+ fail("Exception with which request failed does not mention cleartext: " + e);
+ }
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
+ assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+ }
+
+ public void testAndroidHttpClient() throws Exception {
+ if (mCleartextTrafficExpectedToBePermitted) {
+ assertAndroidHttpClientCleartextRequestSucceeds();
+ } else {
+ assertAndroidHttpClientCleartextRequestBlocked();
+ }
+ }
+
+ private void assertAndroidHttpClientCleartextRequestSucceeds() throws Exception {
+ URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
+ AndroidHttpClient httpClient = AndroidHttpClient.newInstance(null);
+ try {
+ HttpResponse response = httpClient.execute(new HttpGet(url.toString()));
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ } finally {
+ httpClient.close();
+ }
+ Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
+ assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+ }
+
+ private void assertAndroidHttpClientCleartextRequestBlocked() throws Exception {
+ URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
+ AndroidHttpClient httpClient = AndroidHttpClient.newInstance(null);
+ try {
+ HttpResponse response = httpClient.execute(new HttpGet(url.toString()));
+ fail();
+ } catch (IOException e) {
+ if ((e.getMessage() == null) || (!e.getMessage().toLowerCase().contains("cleartext"))) {
+ fail("Exception with which request failed does not mention cleartext: " + e);
+ }
+ } finally {
+ httpClient.close();
+ }
+ Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
+ assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+ }
+
+ public void testMediaPlayer() throws Exception {
+ if (mCleartextTrafficExpectedToBePermitted) {
+ assertMediaPlayerCleartextRequestSucceeds();
+ } else {
+ assertMediaPlayerCleartextRequestBlocked();
+ }
+ }
+
+ private void assertMediaPlayerCleartextRequestSucceeds() throws Exception {
+ MediaPlayer mediaPlayer = new MediaPlayer();
+ Uri uri = Uri.parse(mHttpOnlyWebServer.getUserAgentUrl());
+ mediaPlayer.setDataSource(getContext(), uri);
+
+ try {
+ mediaPlayer.prepare();
+ } catch (IOException expected) {
+ } finally {
+ try {
+ mediaPlayer.stop();
+ } catch (IllegalStateException ignored) {
+ }
+ }
+ uri = uri.buildUpon().scheme(null).authority(null).build();
+ assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+ }
+
+ private void assertMediaPlayerCleartextRequestBlocked() throws Exception {
+ MediaPlayer mediaPlayer = new MediaPlayer();
+ Uri uri = Uri.parse(mHttpOnlyWebServer.getUserAgentUrl());
+ mediaPlayer.setDataSource(getContext(), uri);
+
+ try {
+ mediaPlayer.prepare();
+ } catch (IOException expected) {
+ } finally {
+ try {
+ mediaPlayer.stop();
+ } catch (IllegalStateException ignored) {
+ }
+ }
+ uri = uri.buildUpon().scheme(null).authority(null).build();
+ assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+ }
+
+ public void testDownloadManager() throws Exception {
+ Uri uri = Uri.parse(mHttpOnlyWebServer.getTestDownloadUrl("netsecpolicy", 0));
+ int[] result = downloadUsingDownloadManager(uri);
+ int status = result[0];
+ int reason = result[1];
+ uri = uri.buildUpon().scheme(null).authority(null).build();
+ if (mCleartextTrafficExpectedToBePermitted) {
+ assertEquals(DownloadManager.STATUS_SUCCESSFUL, status);
+ assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+ } else {
+ assertEquals(DownloadManager.STATUS_FAILED, status);
+ assertEquals(400, reason);
+ assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+ }
+ }
+
+
+ private int[] downloadUsingDownloadManager(Uri uri) throws Exception {
+ DownloadManager downloadManager =
+ (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
+ removeAllDownloads(downloadManager);
+ BroadcastReceiver downloadCompleteReceiver = null;
+ try {
+ final SettableFuture<Intent> downloadCompleteIntentFuture = new SettableFuture<Intent>();
+ downloadCompleteReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ downloadCompleteIntentFuture.set(intent);
+ }
+ };
+ getContext().registerReceiver(
+ downloadCompleteReceiver,
+ new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+
+ Intent downloadCompleteIntent;
+
+ long downloadId = downloadManager.enqueue(new DownloadManager.Request(uri));
+ downloadCompleteIntent = downloadCompleteIntentFuture.get(5, TimeUnit.SECONDS);
+
+ assertEquals(downloadId,
+ downloadCompleteIntent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
+ Cursor c = downloadManager.query(
+ new DownloadManager.Query().setFilterById(downloadId));
+ try {
+ if (!c.moveToNext()) {
+ fail("Download not found");
+ return null;
+ }
+ int status = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
+ int reason = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON));
+ return new int[] {status, reason};
+ } finally {
+ c.close();
+ }
+ } finally {
+ if (downloadCompleteReceiver != null) {
+ getContext().unregisterReceiver(downloadCompleteReceiver);
+ }
+ removeAllDownloads(downloadManager);
+ }
+ }
+
+ private static void removeAllDownloads(DownloadManager downloadManager) {
+ Cursor cursor = null;
+ try {
+ DownloadManager.Query query = new DownloadManager.Query();
+ cursor = downloadManager.query(query);
+ if (cursor.getCount() == 0) {
+ return;
+ }
+ long[] removeIds = new long[cursor.getCount()];
+ int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+ for (int i = 0; cursor.moveToNext(); i++) {
+ removeIds[i] = cursor.getLong(columnIndex);
+ }
+ assertEquals(removeIds.length, downloadManager.remove(removeIds));
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ private static class SettableFuture<T> implements Future<T> {
+
+ private final Object mLock = new Object();
+ private boolean mDone;
+ private boolean mCancelled;
+ private T mValue;
+ private Throwable mException;
+
+ public void set(T value) {
+ synchronized (mLock) {
+ if (!mDone) {
+ mValue = value;
+ mDone = true;
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ public void setException(Throwable exception) {
+ synchronized (mLock) {
+ if (!mDone) {
+ mException = exception;
+ mDone = true;
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ synchronized (mLock) {
+ if (mDone) {
+ return false;
+ }
+ mCancelled = true;
+ mDone = true;
+ mLock.notifyAll();
+ return true;
+ }
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ synchronized (mLock) {
+ while (!mDone) {
+ mLock.wait();
+ }
+ return getValue();
+ }
+ }
+
+ @Override
+ public T get(long timeout, TimeUnit timeUnit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ synchronized (mLock) {
+ if (mDone) {
+ return getValue();
+ }
+ long timeoutMillis = timeUnit.toMillis(timeout);
+ long deadlineTimeMillis = System.currentTimeMillis() + timeoutMillis;
+
+ while (!mDone) {
+ long millisTillDeadline = deadlineTimeMillis - System.currentTimeMillis();
+ if ((millisTillDeadline <= 0) || (millisTillDeadline > timeoutMillis)) {
+ throw new TimeoutException();
+ }
+ mLock.wait(millisTillDeadline);
+ }
+ return getValue();
+ }
+ }
+
+ private T getValue() throws ExecutionException {
+ synchronized (mLock) {
+ if (!mDone) {
+ throw new IllegalStateException("Not yet done");
+ }
+ if (mCancelled) {
+ throw new CancellationException();
+ }
+ if (mException != null) {
+ throw new ExecutionException(mException);
+ }
+ return mValue;
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ synchronized (mLock) {
+ return mCancelled;
+ }
+ }
+
+ @Override
+ public boolean isDone() {
+ synchronized (mLock) {
+ return mDone;
+ }
+ }
+ }
+}
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
new file mode 100644
index 0000000..0441f2b
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner \
+ ctstestserver \
+ org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src ../src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficFalseTestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsNetSecPolicyUsesCleartextTrafficFalse
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
new file mode 100644
index 0000000..49385f8
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.netsecpolicy.usescleartext.false">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.netsecpolicy.usescleartext.false.cts"
+ android:label="Tests for NetworkSecurityPolicy cleartext traffic policy when it is set to denied.">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/src/android/security/NetworkSecurityPolicyCleartextDeniedTest.java b/tests/tests/netsecpolicy/usescleartexttraffic-false/src/android/security/NetworkSecurityPolicyCleartextDeniedTest.java
new file mode 100644
index 0000000..f5d9770
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/src/android/security/NetworkSecurityPolicyCleartextDeniedTest.java
@@ -0,0 +1,9 @@
+package android.security;
+
+public class NetworkSecurityPolicyCleartextDeniedTest extends NetworkSecurityPolicyTestBase {
+
+ public NetworkSecurityPolicyCleartextDeniedTest() {
+ super(false // expect cleartext traffic to be blocked
+ );
+ }
+}
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
new file mode 100644
index 0000000..5a4a41d
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner \
+ ctstestserver \
+ org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src ../src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficTrueTestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsNetSecPolicyUsesCleartextTrafficTrue
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
new file mode 100644
index 0000000..be698f2
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.netsecpolicy.usescleartext.true">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.netsecpolicy.usescleartext.true.cts"
+ android:label="Tests for NetworkSecurityPolicy cleartext traffic policy when it is set to permitted.">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/src/android/security/NetworkSecurityPolicyCleartextPermittedTest.java b/tests/tests/netsecpolicy/usescleartexttraffic-true/src/android/security/NetworkSecurityPolicyCleartextPermittedTest.java
new file mode 100644
index 0000000..83c1049
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/src/android/security/NetworkSecurityPolicyCleartextPermittedTest.java
@@ -0,0 +1,9 @@
+package android.security;
+
+public class NetworkSecurityPolicyCleartextPermittedTest extends NetworkSecurityPolicyTestBase {
+
+ public NetworkSecurityPolicyCleartextPermittedTest() {
+ super(true // expect cleartext traffic to be permitted
+ );
+ }
+}
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
new file mode 100644
index 0000000..faa3c23
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner \
+ ctstestserver \
+ org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src ../src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficUnspecifiedTestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsNetSecPolicyUsesCleartextTrafficUnspecified
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
new file mode 100644
index 0000000..7bd8742
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.netsecpolicy.usescleartext.unspecified">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.netsecpolicy.usescleartext.unspecified.cts"
+ android:label="Tests for NetworkSecurityPolicy cleartext traffic policy when it is not specified.">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/src/android/security/NetworkSecurityPolicyCleartextUnspecifiedTest.java b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/src/android/security/NetworkSecurityPolicyCleartextUnspecifiedTest.java
new file mode 100644
index 0000000..5690f31
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/src/android/security/NetworkSecurityPolicyCleartextUnspecifiedTest.java
@@ -0,0 +1,9 @@
+package android.security;
+
+public class NetworkSecurityPolicyCleartextUnspecifiedTest extends NetworkSecurityPolicyTestBase {
+
+ public NetworkSecurityPolicyCleartextUnspecifiedTest() {
+ super(true // expect cleartext traffic to be permitted
+ );
+ }
+}
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 9dfb86e..f4b140e 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -31,6 +31,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/android/os/cts/IParcelFileDescriptorPeer.aidl \
src/android/os/cts/IEmptyService.aidl \
+ src/android/os/cts/ISeccompIsolatedService.aidl \
src/android/os/cts/ISecondary.aidl
LOCAL_PACKAGE_NAME := CtsOsTestCases
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index f225903..deb7045 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -120,6 +120,10 @@
</intent-filter>
</service>
+ <service android:name="android.os.cts.SeccompTest$IsolatedService"
+ android:isolatedProcess="true">
+ </service>
+
<service android:name="android.os.cts.MessengerService"
android:process=":messengerService">
</service>
diff --git a/tests/tests/os/jni/Android.mk b/tests/tests/os/jni/Android.mk
index fab1ec2..24a0651 100644
--- a/tests/tests/os/jni/Android.mk
+++ b/tests/tests/os/jni/Android.mk
@@ -47,7 +47,9 @@
endif
ifeq ($(ARCH_SUPPORTS_SECCOMP),1)
- LOCAL_SRC_FILES += seccomp-tests/tests/seccomp_bpf_tests.c
+ LOCAL_SRC_FILES += seccomp-tests/tests/seccomp_bpf_tests.c \
+ seccomp_sample_program.cpp
+
# This define controls the behavior of OSFeatures.needsSeccompSupport().
LOCAL_CFLAGS += -DARCH_SUPPORTS_SECCOMP
endif
diff --git a/tests/tests/os/jni/android_os_cts_SeccompTest.cpp b/tests/tests/os/jni/android_os_cts_SeccompTest.cpp
index cd1543d..528696b 100644
--- a/tests/tests/os/jni/android_os_cts_SeccompTest.cpp
+++ b/tests/tests/os/jni/android_os_cts_SeccompTest.cpp
@@ -17,7 +17,15 @@
#include <android/log.h>
#include <jni.h>
#include <string.h>
+#include <time.h>
+#if defined(ARCH_SUPPORTS_SECCOMP)
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <sys/syscall.h>
+#endif
+
+#include "seccomp_sample_program.h"
#include "seccomp-tests/tests/test_harness.h"
// Forward declare from seccomp_bpf_tests.c.
@@ -46,9 +54,33 @@
return false;
}
+jboolean android_security_cts_SeccompBpfTest_installTestFilter(JNIEnv*, jclass) {
+#if !defined(ARCH_SUPPORTS_SECCOMP)
+ return false;
+#else
+ struct sock_fprog prog = GetTestSeccompFilterProgram();
+
+ if (prog.len == 0)
+ return false;
+
+ int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog);
+ return rv == 0;
+#endif
+}
+
+jint android_security_cts_SeccompBpfTest_getClockBootTime(JNIEnv*, jclass) {
+ struct timespec ts;
+ int rv = clock_gettime(CLOCK_BOOTTIME, &ts);
+ return rv;
+}
+
static JNINativeMethod methods[] = {
{ "runKernelUnitTest", "(Ljava/lang/String;)Z",
(void*)android_security_cts_SeccompBpfTest_runKernelUnitTest },
+ { "installTestFilter", "()Z",
+ (void*)android_security_cts_SeccompBpfTest_installTestFilter },
+ { "getClockBootTime", "()I",
+ (void*)android_security_cts_SeccompBpfTest_getClockBootTime },
};
int register_android_os_cts_SeccompTest(JNIEnv* env) {
diff --git a/tests/tests/os/jni/seccomp_sample_program.cpp b/tests/tests/os/jni/seccomp_sample_program.cpp
new file mode 100644
index 0000000..e291e8a
--- /dev/null
+++ b/tests/tests/os/jni/seccomp_sample_program.cpp
@@ -0,0 +1,1458 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <linux/filter.h>
+
+// This file defines a sample seccomp-bpf policy. It is taken from the
+// Chromium renderer process policy applied to isolatedProcess services.
+//
+// In the future, this policy should be further restricted to just the set
+// of system calls that an isolatedProcess should be allowed to make.
+
+#if defined(__arm__)
+struct sock_filter kTestSeccompFilter[] = {
+ {0x20, 0, 0, 0x4},
+ {0x15, 1, 0, 0x40000028},
+ {0x6, 0, 0, 0x30006},
+ {0x20, 0, 0, 0x0},
+ {0x35, 0, 90, 0xab},
+ {0x35, 0, 43, 0x108},
+ {0x35, 0, 21, 0x14f},
+ {0x35, 0, 10, 0x168},
+ {0x35, 0, 5, 0x181},
+ {0x35, 0, 2, 0xf0006},
+ {0x35, 0, 58, 0xffff0},
+ {0x35, 57, 55, 0xffff1},
+ {0x35, 0, 43, 0x182},
+ {0x35, 53, 55, 0xf0001},
+ {0x35, 0, 2, 0x16f},
+ {0x35, 0, 53, 0x17e},
+ {0x35, 52, 39, 0x180},
+ {0x35, 38, 51, 0x16e},
+ {0x35, 0, 5, 0x15b},
+ {0x35, 0, 2, 0x160},
+ {0x35, 0, 35, 0x161},
+ {0x35, 45, 47, 0x165},
+ {0x35, 0, 46, 0x15c},
+ {0x35, 45, 32, 0x15d},
+ {0x35, 0, 2, 0x152},
+ {0x35, 0, 30, 0x153},
+ {0x35, 40, 42, 0x15a},
+ {0x35, 41, 39, 0x151},
+ {0x35, 0, 10, 0x121},
+ {0x35, 0, 5, 0x138},
+ {0x35, 0, 2, 0x143},
+ {0x35, 0, 24, 0x147},
+ {0x35, 23, 34, 0x148},
+ {0x35, 0, 22, 0x139},
+ {0x35, 32, 34, 0x142},
+ {0x35, 0, 2, 0x127},
+ {0x35, 0, 30, 0x12a},
+ {0x35, 31, 18, 0x135},
+ {0x35, 30, 28, 0x126},
+ {0x35, 0, 4, 0x10e},
+ {0x35, 0, 2, 0x119},
+ {0x35, 0, 14, 0x11e},
+ {0x35, 137, 26, 0x120},
+ {0x35, 23, 25, 0x118},
+ {0x35, 0, 3, 0x10b},
+ {0x35, 0, 23, 0x10c},
+ {0x35, 9, 0, 0x10d},
+ {0x5, 0, 0, 0x110},
+ {0x35, 7, 20, 0x10a},
+ {0x35, 0, 25, 0xce},
+ {0x35, 0, 12, 0xee},
+ {0x35, 0, 6, 0xf9},
+ {0x35, 0, 2, 0x100},
+ {0x35, 0, 13, 0x101},
+ {0x35, 129, 14, 0x107},
+ {0x35, 1, 0, 0xfa},
+ {0x5, 0, 0, 0x10d},
+ {0x35, 11, 9, 0xfd},
+ {0x35, 0, 2, 0xf0},
+ {0x35, 0, 148, 0xf1},
+ {0x35, 6, 8, 0xf8},
+ {0x35, 7, 0, 0xef},
+ {0x5, 0, 0, 0x106},
+ {0x35, 0, 7, 0xda},
+ {0x35, 0, 3, 0xde},
+ {0x35, 0, 3, 0xe0},
+ {0x35, 2, 0, 0xe1},
+ {0x5, 0, 0, 0x103},
+ {0x35, 1, 0, 0xdc},
+ {0x5, 0, 0, 0x102},
+ {0x35, 209, 172, 0xdd},
+ {0x35, 0, 2, 0xd2},
+ {0x35, 0, 253, 0xd3},
+ {0x35, 252, 253, 0xd4},
+ {0x35, 252, 251, 0xd1},
+ {0x35, 0, 10, 0xb9},
+ {0x35, 0, 5, 0xc1},
+ {0x35, 0, 2, 0xc7},
+ {0x35, 0, 248, 0xcb},
+ {0x35, 247, 246, 0xcd},
+ {0x35, 0, 245, 0xc5},
+ {0x35, 244, 245, 0xc6},
+ {0x35, 0, 2, 0xbb},
+ {0x35, 0, 244, 0xbf},
+ {0x35, 162, 242, 0xc0},
+ {0x35, 241, 240, 0xba},
+ {0x35, 0, 4, 0xb2},
+ {0x35, 0, 2, 0xb5},
+ {0x35, 0, 239, 0xb6},
+ {0x35, 237, 236, 0xb8},
+ {0x35, 236, 237, 0xb4},
+ {0x35, 0, 2, 0xad},
+ {0x35, 0, 234, 0xb0},
+ {0x35, 233, 234, 0xb1},
+ {0x35, 156, 232, 0xac},
+ {0x35, 0, 42, 0x52},
+ {0x35, 0, 21, 0x7e},
+ {0x35, 0, 10, 0x96},
+ {0x35, 0, 5, 0xa4},
+ {0x35, 0, 2, 0xa8},
+ {0x35, 0, 226, 0xa9},
+ {0x35, 224, 226, 0xaa},
+ {0x35, 0, 223, 0xa5},
+ {0x35, 224, 223, 0xa6},
+ {0x35, 0, 2, 0x9e},
+ {0x35, 0, 221, 0x9f},
+ {0x35, 220, 221, 0xa2},
+ {0x35, 220, 219, 0x98},
+ {0x35, 0, 5, 0x8c},
+ {0x35, 0, 2, 0x90},
+ {0x35, 0, 217, 0x91},
+ {0x35, 216, 215, 0x94},
+ {0x35, 0, 214, 0x8d},
+ {0x35, 213, 212, 0x8e},
+ {0x35, 0, 2, 0x85},
+ {0x35, 0, 210, 0x86},
+ {0x35, 209, 211, 0x8a},
+ {0x35, 210, 209, 0x7f},
+ {0x35, 0, 10, 0x64},
+ {0x35, 0, 5, 0x73},
+ {0x35, 0, 2, 0x7a},
+ {0x35, 0, 205, 0x7b},
+ {0x35, 153, 205, 0x7d},
+ {0x35, 0, 204, 0x77},
+ {0x35, 203, 202, 0x79},
+ {0x35, 0, 2, 0x6c},
+ {0x35, 0, 200, 0x6d},
+ {0x35, 199, 200, 0x72},
+ {0x35, 197, 199, 0x6a},
+ {0x35, 0, 4, 0x5b},
+ {0x35, 0, 2, 0x60},
+ {0x35, 0, 195, 0x62},
+ {0x35, 193, 195, 0x63},
+ {0x35, 192, 193, 0x5c},
+ {0x35, 0, 2, 0x54},
+ {0x35, 0, 192, 0x55},
+ {0x35, 191, 189, 0x57},
+ {0x35, 188, 190, 0x53},
+ {0x35, 0, 21, 0x2d},
+ {0x35, 0, 10, 0x3e},
+ {0x35, 0, 5, 0x46},
+ {0x35, 0, 2, 0x4f},
+ {0x35, 0, 185, 0x50},
+ {0x35, 182, 183, 0x51},
+ {0x35, 0, 181, 0x48},
+ {0x35, 181, 182, 0x4e},
+ {0x35, 0, 2, 0x41},
+ {0x35, 0, 180, 0x43},
+ {0x35, 179, 178, 0x44},
+ {0x35, 177, 176, 0x3f},
+ {0x35, 0, 5, 0x33},
+ {0x35, 0, 2, 0x38},
+ {0x35, 0, 175, 0x3c},
+ {0x35, 174, 172, 0x3d},
+ {0x35, 0, 173, 0x36},
+ {0x35, 124, 171, 0x37},
+ {0x35, 0, 2, 0x2f},
+ {0x35, 0, 169, 0x30},
+ {0x35, 168, 169, 0x31},
+ {0x35, 166, 167, 0x2e},
+ {0x35, 0, 10, 0x17},
+ {0x35, 0, 5, 0x21},
+ {0x35, 0, 2, 0x26},
+ {0x35, 0, 162, 0x29},
+ {0x35, 163, 162, 0x2b},
+ {0x35, 0, 160, 0x22},
+ {0x35, 153, 161, 0x25},
+ {0x35, 0, 2, 0x19},
+ {0x35, 0, 159, 0x1d},
+ {0x35, 158, 157, 0x1e},
+ {0x35, 156, 155, 0x18},
+ {0x35, 0, 4, 0xd},
+ {0x35, 0, 2, 0x11},
+ {0x35, 0, 154, 0x13},
+ {0x35, 153, 152, 0x15},
+ {0x35, 150, 152, 0xe},
+ {0x35, 0, 2, 0x3},
+ {0x35, 0, 149, 0x7},
+ {0x35, 147, 149, 0x8},
+ {0x35, 146, 147, 0x2},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 140, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 143, 144, 0x1},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 136, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 139, 0, 0x1},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 132, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 135, 0, 0x6},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 128, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 131, 0, 0x2},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 124, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 127, 0, 0x0},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 120, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 123, 0, 0x5},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 116, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 119, 120, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 112, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 115, 0xfffffe7f},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 108, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 110, 0, 0x1},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 103, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 105, 0, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 98, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 100, 0, 0x4},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 93, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 95, 0, 0x5},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 88, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 90, 0, 0x9},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 83, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 85, 0, 0xa},
+ {0x6, 0, 0, 0x30005},
+ {0x20, 0, 0, 0x24},
+ {0x15, 0, 77, 0x0},
+ {0x20, 0, 0, 0x20},
+ {0x15, 80, 79, 0x4},
+ {0x20, 0, 0, 0x2c},
+ {0x15, 0, 73, 0x0},
+ {0x20, 0, 0, 0x28},
+ {0x45, 77, 76, 0xfffdb7cc},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 69, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 72, 0, 0x10},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 65, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 68, 0, 0xf},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 61, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 64, 0, 0x3},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 57, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 60, 0, 0x4},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 53, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 56, 0, 0x53564d41},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 49, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 52, 0, 0x29},
+ {0x6, 0, 0, 0x30004},
+ {0x20, 0, 0, 0x24},
+ {0x15, 0, 44, 0x0},
+ {0x20, 0, 0, 0x20},
+ {0x45, 48, 47, 0xfffffff8},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 40, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 43, 0, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 36, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 39, 0, 0x1},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 32, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 35, 0, 0x2},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 28, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 31, 0, 0x6},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 24, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 27, 0, 0x7},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 20, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 23, 0, 0x5},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 16, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 19, 0, 0x0},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 12, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 15, 0, 0x406},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 8, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 0, 12, 0x4},
+ {0x20, 0, 0, 0x24},
+ {0x15, 0, 4, 0x0},
+ {0x20, 0, 0, 0x20},
+ {0x45, 8, 7, 0xfff1e3fc},
+ {0x20, 0, 0, 0x14},
+ {0x15, 1, 0, 0x0},
+ {0x6, 0, 0, 0x30003},
+ {0x20, 0, 0, 0x10},
+ {0x15, 2, 0, 0x2da4},
+ {0x6, 0, 0, 0x30002},
+ {0x6, 0, 0, 0x50001},
+ {0x6, 0, 0, 0x7fff0000},
+ {0x6, 0, 0, 0x30001},
+};
+#elif defined(__aarch64__)
+// Note: aarch64 is not required to support seccomp-bpf yet, but some Nexus
+// devices do support it. For completeness, this test BPF program is provided.
+struct sock_filter kTestSeccompFilter[] = {
+ {0x20, 0, 0, 0x4},
+ {0x15, 1, 0, 0xc00000b7},
+ {0x6, 0, 0, 0x30006},
+ {0x20, 0, 0, 0x0},
+ {0x35, 0, 51, 0x88},
+ {0x35, 0, 25, 0xba},
+ {0x35, 0, 12, 0xdf},
+ {0x35, 0, 6, 0xea},
+ {0x35, 0, 3, 0x104},
+ {0x35, 0, 1, 0x114},
+ {0x35, 86, 85, 0x116},
+ {0x35, 85, 81, 0x105},
+ {0x35, 0, 84, 0xf2},
+ {0x35, 83, 82, 0xf3},
+ {0x35, 0, 2, 0xe4},
+ {0x35, 0, 77, 0xe6},
+ {0x35, 92, 80, 0xe9},
+ {0x35, 0, 79, 0xe2},
+ {0x35, 78, 97, 0xe3},
+ {0x35, 0, 6, 0xd1},
+ {0x35, 0, 3, 0xd9},
+ {0x35, 0, 1, 0xdd},
+ {0x35, 100, 73, 0xde},
+ {0x35, 69, 73, 0xdc},
+ {0x35, 0, 68, 0xd5},
+ {0x35, 67, 71, 0xd6},
+ {0x35, 0, 2, 0xcc},
+ {0x35, 0, 69, 0xce},
+ {0x35, 68, 64, 0xd0},
+ {0x35, 0, 66, 0xc7},
+ {0x35, 65, 99, 0xc8},
+ {0x35, 0, 12, 0x9e},
+ {0x35, 0, 6, 0xa6},
+ {0x35, 0, 3, 0xa9},
+ {0x35, 0, 1, 0xac},
+ {0x35, 61, 57, 0xb3},
+ {0x35, 60, 56, 0xaa},
+ {0x35, 0, 58, 0xa7},
+ {0x35, 58, 98, 0xa8},
+ {0x35, 0, 2, 0xa1},
+ {0x35, 0, 56, 0xa3},
+ {0x35, 55, 51, 0xa4},
+ {0x35, 0, 50, 0x9f},
+ {0x35, 49, 52, 0xa0},
+ {0x35, 0, 6, 0x94},
+ {0x35, 0, 3, 0x97},
+ {0x35, 0, 1, 0x9c},
+ {0x35, 49, 45, 0x9d},
+ {0x35, 48, 47, 0x99},
+ {0x35, 0, 43, 0x95},
+ {0x35, 42, 45, 0x96},
+ {0x35, 0, 2, 0x8b},
+ {0x35, 0, 40, 0x8e},
+ {0x35, 42, 43, 0x8f},
+ {0x35, 0, 42, 0x89},
+ {0x35, 41, 37, 0x8a},
+ {0x35, 0, 25, 0x4e},
+ {0x35, 0, 12, 0x65},
+ {0x35, 0, 6, 0x80},
+ {0x35, 0, 3, 0x83},
+ {0x35, 0, 1, 0x85},
+ {0x35, 31, 35, 0x86},
+ {0x35, 30, 117, 0x84},
+ {0x35, 0, 29, 0x81},
+ {0x35, 122, 115, 0x82},
+ {0x35, 0, 2, 0x72},
+ {0x35, 0, 30, 0x7c},
+ {0x35, 29, 25, 0x7d},
+ {0x35, 0, 24, 0x66},
+ {0x35, 118, 27, 0x71},
+ {0x35, 0, 6, 0x5b},
+ {0x35, 0, 3, 0x61},
+ {0x35, 0, 1, 0x63},
+ {0x35, 23, 22, 0x64},
+ {0x35, 155, 22, 0x62},
+ {0x35, 0, 20, 0x5c},
+ {0x35, 16, 20, 0x5d},
+ {0x35, 0, 2, 0x58},
+ {0x35, 0, 17, 0x59},
+ {0x35, 13, 17, 0x5a},
+ {0x35, 0, 15, 0x4f},
+ {0x35, 15, 11, 0x51},
+ {0x35, 0, 15, 0x2c},
+ {0x35, 0, 6, 0x3b},
+ {0x35, 0, 3, 0x3e},
+ {0x35, 0, 1, 0x48},
+ {0x35, 10, 6, 0x4a},
+ {0x35, 9, 5, 0x44},
+ {0x35, 0, 4, 0x3c},
+ {0x35, 6, 7, 0x3d},
+ {0x35, 0, 3, 0x34},
+ {0x35, 0, 4, 0x38},
+ {0x35, 4, 0, 0x3a},
+ {0x5, 0, 0, 0x104},
+ {0x35, 0, 2, 0x2d},
+ {0x35, 1, 0, 0x33},
+ {0x5, 0, 0, 0x102},
+ {0x5, 0, 0, 0x102},
+ {0x35, 0, 5, 0x1d},
+ {0x35, 0, 2, 0x21},
+ {0x35, 0, 254, 0x27},
+ {0x35, 253, 254, 0x2b},
+ {0x35, 0, 251, 0x1e},
+ {0x35, 250, 252, 0x20},
+ {0x35, 0, 2, 0x14},
+ {0x35, 0, 248, 0x19},
+ {0x35, 249, 179, 0x1a},
+ {0x35, 0, 248, 0x11},
+ {0x35, 247, 246, 0x13},
+ {0x20, 0, 0, 0x24},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 235, 0xffffffff},
+ {0x20, 0, 0, 0x20},
+ {0x45, 0, 233, 0x80000000},
+ {0x20, 0, 0, 0x20},
+ {0x15, 238, 239, 0x4},
+ {0x20, 0, 0, 0x24},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 228, 0xffffffff},
+ {0x20, 0, 0, 0x20},
+ {0x45, 0, 226, 0x80000000},
+ {0x20, 0, 0, 0x20},
+ {0x45, 233, 231, 0xfffffff8},
+ {0x20, 0, 0, 0x2c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 221, 0xffffffff},
+ {0x20, 0, 0, 0x28},
+ {0x45, 0, 219, 0x80000000},
+ {0x20, 0, 0, 0x28},
+ {0x45, 226, 224, 0xfffdb7cc},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 214, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 212, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 217, 219, 0x1},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 207, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 205, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 210, 0, 0x10},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 200, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 198, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 203, 0, 0xf},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 193, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 191, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 196, 0, 0x3},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 186, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 184, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 189, 0, 0x4},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 179, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 177, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 182, 0, 0x53564d41},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 172, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 170, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 175, 0, 0x29},
+ {0x6, 0, 0, 0x30005},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 164, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 162, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 167, 0, 0x1393},
+ {0x6, 0, 0, 0x30004},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 156, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 154, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 159, 0, 0x1},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 149, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 147, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 152, 0, 0x6},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 142, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 140, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 145, 0, 0x2},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 135, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 133, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 138, 0, 0x0},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 128, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 126, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 131, 0, 0x5},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 121, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 119, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 124, 126, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 114, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 112, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 117, 0xfffffe7f},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 107, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 105, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 109, 0, 0x1},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 99, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 97, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 101, 0, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 91, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 89, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 93, 0, 0x4},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 83, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 81, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 85, 0, 0x5},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 75, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 73, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 77, 0, 0x9},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 67, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 65, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 69, 0, 0xa},
+ {0x6, 0, 0, 0x30003},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 58, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 56, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 61, 0, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 51, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 49, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 54, 0, 0x1},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 44, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 42, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 47, 0, 0x2},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 37, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 35, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 40, 0, 0x6},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 30, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 28, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 33, 0, 0x7},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 23, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 21, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 26, 0, 0x5},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 16, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 14, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 19, 0, 0x0},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 9, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 7, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 12, 0, 0x406},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 4, 0, 0x0},
+ {0x15, 0, 2, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 1, 0, 0x80000000},
+ {0x6, 0, 0, 0x30002},
+ {0x20, 0, 0, 0x18},
+ {0x15, 0, 6, 0x4},
+ {0x20, 0, 0, 0x24},
+ {0x15, 0, 4, 0x0},
+ {0x20, 0, 0, 0x20},
+ {0x45, 2, 0, 0xffe1e3fc},
+ {0x6, 0, 0, 0x7fff0000},
+ {0x6, 0, 0, 0x50001},
+ {0x6, 0, 0, 0x30001},
+};
+#elif defined(__i386__)
+struct sock_filter kTestSeccompFilter[] = {
+ {0x20, 0, 0, 0x4},
+ {0x15, 1, 0, 0x40000003},
+ {0x6, 0, 0, 0x30007},
+ {0x20, 0, 0, 0x0},
+ {0x45, 0, 1, 0x40000000},
+ {0x6, 0, 0, 0x30006},
+ {0x35, 0, 87, 0x94},
+ {0x35, 0, 43, 0xdd},
+ {0x35, 0, 20, 0x11c},
+ {0x35, 0, 10, 0x13f},
+ {0x35, 0, 5, 0x149},
+ {0x35, 0, 2, 0x163},
+ {0x35, 0, 79, 0x164},
+ {0x35, 78, 73, 0x165},
+ {0x35, 0, 78, 0x14c},
+ {0x35, 71, 76, 0x161},
+ {0x35, 0, 2, 0x141},
+ {0x35, 0, 74, 0x144},
+ {0x35, 73, 68, 0x145},
+ {0x35, 67, 73, 0x140},
+ {0x35, 0, 4, 0x12d},
+ {0x35, 0, 2, 0x136},
+ {0x35, 0, 69, 0x137},
+ {0x35, 68, 63, 0x138},
+ {0x35, 68, 62, 0x134},
+ {0x35, 0, 2, 0x127},
+ {0x35, 0, 66, 0x128},
+ {0x35, 65, 59, 0x12c},
+ {0x35, 63, 64, 0x11d},
+ {0x35, 0, 11, 0xfe},
+ {0x35, 0, 6, 0x10a},
+ {0x35, 0, 3, 0x10e},
+ {0x35, 1, 0, 0x10f},
+ {0x5, 0, 0, 0x135},
+ {0x35, 57, 52, 0x110},
+ {0x35, 0, 56, 0x10c},
+ {0x35, 55, 50, 0x10d},
+ {0x35, 0, 2, 0x102},
+ {0x35, 0, 54, 0x103},
+ {0x35, 135, 52, 0x109},
+ {0x35, 51, 52, 0x101},
+ {0x35, 0, 4, 0xef},
+ {0x35, 0, 2, 0xf1},
+ {0x35, 0, 48, 0xfc},
+ {0x35, 42, 48, 0xfd},
+ {0x35, 153, 46, 0xf0},
+ {0x35, 0, 3, 0xe0},
+ {0x35, 0, 45, 0xe1},
+ {0x35, 0, 43, 0xee},
+ {0x5, 0, 0, 0x12a},
+ {0x35, 41, 252, 0xde},
+ {0x35, 0, 20, 0xb6},
+ {0x35, 0, 10, 0xc7},
+ {0x35, 0, 5, 0xd2},
+ {0x35, 0, 2, 0xd9},
+ {0x35, 0, 36, 0xdb},
+ {0x35, 30, 177, 0xdc},
+ {0x35, 0, 29, 0xd3},
+ {0x35, 28, 34, 0xd4},
+ {0x35, 0, 2, 0xcd},
+ {0x35, 0, 32, 0xce},
+ {0x35, 31, 25, 0xd1},
+ {0x35, 24, 30, 0xcb},
+ {0x35, 0, 4, 0xbf},
+ {0x35, 0, 2, 0xc1},
+ {0x35, 0, 21, 0xc5},
+ {0x35, 20, 26, 0xc6},
+ {0x35, 231, 25, 0xc0},
+ {0x35, 0, 2, 0xb9},
+ {0x35, 0, 17, 0xba},
+ {0x35, 21, 22, 0xbb},
+ {0x35, 21, 15, 0xb8},
+ {0x35, 0, 9, 0xa9},
+ {0x35, 0, 4, 0xb0},
+ {0x35, 0, 2, 0xb2},
+ {0x35, 0, 16, 0xb4},
+ {0x35, 15, 16, 0xb5},
+ {0x35, 15, 14, 0xb1},
+ {0x35, 0, 2, 0xab},
+ {0x35, 0, 13, 0xac},
+ {0x35, 12, 157, 0xad},
+ {0x35, 5, 10, 0xaa},
+ {0x35, 0, 5, 0xa2},
+ {0x35, 0, 2, 0xa5},
+ {0x35, 0, 8, 0xa6},
+ {0x35, 7, 6, 0xa8},
+ {0x35, 0, 6, 0xa4},
+ {0x5, 0, 0, 0x105},
+ {0x35, 0, 2, 0x98},
+ {0x35, 0, 2, 0x9e},
+ {0x35, 1, 2, 0x9f},
+ {0x35, 1, 0, 0x96},
+ {0x5, 0, 0, 0x102},
+ {0x5, 0, 0, 0x100},
+ {0x35, 0, 40, 0x4f},
+ {0x35, 0, 20, 0x6e},
+ {0x35, 0, 10, 0x7d},
+ {0x35, 0, 5, 0x8a},
+ {0x35, 0, 2, 0x8e},
+ {0x35, 0, 250, 0x90},
+ {0x35, 249, 250, 0x91},
+ {0x35, 0, 247, 0x8c},
+ {0x35, 246, 247, 0x8d},
+ {0x35, 0, 2, 0x7f},
+ {0x35, 0, 246, 0x85},
+ {0x35, 245, 243, 0x86},
+ {0x35, 243, 156, 0x7e},
+ {0x35, 0, 4, 0x76},
+ {0x35, 0, 2, 0x79},
+ {0x35, 0, 241, 0x7a},
+ {0x35, 240, 239, 0x7b},
+ {0x35, 238, 239, 0x77},
+ {0x35, 0, 2, 0x72},
+ {0x35, 0, 236, 0x73},
+ {0x35, 234, 236, 0x75},
+ {0x35, 235, 233, 0x6f},
+ {0x35, 0, 9, 0x60},
+ {0x35, 0, 4, 0x66},
+ {0x35, 0, 2, 0x6a},
+ {0x35, 0, 229, 0x6c},
+ {0x35, 230, 229, 0x6d},
+ {0x35, 229, 145, 0x67},
+ {0x35, 0, 2, 0x63},
+ {0x35, 0, 225, 0x64},
+ {0x35, 224, 226, 0x65},
+ {0x35, 225, 224, 0x62},
+ {0x35, 0, 4, 0x57},
+ {0x35, 0, 2, 0x5a},
+ {0x35, 0, 170, 0x5b},
+ {0x35, 219, 220, 0x5c},
+ {0x35, 218, 220, 0x59},
+ {0x35, 0, 2, 0x51},
+ {0x35, 0, 216, 0x52},
+ {0x35, 215, 216, 0x53},
+ {0x35, 215, 216, 0x50},
+ {0x35, 0, 20, 0x29},
+ {0x35, 0, 10, 0x38},
+ {0x35, 0, 5, 0x41},
+ {0x35, 0, 2, 0x46},
+ {0x35, 0, 209, 0x48},
+ {0x35, 209, 210, 0x4e},
+ {0x35, 0, 209, 0x43},
+ {0x35, 208, 207, 0x44},
+ {0x35, 0, 2, 0x3d},
+ {0x35, 0, 206, 0x3e},
+ {0x35, 204, 203, 0x3f},
+ {0x35, 202, 204, 0x3c},
+ {0x35, 0, 4, 0x30},
+ {0x35, 0, 2, 0x33},
+ {0x35, 0, 201, 0x36},
+ {0x35, 152, 199, 0x37},
+ {0x35, 198, 199, 0x31},
+ {0x35, 0, 2, 0x2d},
+ {0x35, 0, 196, 0x2e},
+ {0x35, 195, 194, 0x2f},
+ {0x35, 195, 194, 0x2b},
+ {0x35, 0, 9, 0x17},
+ {0x35, 0, 4, 0x1f},
+ {0x35, 0, 2, 0x22},
+ {0x35, 0, 191, 0x25},
+ {0x35, 188, 182, 0x26},
+ {0x35, 187, 189, 0x21},
+ {0x35, 0, 2, 0x19},
+ {0x35, 0, 187, 0x1d},
+ {0x35, 184, 185, 0x1e},
+ {0x35, 184, 183, 0x18},
+ {0x35, 0, 4, 0xe},
+ {0x35, 0, 2, 0x12},
+ {0x35, 0, 180, 0x13},
+ {0x35, 181, 180, 0x15},
+ {0x35, 180, 178, 0x11},
+ {0x35, 0, 2, 0x3},
+ {0x35, 0, 177, 0x8},
+ {0x35, 176, 175, 0xd},
+ {0x35, 174, 175, 0x2},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 168, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 171, 0, 0x1},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 164, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 167, 0, 0x6},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 160, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 163, 0, 0x2},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 156, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 159, 0, 0x0},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 152, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 155, 0, 0x5},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 148, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 151, 152, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 144, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 147, 0xfffffe7f},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 140, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 142, 0, 0x1},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 135, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 137, 0, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 130, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 132, 0, 0x4},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 125, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 127, 0, 0x5},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 120, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 122, 0, 0x9},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 115, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 117, 0, 0xa},
+ {0x6, 0, 0, 0x30005},
+ {0x20, 0, 0, 0x24},
+ {0x15, 0, 109, 0x0},
+ {0x20, 0, 0, 0x20},
+ {0x15, 112, 111, 0x4},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 105, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 108, 0, 0x10},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 101, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 104, 0, 0xf},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 97, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 100, 0, 0x3},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 93, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 96, 0, 0x4},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 89, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 92, 0, 0x53564d41},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 85, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 88, 0, 0x29},
+ {0x6, 0, 0, 0x30004},
+ {0x20, 0, 0, 0x24},
+ {0x15, 0, 80, 0x0},
+ {0x20, 0, 0, 0x20},
+ {0x45, 84, 83, 0xfffffff8},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 76, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 79, 0, 0x8},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 72, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 75, 0, 0xd},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 68, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 71, 0, 0xa},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 64, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 67, 0, 0x9},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 60, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 63, 0, 0xc},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 56, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 59, 0, 0xb},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 52, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 55, 0, 0x11},
+ {0x20, 0, 0, 0x14},
+ {0x15, 0, 48, 0x0},
+ {0x20, 0, 0, 0x10},
+ {0x15, 51, 50, 0x10},
+ {0x20, 0, 0, 0x2c},
+ {0x15, 0, 44, 0x0},
+ {0x20, 0, 0, 0x28},
+ {0x45, 48, 47, 0xfffdb7cc},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 40, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 43, 0, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 36, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 39, 0, 0x1},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 32, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 35, 0, 0x2},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 28, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 31, 0, 0x6},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 24, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 27, 0, 0x7},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 20, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 23, 0, 0x5},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 16, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 19, 0, 0x0},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 12, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 15, 0, 0x406},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 0, 8, 0x0},
+ {0x20, 0, 0, 0x18},
+ {0x15, 0, 12, 0x4},
+ {0x20, 0, 0, 0x24},
+ {0x15, 0, 4, 0x0},
+ {0x20, 0, 0, 0x20},
+ {0x45, 8, 7, 0xfff363fc},
+ {0x20, 0, 0, 0x14},
+ {0x15, 1, 0, 0x0},
+ {0x6, 0, 0, 0x30003},
+ {0x20, 0, 0, 0x10},
+ {0x15, 2, 0, 0x9d0},
+ {0x6, 0, 0, 0x30002},
+ {0x6, 0, 0, 0x50001},
+ {0x6, 0, 0, 0x7fff0000},
+ {0x6, 0, 0, 0x30001},
+};
+#elif defined(__x86_64__)
+struct sock_filter kTestSeccompFilter[] = {
+ {0x20, 0, 0, 0x4},
+ {0x15, 1, 0, 0xc000003e},
+ {0x6, 0, 0, 0x30007},
+ {0x20, 0, 0, 0x0},
+ {0x45, 0, 1, 0x40000000},
+ {0x6, 0, 0, 0x30006},
+ {0x35, 0, 59, 0x7f},
+ {0x35, 0, 29, 0xe4},
+ {0x35, 0, 14, 0x111},
+ {0x35, 0, 7, 0x120},
+ {0x35, 0, 3, 0x13c},
+ {0x35, 0, 1, 0x13f},
+ {0x35, 102, 95, 0x140},
+ {0x35, 101, 94, 0x13e},
+ {0x35, 0, 1, 0x123},
+ {0x35, 99, 94, 0x126},
+ {0x35, 98, 91, 0x121},
+ {0x35, 0, 3, 0x119},
+ {0x35, 0, 1, 0x11d},
+ {0x35, 95, 88, 0x11e},
+ {0x35, 94, 89, 0x11a},
+ {0x35, 0, 86, 0x112},
+ {0x35, 85, 92, 0x118},
+ {0x35, 0, 6, 0xf8},
+ {0x35, 0, 3, 0x106},
+ {0x35, 0, 1, 0x10e},
+ {0x35, 88, 83, 0x110},
+ {0x35, 80, 82, 0x107},
+ {0x35, 0, 86, 0x101},
+ {0x35, 78, 80, 0x102},
+ {0x35, 0, 4, 0xea},
+ {0x35, 0, 1, 0xec},
+ {0x35, 77, 82, 0xf7},
+ {0x35, 74, 0, 0xeb},
+ {0x5, 0, 0, 0x12a},
+ {0x35, 0, 89, 0xe5},
+ {0x35, 73, 78, 0xe7},
+ {0x35, 0, 15, 0xac},
+ {0x35, 0, 7, 0xcb},
+ {0x35, 0, 3, 0xd9},
+ {0x35, 0, 1, 0xdc},
+ {0x35, 73, 66, 0xdd},
+ {0x35, 67, 65, 0xda},
+ {0x35, 0, 1, 0xd5},
+ {0x35, 70, 65, 0xd6},
+ {0x35, 62, 69, 0xd4},
+ {0x35, 0, 4, 0xbb},
+ {0x35, 0, 1, 0xc9},
+ {0x35, 118, 61, 0xca},
+ {0x35, 0, 65, 0xc8},
+ {0x5, 0, 0, 0x121},
+ {0x35, 0, 56, 0xae},
+ {0x35, 57, 62, 0xba},
+ {0x35, 0, 6, 0x8a},
+ {0x35, 0, 3, 0x95},
+ {0x35, 0, 1, 0x9d},
+ {0x35, 58, 166, 0x9e},
+ {0x35, 57, 52, 0x97},
+ {0x35, 0, 56, 0x8c},
+ {0x35, 55, 50, 0x8e},
+ {0x35, 0, 3, 0x83},
+ {0x35, 0, 1, 0x87},
+ {0x35, 45, 52, 0x88},
+ {0x35, 44, 46, 0x84},
+ {0x35, 0, 50, 0x80},
+ {0x35, 49, 44, 0x81},
+ {0x35, 0, 28, 0x3b},
+ {0x35, 0, 14, 0x69},
+ {0x35, 0, 7, 0x74},
+ {0x35, 0, 3, 0x79},
+ {0x35, 0, 1, 0x7c},
+ {0x35, 36, 38, 0x7e},
+ {0x35, 35, 42, 0x7a},
+ {0x35, 0, 1, 0x77},
+ {0x35, 35, 33, 0x78},
+ {0x35, 34, 32, 0x76},
+ {0x35, 0, 3, 0x6e},
+ {0x35, 0, 1, 0x71},
+ {0x35, 31, 29, 0x73},
+ {0x35, 35, 30, 0x6f},
+ {0x35, 0, 27, 0x6b},
+ {0x35, 33, 28, 0x6d},
+ {0x35, 0, 6, 0x4a},
+ {0x35, 0, 3, 0x62},
+ {0x35, 0, 1, 0x67},
+ {0x35, 24, 29, 0x68},
+ {0x35, 23, 28, 0x66},
+ {0x35, 0, 27, 0x4c},
+ {0x35, 21, 19, 0x60},
+ {0x35, 0, 3, 0x3f},
+ {0x35, 0, 1, 0x48},
+ {0x35, 18, 174, 0x49},
+ {0x35, 15, 17, 0x40},
+ {0x35, 0, 14, 0x3c},
+ {0x35, 238, 15, 0x3e},
+ {0x35, 0, 15, 0x1d},
+ {0x35, 0, 6, 0x31},
+ {0x35, 0, 3, 0x36},
+ {0x35, 0, 1, 0x39},
+ {0x35, 15, 8, 0x3a},
+ {0x35, 9, 14, 0x37},
+ {0x35, 0, 6, 0x33},
+ {0x35, 238, 12, 0x35},
+ {0x35, 0, 3, 0x27},
+ {0x35, 0, 1, 0x29},
+ {0x35, 4, 2, 0x2c},
+ {0x35, 8, 3, 0x28},
+ {0x35, 1, 0, 0x20},
+ {0x5, 0, 0, 0x105},
+ {0x35, 5, 0, 0x24},
+ {0x5, 0, 0, 0x104},
+ {0x35, 0, 7, 0xb},
+ {0x35, 0, 4, 0x15},
+ {0x35, 0, 2, 0x1a},
+ {0x35, 233, 0, 0x1c},
+ {0x5, 0, 0, 0x100},
+ {0x35, 254, 253, 0x16},
+ {0x35, 0, 253, 0x12},
+ {0x35, 252, 253, 0x13},
+ {0x35, 0, 3, 0x6},
+ {0x35, 0, 1, 0x9},
+ {0x35, 233, 240, 0xa},
+ {0x35, 248, 247, 0x7},
+ {0x35, 0, 247, 0x4},
+ {0x35, 246, 245, 0x5},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 239, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 237, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 239, 0, 0x1},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 232, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 230, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 232, 0, 0x6},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 225, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 223, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 225, 0, 0x2},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 218, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 216, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 218, 0, 0x0},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 211, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 209, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 211, 0, 0x5},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 204, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 202, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 204, 205, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 197, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 195, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 197, 0xfffffe7f},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 190, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 188, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 189, 0, 0x1},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 182, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 180, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 181, 0, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 174, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 172, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 173, 0, 0x4},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 166, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 164, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 165, 0, 0x5},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 158, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 156, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 157, 0, 0x9},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 150, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 148, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x54, 0, 0, 0xfffffe7f},
+ {0x15, 149, 0, 0xa},
+ {0x6, 0, 0, 0x30005},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 141, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 139, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 141, 0, 0x10},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 134, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 132, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 134, 0, 0xf},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 127, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 125, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 127, 0, 0x3},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 120, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 118, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 120, 0, 0x4},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 113, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 111, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 113, 0, 0x53564d41},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 106, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 104, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 106, 0, 0x29},
+ {0x6, 0, 0, 0x30004},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 98, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 96, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 98, 0, 0x3},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 91, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 89, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 91, 0, 0x1},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 84, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 82, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 84, 0, 0x2},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 77, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 75, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 77, 0, 0x6},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 70, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 68, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 70, 0, 0x7},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 63, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 61, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 63, 0, 0x5},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 56, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 54, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 56, 0, 0x0},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 49, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 47, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 49, 0, 0x406},
+ {0x20, 0, 0, 0x1c},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 42, 0xffffffff},
+ {0x20, 0, 0, 0x18},
+ {0x45, 0, 40, 0x80000000},
+ {0x20, 0, 0, 0x18},
+ {0x15, 0, 43, 0x4},
+ {0x20, 0, 0, 0x24},
+ {0x15, 0, 41, 0x0},
+ {0x20, 0, 0, 0x20},
+ {0x45, 39, 38, 0xffe363fc},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 31, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 29, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 31, 0, 0xa57},
+ {0x6, 0, 0, 0x30003},
+ {0x20, 0, 0, 0x14},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 23, 0xffffffff},
+ {0x20, 0, 0, 0x10},
+ {0x45, 0, 21, 0x80000000},
+ {0x20, 0, 0, 0x10},
+ {0x15, 23, 24, 0x1},
+ {0x20, 0, 0, 0x24},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 16, 0xffffffff},
+ {0x20, 0, 0, 0x20},
+ {0x45, 0, 14, 0x80000000},
+ {0x20, 0, 0, 0x20},
+ {0x15, 16, 15, 0x4},
+ {0x20, 0, 0, 0x24},
+ {0x15, 3, 0, 0x0},
+ {0x15, 0, 9, 0xffffffff},
+ {0x20, 0, 0, 0x20},
+ {0x45, 0, 7, 0x80000000},
+ {0x20, 0, 0, 0x20},
+ {0x45, 10, 9, 0xfffffff8},
+ {0x20, 0, 0, 0x2c},
+ {0x15, 4, 0, 0x0},
+ {0x15, 0, 2, 0xffffffff},
+ {0x20, 0, 0, 0x28},
+ {0x45, 1, 0, 0x80000000},
+ {0x6, 0, 0, 0x30002},
+ {0x20, 0, 0, 0x28},
+ {0x45, 2, 1, 0xfffdb7cc},
+ {0x6, 0, 0, 0x50001},
+ {0x6, 0, 0, 0x7fff0000},
+ {0x6, 0, 0, 0x30001},
+};
+#endif
+
+struct sock_fprog GetTestSeccompFilterProgram() {
+ struct sock_fprog prog = {
+ .len = sizeof(kTestSeccompFilter) / sizeof(struct sock_filter),
+ .filter = kTestSeccompFilter
+ };
+ return prog;
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java b/tests/tests/os/jni/seccomp_sample_program.h
similarity index 72%
rename from apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java
rename to tests/tests/os/jni/seccomp_sample_program.h
index 4231db7..1293572 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSettingsActivity.java
+++ b/tests/tests/os/jni/seccomp_sample_program.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package com.android.cts.verifier.tv;
+#include <linux/filter.h>
-import android.preference.PreferenceActivity;
-
-public class MockTvInputSettingsActivity extends PreferenceActivity {
-
-}
+struct sock_fprog GetTestSeccompFilterProgram();
diff --git a/tests/tests/os/src/android/os/cts/ISeccompIsolatedService.aidl b/tests/tests/os/src/android/os/cts/ISeccompIsolatedService.aidl
new file mode 100644
index 0000000..5234eff
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ISeccompIsolatedService.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.cts;
+
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+
+interface ISeccompIsolatedService {
+ boolean installFilter();
+
+ boolean createThread();
+
+ boolean getSystemInfo();
+
+ boolean writeToFile(in ParcelFileDescriptor fd);
+
+ boolean openAshmem();
+
+ boolean openDevFile();
+
+ void violatePolicy();
+}
diff --git a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
index 2287660..3bcc4b9 100644
--- a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
@@ -358,6 +358,15 @@
}
}
+ // http://b/21578056
+ public void testFileNamesWithNonBmpChars() throws Exception {
+ final File file = File.createTempFile("treble_clef_\ud834\udd1e", ".tmp");
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ assertNotNull(pfd);
+ pfd.close();
+ }
+
static ParcelFileDescriptor makeParcelFileDescriptor(Context con) throws Exception {
final String fileName = "testParcelFileDescriptor";
diff --git a/tests/tests/os/src/android/os/cts/SeccompTest.java b/tests/tests/os/src/android/os/cts/SeccompTest.java
index e8de783..4c2f78f 100644
--- a/tests/tests/os/src/android/os/cts/SeccompTest.java
+++ b/tests/tests/os/src/android/os/cts/SeccompTest.java
@@ -16,9 +16,36 @@
package android.os.cts;
-import junit.framework.TestCase;
+import android.app.Service;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.MemoryFile;
+import android.os.SystemClock;
+import android.os.Build;
+import android.util.Log;
+import android.test.AndroidTestCase;
-public class SeccompTest extends TestCase {
+import com.google.common.util.concurrent.AbstractFuture;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.Date;
+
+public class SeccompTest extends AndroidTestCase {
+ final static String TAG = "SeccompTest";
+
static {
System.loadLibrary("ctsos_jni");
}
@@ -142,7 +169,218 @@
}
/**
+ * Integration test for seccomp-bpf policy applied to an isolatedProcess=true
+ * service. This will perform various operations in an isolated process under a
+ * fairly restrictive seccomp policy.
+ */
+ public void testIsolatedServicePolicy() throws InterruptedException, ExecutionException,
+ RemoteException {
+ if (!OSFeatures.needsSeccompSupport())
+ return;
+
+ final IsolatedServiceConnection peer = new IsolatedServiceConnection();
+ final Intent intent = new Intent(getContext(), IsolatedService.class);
+ assertTrue(getContext().bindService(intent, peer, Context.BIND_AUTO_CREATE));
+
+ final ISeccompIsolatedService service = peer.get();
+
+ // installFilter() must be called first, to set the seccomp policy.
+ assertTrue(service.installFilter());
+ assertTrue(service.createThread());
+ assertTrue(service.getSystemInfo());
+ doFileWriteTest(service);
+ assertTrue(service.openAshmem());
+ assertTrue(service.openDevFile());
+
+ getContext().unbindService(peer);
+ }
+
+ /**
+ * Integration test for seccomp-bpf policy with isolatedProcess, where the
+ * process then violates the policy and gets killed by the kernel.
+ */
+ public void testViolateIsolatedServicePolicy() throws InterruptedException,
+ ExecutionException, RemoteException {
+ if (!OSFeatures.needsSeccompSupport())
+ return;
+
+ final IsolatedServiceConnection peer = new IsolatedServiceConnection();
+ final Intent intent = new Intent(getContext(), IsolatedService.class);
+ assertTrue(getContext().bindService(intent, peer, Context.BIND_AUTO_CREATE));
+
+ final ISeccompIsolatedService service = peer.get();
+
+ assertTrue(service.installFilter());
+ boolean gotRemoteException = false;
+ try {
+ service.violatePolicy();
+ } catch (RemoteException e) {
+ gotRemoteException = true;
+ }
+ assertTrue(gotRemoteException);
+
+ getContext().unbindService(peer);
+ }
+
+ private void doFileWriteTest(ISeccompIsolatedService service) throws RemoteException {
+ final String fileName = "seccomp_test";
+ ParcelFileDescriptor fd = null;
+ try {
+ FileOutputStream fOut = getContext().openFileOutput(fileName, 0);
+ fd = ParcelFileDescriptor.dup(fOut.getFD());
+ fOut.close();
+ } catch (FileNotFoundException e) {
+ fail(e.getMessage());
+ return;
+ } catch (IOException e) {
+ fail(e.getMessage());
+ return;
+ }
+
+ assertTrue(service.writeToFile(fd));
+
+ try {
+ FileInputStream fIn = getContext().openFileInput(fileName);
+ assertEquals('!', fIn.read());
+ fIn.close();
+ } catch (FileNotFoundException e) {
+ fail(e.getMessage());
+ } catch (IOException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ class IsolatedServiceConnection extends AbstractFuture<ISeccompIsolatedService>
+ implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ set(ISeccompIsolatedService.Stub.asInterface(service));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+
+ @Override
+ public ISeccompIsolatedService get() throws InterruptedException, ExecutionException {
+ try {
+ return get(10, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static class IsolatedService extends Service {
+ private final ISeccompIsolatedService.Stub mService = new ISeccompIsolatedService.Stub() {
+ public boolean installFilter() {
+ return installTestFilter();
+ }
+
+ public boolean createThread() {
+ Thread thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ }
+ });
+ thread.run();
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean getSystemInfo() {
+ long uptimeMillis = SystemClock.uptimeMillis();
+ if (uptimeMillis < 1) {
+ Log.d(TAG, "SystemClock failed");
+ return false;
+ }
+
+ String version = Build.VERSION.CODENAME;
+ if (version.length() == 0) {
+ Log.d(TAG, "Build.VERSION failed");
+ return false;
+ }
+
+ long time = (new Date()).getTime();
+ if (time < 100) {
+ Log.d(TAG, "getTime failed");
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean writeToFile(ParcelFileDescriptor fd) {
+ FileOutputStream fOut = new FileOutputStream(fd.getFileDescriptor());
+ try {
+ fOut.write('!');
+ fOut.close();
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean openAshmem() {
+ byte[] buffer = {'h', 'e', 'l', 'l', 'o'};
+ try {
+ MemoryFile file = new MemoryFile("seccomp_isolated_test", 32);
+ file.writeBytes(buffer, 0, 0, buffer.length);
+ file.close();
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ public boolean openDevFile() {
+ try {
+ FileInputStream fIn = new FileInputStream("/dev/zero");
+ boolean succeed = fIn.read() == 0;
+ succeed &= fIn.read() == 0;
+ succeed &= fIn.read() == 0;
+ fIn.close();
+ return succeed;
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ public void violatePolicy() {
+ getClockBootTime();
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mService;
+ }
+ }
+
+ /**
* Runs the seccomp_bpf_unittest of the given name.
*/
private native boolean runKernelUnitTest(final String name);
+
+ /**
+ * Installs a test seccomp-bpf filter program that.
+ */
+ private native static boolean installTestFilter();
+
+ /**
+ * Attempts to get the CLOCK_BOOTTIME, which is a violation of the
+ * policy specified by installTestFilter().
+ */
+ private native static int getClockBootTime();
}
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 797f91f..7ebe817 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -58,7 +58,7 @@
+ android.os.Process.myUid();
// Insecure connection should be detected
- ((HttpURLConnection) new URL("http://android.com/").openConnection()).getResponseCode();
+ ((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
// Give system enough time to finish logging
SystemClock.sleep(5000);
@@ -76,7 +76,7 @@
+ android.os.Process.myUid();
// Secure connection should be ignored
- ((HttpURLConnection) new URL("https://android.com/").openConnection()).getResponseCode();
+ ((HttpURLConnection) new URL("https://example.com/").openConnection()).getResponseCode();
// Give system enough time to finish logging
SystemClock.sleep(5000);
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 4050f53..04b8554 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -158,7 +158,6 @@
assertFalse(f.canRead());
assertFalse(f.canWrite());
assertFalse(f.canExecute());
- assertFalse(f.exists());
}
@MediumTest
@@ -179,7 +178,6 @@
assertFalse(f.canRead());
assertFalse(f.canWrite());
assertFalse(f.canExecute());
- assertFalse(f.exists());
}
@MediumTest
diff --git a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
index 105fb81..2dd5892 100644
--- a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
@@ -16,7 +16,6 @@
package android.permission.cts;
-import android.provider.Browser;
import android.provider.CallLog;
import android.provider.Contacts;
import android.test.AndroidTestCase;
@@ -77,45 +76,4 @@
assertWritingContentUriRequiresPermission(android.provider.Settings.System.CONTENT_URI,
android.Manifest.permission.WRITE_SETTINGS);
}
-
- /**
- * Verify that read and write to browser bookmarks requires permissions.
- * <p>Tests Permission:
- * {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
- */
- public void testReadBookmarks() {
- assertReadingContentUriRequiresPermission(Browser.BOOKMARKS_URI,
- android.Manifest.permission.READ_HISTORY_BOOKMARKS);
- }
-
- /**
- * Verify that read and write to browser bookmarks requires permissions.
- * <p>Tests Permission:
- {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
- */
- public void testWriteBookmarks() {
- assertWritingContentUriRequiresPermission(Browser.BOOKMARKS_URI,
- android.Manifest.permission.WRITE_HISTORY_BOOKMARKS);
- }
-
- /**
- * Verify that read and write to browser history requires permissions.
- * <p>Tests Permission:
- * {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
- */
- public void testReadBrowserHistory() {
- assertReadingContentUriRequiresPermission(Browser.SEARCHES_URI,
- android.Manifest.permission.READ_HISTORY_BOOKMARKS);
- }
-
- /**
- * Verify that read and write to browser history requires permissions.
- * <p>Tests Permission:
- {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
- */
- public void testWriteBrowserHistory() {
- assertWritingContentUriRequiresPermission(Browser.SEARCHES_URI,
- android.Manifest.permission.WRITE_HISTORY_BOOKMARKS);
- }
}
-
diff --git a/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java
deleted file mode 100644
index eaf6fdf..0000000
--- a/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2012 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.permission2.cts;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.StreamItems;
-import android.test.AndroidTestCase;
-
-public class ReadSocialStreamPermissionTest extends AndroidTestCase {
-
- private ContentResolver mResolver;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResolver = mContext.getContentResolver();
- }
-
- public void testReadSocialStreamPermission_byRawContactId() throws Exception {
- try {
- mResolver.query(Uri.withAppendedPath(
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, 1337),
- Contacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null);
- fail("Expected a READ_SOCIAL_STREAM exception");
- } catch (SecurityException e) {
- // Expect a READ_SOCIAL_STREAM exception.
- }
- }
-
- public void testReadSocialStreamPermission_byContactId() throws Exception {
- try {
- mResolver.query(Uri.withAppendedPath(
- ContentUris.withAppendedId(Contacts.CONTENT_URI, 1337),
- Contacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null);
- fail("Expected a READ_SOCIAL_STREAM exception");
- } catch (SecurityException e) {
- // Expect a READ_SOCIAL_STREAM exception.
- }
- }
-
- public void testReadSocialStreamPermission_byLookUpKey() throws Exception {
- try {
- mResolver.query(Contacts.CONTENT_LOOKUP_URI.buildUpon()
- .appendPath("lookDownKey")
- .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY)
- .build(), null, null, null, null);
- fail("Expected a READ_SOCIAL_STREAM exception");
- } catch (SecurityException e) {
- // Expect a READ_SOCIAL_STREAM exception.
- }
- }
-
- public void testReadSocialStreamPermission_byStreamItemId() throws Exception {
- try {
- mResolver.query(ContentUris.withAppendedId(StreamItems.CONTENT_URI, 1337),
- null, null, null, null);
- fail("Expected a READ_SOCIAL_STREAM exception");
- } catch (SecurityException e) {
- // Expect a READ_SOCIAL_STREAM exception.
- }
- }
-}
diff --git a/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java
deleted file mode 100644
index 262fcfa..0000000
--- a/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012 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.permission2.cts;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.net.Uri;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.StreamItems;
-import android.test.AndroidTestCase;
-
-public class WriteSocialStreamPermissionTest extends AndroidTestCase {
-
- private ContentResolver mResolver;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResolver = mContext.getContentResolver();
- }
-
- public void testWriteSocialStreamPermission_byContentDirectory() throws Exception {
- try {
- ContentValues values = new ContentValues();
- mResolver.insert(Uri.withAppendedPath(
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, 1337),
- RawContacts.StreamItems.CONTENT_DIRECTORY), values);
- fail("Expected a WRITE_SOCIAL_STREAM exception");
- } catch (SecurityException e) {
- // Expect a WRITE_SOCIAL_STREAM exception.
- }
- }
-
- public void testWriteSocialStreamPermission_byContentUri() throws Exception {
- try {
- ContentValues values = new ContentValues();
- mResolver.insert(StreamItems.CONTENT_URI, values);
- fail("Expected a WRITE_SOCIAL_STREAM exception");
- } catch (SecurityException e) {
- // Expect a WRITE_SOCIAL_STREAM exception.
- }
- }
-}
diff --git a/tests/tests/provider/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 855c4ce..b1a7130 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -27,8 +27,6 @@
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
- <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
- <uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -39,10 +37,8 @@
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_SOCIAL_STREAM" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
- <uses-permission android:name="android.permission.READ_SOCIAL_STREAM" />
<application>
<uses-library android:name="android.test.runner"/>
diff --git a/tests/tests/provider/src/android/provider/cts/BrowserTest.java b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
deleted file mode 100644
index 9f96412..0000000
--- a/tests/tests/provider/src/android/provider/cts/BrowserTest.java
+++ /dev/null
@@ -1,697 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider.cts;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.Browser;
-import android.provider.Browser.BookmarkColumns;
-import android.provider.Browser.SearchColumns;
-import android.test.ActivityInstrumentationTestCase2;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-public class BrowserTest extends ActivityInstrumentationTestCase2<BrowserStubActivity> {
- public BrowserTest() {
- super("com.android.cts.provider", BrowserStubActivity.class);
- }
-
- private Context mContext;
- private ContentResolver mContentResolver;
- private ContentProviderClient mProvider;
- private BrowserStubActivity mActivity;
- private boolean mMasterSyncEnabled;
-
- // the backup for the 2 tables which we will modify in test cases
- private ArrayList<ContentValues> mBookmarksBackup;
- private ArrayList<ContentValues> mSearchesBackup;
-
- private static final String ADD_BOOKMARK_CLASS_NAME =
- "com.android.browser.AddBookmarkPage";
- private static final String COMPOSE_MESSAGE_CLASS_NAME =
- "com.android.mms.ui.ComposeMessageActivity";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mContext = getInstrumentation().getTargetContext();
- mContentResolver = mContext.getContentResolver();
- mProvider = mContentResolver.acquireContentProviderClient(
- Browser.BOOKMARKS_URI.getAuthority());
- mBookmarksBackup = new ArrayList<ContentValues>();
- mSearchesBackup = new ArrayList<ContentValues>();
-
- // Disable sync
- mMasterSyncEnabled = ContentResolver.getMasterSyncAutomatically();
- ContentResolver.setMasterSyncAutomatically(false);
-
- // backup the current contents in database
- Cursor cursor = mProvider.query(Browser.BOOKMARKS_URI, null, null, null, null, null);
- while (cursor.moveToNext()) {
- String[] colNames = cursor.getColumnNames();
- ContentValues value = new ContentValues();
-
- for (int i = 0; i < colNames.length; i++) {
- switch (cursor.getType(i)) {
- case Cursor.FIELD_TYPE_BLOB:
- value.put(colNames[i], cursor.getBlob(i));
- break;
- case Cursor.FIELD_TYPE_FLOAT:
- value.put(colNames[i], cursor.getFloat(i));
- break;
- case Cursor.FIELD_TYPE_INTEGER:
- if (!"_ID".equalsIgnoreCase(colNames[i])) {
- value.put(colNames[i], cursor.getLong(i));
- }
- break;
- case Cursor.FIELD_TYPE_STRING:
- value.put(colNames[i], cursor.getString(i));
- break;
- }
- }
- mBookmarksBackup.add(value);
- }
-
- cursor.close();
-
- cursor = mProvider.query(Browser.SEARCHES_URI, null, null, null, null, null);
- if (cursor.moveToFirst()) {
- while (!cursor.isAfterLast()) {
- ContentValues value = new ContentValues();
-
- value.put(SearchColumns._ID, cursor.getInt(0));
- value.put(SearchColumns.SEARCH, cursor.getString(1));
- value.put(SearchColumns.DATE, cursor.getLong(2));
- mSearchesBackup.add(value);
-
- cursor.moveToNext();
- };
- }
- cursor.close();
-
- mProvider.delete(Browser.BOOKMARKS_URI, null, null);
- mProvider.delete(Browser.SEARCHES_URI, null, null);
-
- mActivity = getActivity();
- }
-
- @Override
- protected void tearDown() throws Exception {
- try {
- // clear all new contents added in test cases.
- mProvider.delete(Browser.BOOKMARKS_URI, null, null);
- mProvider.delete(Browser.SEARCHES_URI, null, null);
-
- // recover the old backup contents
- for (ContentValues value : mBookmarksBackup) {
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- }
-
- for (ContentValues value : mSearchesBackup) {
- mProvider.insert(Browser.SEARCHES_URI, value);
- }
-
- // Re-enable sync
- ContentResolver.setMasterSyncAutomatically(mMasterSyncEnabled);
- } finally {
- super.tearDown();
- }
- }
-
- public void testAccessSearches() {
- final String searchString = "search string";
- final String searchStringAnother = "another search string";
- Cursor cursor;
-
- try {
- Browser.addSearchUrl(mContentResolver, searchString);
- cursor = mProvider.query(
- Browser.SEARCHES_URI,
- Browser.SEARCHES_PROJECTION,
- null, null, null, null);
- assertEquals(1, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(searchString,
- cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
- long oldDate = cursor.getLong(Browser.SEARCHES_PROJECTION_DATE_INDEX);
- cursor.close();
-
- Browser.addSearchUrl(mContentResolver, searchString);
- cursor = mProvider.query(Browser.SEARCHES_URI,
- Browser.SEARCHES_PROJECTION,
- null, null, null, null);
- assertEquals(1, cursor.getCount());
- cursor.moveToFirst();
- long date = cursor.getLong(Browser.SEARCHES_PROJECTION_DATE_INDEX);
- assertTrue(date > oldDate);
- assertEquals(searchString,
- cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
- cursor.close();
-
- Browser.addSearchUrl(mContentResolver, searchStringAnother);
- cursor = mProvider.query(Browser.SEARCHES_URI,
- Browser.SEARCHES_PROJECTION,
- null, null, null, null);
- assertEquals(2, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(searchString,
- cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
- cursor.moveToNext();
- assertEquals(searchStringAnother,
- cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
- cursor.close();
-
- Browser.clearSearches(mContentResolver);
- cursor = mProvider.query(
- Browser.SEARCHES_URI,
- Browser.SEARCHES_PROJECTION,
- null, null, null, null);
- assertEquals(0, cursor.getCount());
- } catch (RemoteException e) {
- fail("Unexpected RemoteException");
- }
- }
-
- public void testGetAllBookmarks() {
- Cursor cursor;
- final String bookmarkUrl1 = "www.bookmark1.com";
- final String bookmarkUrl2 = "www.bookmark2.com";
- final String historyUrl = "www.history.com";
-
- try {
- cursor = Browser.getAllBookmarks(mContentResolver);
- assertEquals(0, cursor.getCount());
- cursor.close();
-
- ContentValues value = new ContentValues();
- value.put(BookmarkColumns.URL, bookmarkUrl1);
- value.put(BookmarkColumns.BOOKMARK, 1);
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- value.put(BookmarkColumns.URL, bookmarkUrl2);
- value.put(BookmarkColumns.BOOKMARK, 1);
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- value.put(BookmarkColumns.URL, historyUrl);
- value.put(BookmarkColumns.BOOKMARK, 0);
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- cursor = Browser.getAllBookmarks(mContentResolver);
- assertEquals(2, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(bookmarkUrl1, cursor.getString(0));
- cursor.moveToNext();
- assertEquals(bookmarkUrl2, cursor.getString(0));
- } catch (RemoteException e) {
- fail("unexpected RemoteException");
- }
- }
-
- public void testGetAllVisitedUrls() {
- Cursor cursor;
- final String visitedUrl1 = "www.visited1.com";
- final String visitedUrl2 = "www.visited2.com";
- final String visitedUrl3 = "www.visited3.com";
-
- try {
- cursor = Browser.getAllVisitedUrls(mContentResolver);
- assertEquals(0, cursor.getCount());
- cursor.close();
-
- ContentValues value = new ContentValues();
- value.put(BookmarkColumns.URL, visitedUrl1);
- value.put(BookmarkColumns.BOOKMARK, 1);
- value.put(BookmarkColumns.VISITS, 1);
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- value.put(BookmarkColumns.URL, visitedUrl2);
- value.put(BookmarkColumns.BOOKMARK, 0);
- value.put(BookmarkColumns.VISITS, 5);
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- value.put(BookmarkColumns.URL, visitedUrl3);
- value.put(BookmarkColumns.BOOKMARK, 1);
- value.put(BookmarkColumns.VISITS, 0);
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- cursor = Browser.getAllVisitedUrls(mContentResolver);
- assertEquals(3, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(visitedUrl1, cursor.getString(0));
- cursor.moveToNext();
- assertEquals(visitedUrl2, cursor.getString(0));
- cursor.moveToNext();
- assertEquals(visitedUrl3, cursor.getString(0));
- } catch (RemoteException e) {
- fail("unexpected RemoteException");
- }
- }
-
- public void testUpdateVisitedHistory() {
- Cursor cursor;
- final String visitedHistoryUrl = "www.visited-history.com";
- long oldTime = 0;
-
- try {
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- null, null, null, null);
- assertEquals(0, cursor.getCount());
- cursor.close();
- Browser.updateVisitedHistory(mContentResolver, visitedHistoryUrl, true);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- null, null, null, null);
- assertEquals(1, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(visitedHistoryUrl, cursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
- assertEquals(visitedHistoryUrl,
- cursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
- assertEquals(0, cursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX));
- assertEquals(1, cursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX));
- oldTime = cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX);
- cursor.close();
-
- Browser.updateVisitedHistory(mContentResolver, visitedHistoryUrl, true);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- null, null, null, null);
- assertEquals(1, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(visitedHistoryUrl, cursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
- assertEquals(visitedHistoryUrl,
- cursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
- assertEquals(0, cursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX));
- assertEquals(2, cursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX));
- assertTrue(oldTime < cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
- cursor.close();
- } catch (RemoteException e1) {
- fail("unexpected RemoteException");
- }
- }
-
- public void testAccessHistory() {
- Cursor cursor;
- // NOTE: this value must keep same with the Browser.MAX_HISTORY_COUNT.
- final int MAX_HISTORY_COUNT = 250;
- final String bookmarkUrl = "www.visited-bookmark.com";
- final String historyUrlPrefix = "www.visited-history";
- final String historyUrlPostfix = ".com";
- final long TIME_BASE = new Date().getTime() - 1000;
- try {
- assertFalse(Browser.canClearHistory(mContentResolver));
- Browser.clearHistory(mContentResolver);
- assertFalse(Browser.canClearHistory(mContentResolver));
-
- // The total number of the history table's rows: MAX_HISTORY_COUNT,
- // but there is 1 row is a bookmark.
- // The date of the history is from 1 ~ (MAX_HISTORY_COUNT - 1).
- ContentValues value = new ContentValues();
- for (int i = 1; i <= MAX_HISTORY_COUNT - 1; i++) {
- value.put(BookmarkColumns.URL, historyUrlPrefix + i + historyUrlPostfix);
- value.put(BookmarkColumns.BOOKMARK, 0);
- value.put(BookmarkColumns.DATE, i + TIME_BASE);
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- }
- value.put(BookmarkColumns.URL, bookmarkUrl);
- value.put(BookmarkColumns.BOOKMARK, 1);
- value.put(BookmarkColumns.VISITS, 5);
- value.put(BookmarkColumns.DATE, new Date().getTime());
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- value.clear();
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- null, null, null, null);
- assertEquals(MAX_HISTORY_COUNT, cursor.getCount());
- cursor.close();
- Browser.truncateHistory(mContentResolver);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- null, null, null, null);
- assertEquals(MAX_HISTORY_COUNT, cursor.getCount());
- cursor.close();
-
- // Add more one history which is not a bookmark,
- // then the history rows except bookmark is MAX_HISTORY_COUNT.
- value.put(BookmarkColumns.URL, historyUrlPrefix
- + MAX_HISTORY_COUNT + historyUrlPostfix);
- value.put(BookmarkColumns.BOOKMARK, 0);
- value.put(BookmarkColumns.DATE, MAX_HISTORY_COUNT + TIME_BASE);
- mProvider.insert(Browser.BOOKMARKS_URI, value);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- null, null, null, null);
- assertEquals(MAX_HISTORY_COUNT + 1, cursor.getCount());
- cursor.close();
- Browser.truncateHistory(mContentResolver);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- null, null, null, null);
- assertEquals(MAX_HISTORY_COUNT + 1 - Browser.TRUNCATE_N_OLDEST, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(Browser.TRUNCATE_N_OLDEST + 1 + TIME_BASE,
- cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
- cursor.close();
-
- // Delete specified history
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- BookmarkColumns.BOOKMARK + " = 0",
- null, BookmarkColumns.DATE, null);
- int historyCountBeforeDelete = cursor.getCount();
- cursor.moveToLast();
- assertEquals(MAX_HISTORY_COUNT + TIME_BASE,
- cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
- cursor.close();
- Browser.deleteFromHistory(mContentResolver,
- historyUrlPrefix + MAX_HISTORY_COUNT + historyUrlPostfix);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- BookmarkColumns.BOOKMARK + " = 0",
- null, BookmarkColumns.DATE, null);
- int historyCountAfterDelete = cursor.getCount();
- assertEquals(historyCountBeforeDelete - 1, historyCountAfterDelete);
- cursor.moveToLast();
- assertEquals(MAX_HISTORY_COUNT - 1 + TIME_BASE,
- cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
- cursor.close();
-
- // Specify a url which is not existed in current table.
- historyCountBeforeDelete = historyCountAfterDelete;
- Browser.deleteFromHistory(mContentResolver, "delete a not-existed url");
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- BookmarkColumns.BOOKMARK + " = 0",
- null, BookmarkColumns.DATE, null);
- historyCountAfterDelete = cursor.getCount();
- assertEquals(historyCountBeforeDelete, historyCountAfterDelete);
- cursor.close();
-
- // Specify the history in a time frame to be deleted
- historyCountBeforeDelete = historyCountAfterDelete;
- long begin = 6 + TIME_BASE;
- long end = 20 + TIME_BASE;
- Browser.deleteHistoryTimeFrame(mContentResolver, begin, end);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- BookmarkColumns.BOOKMARK + " = 0",
- null, BookmarkColumns.DATE, null);
- historyCountAfterDelete = cursor.getCount();
- assertEquals(historyCountBeforeDelete - (end - begin), historyCountAfterDelete);
- cursor.moveToFirst();
- assertEquals(end, cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
- cursor.close();
-
- // Specify the history in a time frame (not specify begin) to be deleted.
- historyCountBeforeDelete = historyCountAfterDelete;
- long firstDate = end;
- begin = -1;
- end = 34 + TIME_BASE;
- Browser.deleteHistoryTimeFrame(mContentResolver, begin, end);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- BookmarkColumns.BOOKMARK + " = 0",
- null, BookmarkColumns.DATE, null);
- historyCountAfterDelete = cursor.getCount();
- assertEquals(historyCountBeforeDelete - (end - firstDate), historyCountAfterDelete);
- cursor.moveToFirst();
- assertEquals(end, cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
- cursor.moveToLast();
- long lastDate = cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX);
- cursor.close();
-
- // Specify the history in a time frame (not specify end) to be deleted.
- historyCountBeforeDelete = historyCountAfterDelete;
- begin = MAX_HISTORY_COUNT - 10 + TIME_BASE;
- end = -1;
- Browser.deleteHistoryTimeFrame(mContentResolver, begin, end);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- BookmarkColumns.BOOKMARK + " = 0",
- null, BookmarkColumns.DATE, null);
- historyCountAfterDelete = cursor.getCount();
- assertEquals(historyCountBeforeDelete - (lastDate - begin + 1),
- historyCountAfterDelete);
- cursor.moveToLast();
- assertEquals(begin - 1, cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
- cursor.close();
-
- // Clear all history.
- assertTrue(Browser.canClearHistory(mContentResolver));
- Browser.clearHistory(mContentResolver);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- null, null, BookmarkColumns.DATE, null);
- assertEquals(1, cursor.getCount());
- cursor.moveToFirst();
- assertEquals(bookmarkUrl, cursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
- assertEquals(0, cursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX));
- assertEquals(0, cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
- cursor.close();
- assertFalse(Browser.canClearHistory(mContentResolver));
- } catch (RemoteException e) {
- fail("unexpected RemoteException");
- }
- }
-
- public void testRequestAllIcons() {
- Browser.requestAllIcons(mContentResolver, null, null);
- }
-
- public void testSaveBookmark() {
- // TODO: send KEYCODE_DPAD_CENTER to skip the resolve page, but no effect.
-// assertFalse(isRunning(ADD_BOOKMARK_CLASS_NAME));
-// Browser.saveBookmark(mActivity, "bookmark title", "www.bookmark.com");
-// try {
-// Thread.sleep(2000);
-// } catch (InterruptedException e) {
-// e.printStackTrace();
-// }
-// getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
-// assertStarted(ADD_BOOKMARK_CLASS_NAME, 6000);
-
- // TODO: how to finish the activity.
- }
-
- public void testSendString() {
- // assertFalse(isRunning(COMPOSE_MESSAGE_CLASS_NAME));
- // Browser.sendString(mActivity, "string to be sent");
- // assertStarted(COMPOSE_MESSAGE_CLASS_NAME, 5000);
-
- // TODO: how to finish the activity.
- }
-
- private boolean isRunning(String className) {
- ActivityManager activityManager = (ActivityManager) mActivity
- .getSystemService(Context.ACTIVITY_SERVICE);
- List<RunningTaskInfo> list = activityManager.getRunningTasks(1);
- RunningTaskInfo info = list.get(0);
- if (null == info || null == info.topActivity) {
- return false;
- }
-
- if (className.equals(info.topActivity.getClassName())) {
- return true;
- }
-
- return false;
- }
-
- private void assertStarted(String className, long timeout) {
- final long timeSlice = 200;
- while (timeout > 0) {
- try {
- Thread.sleep(timeSlice);
- } catch (InterruptedException e) {
- fail("unexpected InterruptedException");
- }
- if (isRunning(className)) {
- return;
- }
- timeout -= timeSlice;
- }
- fail("has not started BrowserActivity yet");
- }
-
- /**
- * Test case just for the actual content provider behavior on Bookmarks table.
- * It does not test any APIs in android.provider.Browser.java, so we cannot add
- * annotation for it.
- */
- public void testBookmarksTable() {
- final String[] BOOKMARKS_PROJECTION = new String[] {
- BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
- BookmarkColumns.DATE, BookmarkColumns.CREATED, BookmarkColumns.BOOKMARK,
- BookmarkColumns.TITLE, BookmarkColumns.FAVICON };
- final int ID_INDEX = 0;
- final int URL_INDEX = 1;
- final int VISITS_INDEX = 2;
- final int DATE_INDEX = 3;
- final int CREATED_INDEX = 4;
- final int BOOKMARK_INDEX = 5;
- final int TITLE_INDEX = 6;
- final int FAVICON_INDEX = 7;
-
- String insertBookmarkTitle = "bookmark_insert";
- String insertBookmarkUrl = "www.bookmark_insert.com";
-
- String updateBookmarkTitle = "bookmark_update";
- String updateBookmarkUrl = "www.bookmark_update.com";
- try {
- // Test: insert
- ContentValues value = new ContentValues();
- long createDate = new Date().getTime();
- value.put(BookmarkColumns.TITLE, insertBookmarkTitle);
- value.put(BookmarkColumns.URL, insertBookmarkUrl);
- value.put(BookmarkColumns.VISITS, 0);
- value.put(BookmarkColumns.DATE, createDate);
- value.put(BookmarkColumns.CREATED, createDate);
- value.put(BookmarkColumns.BOOKMARK, 0);
-
- Uri insertUri = mProvider.insert(Browser.BOOKMARKS_URI, value);
- Cursor cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- BOOKMARKS_PROJECTION,
- BookmarkColumns.TITLE + " = ?",
- new String[] {insertBookmarkTitle},
- BookmarkColumns.DATE, null);
- assertTrue(cursor.moveToNext());
- assertEquals(insertBookmarkTitle, cursor.getString(TITLE_INDEX));
- assertEquals(insertBookmarkUrl,cursor.getString(URL_INDEX));
- assertEquals(0,cursor.getInt(VISITS_INDEX));
- assertEquals(createDate, cursor.getLong(DATE_INDEX));
- assertEquals(createDate, cursor.getLong(CREATED_INDEX));
- assertEquals(0, cursor.getInt(BOOKMARK_INDEX));
- assertNull(cursor.getBlob(FAVICON_INDEX));
- int Id = cursor.getInt(ID_INDEX);
- cursor.close();
-
- // Test: update
- value.clear();
- long updateDate = new Date().getTime();
- value.put(BookmarkColumns.TITLE, updateBookmarkTitle);
- value.put(BookmarkColumns.URL, updateBookmarkUrl);
- value.put(BookmarkColumns.VISITS, 1);
- value.put(BookmarkColumns.DATE, updateDate);
-
- mProvider.update(Browser.BOOKMARKS_URI, value,
- BookmarkColumns.TITLE + " = ?",
- new String[] {insertBookmarkTitle});
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- BOOKMARKS_PROJECTION,
- BookmarkColumns._ID + " = " + Id,
- null, null, null);
- assertTrue(cursor.moveToNext());
- assertEquals(updateBookmarkTitle, cursor.getString(TITLE_INDEX));
- assertEquals(updateBookmarkUrl,cursor.getString(URL_INDEX));
- assertEquals(1,cursor.getInt(VISITS_INDEX));
- assertEquals(updateDate, cursor.getLong(DATE_INDEX));
- assertEquals(createDate, cursor.getLong(CREATED_INDEX));
- assertEquals(0, cursor.getInt(BOOKMARK_INDEX));
- assertNull(cursor.getBlob(FAVICON_INDEX));
- assertEquals(Id, cursor.getInt(ID_INDEX));
-
- // Test: delete
- mProvider.delete(insertUri, null, null);
- cursor = mProvider.query(
- Browser.BOOKMARKS_URI,
- BOOKMARKS_PROJECTION,
- BookmarkColumns._ID + " = " + Id,
- null, null, null);
- assertEquals(0, cursor.getCount());
- } catch (RemoteException e) {
- fail("Unexpected RemoteException");
- }
- }
-
- /**
- * Test case just for the actual content provider behavior on Searches table.
- * It does not test any APIs in android.provider.Browser.java, so we cannot add
- * annotation for it.
- */
- public void testSearchesTable() {
- final int ID_INDEX = 0;
- String insertSearch = "search_insert";
- String updateSearch = "search_update";
-
- try {
- // Test: insert
- ContentValues value = new ContentValues();
- long createDate = new Date().getTime();
- value.put(SearchColumns.SEARCH, insertSearch);
- value.put(SearchColumns.DATE, createDate);
-
- Uri insertUri = mProvider.insert(Browser.SEARCHES_URI, value);
- Cursor cursor = mProvider.query(Browser.SEARCHES_URI,
- Browser.SEARCHES_PROJECTION, SearchColumns.SEARCH + " = ?",
- new String[] {insertSearch}, null, null);
- assertTrue(cursor.moveToNext());
- assertEquals(insertSearch,
- cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
- assertEquals(createDate,
- cursor.getLong(Browser.SEARCHES_PROJECTION_DATE_INDEX));
- int Id = cursor.getInt(ID_INDEX);
- cursor.close();
-
- // Test: update
- value.clear();
- long updateDate = new Date().getTime();
- value.put(SearchColumns.SEARCH, updateSearch);
- value.put(SearchColumns.DATE, updateDate);
-
- mProvider.update(Browser.SEARCHES_URI, value,
- SearchColumns._ID + " = " + Id, null);
- cursor = mProvider.query(Browser.SEARCHES_URI,
- Browser.SEARCHES_PROJECTION,
- SearchColumns._ID + " = " + Id, null, null, null);
- assertTrue(cursor.moveToNext());
- assertEquals(updateSearch,
- cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
- assertEquals(updateDate,
- cursor.getLong(Browser.SEARCHES_PROJECTION_DATE_INDEX));
- assertEquals(Id, cursor.getInt(ID_INDEX));
-
- // Test: delete
- mProvider.delete(insertUri, null, null);
- cursor = mProvider.query(Browser.SEARCHES_URI,
- Browser.SEARCHES_PROJECTION,
- SearchColumns._ID + " = " + Id, null, null, null);
- assertEquals(0, cursor.getCount());
- } catch (RemoteException e) {
- fail("Unexpected RemoteException");
- }
- }
-}
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
index 2f5edd0..c9e2213 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
@@ -61,8 +61,8 @@
// an arbitrary int used by some tests
private static final int SOME_ARBITRARY_INT = 143234;
- // 10 sec timeout for reminder broadcast (but shouldn't usually take this long).
- private static final int POLLING_TIMEOUT = 10000;
+ // 15 sec timeout for reminder broadcast (but shouldn't usually take this long).
+ private static final int POLLING_TIMEOUT = 15000;
// @formatter:off
private static final String[] TIME_ZONES = new String[] {
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_AggregationSuggestionsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_AggregationSuggestionsTest.java
index 1aa2b64..64c1a66 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_AggregationSuggestionsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_AggregationSuggestionsTest.java
@@ -191,5 +191,23 @@
return new long[] {rawContact1.getContactId(),
rawContact2.getContactId(), rawContact3.getContactId()};
}
+
+ public void testAggregationSuggestionsQueryBuilderWithContactId() throws Exception {
+ Uri uri = new AggregationSuggestions.Builder().setContactId(12).setLimit(7).build();
+ assertEquals("content://com.android.contacts/contacts/12/suggestions?limit=7",
+ uri.toString());
+ }
+
+ public void testAggregationSuggestionsQueryBuilderWithValues() throws Exception {
+ Uri uri = new AggregationSuggestions.Builder()
+ .addNameParameter("name1")
+ .addNameParameter("name2")
+ .setLimit(7)
+ .build();
+ assertEquals("content://com.android.contacts/contacts/0/suggestions?"
+ + "limit=7"
+ + "&query=name%3Aname1"
+ + "&query=name%3Aname2", uri.toString());
+ }
}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_QuickContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_QuickContactsTest.java
new file mode 100644
index 0000000..79633e7
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_QuickContactsTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Contacts;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.QuickContact;
+import android.test.InstrumentationTestCase;
+import android.test.UiThreadTest;
+import android.view.View;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ContactsContract_QuickContactsTest extends InstrumentationTestCase {
+
+ final String EXCLUDED_MIME_TYPES[] = {"exclude1", "exclude2"};
+ final String PLAIN_MIME_TYPE = "text/plain";
+ final Uri FAKE_CONTACT_URI = ContentUris.withAppendedId(Contacts.CONTENT_URI, 0);
+
+ @UiThreadTest
+ public void testPrioritizedMimeTypeAndExcludedMimeTypes() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(2);
+ Context context = new ContextWrapper(getInstrumentation().getContext()) {
+ @Override
+ public void startActivity(Intent intent) {
+ testCallback(intent);
+ }
+
+ @Override
+ public void startActivityAsUser(Intent intent, UserHandle user) {
+ testCallback(intent);
+ }
+
+ @Override
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+ testCallback(intent);
+ }
+
+ private void testCallback(Intent intent) {
+ assertEquals(PLAIN_MIME_TYPE, intent.getStringExtra(
+ QuickContact.EXTRA_PRIORITIZED_MIMETYPE));
+ String excludedMimeTypes[]
+ = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
+ assertTrue(Arrays.equals(excludedMimeTypes, EXCLUDED_MIME_TYPES));
+ latch.countDown();
+ }
+ };
+
+ // Execute
+ ContactsContract.QuickContact.showQuickContact(context, new View(context),
+ FAKE_CONTACT_URI, EXCLUDED_MIME_TYPES, PLAIN_MIME_TYPE);
+ ContactsContract.QuickContact.showQuickContact(context, (Rect) null,
+ FAKE_CONTACT_URI, EXCLUDED_MIME_TYPES, PLAIN_MIME_TYPE);
+
+ // Verify: the start activity call sets the prioritized mimetype and excludes mimetypes.
+ // We don't know which method will be used to start the activity, so we check all options.
+ assertTrue(latch.await(1, TimeUnit.SECONDS));
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/renderscript/Android.mk b/tests/tests/renderscript/Android.mk
index 1ea800e..7b30859 100644
--- a/tests/tests/renderscript/Android.mk
+++ b/tests/tests/renderscript/Android.mk
@@ -33,6 +33,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_RENDERSCRIPT_FLAGS := -Wno-error=deprecated-declarations
+
LOCAL_SDK_VERSION := current
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
index f9d62ac..a52d308 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
@@ -386,21 +386,14 @@
ScriptGroup.Input unbound = builder.addInput();
ScriptGroup.Closure c = null;
- ScriptGroup.Future f = null;
- int stride;
- for (stride = ARRAY_SIZE / 2; stride >= 1; stride >>= 1) {
+ ScriptGroup.Binding b2 = new ScriptGroup.Binding(s.getFieldID_a(), unbound);
+ for (int stride = ARRAY_SIZE / 2; stride >= 1; stride >>= 1) {
ScriptGroup.Binding b1 = new ScriptGroup.Binding(s.getFieldID_reduction_stride(),
stride);
- ScriptGroup.Binding b2;
- if (f == null) {
- b2 = new ScriptGroup.Binding(s.getFieldID_a_in(), unbound);
- } else {
- b2 = new ScriptGroup.Binding(s.getFieldID_a_in(), f);
- }
c = builder.addKernel(s.getKernelID_add(),
Type.createX(mRS, Element.I32_4(mRS), stride),
b1, b2);
- f = c.getReturn();
+ b2 = new ScriptGroup.Binding(s.getFieldID_a(), c.getReturn());
}
if (c == null) {
@@ -427,6 +420,64 @@
}
/**
+ * Tests that the kernel output to a global can be used as a future
+ */
+ public void testBuilder2KernelOutputToGlobal() {
+ ScriptC_reduction s = new ScriptC_reduction(mRS);
+
+ int[] array = new int[ARRAY_SIZE * 4];
+
+ for (int i = 0; i < ARRAY_SIZE; i++) {
+ array[i*4] = i;
+ array[i*4 + 1] = i;
+ array[i*4 + 2] = i;
+ array[i*4 + 3] = i;
+ }
+
+ Allocation input = Allocation.createSized(mRS, Element.I32_4(mRS), ARRAY_SIZE);
+ input.copyFrom(array);
+ Allocation input1 = Allocation.createSized(mRS, Element.I32_4(mRS), ARRAY_SIZE);
+
+ ScriptGroup.Builder2 builder = new ScriptGroup.Builder2(mRS);
+
+ ScriptGroup.Input unbound = builder.addInput();
+
+ ScriptGroup.Closure c = null;
+ ScriptGroup.Binding b2 = new ScriptGroup.Binding(s.getFieldID_a(), unbound);
+ for (int stride = ARRAY_SIZE / 2; stride >= 1; stride >>= 1) {
+ ScriptGroup.Binding b1 = new ScriptGroup.Binding(s.getFieldID_reduction_stride(),
+ stride);
+ c = builder.addKernel(s.getKernelID_add2(),
+ Type.createX(mRS, Element.I32_4(mRS), stride),
+ b1, b2);
+ b2 = new ScriptGroup.Binding(s.getFieldID_a(),
+ c.getGlobal(s.getFieldID_a()));
+ }
+
+ if (c == null) {
+ return;
+ }
+
+ ScriptGroup group = builder.create("SummationGlobal", c.getGlobal(s.getFieldID_a()));
+
+ int[] a = new int[4 * ARRAY_SIZE];
+ ((Allocation)group.execute(input, input1)[0]).copyTo(a);
+
+ mRS.finish();
+
+ boolean failed = false;
+ for (int i = 0; i < 4; i++) {
+ if (failed == false && a[i] != ARRAY_SIZE * (ARRAY_SIZE - 1) / 2) {
+ Log.e(TAG,
+ "a["+i+"]="+a[i]+", should be "+ (ARRAY_SIZE * (ARRAY_SIZE - 1) / 2));
+ failed = true;
+ }
+ }
+
+ assertTrue(!failed);
+ }
+
+ /**
* Tests that invoke-to-kernel dependency is handled correctly
*/
public void testBuilder2InvokeToKernelDependency() {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/reduction.rs b/tests/tests/renderscript/src/android/renderscript/cts/reduction.rs
index 860e42d..19de6fe 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/reduction.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/reduction.rs
@@ -17,10 +17,19 @@
#pragma version(1)
#pragma rs java_package_name(android.renderscript.cts)
-rs_allocation a_in;
+rs_allocation a;
int reduction_stride;
int4 __attribute__((kernel)) add(uint x)
{
- return rsGetElementAt_int4(a_in, x) + rsGetElementAt_int4(a_in, x + reduction_stride);
+ return rsGetElementAt_int4(a, x) + rsGetElementAt_int4(a, x + reduction_stride);
+}
+
+int4 __attribute__((kernel)) add2(uint x)
+{
+ rsSetElementAt_int4(a,
+ rsGetElementAt_int4(a, x) +
+ rsGetElementAt_int4(a, x + reduction_stride),
+ x);
+ return 0;
}
diff --git a/tests/tests/rscpp/librscpptest/shared.rsh b/tests/tests/rscpp/librscpptest/shared.rsh
index 3bc52f2..c5599d7 100644
--- a/tests/tests/rscpp/librscpptest/shared.rsh
+++ b/tests/tests/rscpp/librscpptest/shared.rsh
@@ -3,11 +3,11 @@
static int64_t g_time;
-static void start(void) {
+static inline void start(void) {
g_time = rsUptimeMillis();
}
-static float end(void) {
+static inline float end(void) {
int64_t t = rsUptimeMillis() - g_time;
return ((float)t) / 1000.f;
}
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index 7106385..6bef886 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -28,7 +28,6 @@
android_security_cts_LinuxRngTest.cpp \
android_security_cts_LoadEffectLibraryTest.cpp \
android_security_cts_NativeCodeTest.cpp \
- android_security_cts_SeccompDeathTestService.cpp \
android_security_cts_SELinuxTest.cpp \
android_security_cts_MMapExecutableTest.cpp \
android_security_cts_AudioPolicyBinderTest.cpp
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index 424dbaf..1051a82 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -22,7 +22,6 @@
extern int register_android_security_cts_LinuxRngTest(JNIEnv*);
extern int register_android_security_cts_NativeCodeTest(JNIEnv*);
extern int register_android_security_cts_LoadEffectLibraryTest(JNIEnv*);
-extern int register_android_security_cts_SeccompDeathTestService(JNIEnv*);
extern int register_android_security_cts_SELinuxTest(JNIEnv*);
extern int register_android_security_cts_MMapExecutableTest(JNIEnv* env);
extern int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env);
@@ -50,10 +49,6 @@
return JNI_ERR;
}
- if (register_android_security_cts_SeccompDeathTestService(env)) {
- return JNI_ERR;
- }
-
if (register_android_security_cts_SELinuxTest(env)) {
return JNI_ERR;
}
diff --git a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
index 00765c6..350309b 100644
--- a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
@@ -34,6 +34,7 @@
#include <errno.h>
#include <inttypes.h>
#include <linux/sysctl.h>
+#include <arpa/inet.h>
/*
* Returns true iff this device is vulnerable to CVE-2013-2094.
@@ -227,6 +228,28 @@
return !vulnerable;
}
+static jboolean android_security_cts_NativeCodeTest_doPingPongRootTest(JNIEnv*, jobject)
+{
+ int icmp_sock;
+ struct sockaddr sock_addr;
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ icmp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+ sock_addr.sa_family = AF_INET;
+
+ /* first connect */
+ connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+ /* disconnect */
+ sock_addr.sa_family = AF_UNSPEC;
+ connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+ /* second disconnect -> crash */
+ sock_addr.sa_family = AF_UNSPEC;
+ connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+ return true;
+}
static JNINativeMethod gMethods[] = {
{ "doPerfEventTest", "()Z",
@@ -241,6 +264,8 @@
(void *) android_security_cts_NativeCodeTest_doFutexTest },
{ "doNvmapIocFromIdTest", "()Z",
(void *) android_security_cts_NativeCodeTest_doNvmapIocFromIdTest },
+ { "doPingPongRootTest", "()Z",
+ (void *) android_security_cts_NativeCodeTest_doPingPongRootTest },
};
int register_android_security_cts_NativeCodeTest(JNIEnv* env)
@@ -249,3 +274,4 @@
return env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(JNINativeMethod));
}
+
diff --git a/tests/tests/security/jni/android_security_cts_SeccompDeathTestService.cpp b/tests/tests/security/jni/android_security_cts_SeccompDeathTestService.cpp
deleted file mode 100644
index eb32521..0000000
--- a/tests/tests/security/jni/android_security_cts_SeccompDeathTestService.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <jni.h>
-#include <signal.h>
-#include <unistd.h>
-
-void android_security_cts_SeccompDeathTestService_testSigSysSelf(JNIEnv* env, jobject thiz)
-{
- kill(getpid(), SIGSYS);
-}
-
-static JNINativeMethod methods[] = {
- { "testSigSysSelf", "()V",
- (void *)android_security_cts_SeccompDeathTestService_testSigSysSelf }
-};
-
-int register_android_security_cts_SeccompDeathTestService(JNIEnv* env) {
- jclass clazz = env->FindClass("android/security/cts/SeccompDeathTestService");
- return env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(JNINativeMethod));
-}
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index a2f8c09..ab41b4f 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -58,6 +58,12 @@
doNvmapIocFromIdTest());
}
+ public void testPingPongRoot() throws Exception {
+ assertTrue("Device is vulnerable to CVE-2015-3636, a vulnerability in the ping "
+ + "socket implementation. Please apply the security patch at "
+ + "https://github.com/torvalds/linux/commit/a134f083e79f",
+ doPingPongRootTest());
+ }
/**
* Returns true iff this device is vulnerable to CVE-2013-2094.
* A patch for CVE-2013-2094 can be found at
@@ -120,4 +126,18 @@
* false if the device is vulnerable.
*/
private static native boolean doCVE20141710Test();
+
+ /**
+ * CVE-2015-3636
+ *
+ * Returns true if the patch is applied, crashes the system otherwise.
+ *
+ * Detects if the following patch is present.
+ * https://github.com/torvalds/linux/commit/a134f083e79f
+ *
+ * Credit: Wen Xu and wushi of KeenTeam.
+ * http://seclists.org/oss-sec/2015/q2/333
+ */
+ private static native boolean doPingPongRootTest();
+
}
diff --git a/tests/tests/security/src/android/security/cts/SeccompBpfTest.java b/tests/tests/security/src/android/security/cts/SeccompBpfTest.java
deleted file mode 100644
index b7d8f2e..0000000
--- a/tests/tests/security/src/android/security/cts/SeccompBpfTest.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.test.AndroidTestCase;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * Test for seccomp-bpf sandboxing technology. This makes use of the
- * SeccompDeathTestService to run sandboxing death tests out-of-process.
- */
-public class SeccompBpfTest extends AndroidTestCase implements ServiceConnection,
- IBinder.DeathRecipient {
- static final String TAG = "SeccompBpfTest";
-
- /**
- * Message sent from the SeccompDeathTestService before it runs a test.
- */
- static final int MSG_TEST_STARTED = 1;
- /**
- * Message sent from the SeccompDeathTestService after a test exits cleanly.
- */
- static final int MSG_TEST_ENDED_CLEAN = 2;
-
- /**
- * Dedicated thread used to receive messages from the SeccompDeathTestService.
- */
- final private HandlerThread mHandlerThread = new HandlerThread("SeccompBpfTest handler");
- /**
- * Messenger that runs on mHandlerThread.
- */
- private Messenger mMessenger;
-
- /**
- * Condition that blocks the test/instrumentation thread that runs the
- * test cases, while the SeccompDeathTestService runs the test out-of-process.
- */
- final private ConditionVariable mCondition = new ConditionVariable();
-
- /**
- * The SeccompDeathTestService number to run.
- */
- private int mTestNumber = -1;
-
- /**
- * If the test has started.
- */
- private boolean mTestStarted = false;
- /**
- * If the test ended (either cleanly or with death).
- */
- private boolean mTestEnded = false;
- /**
- * If the test ended cleanly or died.
- */
- private boolean mTestDied = false;
-
- public void testDeathTest() {
- runDeathTest(SeccompDeathTestService.TEST_DEATH_TEST);
- assertTrue(mTestDied);
- }
-
- public void testCleanTest() {
- runDeathTest(SeccompDeathTestService.TEST_CLEAN_TEST);
- assertFalse(mTestDied);
- }
-
- public void testSigSysSelf() {
- runDeathTest(SeccompDeathTestService.TEST_SIGSYS_SELF);
- assertTrue(mTestDied);
- }
-
- /**
- * Runs a death test by its test number, which needs to match a value in
- * SeccompDeathTestService.
- *
- * This blocks until the completion of the test, after which the test body
- * can use mTestEnded/mTestDied to see if the test died.
- */
- public void runDeathTest(final int testNumber) {
- mTestStarted = false;
- mTestEnded = false;
- mTestDied = false;
-
- mTestNumber = testNumber;
-
- Log.d(TAG, "Starting runDeathTest");
- launchDeathTestService();
- mCondition.block();
-
- assertTrue(mTestStarted);
- assertTrue(mTestEnded);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mHandlerThread.start();
- mMessenger = new Messenger(new Handler(mHandlerThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_TEST_STARTED:
- onTestStarted();
- break;
- case MSG_TEST_ENDED_CLEAN:
- onTestEnded(false);
- break;
- default:
- super.handleMessage(msg);
- }
- }
- });
- }
-
- @Override
- public void tearDown() throws Exception {
- try {
- mHandlerThread.quitSafely();
- } finally {
- super.tearDown();
- }
- }
-
- private void launchDeathTestService() {
- Log.d(TAG, "launchDeathTestService");
- mCondition.close();
-
- Intent intent = new Intent();
- intent.setComponent(new ComponentName("com.android.cts.security", "android.security.cts.SeccompDeathTestService"));
-
- if (!getContext().bindService(intent, this, Context.BIND_AUTO_CREATE)) {
- mCondition.open();
- fail("Failed to start DeathTestService");
- }
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "onServiceConnected");
-
- Messenger remoteMessenger = new Messenger(service);
- Message msg = Message.obtain(null, SeccompDeathTestService.MSG_RUN_TEST);
- msg.getData().putBinder(SeccompDeathTestService.REPLY_BINDER_NAME, mMessenger.getBinder());
- msg.getData().putInt(SeccompDeathTestService.RUN_TEST_IDENTIFIER, mTestNumber);
-
- try {
- service.linkToDeath(this, 0);
- remoteMessenger.send(msg);
- } catch (RemoteException e) {
- Log.e(TAG, "Error setting up SeccompDeathTestService: " + e.getMessage());
- }
- Log.d(TAG, "Send MSG_TEST_START");
- }
-
- private void onTestStarted() {
- Log.d(TAG, "onTestStarted");
- mTestStarted = true;
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "onServiceDisconnected");
- }
-
- @Override
- public void binderDied() {
- Log.d(TAG, "binderDied");
- if (mTestEnded)
- return;
- onTestEnded(true);
- }
-
- private void onTestEnded(boolean died) {
- Log.d(TAG, "onTestEnded, died=" + died);
- mTestEnded = true;
- mTestDied = died;
- getContext().unbindService(this);
- mCondition.open();
- }
-}
diff --git a/tests/tests/security/src/android/security/cts/SeccompDeathTestService.java b/tests/tests/security/src/android/security/cts/SeccompDeathTestService.java
deleted file mode 100644
index c78ea35..0000000
--- a/tests/tests/security/src/android/security/cts/SeccompDeathTestService.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * Service used to run tests for seccomp-bpf sandboxing. Since sandbox violations
- * result in process termination, they cannot be run from within the test case
- * itself. The SeccompBpfTest starts this service to run code out-of-process and
- * then observes when the Binder channel dies. If the test does not die, the
- * service reports back to the test that it exited cleanly.
- */
-public class SeccompDeathTestService extends Service {
- static final String TAG = SeccompBpfTest.TAG;
-
- static {
- System.loadLibrary("ctssecurity_jni");
- }
-
- /**
- * Message sent from SeccompBpfTest to run a test.
- */
- final static int MSG_RUN_TEST = 100;
- /**
- * In MSG_RUN_TEST, the test number to run.
- */
- final static String RUN_TEST_IDENTIFIER = "android.security.cts.SeccompDeathTestService.testID";
- /**
- * In MSG_RUN_TEST, the Binder on which to report clean death.
- */
- static final String REPLY_BINDER_NAME = "android.security.cts.SeccompBpfTest";
-
- // Test numbers that map to test methods in this service.
- final static int TEST_DEATH_TEST = 1;
- final static int TEST_CLEAN_TEST = 2;
- final static int TEST_SIGSYS_SELF = 3;
-
- final private Messenger mMessenger = new Messenger(new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_RUN_TEST:
- runTest(msg);
- break;
- default:
- super.handleMessage(msg);
- }
- }
- });
-
- @Override
- public IBinder onBind(Intent intent) {
- Log.d(TAG, "onBind");
- return mMessenger.getBinder();
- }
-
- private void runTest(Message msg) {
- Log.d(TAG, "runTest");
- IBinder harnessBinder = msg.getData().getBinder(REPLY_BINDER_NAME);
- Messenger harness = new Messenger(harnessBinder);
-
- try {
- Log.d(TAG, "Send MSG_TEST_STARTED");
- harness.send(Message.obtain(null, SeccompBpfTest.MSG_TEST_STARTED));
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to MSG_TEST_STARTED: " + e.getMessage());
- }
-
- demuxTest(msg.getData().getInt(RUN_TEST_IDENTIFIER));
-
- try {
- Log.d(TAG, "Send MSG_TEST_ENDED_CLEAN");
- harness.send(Message.obtain(null, SeccompBpfTest.MSG_TEST_ENDED_CLEAN));
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to MSG_TEST_ENDED_CLEAN: " + e.getMessage());
- }
- }
-
- private void demuxTest(int testNumber) {
- switch (testNumber) {
- case TEST_DEATH_TEST:
- testDeath();
- break;
- case TEST_CLEAN_TEST:
- break;
- case TEST_SIGSYS_SELF:
- testSigSysSelf();
- break;
- default:
- throw new RuntimeException("Unknown test number " + testNumber);
- }
- }
-
- public void testDeath() {
- String s = null;
- s.hashCode();
- }
-
- public native void testSigSysSelf();
-}
diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml
index 700f398..6e9e675 100644
--- a/tests/tests/telecom/AndroidManifest.xml
+++ b/tests/tests/telecom/AndroidManifest.xml
@@ -25,7 +25,7 @@
<application>
<uses-library android:name="android.test.runner" />
- <service android:name="android.telecom.cts.MockConnectionService"
+ <service android:name="android.telecom.cts.CtsConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" >
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
diff --git a/tests/tests/telecom/res/drawable/ic_phone_24dp.png b/tests/tests/telecom/res/drawable/ic_phone_24dp.png
new file mode 100644
index 0000000..b0e0205
--- /dev/null
+++ b/tests/tests/telecom/res/drawable/ic_phone_24dp.png
Binary files differ
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index a569058..c862cc3 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -20,31 +20,34 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.telecom.Call;
import android.telecom.CallAudioState;
import android.telecom.Connection;
-import android.telecom.ConnectionRequest;
import android.telecom.InCallService;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
-import android.telecom.cts.MockConnectionService.ConnectionServiceCallbacks;
+import android.telecom.VideoProfile;
import android.telecom.cts.MockInCallService.InCallServiceCallbacks;
import android.test.InstrumentationTestCase;
import android.text.TextUtils;
import android.util.Log;
-import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/**
- * Base class for Telecom CTS tests that require a {@link MockConnectionService} and
+ * Base class for Telecom CTS tests that require a {@link CtsConnectionService} and
* {@link MockInCallService} to verify Telecom functionality.
*/
public class BaseTelecomTestWithMockServices extends InstrumentationTestCase {
+
+ public static final int FLAG_REGISTER = 0x1;
+ public static final int FLAG_ENABLE = 0x2;
+
public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID);
@@ -52,10 +55,12 @@
TEST_PHONE_ACCOUNT_HANDLE, LABEL)
.setAddress(Uri.parse("tel:555-TEST"))
.setSubscriptionAddress(Uri.parse("tel:555-TEST"))
- .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
+ PhoneAccount.CAPABILITY_VIDEO_CALLING)
.setHighlightColor(Color.RED)
.setShortDescription(LABEL)
- .setSupportedUriSchemes(Arrays.asList("tel"))
+ .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
.build();
private static int sCounter = 0;
@@ -63,8 +68,8 @@
Context mContext;
TelecomManager mTelecomManager;
InCallServiceCallbacks mInCallCallbacks;
- ConnectionServiceCallbacks mConnectionCallbacks;
String mPreviousDefaultDialer = null;
+ MockConnectionService connectionService = new MockConnectionService();
@Override
protected void setUp() throws Exception {
@@ -73,8 +78,6 @@
mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
if (shouldTestTelecom(mContext)) {
- mTelecomManager.registerPhoneAccount(TEST_PHONE_ACCOUNT);
- TestUtils.enablePhoneAccount(getInstrumentation(), TEST_PHONE_ACCOUNT_HANDLE);
mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
TestUtils.setDefaultDialer(getInstrumentation(), PACKAGE);
setupCallbacks();
@@ -84,14 +87,49 @@
@Override
protected void tearDown() throws Exception {
if (shouldTestTelecom(mContext)) {
+ cleanupCalls();
if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
}
- mTelecomManager.unregisterPhoneAccount(TEST_PHONE_ACCOUNT_HANDLE);
+ tearDownConnectionService(TEST_PHONE_ACCOUNT);
}
super.tearDown();
}
+ protected PhoneAccount setupConnectionService(CtsConnectionService connectionService,
+ int flags)
+ throws Exception {
+ if (connectionService == null) {
+ connectionService = this.connectionService;
+ }
+ CtsConnectionService.setUp(TEST_PHONE_ACCOUNT, connectionService);
+
+ if ((flags & FLAG_REGISTER) != 0) {
+ mTelecomManager.registerPhoneAccount(TEST_PHONE_ACCOUNT);
+ }
+ if ((flags & FLAG_ENABLE) != 0) {
+ TestUtils.enablePhoneAccount(getInstrumentation(),
+ TEST_PHONE_ACCOUNT_HANDLE);
+ }
+
+ return TEST_PHONE_ACCOUNT;
+ }
+
+ protected void tearDownConnectionService(PhoneAccount account) throws Exception {
+ mTelecomManager.unregisterPhoneAccount(account.getAccountHandle());
+ CtsConnectionService.tearDown();
+ this.connectionService = null;
+ }
+
+ protected void startCallTo(Uri address, PhoneAccountHandle accountHandle) {
+ final Intent intent = new Intent(Intent.ACTION_CALL, address);
+ if (accountHandle != null) {
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
+ }
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+
private void sleep(long ms) {
try {
Thread.sleep(ms);
@@ -108,27 +146,11 @@
};
MockInCallService.setCallbacks(mInCallCallbacks);
-
- mConnectionCallbacks = new ConnectionServiceCallbacks() {
- @Override
- public void onCreateOutgoingConnection(MockConnection connection,
- ConnectionRequest request) {
- this.lock.release();
- }
-
- @Override
- public void onCreateIncomingConnection(MockConnection connection,
- ConnectionRequest request) {
- this.lock.release();
- }
- };
-
- MockConnectionService.setCallbacks(mConnectionCallbacks);
}
/**
* Puts Telecom in a state where there is an incoming call provided by the
- * {@link MockConnectionService} which can be tested.
+ * {@link CtsConnectionService} which can be tested.
*/
void addAndVerifyNewIncomingCall(Uri incomingHandle, Bundle extras) {
if (extras == null) {
@@ -151,7 +173,7 @@
/**
* Puts Telecom in a state where there is an active call provided by the
- * {@link MockConnectionService} which can be tested.
+ * {@link CtsConnectionService} which can be tested.
*/
void placeAndVerifyCall() {
placeAndVerifyCall(null);
@@ -159,10 +181,28 @@
/**
* Puts Telecom in a state where there is an active call provided by the
- * {@link MockConnectionService} which can be tested.
+ * {@link CtsConnectionService} which can be tested.
+ *
+ * @param videoState the video state of the call.
+ */
+ void placeAndVerifyCall(int videoState) {
+ placeAndVerifyCall(null, videoState);
+ }
+
+ /**
+ * Puts Telecom in a state where there is an active call provided by the
+ * {@link CtsConnectionService} which can be tested.
*/
void placeAndVerifyCall(Bundle extras) {
- placeNewCallWithPhoneAccount(extras);
+ placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY);
+ }
+
+ /**
+ * Puts Telecom in a state where there is an active call provided by the
+ * {@link CtsConnectionService} which can be tested.
+ */
+ void placeAndVerifyCall(Bundle extras, int videoState) {
+ placeNewCallWithPhoneAccount(extras, videoState);
try {
if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
@@ -176,80 +216,73 @@
mInCallCallbacks.getService().getCallCount());
}
- void verifyConnectionForOutgoingCall() {
+ MockConnection verifyConnectionForOutgoingCall() {
try {
- if (!mConnectionCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+ if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS)) {
fail("No outgoing call connection requested by Telecom");
}
} catch (InterruptedException e) {
Log.i(TAG, "Test interrupted!");
}
- assertNotNull("Telecom should bind to and create ConnectionService",
- mConnectionCallbacks.getService());
assertNotNull("Telecom should create outgoing connection for outgoing call",
- mConnectionCallbacks.outgoingConnection);
+ connectionService.outgoingConnection);
assertNull("Telecom should not create incoming connection for outgoing call",
- mConnectionCallbacks.incomingConnection);
+ connectionService.incomingConnection);
- final MockConnection connection = mConnectionCallbacks.outgoingConnection;
- connection.setDialing();
- connection.setActive();
-
- assertEquals(Connection.STATE_ACTIVE, connection.getState());
+ connectionService.outgoingConnection.setDialing();
+ connectionService.outgoingConnection.setActive();
+ assertEquals(Connection.STATE_ACTIVE,
+ connectionService.outgoingConnection.getState());
+ return connectionService.outgoingConnection;
}
- void verifyConnectionForIncomingCall() {
+ MockConnection verifyConnectionForIncomingCall() {
try {
- if (!mConnectionCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
- fail("No incoming call connection requested by Telecom");
+ if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS)) {
+ fail("No outgoing call connection requested by Telecom");
}
} catch (InterruptedException e) {
Log.i(TAG, "Test interrupted!");
}
- assertNotNull("Telecom should bind to and create ConnectionService",
- mConnectionCallbacks.getService());
assertNull("Telecom should not create outgoing connection for outgoing call",
- mConnectionCallbacks.outgoingConnection);
+ connectionService.outgoingConnection);
assertNotNull("Telecom should create incoming connection for outgoing call",
- mConnectionCallbacks.incomingConnection);
+ connectionService.incomingConnection);
- final MockConnection connection = mConnectionCallbacks.incomingConnection;
- connection.setRinging();
- assertEquals(Connection.STATE_RINGING, connection.getState());
+ connectionService.incomingConnection.setRinging();
+ assertEquals(Connection.STATE_RINGING,
+ connectionService.incomingConnection.getState());
+ return connectionService.incomingConnection;
}
/**
- * Disconnect the created test call, verify that Telecom has cleared all calls and has
- * unbound from the {@link ConnectionService}.
+ * Disconnect the created test call and verify that Telecom has cleared all calls.
*/
- void cleanupAndVerifyUnbind() {
+ void cleanupCalls() {
if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
- mInCallCallbacks.prepareForUnbind();
-
mInCallCallbacks.getService().disconnectLastCall();
assertNumCalls(mInCallCallbacks.getService(), 0);
-
- try {
- if (!mInCallCallbacks.unbindLock.tryAcquire(3, TimeUnit.SECONDS)) {
- fail("Telecom did not unbind from InCallService after all calls removed.");
- }
- } catch (InterruptedException e) {
- Log.i(TAG, "Test interrupted!");
- }
}
}
/**
- * Place a new outgoing call via the {@link MockConnectionService}
+ * Place a new outgoing call via the {@link CtsConnectionService}
*/
- private void placeNewCallWithPhoneAccount(Bundle extras) {
+ private void placeNewCallWithPhoneAccount(Bundle extras, int videoState) {
if (extras == null) {
extras = new Bundle();
}
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEST_PHONE_ACCOUNT_HANDLE);
- mTelecomManager.placeCall(getTestNumber(), extras);
+
+ if (!VideoProfile.isAudioOnly(videoState)) {
+ extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
+ }
+
+ mTelecomManager.placeCall(createTestNumber(), extras);
}
/**
@@ -257,8 +290,12 @@
* calls if multiple calls to the same number are placed within a short period of time which
* can cause certain tests to fail.
*/
- Uri getTestNumber() {
- return Uri.fromParts("tel", String.valueOf(sCounter++), null);
+ Uri createTestNumber() {
+ return Uri.fromParts("tel", String.valueOf(++sCounter), null);
+ }
+
+ public static Uri getTestNumber() {
+ return Uri.fromParts("tel", String.valueOf(sCounter), null);
}
void assertNumCalls(final MockInCallService inCallService, final int numCalls) {
@@ -416,8 +453,33 @@
assertEquals(description, condition.expected(), condition.actual());
}
- private interface Condition {
+ /**
+ * Performs some work, and waits for the condition to be met. If the condition is not met in
+ * each step of the loop, the work is performed again.
+ *
+ * @param work The work to perform.
+ * @param condition The condition.
+ * @param timeout The timeout.
+ * @param description Description of the work being performed.
+ */
+ void doWorkAndWaitUntilConditionIsTrueOrTimeout(Work work, Condition condition, long timeout,
+ String description) {
+ final long start = System.currentTimeMillis();
+ work.doWork();
+ while (!condition.expected().equals(condition.actual())
+ && System.currentTimeMillis() - start < timeout) {
+ sleep(50);
+ work.doWork();
+ }
+ assertEquals(description, condition.expected(), condition.actual());
+ }
+
+ protected interface Condition {
Object expected();
Object actual();
}
+
+ protected interface Work {
+ void doWork();
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
new file mode 100644
index 0000000..0625d78
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom.cts;
+
+import static android.telecom.cts.TestUtils.*;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.net.Uri;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.DisconnectCause;
+import android.telecom.GatewayInfo;
+import android.telecom.InCallService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.StatusHints;
+import android.telecom.TelecomManager;
+
+import com.android.cts.telecom.R;
+
+/**
+ * Suites of tests that verifies the various Call details.
+ */
+public class CallDetailsTest extends BaseTelecomTestWithMockServices {
+
+ public static final int CONNECTION_CAPABILITIES =
+ Connection.CAPABILITY_HOLD | Connection.CAPABILITY_MUTE |
+ /**
+ * CAPABILITY_HIGH_DEF_AUDIO & CAPABILITY_WIFI are hidden, so
+ * hardcoding the values for now.
+ */
+ 0x00008000 | 0x00010000;
+ public static final int CALL_CAPABILITIES =
+ Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE;
+ public static final int CALL_PROPERTIES =
+ Call.Details.PROPERTY_HIGH_DEF_AUDIO | Call.Details.PROPERTY_WIFI;
+ public static final String CALLER_DISPLAY_NAME = "CTS test";
+ public static final int CALLER_DISPLAY_NAME_PRESENTATION = TelecomManager.PRESENTATION_ALLOWED;
+
+ private StatusHints mStatusHints;
+ private Bundle mExtras = new Bundle();
+
+ private MockInCallService mInCallService;
+ private Call mCall;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (TestUtils.shouldTestTelecom(mContext)) {
+ PhoneAccount account = setupConnectionService(
+ new MockConnectionService() {
+ @Override
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ Connection connection = super.onCreateOutgoingConnection(
+ connectionManagerPhoneAccount,
+ request);
+ // Modify the connection object created with local values.
+ connection.setConnectionCapabilities(CONNECTION_CAPABILITIES);
+ connection.setCallerDisplayName(
+ CALLER_DISPLAY_NAME,
+ CALLER_DISPLAY_NAME_PRESENTATION);
+ connection.setExtras(mExtras);
+ mStatusHints = new StatusHints(
+ "CTS test",
+ Icon.createWithResource(
+ getInstrumentation().getContext(),
+ R.drawable.ic_phone_24dp),
+ null);
+ connection.setStatusHints(mStatusHints);
+ lock.release();
+ return connection;
+ }
+ }, FLAG_REGISTER | FLAG_ENABLE);
+
+ }
+ /** Place a call as a part of the setup before we test the various
+ * Call details.
+ */
+ placeAndVerifyCall();
+
+ mInCallService = mInCallCallbacks.getService();
+ mCall = mInCallService.getLastCall();
+ }
+
+ /**
+ * Tests whether the getAccountHandle() getter returns the correct object.
+ */
+ public void testAccountHandle() {
+ assertThat(mCall.getDetails().getAccountHandle(), is(PhoneAccountHandle.class));
+ assertEquals(TEST_PHONE_ACCOUNT_HANDLE, mCall.getDetails().getAccountHandle());
+ }
+
+ /**
+ * Tests whether the getCallCapabilities() getter returns the correct object.
+ */
+ public void testCallCapabilities() {
+ assertThat(mCall.getDetails().getCallCapabilities(), is(Integer.class));
+ assertEquals(CALL_CAPABILITIES, mCall.getDetails().getCallCapabilities());
+ }
+
+ /**
+ * Tests whether the getCallerDisplayName() getter returns the correct object.
+ */
+ public void testCallerDisplayName() {
+ assertThat(mCall.getDetails().getCallerDisplayName(), is(String.class));
+ assertEquals(CALLER_DISPLAY_NAME, mCall.getDetails().getCallerDisplayName());
+ }
+
+ /**
+ * Tests whether the getCallerDisplayNamePresentation() getter returns the correct object.
+ */
+ public void testCallerDisplayNamePresentation() {
+ assertThat(mCall.getDetails().getCallerDisplayNamePresentation(), is(Integer.class));
+ assertEquals(CALLER_DISPLAY_NAME_PRESENTATION, mCall.getDetails().getCallerDisplayNamePresentation());
+ }
+
+ /**
+ * Tests whether the getCallProperties() getter returns the correct object.
+ */
+ public void testCallProperties() {
+ assertThat(mCall.getDetails().getCallProperties(), is(Integer.class));
+ assertEquals(CALL_PROPERTIES, mCall.getDetails().getCallProperties());
+ }
+
+ /**
+ * Tests whether the getConnectTimeMillis() getter returns the correct object.
+ */
+ public void testConnectTimeMillis() {
+ assertThat(mCall.getDetails().getConnectTimeMillis(), is(Long.class));
+ }
+
+ /**
+ * Tests whether the getDisconnectCause() getter returns the correct object.
+ */
+ public void testDisconnectCause() {
+ assertThat(mCall.getDetails().getDisconnectCause(), is(DisconnectCause.class));
+ }
+
+ /**
+ * Tests whether the getExtras() getter returns the correct object.
+ */
+ public void testExtras() {
+ assertThat(mCall.getDetails().getExtras(), is(Bundle.class));
+ }
+
+ /**
+ * Tests whether the getIntentExtras() getter returns the correct object.
+ */
+ public void testIntentExtras() {
+ assertThat(mCall.getDetails().getIntentExtras(), is(Bundle.class));
+ }
+
+ /**
+ * Tests whether the getGatewayInfo() getter returns the correct object.
+ */
+ public void testGatewayInfo() {
+ assertThat(mCall.getDetails().getGatewayInfo(), is(GatewayInfo.class));
+ }
+
+ /**
+ * Tests whether the getHandle() getter returns the correct object.
+ */
+ public void testHandle() {
+ assertThat(mCall.getDetails().getHandle(), is(Uri.class));
+ assertEquals(getTestNumber(), mCall.getDetails().getHandle());
+ }
+
+ /**
+ * Tests whether the getHandlePresentation() getter returns the correct object.
+ */
+ public void testHandlePresentation() {
+ assertThat(mCall.getDetails().getHandlePresentation(), is(Integer.class));
+ assertEquals(MockConnectionService.CONNECTION_PRESENTATION, mCall.getDetails().getHandlePresentation());
+ }
+
+ /**
+ * Tests whether the getStatusHints() getter returns the correct object.
+ */
+ public void testStatusHints() {
+ assertThat(mCall.getDetails().getStatusHints(), is(StatusHints.class));
+ assertEquals(mStatusHints.getLabel(), mCall.getDetails().getStatusHints().getLabel());
+ assertEquals(
+ mStatusHints.getIcon().toString(),
+ mCall.getDetails().getStatusHints().getIcon().toString());
+ assertEquals(mStatusHints.getExtras(), mCall.getDetails().getStatusHints().getExtras());
+ }
+
+ /**
+ * Tests whether the getVideoState() getter returns the correct object.
+ */
+ public void testVideoState() {
+ assertThat(mCall.getDetails().getVideoState(), is(Integer.class));
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
new file mode 100644
index 0000000..9cfa0e3
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom.cts;
+
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+import java.util.concurrent.Semaphore;
+
+/**
+ * This is the official ConnectionService for Telecom's CTS App. Since telecom requires that a
+ * CS be registered in the AndroidManifest.xml file, we have to have a single implementation
+ * of a CS and this is it. To test specific CS behavior, tests will implement their own CS and
+ * tell CtsConnectionService to forward any method invocations to that test's implementation.
+ * This is set up using {@link #setUp} and should be cleaned up before the end of the test using
+ * {@link #tearDown}.
+ */
+public class CtsConnectionService extends ConnectionService {
+ private static ConnectionService sConnectionService;
+
+ // ConnectionService used by default as a fallback if no connection service is specified
+ // during test setup.
+ private static ConnectionService mMockConnectionService = new MockConnectionService();
+
+ /**
+ * Used to control whether the {@link MockVideoProvider} will be created when connections are
+ * created. Used by {@link VideoCallTest#testVideoCallDelayProvider()} to test scenario where
+ * the {@link MockVideoProvider} is not created immediately when the Connection is created.
+ */
+ private static Object sLock = new Object();
+
+ public static PhoneAccount setUp(PhoneAccount phoneAccount, ConnectionService connectionService)
+ throws Exception {
+ synchronized(sLock) {
+ if (sConnectionService != null) {
+ throw new Exception("Mock ConnectionService exists. Failed to call tearDown().");
+ }
+ sConnectionService = connectionService;
+ return phoneAccount;
+ }
+ }
+
+ public static void tearDown() {
+ synchronized(sLock) {
+ sConnectionService = null;
+ }
+ }
+
+ @Override
+ public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ synchronized(sLock) {
+ if (sConnectionService != null) {
+ return sConnectionService.onCreateOutgoingConnection(
+ connectionManagerPhoneAccount, request);
+ } else {
+ return mMockConnectionService.onCreateOutgoingConnection(
+ connectionManagerPhoneAccount, request);
+ }
+ }
+ }
+
+ @Override
+ public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ synchronized(sLock) {
+ if (sConnectionService != null) {
+ return sConnectionService.onCreateIncomingConnection(
+ connectionManagerPhoneAccount, request);
+ }
+ return mMockConnectionService.onCreateIncomingConnection(
+ connectionManagerPhoneAccount, request);
+ }
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java b/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java
new file mode 100644
index 0000000..88babef
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom.cts;
+
+import static android.telecom.cts.TestUtils.*;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.media.ToneGenerator;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.telecom.CallAudioState;
+import android.telecom.ConnectionRequest;
+import android.telecom.DisconnectCause;
+import android.telecom.GatewayInfo;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.StatusHints;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.test.InstrumentationTestCase;
+
+import com.android.cts.telecom.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Verifies the parcelable interface of all the telecom objects.
+ */
+public class DataObjectUnitTests extends InstrumentationTestCase {
+
+
+ /**
+ * Tests the PhoneAccount object creation and recreation from a Parcel.
+ */
+ public void testPhoneAccount() throws Exception {
+ Context context = getInstrumentation().getContext();
+ PhoneAccountHandle accountHandle = new PhoneAccountHandle(
+ new ComponentName(PACKAGE, COMPONENT),
+ ACCOUNT_ID);
+ Icon phoneIcon = Icon.createWithResource(context, R.drawable.ic_phone_24dp);
+ Uri tel = Uri.parse("tel:555-TEST");
+ PhoneAccount account = PhoneAccount.builder(
+ accountHandle, LABEL)
+ .setAddress(tel)
+ .setSubscriptionAddress(tel)
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .setHighlightColor(Color.RED)
+ .setShortDescription(LABEL)
+ .setSupportedUriSchemes(Arrays.asList("tel"))
+ .setIcon(phoneIcon)
+ .build();
+ assertNotNull(account);
+ assertEquals(accountHandle, account.getAccountHandle());
+ assertEquals(tel, account.getAddress());
+ assertEquals(tel, account.getSubscriptionAddress());
+ assertEquals(PhoneAccount.CAPABILITY_CALL_PROVIDER, account.getCapabilities());
+ assertEquals(Color.RED, account.getHighlightColor());
+ assertEquals(LABEL, account.getShortDescription());
+ assertEquals(Arrays.asList("tel"), account.getSupportedUriSchemes());
+ assertEquals(phoneIcon.toString(), account.getIcon().toString());
+ assertEquals(0, account.describeContents());
+
+ // Create a parcel of the object and recreate the object back
+ // from the parcel.
+ Parcel p = Parcel.obtain();
+ account.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ PhoneAccount parcelAccount = PhoneAccount.CREATOR.createFromParcel(p);
+ assertNotNull(parcelAccount);
+ assertEquals(accountHandle, parcelAccount.getAccountHandle());
+ assertEquals(tel, parcelAccount.getAddress());
+ assertEquals(tel, parcelAccount.getSubscriptionAddress());
+ assertEquals(PhoneAccount.CAPABILITY_CALL_PROVIDER, parcelAccount.getCapabilities());
+ assertEquals(Color.RED, parcelAccount.getHighlightColor());
+ assertEquals(LABEL, parcelAccount.getShortDescription());
+ assertEquals(Arrays.asList("tel"), parcelAccount.getSupportedUriSchemes());
+ assertEquals(phoneIcon.toString(), parcelAccount.getIcon().toString());
+ assertEquals(0, parcelAccount.describeContents());
+ p.recycle();
+ }
+
+ /**
+ * Tests the ConnectionRequest object creation and recreation from a Parcel.
+ */
+ public void testConnectionRequest() throws Exception {
+ PhoneAccountHandle accountHandle = new PhoneAccountHandle(
+ new ComponentName(PACKAGE, COMPONENT),
+ ACCOUNT_ID);
+ Bundle extras = new Bundle();
+ extras.putString(
+ TelecomManager.GATEWAY_PROVIDER_PACKAGE,
+ PACKAGE);
+ ConnectionRequest request = new ConnectionRequest(
+ accountHandle,
+ Uri.parse("tel:555-TEST"),
+ extras,
+ VideoProfile.STATE_AUDIO_ONLY);
+ assertEquals(accountHandle, request.getAccountHandle());
+ assertEquals(Uri.parse("tel:555-TEST"), request.getAddress());
+ assertEquals(extras.getString(
+ TelecomManager.GATEWAY_PROVIDER_PACKAGE),
+ request.getExtras().getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE));
+ assertEquals(VideoProfile.STATE_AUDIO_ONLY, request.getVideoState());
+ assertEquals(0, request.describeContents());
+
+ // Create a parcel of the object and recreate the object back
+ // from the parcel.
+ Parcel p = Parcel.obtain();
+ request.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ ConnectionRequest parcelRequest = ConnectionRequest.CREATOR.createFromParcel(p);
+ assertEquals(accountHandle, parcelRequest.getAccountHandle());
+ assertEquals(Uri.parse("tel:555-TEST"), parcelRequest.getAddress());
+ assertEquals(
+ extras.getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE),
+ parcelRequest.getExtras().getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE));
+ assertEquals(VideoProfile.STATE_AUDIO_ONLY, parcelRequest.getVideoState());
+ assertEquals(0, parcelRequest.describeContents());
+ p.recycle();
+ }
+
+ /**
+ * Tests the DisconnectCause object creation and recreation from a Parcel.
+ */
+ public void testDisconnectCause() throws Exception {
+ Context context = getInstrumentation().getContext();
+ final CharSequence label = "Out of service area";
+ final CharSequence description = "Mobile network not available";
+ final String reason = "CTS Testing";
+ DisconnectCause cause = new DisconnectCause(
+ DisconnectCause.ERROR,
+ label,
+ description,
+ reason,
+ ToneGenerator.TONE_CDMA_CALLDROP_LITE);
+ assertEquals(DisconnectCause.ERROR, cause.getCode());
+ assertEquals(label, cause.getLabel());
+ assertEquals(description, cause.getDescription());
+ assertEquals(reason, cause.getReason());
+ assertEquals(ToneGenerator.TONE_CDMA_CALLDROP_LITE, cause.getTone());
+ assertEquals(0, cause.describeContents());
+
+ // Create a parcel of the object and recreate the object back
+ // from the parcel.
+ Parcel p = Parcel.obtain();
+ cause.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ DisconnectCause parcelCause = DisconnectCause.CREATOR.createFromParcel(p);
+ assertEquals(DisconnectCause.ERROR, parcelCause.getCode());
+ assertEquals(label, parcelCause.getLabel());
+ assertEquals(description, parcelCause.getDescription());
+ assertEquals(reason, parcelCause.getReason());
+ assertEquals(ToneGenerator.TONE_CDMA_CALLDROP_LITE, parcelCause.getTone());
+ assertEquals(0, parcelCause.describeContents());
+ assertEquals(cause, parcelCause);
+ p.recycle();
+ }
+
+ /**
+ * Tests the StatusHints object creation and recreation from a Parcel.
+ */
+ public void testStatusHints() throws Exception {
+ Context context = getInstrumentation().getContext();
+ final CharSequence label = "Wi-Fi call";
+ Bundle extras = new Bundle();
+ extras.putString(
+ TelecomManager.GATEWAY_PROVIDER_PACKAGE,
+ PACKAGE);
+ Icon icon = Icon.createWithResource(context, R.drawable.ic_phone_24dp);
+ StatusHints hints = new StatusHints(
+ label,
+ icon,
+ extras);
+ assertEquals(label, hints.getLabel());
+ assertEquals(icon.toString(), hints.getIcon().toString());
+ assertEquals(extras.getString(
+ TelecomManager.GATEWAY_PROVIDER_PACKAGE),
+ hints.getExtras().getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE));
+ assertEquals(0, hints.describeContents());
+
+ // Create a parcel of the object and recreate the object back
+ // from the parcel.
+ Parcel p = Parcel.obtain();
+ hints.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ StatusHints parcelHints = StatusHints.CREATOR.createFromParcel(p);
+ assertEquals(label, parcelHints.getLabel());
+ assertEquals(icon.toString(), parcelHints.getIcon().toString());
+ assertEquals(
+ extras.getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE),
+ parcelHints.getExtras().getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE));
+ assertEquals(0, parcelHints.describeContents());
+ // This fails because Bundle does not have a equals implementation.
+ // assertEquals(hints, parcelHints);
+ p.recycle();
+ }
+
+ /**
+ * Tests the GatewayInfo object creation and recreation from a Parcel.
+ */
+ public void testGatewayInfo() throws Exception {
+ final CharSequence label = "Wi-Fi call";
+ Uri originalAddress = Uri.parse("http://www.google.com");
+ Uri gatewayAddress = Uri.parse("http://www.google.com");
+ GatewayInfo info = new GatewayInfo(
+ PACKAGE,
+ gatewayAddress,
+ originalAddress);
+ assertEquals(PACKAGE, info.getGatewayProviderPackageName());
+ assertEquals(gatewayAddress, info.getGatewayAddress());
+ assertEquals(originalAddress, info.getOriginalAddress());
+ assertEquals(0, info.describeContents());
+
+ // Create a parcel of the object and recreate the object back
+ // from the parcel.
+ Parcel p = Parcel.obtain();
+ info.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ GatewayInfo parcelInfo = GatewayInfo.CREATOR.createFromParcel(p);
+ assertEquals(PACKAGE, parcelInfo.getGatewayProviderPackageName());
+ assertEquals(gatewayAddress, parcelInfo.getGatewayAddress());
+ assertEquals(originalAddress, parcelInfo.getOriginalAddress());
+ assertEquals(0, parcelInfo.describeContents());
+ p.recycle();
+ }
+
+ /**
+ * Tests the CallAudioState object creation and recreation from a Parcel.
+ */
+ public void testCallAudioState() throws Exception {
+ CallAudioState audioState = new CallAudioState(
+ true,
+ CallAudioState.ROUTE_EARPIECE,
+ CallAudioState.ROUTE_WIRED_OR_EARPIECE);
+ assertEquals(true, audioState.isMuted());
+ assertEquals(CallAudioState.ROUTE_EARPIECE, audioState.getRoute());
+ assertEquals(CallAudioState.ROUTE_WIRED_OR_EARPIECE, audioState.getSupportedRouteMask());
+ assertEquals(0, audioState.describeContents());
+
+ // Create a parcel of the object and recreate the object back
+ // from the parcel.
+ Parcel p = Parcel.obtain();
+ audioState.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ CallAudioState parcelAudioState = CallAudioState.CREATOR.createFromParcel(p);
+ assertEquals(true, parcelAudioState.isMuted());
+ assertEquals(CallAudioState.ROUTE_EARPIECE, parcelAudioState.getRoute());
+ assertEquals(CallAudioState.ROUTE_WIRED_OR_EARPIECE, parcelAudioState.getSupportedRouteMask());
+ assertEquals(0, parcelAudioState.describeContents());
+ assertEquals(audioState, parcelAudioState);
+ p.recycle();
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java b/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
index 947ebc2..b574a96 100644
--- a/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
@@ -16,8 +16,10 @@
package android.telecom.cts;
+import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.provider.VoicemailContract.Voicemails;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -35,12 +37,23 @@
private TelecomManager mTelecomManager;
private PhoneAccountHandle mPhoneAccountHandle;
private String mPreviousDefaultDialer = null;
+ private String mSystemDialer = null;
@Override
protected void setUp() throws Exception {
super.setUp();
mContext = getInstrumentation().getContext();
+
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
+ // Reset the current dialer to the system dialer, to ensure that we start each test
+ // without being the default dialer.
+ mSystemDialer = TestUtils.getSystemDialer(getInstrumentation());
+ if (!TextUtils.isEmpty(mSystemDialer)) {
+ TestUtils.setDefaultDialer(getInstrumentation(), mSystemDialer);
+ }
mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
final List<PhoneAccountHandle> accounts = mTelecomManager.getCallCapablePhoneAccounts();
if (accounts != null && !accounts.isEmpty()) {
@@ -51,18 +64,26 @@
@Override
protected void tearDown() throws Exception {
if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
+ // Restore the default dialer to whatever the default dialer was before the tests
+ // were started. This may or may not be the system dialer.
TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
}
super.tearDown();
}
public void testGetDefaultDialerPackage() throws Exception {
- assertEquals(mPreviousDefaultDialer, mTelecomManager.getDefaultDialerPackage());
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ assertEquals(mSystemDialer, mTelecomManager.getDefaultDialerPackage());
TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
assertEquals(TestUtils.PACKAGE, mTelecomManager.getDefaultDialerPackage());
}
- public void testVoicemailReadWrite_correctlyThrowsSecurityException() throws Exception {
+ public void testVoicemailReadWritePermissions() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
try {
mContext.getContentResolver().query(Voicemails.CONTENT_URI, null, null, null, null);
fail("Reading voicemails should throw SecurityException if not default Dialer");
@@ -93,7 +114,10 @@
Voicemails._ID + "=999 AND 1=2", null);
}
- public void testSilenceRinger_correctlyThrowsSecurityException() throws Exception {
+ public void testSilenceRingerPermissions() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
try {
mTelecomManager.silenceRinger();
fail("TelecomManager.silenceRinger should throw SecurityException if not default "
@@ -106,8 +130,11 @@
mTelecomManager.silenceRinger();
}
- public void testCancelMissedCallsNotification_correctlyThrowsSecurityException()
+ public void testCancelMissedCallsNotificationPermissions()
throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
try {
mTelecomManager.cancelMissedCallsNotification();
fail("TelecomManager.cancelMissedCallsNotification should throw SecurityException if "
@@ -120,8 +147,11 @@
mTelecomManager.cancelMissedCallsNotification();
}
- public void testHandlePinMmi_correctlyThrowsSecurityException()
+ public void testHandlePinMmPermissions()
throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
try {
mTelecomManager.handleMmi("0");
fail("TelecomManager.handleMmi should throw SecurityException if not default dialer");
@@ -140,7 +170,10 @@
mTelecomManager.handleMmi("0", mPhoneAccountHandle);
}
- public void testGetAdnForPhoneAccount_correctlyThrowsSecurityException() throws Exception {
+ public void testGetAdnForPhoneAccountPermissions() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
try {
mTelecomManager.getAdnUriForPhoneAccount(mPhoneAccountHandle);
fail("TelecomManager.getAdnUriForPhoneAccount should throw SecurityException if "
@@ -152,4 +185,32 @@
// No exception if the calling package is the default dialer.
mTelecomManager.getAdnUriForPhoneAccount(mPhoneAccountHandle);
}
+
+ public void testSetDefaultDialerNoDialIntent_notSupported() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ final PackageManager pm = mContext.getPackageManager();
+ final ComponentName name = new ComponentName(mContext,
+ "android.telecom.cts.MockDialerActivity");
+ try {
+ pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+
+ final String result =
+ TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+ assertNotSame(result, TestUtils.PACKAGE);
+ assertTrue("Expected failure indicating that this was not an installed dialer app",
+ result.contains("is not an installed Dialer app"));
+ } finally {
+ pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
+ }
+
+ // Now that the activity is present again in the package manager, this should succeed.
+ final String result = TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+ assertTrue("Expected success message indicating that " + TestUtils.PACKAGE + " was set as "
+ + "default dialer.", result.contains("set as default dialer"));
+ assertEquals(TestUtils.PACKAGE, TestUtils.getDefaultDialer(getInstrumentation()));
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index c4ec5c4..fbbf3ca 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -23,55 +23,53 @@
import android.telecom.Connection;
import android.telecom.ConnectionService;
import android.telecom.InCallService;
+import android.telecom.VideoProfile;
/**
- * Extended suite of tests that use {@link MockConnectionService} and {@link MockInCallService} to
+ * Extended suite of tests that use {@link CtsConnectionService} and {@link MockInCallService} to
* verify the functionality of the Telecom service.
*/
public class ExtendedInCallServiceTest extends BaseTelecomTestWithMockServices {
+
@Override
protected void setUp() throws Exception {
super.setUp();
- if (shouldTestTelecom(mContext)) {
- placeAndVerifyCall();
- verifyConnectionForOutgoingCall();
+ if (TestUtils.shouldTestTelecom(mContext)) {
+ setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
}
}
- @Override
- protected void tearDown() throws Exception {
- if (shouldTestTelecom(mContext)) {
- cleanupAndVerifyUnbind();
- }
- super.tearDown();
- }
-
- public void testWithMockConnection_AddNewOutgoingCallAndThenDisconnect() {
+ public void testAddNewOutgoingCallAndThenDisconnect() {
if (!shouldTestTelecom(mContext)) {
return;
}
+ placeAndVerifyCall();
+ verifyConnectionForOutgoingCall();
+
final MockInCallService inCallService = mInCallCallbacks.getService();
inCallService.disconnectLastCall();
assertNumCalls(inCallService, 0);
}
- public void testWithMockConnection_MuteAndUnmutePhone() {
+ public void testMuteAndUnmutePhone() {
if (!shouldTestTelecom(mContext)) {
return;
}
+ placeAndVerifyCall();
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
final MockInCallService inCallService = mInCallCallbacks.getService();
final Call call = inCallService.getLastCall();
- final MockConnection connection = mConnectionCallbacks.outgoingConnection;
assertCallState(call, Call.STATE_ACTIVE);
assertMuteState(connection, false);
- inCallService.setMuted(true);;
+ inCallService.setMuted(true);
assertMuteState(connection, true);
assertMuteState(inCallService, true);
@@ -81,13 +79,15 @@
assertMuteState(inCallService, false);
}
- public void testWithMockConnection_SwitchAudioRoutes() {
+ public void testSwitchAudioRoutes() {
if (!shouldTestTelecom(mContext)) {
return;
}
+ placeAndVerifyCall();
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
final MockInCallService inCallService = mInCallCallbacks.getService();
- final MockConnection connection = mConnectionCallbacks.outgoingConnection;
final Call call = inCallService.getLastCall();
assertCallState(call, Call.STATE_ACTIVE);
@@ -107,14 +107,19 @@
/**
* Tests that DTMF Tones are sent from the {@link InCallService} to the
* {@link ConnectionService} in the correct sequence.
+ *
+ * @see {@link Call#playDtmfTone(char)}
+ * @see {@link Call#stopDtmfTone()}
*/
- public void testWithMockConnection_DtmfTones() {
+ public void testPlayAndStopDtmfTones() {
if (!shouldTestTelecom(mContext)) {
return;
}
+ placeAndVerifyCall();
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
final MockInCallService inCallService = mInCallCallbacks.getService();
- final MockConnection connection = mConnectionCallbacks.outgoingConnection;
final Call call = inCallService.getLastCall();
assertCallState(call, Call.STATE_ACTIVE);
@@ -127,19 +132,27 @@
call.playDtmfTone('2');
assertDtmfString(connection, "12");
+ call.stopDtmfTone();
+ assertDtmfString(connection, "12.");
+
call.playDtmfTone('3');
call.playDtmfTone('4');
call.playDtmfTone('5');
- assertDtmfString(connection, "12345");
+ assertDtmfString(connection, "12.345");
+
+ call.stopDtmfTone();
+ assertDtmfString(connection, "12.345.");
}
- public void testWithMockConnection_HoldAndUnholdCall() {
+ public void testHoldAndUnholdCall() {
if (!shouldTestTelecom(mContext)) {
return;
}
+ placeAndVerifyCall();
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
final MockInCallService inCallService = mInCallCallbacks.getService();
- final MockConnection connection = mConnectionCallbacks.outgoingConnection;
final Call call = inCallService.getLastCall();
@@ -153,4 +166,57 @@
assertCallState(call, Call.STATE_ACTIVE);
assertEquals(Connection.STATE_ACTIVE, connection.getState());
}
+
+ public void testAnswerIncomingCallAudioOnly() {
+ addAndVerifyNewIncomingCall(getTestNumber(), null);
+ final MockConnection connection = verifyConnectionForIncomingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+
+ final Call call = inCallService.getLastCall();
+
+ assertCallState(call, Call.STATE_RINGING);
+ assertConnectionState(connection, Connection.STATE_RINGING);
+
+ call.answer(VideoProfile.STATE_AUDIO_ONLY);
+
+ assertCallState(call, Call.STATE_ACTIVE);
+ assertConnectionState(connection, Connection.STATE_ACTIVE);
+ }
+
+ public void testAnswerIncomingCallAsVideo_SendsCorrectVideoState() {
+ addAndVerifyNewIncomingCall(getTestNumber(), null);
+ final MockConnection connection = verifyConnectionForIncomingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+
+ final Call call = inCallService.getLastCall();
+
+ assertCallState(call, Call.STATE_RINGING);
+ assertConnectionState(connection, Connection.STATE_RINGING);
+
+ call.answer(VideoProfile.STATE_BIDIRECTIONAL);
+
+ assertCallState(call, Call.STATE_ACTIVE);
+ assertConnectionState(connection, Connection.STATE_ACTIVE);
+ assertEquals("Connection did not receive VideoState for answered call",
+ VideoProfile.STATE_BIDIRECTIONAL, connection.videoState);
+ }
+
+ public void testRejectIncomingCall() {
+ addAndVerifyNewIncomingCall(getTestNumber(), null);
+ final MockConnection connection = verifyConnectionForIncomingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+
+ final Call call = inCallService.getLastCall();
+
+ assertCallState(call, Call.STATE_RINGING);
+ assertConnectionState(connection, Connection.STATE_RINGING);
+
+ call.reject(false, null);
+
+ assertCallState(call, Call.STATE_DISCONNECTED);
+ assertConnectionState(connection, Connection.STATE_DISCONNECTED);
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
index 67a0fe0..bf9d730 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
@@ -20,6 +20,7 @@
import android.telecom.CallAudioState;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
+import android.telecom.VideoProfile;
import android.util.Log;
/**
@@ -31,10 +32,18 @@
private CallAudioState mCallAudioState =
new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, ROUTE_EARPIECE | ROUTE_SPEAKER);
private int mState = STATE_NEW;
+ public int videoState = VideoProfile.STATE_AUDIO_ONLY;
private String mDtmfString = "";
+ private MockVideoProvider mMockVideoProvider;
@Override
public void onAnswer() {
+ onAnswer(VideoProfile.STATE_AUDIO_ONLY);
+ }
+
+ @Override
+ public void onAnswer(int videoState) {
+ this.videoState = videoState;
setActive();
}
@@ -69,6 +78,11 @@
}
@Override
+ public void onStopDtmfTone() {
+ mDtmfString += ".";
+ }
+
+ @Override
public void onCallAudioStateChanged(CallAudioState state) {
mCallAudioState = state;
}
@@ -89,4 +103,45 @@
public String getDtmfString() {
return mDtmfString;
}
+
+ /**
+ * Creates a mock video provider for this connection.
+ */
+ public void createMockVideoProvider() {
+ final MockVideoProvider mockVideoProvider = new MockVideoProvider(this);
+ mMockVideoProvider = mockVideoProvider;
+ setVideoProvider(mockVideoProvider);
+ }
+
+ public void sendMockVideoQuality(int videoQuality) {
+ if (mMockVideoProvider == null) {
+ return;
+ }
+ mMockVideoProvider.sendMockVideoQuality(videoQuality);
+ }
+
+ public void sendMockCallSessionEvent(int event) {
+ if (mMockVideoProvider == null) {
+ return;
+ }
+ mMockVideoProvider.sendMockCallSessionEvent(event);
+ }
+
+ public void sendMockPeerWidth(int width) {
+ if (mMockVideoProvider == null) {
+ return;
+ }
+ mMockVideoProvider.sendMockPeerWidth(width);
+ }
+
+ public void sendMockSessionModifyRequest(VideoProfile request) {
+ if (mMockVideoProvider == null) {
+ return;
+ }
+ mMockVideoProvider.sendMockSessionModifyRequest(request);
+ }
+
+ public MockVideoProvider getMockVideoProvider() {
+ return mMockVideoProvider;
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
index d6d6b67..250f197 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
@@ -18,118 +18,61 @@
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
-import android.telecom.ConnectionService;
-import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import java.util.concurrent.Semaphore;
/**
- * This is the official ConnectionService for Telecom's CTS App. Since telecom requires that a
- * CS be registered in the AndroidManifest.xml file, we have to have a single implementation
- * of a CS and this is it. To test specific CS behavior, tests will implement their own CS and
- * tell MockConnectionService to forward any method invocations to that test's implementation.
- * This is set up using {@link #setUp} and should be cleaned up before the end of the test using
- * {@link #tearDown}.
+ * Default implementation of a {@link CtsConnectionService}. This is used for the majority
+ * of Telecom CTS tests that simply require that a outgoing call is placed, or incoming call is
+ * received.
*/
-public class MockConnectionService extends ConnectionService {
- private static ConnectionServiceCallbacks sCallbacks;
- private static ConnectionService sConnectionService;
- private static Object sLock = new Object();
+public class MockConnectionService extends CtsConnectionService {
+ public static final int CONNECTION_PRESENTATION = TelecomManager.PRESENTATION_ALLOWED;
- public static abstract class ConnectionServiceCallbacks {
- private MockConnectionService mService;
- public MockConnection outgoingConnection;
- public MockConnection incomingConnection;
- public Semaphore lock = new Semaphore(0);
+ /**
+ * Used to control whether the {@link MockVideoProvider} will be created when connections are
+ * created. Used by {@link VideoCallTest#testVideoCallDelayProvider()} to test scenario where
+ * the {@link MockVideoProvider} is not created immediately when the Connection is created.
+ */
+ private boolean mCreateVideoProvider = true;
- public void onCreateOutgoingConnection(MockConnection connection,
- ConnectionRequest request) {};
- public void onCreateIncomingConnection(MockConnection connection,
- ConnectionRequest request) {};
-
- final public MockConnectionService getService() {
- return mService;
- }
-
- final public void setService(MockConnectionService service) {
- mService = service;
- }
- }
-
- public static PhoneAccount setUp(PhoneAccount phoneAccount, ConnectionService connectionService)
- throws Exception {
- synchronized(sLock) {
- if (sConnectionService != null) {
- throw new Exception("Mock ConnectionService exists. Failed to call tearDown().");
- }
- sConnectionService = connectionService;
- return phoneAccount;
- }
- }
-
- public static void tearDown() {
- synchronized(sLock) {
- sConnectionService = null;
- }
- }
+ public Semaphore lock = new Semaphore(0);
+ public MockConnection outgoingConnection;
+ public MockConnection incomingConnection;
@Override
public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
ConnectionRequest request) {
- synchronized(sLock) {
- if (sConnectionService != null) {
- return sConnectionService.onCreateOutgoingConnection(
- connectionManagerPhoneAccount, request);
- }
- }
final MockConnection connection = new MockConnection();
- connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
-
- final ConnectionServiceCallbacks callbacks = getCallbacks();
- if (callbacks != null) {
- callbacks.setService(this);
- callbacks.outgoingConnection = connection;
- callbacks.onCreateOutgoingConnection(connection, request);
+ connection.setAddress(request.getAddress(), CONNECTION_PRESENTATION);
+ if (mCreateVideoProvider) {
+ connection.createMockVideoProvider();
+ } else {
+ mCreateVideoProvider = true;
}
+ connection.setVideoState(request.getVideoState());
+
+ outgoingConnection = connection;
+ lock.release();
return connection;
}
@Override
public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
ConnectionRequest request) {
- synchronized(sLock) {
- if (sConnectionService != null) {
- return sConnectionService.onCreateIncomingConnection(
- connectionManagerPhoneAccount, request);
- }
- }
-
final MockConnection connection = new MockConnection();
- connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
+ connection.setAddress(request.getAddress(), CONNECTION_PRESENTATION);
+ connection.createMockVideoProvider();
+ connection.setVideoState(request.getVideoState());
- final ConnectionServiceCallbacks callbacks = getCallbacks();
- if (callbacks != null) {
- callbacks.setService(this);
- callbacks.incomingConnection = connection;
- callbacks.onCreateIncomingConnection(connection, request);
- }
+ incomingConnection = connection;
+ lock.release();
return connection;
}
- public static void setCallbacks(ConnectionServiceCallbacks callbacks) {
- synchronized (sLock) {
- sCallbacks = callbacks;
- }
- }
-
- private ConnectionServiceCallbacks getCallbacks() {
- synchronized (sLock) {
- if (sCallbacks != null) {
- sCallbacks.setService(this);
- }
- return sCallbacks;
- }
+ public void setCreateVideoProvider(boolean createVideoProvider) {
+ mCreateVideoProvider = createVideoProvider;
}
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index 3c48ddd..e725466 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -16,23 +16,26 @@
package android.telecom.cts;
-import android.content.Intent;
import android.telecom.Call;
import android.telecom.InCallService;
+import android.util.ArrayMap;
import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.Semaphore;
public class MockInCallService extends InCallService {
private ArrayList<Call> mCalls = new ArrayList<>();
private static InCallServiceCallbacks sCallbacks;
+ private Map<Call, MockVideoCallCallback> mVideoCallCallbacks =
+ new ArrayMap<Call, MockVideoCallCallback>();
private static final Object sLock = new Object();
public static abstract class InCallServiceCallbacks {
private MockInCallService mService;
public Semaphore lock = new Semaphore(0);
- public Semaphore unbindLock = null;
public void onCallAdded(Call call, int numCalls) {};
public void onCallRemoved(Call call, int numCalls) {};
@@ -45,21 +48,40 @@
final public void setService(MockInCallService service) {
mService = service;
}
-
- final public void prepareForUnbind() {
- unbindLock = new Semaphore(0);
- }
}
+ /**
+ * Note that the super implementations of the callback methods are all no-ops, but we call
+ * them anyway to make sure that the CTS coverage tool detects that we are testing them.
+ */
private Call.Callback mCallCallback = new Call.Callback() {
@Override
public void onStateChanged(Call call, int state) {
+ super.onStateChanged(call, state);
if (getCallbacks() != null) {
getCallbacks().onCallStateChanged(call, state);
}
}
+
+ @Override
+ public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {
+ super.onVideoCallChanged(call, videoCall);
+ saveVideoCall(call, videoCall);
+ }
};
+ private void saveVideoCall(Call call, VideoCall videoCall) {
+ if (videoCall != null) {
+ if (!mVideoCallCallbacks.containsKey(call)) {
+ MockVideoCallCallback listener = new MockVideoCallCallback(call);
+ videoCall.registerCallback(listener);
+ mVideoCallCallbacks.put(call, listener);
+ }
+ } else {
+ mVideoCallCallbacks.remove(call);
+ }
+ }
+
@Override
public android.os.IBinder onBind(android.content.Intent intent) {
if (getCallbacks() != null) {
@@ -69,18 +91,16 @@
}
@Override
- public boolean onUnbind(Intent intent) {
- if (getCallbacks() != null && getCallbacks().unbindLock != null) {
- getCallbacks().unbindLock.release();
- }
- return super.onUnbind(intent);
- }
-
- @Override
public void onCallAdded(Call call) {
+ super.onCallAdded(call);
if (!mCalls.contains(call)) {
mCalls.add(call);
call.registerCallback(mCallCallback);
+
+ VideoCall videoCall = call.getVideoCall();
+ if (videoCall != null) {
+ saveVideoCall(call, videoCall);
+ }
}
if (getCallbacks() != null) {
getCallbacks().onCallAdded(call, mCalls.size());
@@ -89,9 +109,11 @@
@Override
public void onCallRemoved(Call call) {
+ super.onCallRemoved(call);
mCalls.remove(call);
if (getCallbacks() != null) {
getCallbacks().onCallRemoved(call, mCalls.size());
+ saveVideoCall(call, null /* remove videoCall */);
}
}
@@ -133,4 +155,23 @@
return sCallbacks;
}
}
+
+ /**
+ * Determines if a video callback has been registered for the passed in call.
+ *
+ * @param call The call.
+ * @return {@code true} if a video callback has been registered.
+ */
+ public boolean isVideoCallbackRegistered(Call call) {
+ return mVideoCallCallbacks.containsKey(call);
+ }
+
+ /**
+ * Retrieves the video callbacks associated with a call.
+ * @param call The call.
+ * @return The {@link MockVideoCallCallback} instance associated with the call.
+ */
+ public MockVideoCallCallback getVideoCallCallback(Call call) {
+ return mVideoCallCallbacks.get(call);
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockVideoCallCallback.java b/tests/tests/telecom/src/android/telecom/cts/MockVideoCallCallback.java
new file mode 100644
index 0000000..07e159c
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/MockVideoCallCallback.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom.cts;
+
+import android.telecom.Call;
+import android.telecom.InCallService;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+
+/**
+ * Mock video call Callback class.
+ */
+public class MockVideoCallCallback extends InCallService.VideoCall.Callback {
+ private Call mCall;
+ private CameraCapabilities mCameraCapabilities;
+ private long mDataUsage = MockVideoProvider.DATA_USAGE_UNDEFINED;
+ private int mVideoQuality = MockVideoProvider.VIDEO_QUALITY_UNDEFINED;
+ private int mCallSessionEvent = MockVideoProvider.SESSION_EVENT_UNDEFINED;
+ private int mPeerWidth = MockVideoProvider.PEER_WIDTH_UNDEFINED;
+ private VideoProfile mResponseProfile = null;
+ private VideoProfile mRequestProfile = null;
+
+ public MockVideoCallCallback(Call call) {
+ mCall = call;
+ }
+
+ /**
+ * Store incoming session modify request so tests can inspect it.
+ *
+ * @param videoProfile The requested video profile.
+ */
+ @Override
+ public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
+ mRequestProfile = videoProfile;
+ }
+
+ /**
+ * Store incoming session modify response so tests can inspect it.
+ *
+ * @param status Status of the session modify request.
+ * @param requestedProfile The original request which was sent to the peer device.
+ * @param responseProfile The actual profile changes made by the peer device.
+ */
+ @Override
+ public void onSessionModifyResponseReceived(int status, VideoProfile requestedProfile,
+ VideoProfile responseProfile) {
+ mResponseProfile = responseProfile;
+ }
+
+ /**
+ * Store incoming session event so tests can inspect it.
+ *
+ * @param event The event.
+ */
+ @Override
+ public void onCallSessionEvent(int event) {
+ mCallSessionEvent = event;
+ }
+
+ /**
+ * Store incoming peer dimensions so tests can inspect them.
+ *
+ * @param width The updated peer video width.
+ * @param height The updated peer video height.
+ */
+ @Override
+ public void onPeerDimensionsChanged(int width, int height) {
+ mPeerWidth = width;
+ }
+
+ /**
+ * Store incoming video quality so tests can inspect them.
+ *
+ * @param videoQuality The updated peer video quality. Valid values:
+ * {@link VideoProfile#QUALITY_HIGH},
+ * {@link VideoProfile#QUALITY_MEDIUM},
+ * {@link VideoProfile#QUALITY_LOW},
+ */
+ @Override
+ public void onVideoQualityChanged(int videoQuality) {
+ mVideoQuality = videoQuality;
+ }
+
+ /**
+ * Store incoming call data usage so tests can inspect it.
+ *
+ * @param dataUsage The updated data usage (in bytes).
+ */
+ @Override
+ public void onCallDataUsageChanged(long dataUsage) {
+ mDataUsage = dataUsage;
+ }
+
+ /**
+ * Store incoming camera capabilities so tests can inspect them.
+ *
+ * @param cameraCapabilities The changed camera capabilities.
+ */
+ @Override
+ public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) {
+ mCameraCapabilities = cameraCapabilities;
+ }
+
+ /**
+ * Returns the last received {@link CameraCapabilities}.
+ *
+ * @return The {@link CameraCapabilities}.
+ */
+ public CameraCapabilities getCameraCapabilities() {
+ return mCameraCapabilities;
+ }
+
+ /**
+ * Returns the last received data usage.
+ *
+ * @return The data usage.
+ */
+ public long getDataUsage() {
+ return mDataUsage;
+ }
+
+ /**
+ * Returns the last received video quality.
+ *
+ * @return The video quality.
+ */
+ public int getVideoQuality()
+ {
+ return mVideoQuality;
+ }
+
+ /**
+ * Returns the last received call session event.
+ *
+ * @return The call session event.
+ */
+ public int getCallSessionEvent()
+ {
+ return mCallSessionEvent;
+ }
+
+ /**
+ * Returns the last received peer width.
+ *
+ * @return The call session event.
+ */
+ public int getPeerWidth()
+ {
+ return mPeerWidth;
+ }
+
+ /**
+ * Returns the last received response video profile.
+ *
+ * @return The video profile.
+ */
+ public VideoProfile getResponseProfile() {
+ return mResponseProfile;
+ }
+
+ /**
+ * Returns the last requested video profile.
+ *
+ * @return The video profile.
+ */
+ public VideoProfile getRequestProfile() {
+ return mRequestProfile;
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java b/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
new file mode 100644
index 0000000..8ed422f
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom.cts;
+
+import android.net.Uri;
+import android.telecom.Connection;
+import android.telecom.VideoProfile;
+import android.view.Surface;
+
+import android.telecom.Connection.VideoProvider;
+
+/**
+ * Implements a mock video provider implementation.
+ */
+public class MockVideoProvider extends VideoProvider {
+ public static final String CAMERA_NONE = "none";
+ public static final String CAMERA_FRONT = "front";
+ public static final String CAMERA_BACK = "back";
+ public static final int CAMERA_FRONT_DIMENSIONS = 1024;
+ public static final int CAMERA_BACK_DIMENSIONS = 2048;
+ public static final long DATA_USAGE = 1024;
+ public static final long DATA_USAGE_UNDEFINED = -1;
+ public static final int VIDEO_QUALITY_UNDEFINED = -1;
+ public static final int SESSION_EVENT_UNDEFINED = -1;
+ public static final int PEER_WIDTH_UNDEFINED = -1;
+ public static final int DEVICE_ORIENTATION_UNDEFINED = -1;
+ public static final float ZOOM_UNDEFINED = -1.0f;
+
+ private Uri mPauseImageUri;
+ private String mCameraId = CAMERA_NONE;
+ private MockConnection mMockConnection;
+ private int mDeviceOrientation = DEVICE_ORIENTATION_UNDEFINED;
+ private float mZoom = ZOOM_UNDEFINED;
+ private Surface mPreviewSurface = null;
+ private Surface mDisplaySurface = null;
+ private VideoProfile mSessionModifyResponse = null;
+
+ public MockVideoProvider(MockConnection mockConnection) {
+ mMockConnection = mockConnection;
+ }
+
+ @Override
+ public void onSetCamera(String cameraId) {
+ handleCameraChange(cameraId);
+ }
+
+ @Override
+ public void onSetPreviewSurface(Surface surface) {
+ mPreviewSurface = surface;
+ }
+
+ @Override
+ public void onSetDisplaySurface(Surface surface) {
+ mDisplaySurface = surface;
+ }
+
+ @Override
+ public void onSetDeviceOrientation(int rotation) {
+ mDeviceOrientation = rotation;
+ }
+
+ @Override
+ public void onSetZoom(float value) {
+ mZoom = value;
+ }
+
+ /**
+ * Handles a session modification request from the {@link MockInCallService}. Assumes the peer
+ * has accepted the proposed video profile.
+ *
+ * @param fromProfile The video properties prior to the request.
+ * @param toProfile The video properties with the requested changes made.
+ */
+ @Override
+ public void onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+ receiveSessionModifyResponse(Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS,
+ toProfile, toProfile);
+ mMockConnection.setVideoState(toProfile.getVideoState());
+ }
+
+ @Override
+ public void onSendSessionModifyResponse(VideoProfile responseProfile) {
+ mSessionModifyResponse = responseProfile;
+ }
+
+ /**
+ * Responds with the current camera capabilities.
+ */
+ @Override
+ public void onRequestCameraCapabilities() {
+ handleCameraChange(mCameraId);
+ }
+
+ /**
+ * Handles requests to retrieve the connection data usage by returning a fixed usage amount of
+ * {@code 1024} bytes.
+ */
+ @Override
+ public void onRequestConnectionDataUsage() {
+ setCallDataUsage(DATA_USAGE);
+ }
+
+ @Override
+ public void onSetPauseImage(Uri uri) {
+ mPauseImageUri = uri;
+ }
+
+ /**
+ * Handles a change to the current camera selection. Responds by reporting the capabilities of
+ * the camera.
+ */
+ private void handleCameraChange(String cameraId) {
+ mCameraId = cameraId;
+ if (CAMERA_FRONT.equals(mCameraId)) {
+ changeCameraCapabilities(new VideoProfile.CameraCapabilities(CAMERA_FRONT_DIMENSIONS,
+ CAMERA_FRONT_DIMENSIONS));
+ } else if (CAMERA_BACK.equals(mCameraId)) {
+ changeCameraCapabilities(new VideoProfile.CameraCapabilities(CAMERA_BACK_DIMENSIONS,
+ CAMERA_BACK_DIMENSIONS));
+ }
+ }
+
+ /**
+ * Sends a mock video quality value from the provider.
+ *
+ * @param videoQuality The video quality.
+ */
+ public void sendMockVideoQuality(int videoQuality) {
+ changeVideoQuality(videoQuality);
+ }
+
+ /**
+ * Sends a mock call session event from the provider.
+ *
+ * @param event The call session event.
+ */
+ public void sendMockCallSessionEvent(int event) {
+ handleCallSessionEvent(event);
+ }
+
+ /**
+ * Sends a mock peer width from the provider.
+ *
+ * @param width The peer width.
+ */
+ public void sendMockPeerWidth(int width) {
+ changePeerDimensions(width, width);
+ }
+
+ /**
+ * Sends a mock session modify request from the provider.
+ *
+ * @param request The requested profile.
+ */
+ public void sendMockSessionModifyRequest(VideoProfile request) {
+ receiveSessionModifyRequest(request);
+ }
+
+ public int getDeviceOrientation() {
+ return mDeviceOrientation;
+ }
+
+ public float getZoom() {
+ return mZoom;
+ }
+
+ public Surface getPreviewSurface() {
+ return mPreviewSurface;
+ }
+
+ public Surface getDisplaySurface() {
+ return mDisplaySurface;
+ }
+
+ public VideoProfile getSessionModifyResponse() {
+ return mSessionModifyResponse;
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java b/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
index f0df70a..673013f 100644
--- a/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
@@ -18,32 +18,22 @@
import static android.telecom.cts.TestUtils.shouldTestTelecom;
-import android.content.Context;
import android.net.Uri;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
-import android.telecom.ConnectionService;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
/**
* Tests that certain numbers make their way through to the connection service.
*/
-public class NumberDialingTest extends SimpleTelecomTest {
-
- private Context mContext;
+public class NumberDialingTest extends BaseTelecomTestWithMockServices {
/**
* Amount of time to wait for an asynchronous method invocation to ConnectionService.
*/
private static final int CS_WAIT_MILLIS = 2000;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getContext();
- }
-
public void testEndInPound() throws Exception {
if (!shouldTestTelecom(mContext)) {
return;
@@ -52,8 +42,8 @@
final Object[] res = new Object[1];
Uri address = Uri.fromParts("tel", "*1234#", null);
- PhoneAccount account = setupConnectionService("testEndInPound",
- new ConnectionService() {
+ PhoneAccount account = setupConnectionService(
+ new MockConnectionService() {
@Override
public Connection onCreateOutgoingConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
index 5895267..b1c77fd 100644
--- a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -28,11 +28,11 @@
public class OutgoingCallTest extends BaseTelecomTestWithMockServices {
@Override
- protected void tearDown() throws Exception {
- if (shouldTestTelecom(mContext)) {
- cleanupAndVerifyUnbind();
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (TestUtils.shouldTestTelecom(mContext)) {
+ setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
}
- super.tearDown();
}
// TODO: Need to send some commands to the UserManager via adb to do setup
diff --git a/tests/tests/telecom/src/android/telecom/cts/PhoneAccountOperationsTest.java b/tests/tests/telecom/src/android/telecom/cts/PhoneAccountOperationsTest.java
new file mode 100644
index 0000000..fd554ef
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/PhoneAccountOperationsTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom.cts;
+
+import static android.telecom.cts.TestUtils.*;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Color;
+import android.net.Uri;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.test.InstrumentationTestCase;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * Verifies some of the PhoneAccount registration related operations.
+ */
+public class PhoneAccountOperationsTest extends InstrumentationTestCase {
+ public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
+ new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID);
+
+ public static final PhoneAccount TEST_SIM_PHONE_ACCOUNT = PhoneAccount.builder(
+ TEST_PHONE_ACCOUNT_HANDLE, LABEL)
+ .setAddress(Uri.parse("tel:555-TEST"))
+ .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
+ .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
+ .setHighlightColor(Color.RED)
+ .setShortDescription(LABEL)
+ .setSupportedUriSchemes(Arrays.asList("tel"))
+ .build();
+
+ public static final PhoneAccount TEST_NO_SIM_PHONE_ACCOUNT = PhoneAccount.builder(
+ TEST_PHONE_ACCOUNT_HANDLE, LABEL)
+ .setAddress(Uri.parse("tel:555-TEST"))
+ .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .setHighlightColor(Color.RED)
+ .setShortDescription(LABEL)
+ .setSupportedUriSchemes(Arrays.asList("tel"))
+ .build();
+
+ private Context mContext;
+ private TelecomManager mTelecomManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = getInstrumentation().getContext();
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mTelecomManager.unregisterPhoneAccount(TEST_PHONE_ACCOUNT_HANDLE);
+ PhoneAccount retrievedPhoneAccount = mTelecomManager.getPhoneAccount(
+ TEST_PHONE_ACCOUNT_HANDLE);
+ assertNull("Test account not deregistered.", retrievedPhoneAccount);
+ super.tearDown();
+ }
+
+ public void testRegisterPhoneAccount_correctlyThrowsSecurityException() throws Exception {
+ if (!shouldTestTelecom(mContext)) {
+ return;
+ }
+ try {
+ mTelecomManager.registerPhoneAccount(TEST_SIM_PHONE_ACCOUNT);
+ fail("TelecomManager.registerPhoneAccount should throw SecurityException if "
+ + "not a system app.");
+ } catch (SecurityException e) {
+ assertTrue("Unexpected security exception.", (e.getMessage().indexOf(
+ "android.permission.REGISTER_SIM_SUBSCRIPTION") >= 0));
+ }
+ }
+
+ public void testRegisterPhoneAccount_NotEnabledAutomatically() throws Exception {
+ if (!shouldTestTelecom(mContext)) {
+ return;
+ }
+ mTelecomManager.registerPhoneAccount(TEST_NO_SIM_PHONE_ACCOUNT);
+ PhoneAccount retrievedPhoneAccount = mTelecomManager.getPhoneAccount(
+ TEST_PHONE_ACCOUNT_HANDLE);
+ assertNotNull("Failed to retrieve test account.", retrievedPhoneAccount);
+ assertFalse("Phone account should not be automatically enabled.",
+ retrievedPhoneAccount.isEnabled());
+ }
+
+ public void testRegisterPhoneAccount_DisallowEnable() throws Exception {
+ if (!shouldTestTelecom(mContext)) {
+ return;
+ }
+ Method setIsEnabled = null;
+ PhoneAccount.Builder phoneAccountBuilder = PhoneAccount.builder(
+ TEST_PHONE_ACCOUNT_HANDLE, LABEL)
+ .setAddress(Uri.parse("tel:555-TEST"))
+ .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .setHighlightColor(Color.RED)
+ .setShortDescription(LABEL)
+ .setSupportedUriSchemes(Arrays.asList("tel"));
+ try {
+ setIsEnabled = PhoneAccount.Builder.class.getDeclaredMethod(
+ "setIsEnabled", boolean.class);
+ } catch (NoSuchMethodException e) {
+ fail("Failed to find setIsEnabled method.");
+ }
+ setIsEnabled.invoke(phoneAccountBuilder, true);
+ final PhoneAccount phoneAccount = phoneAccountBuilder.build();
+ mTelecomManager.registerPhoneAccount(phoneAccount);
+ PhoneAccount retrievedPhoneAccount = mTelecomManager.getPhoneAccount(
+ TEST_PHONE_ACCOUNT_HANDLE);
+ assertNotNull("Failed to retrieve test account.", retrievedPhoneAccount);
+ assertFalse("3rd party app cannot enable its own phone account.",
+ retrievedPhoneAccount.isEnabled());
+ }
+
+ public void testRegisterPhoneAccount_ListEnabledAccounts() throws Exception {
+ if (!shouldTestTelecom(mContext)) {
+ return;
+ }
+ mTelecomManager.registerPhoneAccount(TEST_NO_SIM_PHONE_ACCOUNT);
+ final List<PhoneAccountHandle> oldAccounts = mTelecomManager.getCallCapablePhoneAccounts();
+ final int oldAccountsListSize = oldAccounts.size();
+ if (oldAccountsListSize > 0) {
+ assertFalse("Enabled Phone accounts should not contain the test account.",
+ oldAccounts.contains(TEST_PHONE_ACCOUNT_HANDLE));
+ }
+ TestUtils.enablePhoneAccount(getInstrumentation(), TEST_PHONE_ACCOUNT_HANDLE);
+ final List<PhoneAccountHandle> newAccounts = mTelecomManager.getCallCapablePhoneAccounts();
+ assertNotNull("No enabled Phone account found.", newAccounts);
+ assertEquals("1 new enabled Phone account expected.", newAccounts.size(),
+ oldAccountsListSize+1);
+ assertTrue("Enabled Phone accounts do not contain the test account.",
+ newAccounts.contains(TEST_PHONE_ACCOUNT_HANDLE));
+ }
+
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/SimpleTelecomTest.java b/tests/tests/telecom/src/android/telecom/cts/SimpleTelecomTest.java
deleted file mode 100644
index 377a999..0000000
--- a/tests/tests/telecom/src/android/telecom/cts/SimpleTelecomTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecom.cts;
-
-import static android.telecom.cts.TestUtils.shouldTestTelecom;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.telecom.Connection;
-import android.telecom.ConnectionRequest;
-import android.telecom.ConnectionService;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.test.InstrumentationTestCase;
-
-/**
- * Scaffolding for a simple telecom test. Provides helper methods for registers a phone account
- * and making calls.
- */
-public class SimpleTelecomTest extends InstrumentationTestCase {
-
- private Context mContext;
- private TelecomManager mTelecomManager;
-
- public static final int FLAG_REGISTER = 0x1;
- public static final int FLAG_ENABLE = 0x2;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getContext();
- mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- }
-
- @Override
- protected void tearDown() throws Exception {
- MockConnectionService.tearDown();
- super.tearDown();
- }
-
- protected PhoneAccount setupConnectionService(
- String testTag, ConnectionService connectionService, int flags) throws Exception {
-
- PhoneAccount.Builder builder = new PhoneAccount.Builder(
- new PhoneAccountHandle(
- new ComponentName("com.android.cts.telecom",
- "android.telecom.cts.MockConnectionService"),
- testTag),
- "TestPA " + testTag)
- .setAddress(Uri.fromParts("tel:", "5417705", null))
- .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
- .setShortDescription("CTS Test Account with ID " + testTag)
- .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
- .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL);
-
- // register and enable the phone account
- PhoneAccount account = builder.build();
- MockConnectionService.setUp(account, connectionService);
-
- if ((flags & FLAG_REGISTER) != 0) {
- mTelecomManager.registerPhoneAccount(account);
- }
- if ((flags & FLAG_ENABLE) != 0) {
- TestUtils.enablePhoneAccount(getInstrumentation(), account.getAccountHandle());
- }
-
- return account;
- }
-
- protected void tearDownConnectionService(PhoneAccount account) throws Exception {
- mTelecomManager.unregisterPhoneAccount(account.getAccountHandle());
- }
-
- protected void startCallTo(Uri address, PhoneAccountHandle accountHandle) {
- final Intent intent = new Intent(Intent.ACTION_CALL, address);
- if (accountHandle != null) {
- intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
- }
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- }
-}
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index 8cca04c..3356be0 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -34,16 +34,20 @@
static final boolean HAS_TELECOM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000;
- public static final String PACKAGE = "com.android.cts.telecom";
- public static final String COMPONENT = "android.telecom.cts.MockConnectionService";
+ // Non-final to allow modification by tests not in this package (e.g. permission-related
+ // tests in the Telecom2 test package.
+ public static String PACKAGE = "com.android.cts.telecom";
+ public static final String COMPONENT = "android.telecom.cts.CtsConnectionService";
public static final String ACCOUNT_ID = "xtstest_CALL_PROVIDER_ID";
- public static final String LABEL = "CTS_MockConnectionService";
+ public static final String LABEL = "CTSConnectionService";
private static final String COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer ";
private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer";
+ private static final String COMMAND_GET_SYSTEM_DIALER = "telecom get-system-dialer";
+
private static final String COMMAND_ENABLE = "telecom set-phone-account-enabled ";
public static boolean shouldTestTelecom(Context context) {
@@ -54,15 +58,19 @@
return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
}
- public static void setDefaultDialer(Instrumentation instrumentation, String packageName)
+ public static String setDefaultDialer(Instrumentation instrumentation, String packageName)
throws Exception {
- executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName);
+ return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName);
}
public static String getDefaultDialer(Instrumentation instrumentation) throws Exception {
return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER);
}
+ public static String getSystemDialer(Instrumentation instrumentation) throws Exception {
+ return executeShellCommand(instrumentation, COMMAND_GET_SYSTEM_DIALER);
+ }
+
public static void enablePhoneAccount(Instrumentation instrumentation,
PhoneAccountHandle handle) throws Exception {
final ComponentName component = handle.getComponentName();
diff --git a/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java b/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java
new file mode 100644
index 0000000..e62d125
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom.cts;
+
+import android.graphics.SurfaceTexture;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telecom.InCallService;
+import android.telecom.VideoProfile;
+import android.util.Log;
+import android.view.Surface;
+import android.view.TextureView;
+
+import static android.telecom.cts.TestUtils.shouldTestTelecom;
+
+/**
+ * Suites of tests that use {@link MockVideoProvider} and {@link MockVideoCallCallback} to verify
+ * the functionality of the video APIs.
+ *
+ * Note: You'll notice the use of {@code work}, and
+ * {@code doWorkAndWaitUntilConditionIsTrueOrTimeout} here. The problem is the
+ * {@link MockVideoProvider} is running using a Handler. To get it to emit mock data that is
+ * in sync with the setup operations performed on the handler, we'd need access to its handler.
+ * The handler of the {@link Connection.VideoProvider} is, however, not public. As a workaround
+ * we will call local methods on the MockVideoProvider. This means there is a chance the
+ * VideoProvider will emit the data we're interested in before the callbacks (on the handler)
+ * are even set up. Consequently, the callbacks we're depending on in our test may not get
+ * called. To compensate we will call the test methods on the provider repeatedly until we
+ * hear back via our callback. Suboptimal, but it works.
+ */
+public class VideoCallTest extends BaseTelecomTestWithMockServices {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (TestUtils.shouldTestTelecom(mContext)) {
+ setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
+ }
+ }
+
+ /**
+ * Tests ability to start a 2-way video call and retrieve its video state.
+ */
+ public void testMakeTwoWayVideoCall() {
+
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+
+ assertCallState(call, Call.STATE_ACTIVE);
+ assertVideoState(call, VideoProfile.STATE_BIDIRECTIONAL);
+ assertVideoCallbackRegistered(inCallService, call, true);
+ }
+
+ /**
+ * Tests ability to start a 1-way video call and retrieve its video state.
+ */
+ public void testMakeOneWayVideoCall() {
+ placeAndVerifyCall(VideoProfile.STATE_TX_ENABLED);
+ verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+
+ assertVideoState(call, VideoProfile.STATE_TX_ENABLED);
+ assertVideoCallbackRegistered(inCallService, call, true);
+ }
+
+ /**
+ * Tests ability to upgrade an audio-only call to a video call.
+ */
+ public void testUpgradeToVideo() {
+ placeAndVerifyCall(VideoProfile.STATE_AUDIO_ONLY);
+ verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoState(call, VideoProfile.STATE_AUDIO_ONLY);
+ assertVideoCallbackRegistered(inCallService, call, true);
+
+ // Send request to upgrade to video.
+ InCallService.VideoCall videoCall = call.getVideoCall();
+ videoCall.sendSessionModifyRequest(new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL));
+ assertVideoState(call, VideoProfile.STATE_BIDIRECTIONAL);
+ assertResponseVideoProfileReceived(inCallService.getVideoCallCallback(call),
+ VideoProfile.STATE_BIDIRECTIONAL);
+ }
+
+ /**
+ * Tests ability to receive a session modification request.
+ */
+ public void testReceiveSessionModifyRequest() {
+ placeAndVerifyCall(VideoProfile.STATE_AUDIO_ONLY);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+
+ assertVideoState(call, VideoProfile.STATE_AUDIO_ONLY);
+ assertVideoCallbackRegistered(inCallService, call, true);
+
+ // Have the video profile mock reception of a request.
+ assertRequestVideoProfileReceived(inCallService.getVideoCallCallback(call),
+ VideoProfile.STATE_BIDIRECTIONAL,
+ new Work() {
+ @Override
+ public void doWork() {
+ connection.sendMockSessionModifyRequest(
+ new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL));
+ }
+ });
+ }
+
+ /**
+ * Tests ability to send a session modification response.
+ */
+ public void testSendSessionModifyResponse() {
+ placeAndVerifyCall(VideoProfile.STATE_AUDIO_ONLY);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+ assertVideoState(call, VideoProfile.STATE_AUDIO_ONLY);
+ assertVideoCallbackRegistered(inCallService, call, true);
+
+ InCallService.VideoCall videoCall = call.getVideoCall();
+ videoCall.sendSessionModifyResponse(new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL));
+ assertSessionModifyResponse(mockVideoProvider, VideoProfile.STATE_BIDIRECTIONAL);
+ }
+
+ /**
+ * Tests ability to start a video call, delaying the creation of the provider until after
+ * the call has been initiated (rather than immediately when the call is created). This more
+ * closely mimics the lifespan of a {@code VideoProvider} instance as it is reasonable to
+ * expect there will be some overhead associated with configuring the camera at the start of
+ * the call.
+ */
+ public void testVideoCallDelayProvider() {
+ // Don't create video provider when call is created initially; we will do this later.
+ try {
+ connectionService.setCreateVideoProvider(false);
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+
+ assertVideoState(call, VideoProfile.STATE_BIDIRECTIONAL);
+ // After initial connection creation there should not be a video provider or callbacks
+ // registered.
+ assertVideoCallbackRegistered(inCallService, call, false);
+
+ // Trigger delayed creation of video provider and registration of callbacks and assert that
+ // it happened.
+ connection.createMockVideoProvider();
+ assertVideoCallbackRegistered(inCallService, call, true);
+
+ // Ensure video providers are created in the future.
+ } finally {
+ connectionService.setCreateVideoProvider(true);
+ }
+ }
+
+
+ /**
+ * Tests ability to change the current camera. Ensures that the camera capabilities are sent
+ * back in response.
+ */
+ public void testChangeCamera() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+ final InCallService.VideoCall videoCall = call.getVideoCall();
+
+ videoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
+ assertCameraCapabilitiesReceived(inCallService.getVideoCallCallback(call),
+ MockVideoProvider.CAMERA_FRONT_DIMENSIONS);
+
+ videoCall.setCamera(MockVideoProvider.CAMERA_BACK);
+ assertCameraCapabilitiesReceived(inCallService.getVideoCallCallback(call),
+ MockVideoProvider.CAMERA_BACK_DIMENSIONS);
+ }
+
+ /**
+ * Tests ability to request the camera capabilities from the video provider.
+ */
+ public void testRequestCameraCapabilities() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+ final InCallService.VideoCall videoCall = call.getVideoCall();
+
+ // First, set the camera.
+ videoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
+ // Retrieve the camera capabilities that are automatically send when the camera is set --
+ // ensures the cached value is cleared first.
+ inCallService.getVideoCallCallback(call).getCameraCapabilities();
+
+ // Now, request capabilities.
+ videoCall.requestCameraCapabilities();
+ assertCameraCapabilitiesReceived(inCallService.getVideoCallCallback(call),
+ MockVideoProvider.CAMERA_FRONT_DIMENSIONS);
+ }
+
+ /**
+ * Tests ability to request data usage from the video provider.
+ */
+ public void testRequestDataUsage() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+ final InCallService.VideoCall videoCall = call.getVideoCall();
+
+ videoCall.requestCallDataUsage();
+ assertCallDataUsageReceived(inCallService.getVideoCallCallback(call),
+ MockVideoProvider.DATA_USAGE);
+ }
+
+ /**
+ * Tests ability to receive changes to the video quality from the video provider.
+ */
+ public void testReceiveVideoQuality() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+
+ assertVideoQualityReceived(inCallService.getVideoCallCallback(call),
+ VideoProfile.QUALITY_HIGH,
+ new Work() {
+ @Override
+ public void doWork() {
+ connection
+ .sendMockVideoQuality(VideoProfile.QUALITY_HIGH);
+ }
+ });
+
+ assertVideoQualityReceived(inCallService.getVideoCallCallback(call),
+ VideoProfile.QUALITY_MEDIUM,
+ new Work() {
+ @Override
+ public void doWork() {
+ connection
+ .sendMockVideoQuality(VideoProfile.QUALITY_MEDIUM);
+ }
+ });
+ }
+
+ /**
+ * Tests ability to receive call session events from the video provider.
+ */
+ public void testReceiveCallSessionEvent() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+
+ assertCallSessionEventReceived(inCallService.getVideoCallCallback(call),
+ Connection.VideoProvider.SESSION_EVENT_CAMERA_READY,
+ new Work() {
+ @Override
+ public void doWork() {
+ connection.sendMockCallSessionEvent(
+ Connection.VideoProvider.SESSION_EVENT_CAMERA_READY);
+ }
+ });
+ }
+
+ /**
+ * Tests ability to receive changes to the peer dimensions from the video provider.
+ */
+ public void testReceivePeerDimensionChange() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+
+ assertPeerWidthChanged(inCallService.getVideoCallCallback(call),
+ MockVideoProvider.CAMERA_BACK_DIMENSIONS,
+ new Work() {
+ @Override
+ public void doWork() {
+ connection.sendMockPeerWidth(MockVideoProvider.CAMERA_BACK_DIMENSIONS);
+ }
+ });
+ }
+
+ /**
+ * Tests ability to set the device orientation via the provider.
+ */
+ public void testSetDeviceOrientation() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+ final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+ final InCallService.VideoCall videoCall = call.getVideoCall();
+
+ // Set device orientation and ensure provider knows about it.
+ videoCall.setDeviceOrientation(90);
+ assertDeviceOrientationChanged(mockVideoProvider, 90);
+ }
+
+ /**
+ * Tests ability to set the preview surface via the provider.
+ */
+ public void testSetPreviewSurface() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+ final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+ final InCallService.VideoCall videoCall = call.getVideoCall();
+
+ Surface surface = new Surface(new SurfaceTexture(1));
+ // Set a surface
+ videoCall.setPreviewSurface(surface);
+ assertPreviewSurfaceChanged(mockVideoProvider, true);
+
+ // Clear the surface
+ videoCall.setPreviewSurface(null);
+ assertPreviewSurfaceChanged(mockVideoProvider, false);
+ }
+
+ /**
+ * Tests ability to set the display surface via the provider.
+ */
+ public void testSetDisplaySurface() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+ final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+ final InCallService.VideoCall videoCall = call.getVideoCall();
+
+ // Set a surface
+ Surface surface = new Surface(new SurfaceTexture(1));
+ videoCall.setDisplaySurface(surface);
+ assertDisplaySurfaceChanged(mockVideoProvider, true);
+
+ // Clear the surface
+ videoCall.setDisplaySurface(null);
+ assertDisplaySurfaceChanged(mockVideoProvider, false);
+ }
+
+ /**
+ * Tests ability to set the camera zoom via the provider.
+ */
+ public void testSetZoom() {
+ placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+ final MockConnection connection = verifyConnectionForOutgoingCall();
+
+ final MockInCallService inCallService = mInCallCallbacks.getService();
+ final Call call = inCallService.getLastCall();
+ assertVideoCallbackRegistered(inCallService, call, true);
+ final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+ final InCallService.VideoCall videoCall = call.getVideoCall();
+
+ videoCall.setZoom(0.0f);
+ assertZoomChanged(mockVideoProvider, 0.0f);
+
+ videoCall.setZoom(10.0f);
+ assertZoomChanged(mockVideoProvider, 10.0f);
+
+ call.disconnect();
+ }
+
+ /**
+ * Asserts that a call video state is as expected.
+ *
+ * @param call The call.
+ * @param videoState The expected video state.
+ */
+ private void assertVideoState(final Call call, final int videoState) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return videoState;
+ }
+
+ @Override
+ public Object actual() {
+ return call.getDetails().getVideoState();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Call should be in videoState " + videoState
+ );
+ }
+
+ /**
+ * Asserts whether the InCallService has registered a video call back (and hence a video call)
+ * for a call.
+ *
+ * @param inCallService The incall service.
+ * @param call The call.
+ * @param isRegistered The expected registration state.
+ */
+ private void assertVideoCallbackRegistered(final MockInCallService inCallService,
+ final Call call, final Boolean isRegistered) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return isRegistered;
+ }
+
+ @Override
+ public Object actual() {
+ return inCallService.isVideoCallbackRegistered(call);
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Video callback registration state should be " + isRegistered
+ );
+ }
+
+ /**
+ * Asserts whether the camera capabilities have changed to an expected value. Compares the
+ * camera height only (the {@link MockVideoProvider} sets height and width to be the same.
+ *
+ * @param videoCallCallback The video call callback.
+ * @param expectedCameraWidthHeight The expected width and height.
+ */
+ private void assertCameraCapabilitiesReceived(final MockVideoCallCallback videoCallCallback,
+ final int expectedCameraWidthHeight) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expectedCameraWidthHeight;
+ }
+
+ @Override
+ public Object actual() {
+ VideoProfile.CameraCapabilities cameraCapabilities =
+ videoCallCallback.getCameraCapabilities();
+ return cameraCapabilities == null ? 0 : cameraCapabilities.getHeight();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Camera width and height should be " + expectedCameraWidthHeight
+ );
+ }
+
+ /**
+ * Asserts whether the call data usage has changed to the expected value.
+ *
+ * @param videoCallCallback The video call callback.
+ * @param expectedDataUsage The expected data usage.
+ */
+ private void assertCallDataUsageReceived(final MockVideoCallCallback videoCallCallback,
+ final long expectedDataUsage) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expectedDataUsage;
+ }
+
+ @Override
+ public Object actual() {
+ return videoCallCallback.getDataUsage();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Data usage should be " + expectedDataUsage
+ );
+ }
+
+ /**
+ * Asserts whether the video quality has changed to the expected value.
+ *
+ * @param videoCallCallback The video call callback.
+ * @param expectedVideoQuality The expected video quality.
+ * @param work The work to perform to have the provider emit the video quality.
+ */
+ private void assertVideoQualityReceived(final MockVideoCallCallback videoCallCallback,
+ final int expectedVideoQuality, final Work work) {
+ doWorkAndWaitUntilConditionIsTrueOrTimeout(
+ work,
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expectedVideoQuality;
+ }
+
+ @Override
+ public Object actual() {
+ return videoCallCallback.getVideoQuality();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Video quality should be " + expectedVideoQuality
+ );
+ }
+
+ /**
+ * Asserts whether the call session event has changed to the expected value.
+ *
+ * @param videoCallCallback The video call callback.
+ * @param expectedEvent The expected event.
+ * @param work The work to be performed to send the call session event from the provider.
+ */
+ private void assertCallSessionEventReceived(final MockVideoCallCallback videoCallCallback,
+ final int expectedEvent, final Work work) {
+ doWorkAndWaitUntilConditionIsTrueOrTimeout(
+ work,
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expectedEvent;
+ }
+
+ @Override
+ public Object actual() {
+ return videoCallCallback.getCallSessionEvent();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Call session event should be " + expectedEvent
+ );
+ }
+
+ /**
+ * Asserts whether the peer width has changed to the expected value.
+ *
+ * @param videoCallCallback The video call callback.
+ * @param expectedWidth The expected width.
+ * @param work The work to be performed to send the peer width from the provider.
+ */
+ private void assertPeerWidthChanged(final MockVideoCallCallback videoCallCallback,
+ final int expectedWidth, final Work work) {
+ doWorkAndWaitUntilConditionIsTrueOrTimeout(
+ work,
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expectedWidth;
+ }
+
+ @Override
+ public Object actual() {
+ return videoCallCallback.getPeerWidth();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Peer width should be " + expectedWidth
+ );
+ }
+
+ /**
+ * Asserts whether the device orientation has changed to the expected value.
+ *
+ * @param mockVideoProvider The mock video provider.
+ * @param expected The expected device orientation.
+ */
+ private void assertDeviceOrientationChanged(final MockVideoProvider mockVideoProvider,
+ final int expected) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expected;
+ }
+
+ @Override
+ public Object actual() {
+ return mockVideoProvider.getDeviceOrientation();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Orientation should be " + expected
+ );
+ }
+
+ /**
+ * Asserts whether the preview surface has been set or not.
+ *
+ * @param mockVideoProvider The mock video provider.
+ * @param expected {@code true} if it is expected the preview surface is not null, {@code false}
+ * if it is expected the preview surface is null.
+ */
+ private void assertPreviewSurfaceChanged(final MockVideoProvider mockVideoProvider,
+ final boolean expected) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expected;
+ }
+
+ @Override
+ public Object actual() {
+ return mockVideoProvider.getPreviewSurface() != null;
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Preview should be set? " + expected
+ );
+ }
+
+ /**
+ * Asserts whether the display surface has been set or not.
+ *
+ * @param mockVideoProvider The mock video provider.
+ * @param expected {@code true} if it is expected the display surface is not null, {@code false}
+ * if it is expected the display surface is null.
+ */
+ private void assertDisplaySurfaceChanged(final MockVideoProvider mockVideoProvider,
+ final boolean expected) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expected;
+ }
+
+ @Override
+ public Object actual() {
+ return mockVideoProvider.getDisplaySurface() != null;
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Display should be set? " + expected
+ );
+ }
+
+ /**
+ * Asserts whether the zoom has changed to the expected value. Note: To make comparisons easier
+ * the floats are cast to ints, so ensure only whole values are used.
+ *
+ * @param mockVideoProvider The mock video provider.
+ * @param expected The expected zoom.
+ */
+ private void assertZoomChanged(final MockVideoProvider mockVideoProvider,
+ final float expected) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ // Cast to int so we're not doing float equality
+ return (int)expected;
+ }
+
+ @Override
+ public Object actual() {
+ // Cast to int so we're not doing float equality
+ return (int)mockVideoProvider.getZoom();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Zoom should be " + expected
+ );
+ }
+
+ /**
+ * Asserts whether a response video profile has been received
+ *
+ * @param videoCallCallback The video call callback.
+ * @param expected The expected video state.
+ */
+ private void assertResponseVideoProfileReceived(final MockVideoCallCallback videoCallCallback,
+ final int expected) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expected;
+ }
+
+ @Override
+ public Object actual() {
+ VideoProfile videoProfile = videoCallCallback.getResponseProfile();
+ return videoProfile == null ? -1 : videoProfile.getVideoState();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Video state should be " + expected
+ );
+ }
+
+ /**
+ * Asserts whether a session modification request has been received.
+ *
+ * @param videoCallCallback The video call callback.
+ * @param expected The expected video state.
+ * @param work The work to be performed to cause the session modification request to be emit
+ * from the provider.
+ */
+ private void assertRequestVideoProfileReceived(final MockVideoCallCallback videoCallCallback,
+ final int expected, final Work work) {
+ doWorkAndWaitUntilConditionIsTrueOrTimeout(
+ work,
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expected;
+ }
+
+ @Override
+ public Object actual() {
+ VideoProfile videoProfile = videoCallCallback.getRequestProfile();
+ return videoProfile == null ? -1 : videoProfile.getVideoState();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Video state should be " + expected
+ );
+ }
+
+ /**
+ * Asserts whether the provider got a session modify response with the expected value.
+ *
+ * @param mockVideoProvider The mock video provider.
+ * @param expected The expected video state of the session modify response.
+ */
+ private void assertSessionModifyResponse(final MockVideoProvider mockVideoProvider,
+ final int expected) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return expected;
+ }
+
+ @Override
+ public Object actual() {
+ VideoProfile responseProfile = mockVideoProvider.getSessionModifyResponse();
+ return responseProfile == null ? -1 : responseProfile.getVideoState();
+ }
+ },
+ TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Session modify response video state should be " + expected
+ );
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/VideoProfileTest.java b/tests/tests/telecom/src/android/telecom/cts/VideoProfileTest.java
new file mode 100644
index 0000000..6c16abb
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/VideoProfileTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom.cts;
+
+import android.telecom.VideoProfile;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests helper methods in the {@link VideoProfile} class.
+ */
+public class VideoProfileTest extends AndroidTestCase {
+ public void testIsAudioOnly() {
+ assertTrue(VideoProfile.isAudioOnly(VideoProfile.STATE_AUDIO_ONLY));
+ assertTrue(VideoProfile.isAudioOnly(VideoProfile.STATE_PAUSED));
+
+ assertFalse(VideoProfile.isAudioOnly(VideoProfile.STATE_BIDIRECTIONAL));
+ assertFalse(VideoProfile.isAudioOnly(VideoProfile.STATE_TX_ENABLED));
+ assertFalse(VideoProfile.isAudioOnly(VideoProfile.STATE_RX_ENABLED));
+ assertFalse(VideoProfile
+ .isAudioOnly(VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+ assertFalse(VideoProfile
+ .isAudioOnly(VideoProfile.STATE_TX_ENABLED | VideoProfile.STATE_PAUSED));
+ assertFalse(VideoProfile
+ .isAudioOnly(VideoProfile.STATE_RX_ENABLED | VideoProfile.STATE_PAUSED));
+ }
+
+ public void testIsVideo() {
+ assertTrue(VideoProfile.isVideo(VideoProfile.STATE_BIDIRECTIONAL));
+ assertTrue(VideoProfile.isVideo(VideoProfile.STATE_RX_ENABLED));
+ assertTrue(VideoProfile.isVideo(VideoProfile.STATE_TX_ENABLED));
+ assertTrue(VideoProfile.isVideo(VideoProfile.STATE_BIDIRECTIONAL |
+ VideoProfile.STATE_PAUSED));
+ assertTrue(VideoProfile.isVideo(VideoProfile.STATE_RX_ENABLED | VideoProfile.STATE_PAUSED));
+ assertTrue(VideoProfile.isVideo(VideoProfile.STATE_TX_ENABLED | VideoProfile.STATE_PAUSED));
+
+ assertFalse(VideoProfile.isVideo(VideoProfile.STATE_AUDIO_ONLY));
+ assertFalse(VideoProfile.isVideo(VideoProfile.STATE_PAUSED));
+ }
+
+ public void testIsBidirectional() {
+ assertTrue(VideoProfile.isBidirectional(VideoProfile.STATE_BIDIRECTIONAL));
+ assertTrue(VideoProfile.isBidirectional(VideoProfile.STATE_BIDIRECTIONAL |
+ VideoProfile.STATE_PAUSED));
+
+ assertFalse(VideoProfile.isBidirectional(VideoProfile.STATE_TX_ENABLED));
+ assertFalse(VideoProfile.isBidirectional(VideoProfile.STATE_TX_ENABLED |
+ VideoProfile.STATE_PAUSED));
+ assertFalse(VideoProfile.isBidirectional(VideoProfile.STATE_RX_ENABLED));
+ assertFalse(VideoProfile.isBidirectional(VideoProfile.STATE_RX_ENABLED |
+ VideoProfile.STATE_PAUSED));
+ }
+
+ public void testIsPaused() {
+ assertTrue(VideoProfile.isPaused(VideoProfile.STATE_PAUSED));
+ assertTrue(VideoProfile.isPaused(VideoProfile.STATE_BIDIRECTIONAL |
+ VideoProfile.STATE_PAUSED));
+ assertTrue(VideoProfile.isPaused(VideoProfile.STATE_TX_ENABLED |
+ VideoProfile.STATE_PAUSED));
+ assertTrue(VideoProfile.isPaused(VideoProfile.STATE_RX_ENABLED |
+ VideoProfile.STATE_PAUSED));
+
+ assertFalse(VideoProfile.isPaused(VideoProfile.STATE_AUDIO_ONLY));
+ assertFalse(VideoProfile.isPaused(VideoProfile.STATE_TX_ENABLED));
+ assertFalse(VideoProfile.isPaused(VideoProfile.STATE_RX_ENABLED));
+ assertFalse(VideoProfile.isPaused(VideoProfile.STATE_BIDIRECTIONAL));
+ }
+
+ public void testIsReceptionEnabled() {
+ assertTrue(VideoProfile.isReceptionEnabled(VideoProfile.STATE_RX_ENABLED));
+ assertTrue(VideoProfile.isReceptionEnabled(VideoProfile.STATE_BIDIRECTIONAL));
+ assertTrue(VideoProfile.isReceptionEnabled(VideoProfile.STATE_RX_ENABLED |
+ VideoProfile.STATE_PAUSED));
+ assertTrue(VideoProfile.isReceptionEnabled(VideoProfile.STATE_BIDIRECTIONAL |
+ VideoProfile.STATE_PAUSED));
+
+ assertFalse(VideoProfile.isReceptionEnabled(VideoProfile.STATE_AUDIO_ONLY));
+ assertFalse(VideoProfile.isReceptionEnabled(VideoProfile.STATE_TX_ENABLED));
+ assertFalse(VideoProfile.isReceptionEnabled(VideoProfile.STATE_PAUSED));
+ }
+
+ public void testIsTransmissionEnabled() {
+ assertTrue(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_TX_ENABLED));
+ assertTrue(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_BIDIRECTIONAL));
+ assertTrue(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_TX_ENABLED |
+ VideoProfile.STATE_PAUSED));
+ assertTrue(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_BIDIRECTIONAL |
+ VideoProfile.STATE_PAUSED));
+
+ assertFalse(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_AUDIO_ONLY));
+ assertFalse(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_RX_ENABLED));
+ assertFalse(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_PAUSED));
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
index 4e154cf..f2e62dc 100644
--- a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
@@ -26,6 +26,15 @@
* media button.
*/
public class WiredHeadsetTest extends BaseTelecomTestWithMockServices {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (TestUtils.shouldTestTelecom(mContext)) {
+ setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
+ }
+ }
+
@Override
protected void tearDown() throws Exception {
if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
@@ -37,9 +46,8 @@
public void testIncomingCallShortPress_acceptsCall() throws Exception {
addAndVerifyNewIncomingCall(getTestNumber(), null);
- verifyConnectionForIncomingCall();
+ final MockConnection connection = verifyConnectionForIncomingCall();
- final MockConnection connection = mConnectionCallbacks.incomingConnection;
final Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_RINGING);
assertConnectionState(connection, Connection.STATE_RINGING);
@@ -51,9 +59,8 @@
public void testIncomingCallLongPress_rejectsCall() throws Exception {
addAndVerifyNewIncomingCall(getTestNumber(), null);
- verifyConnectionForIncomingCall();
+ final MockConnection connection = verifyConnectionForIncomingCall();
- final MockConnection connection = mConnectionCallbacks.incomingConnection;
final Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_RINGING);
assertConnectionState(connection, Connection.STATE_RINGING);
@@ -65,8 +72,7 @@
public void testInCallShortPress_togglesMute() throws Exception {
placeAndVerifyCall();
- verifyConnectionForOutgoingCall();
- final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+ final MockConnection connection = verifyConnectionForOutgoingCall();
final MockInCallService incallService = mInCallCallbacks.getService();
// Verify that sending short presses in succession toggles the mute state of the
@@ -84,9 +90,8 @@
public void testInCallLongPress_hangupCall() throws Exception {
placeAndVerifyCall();
- verifyConnectionForOutgoingCall();
+ final MockConnection connection = verifyConnectionForOutgoingCall();
- final MockConnection connection = mConnectionCallbacks.outgoingConnection;
final Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_ACTIVE);
assertConnectionState(connection, Connection.STATE_ACTIVE);
diff --git a/tests/tests/telecom2/Android.mk b/tests/tests/telecom2/Android.mk
new file mode 100644
index 0000000..71edb7b
--- /dev/null
+++ b/tests/tests/telecom2/Android.mk
@@ -0,0 +1,46 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsTelecomTestCases2
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+src_dirs := src \
+ ../telecom/src
+
+res_dirs := res \
+ ../telecom/res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_AAPT_FLAGS := \
+ --auto-add-overlay \
+ --extra-packages com.android.cts.telecom \
+ --rename-manifest-package com.android.cts.telecom2 \
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/telecom2/AndroidManifest.xml b/tests/tests/telecom2/AndroidManifest.xml
new file mode 100644
index 0000000..e618768
--- /dev/null
+++ b/tests/tests/telecom2/AndroidManifest.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.telecom2">
+ <uses-sdk android:minSdkVersion="21" />
+
+ <!--
+ This app contains tests to verify Telecom's behavior when the app is missing certain
+ permissions.
+ -->
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ <service android:name="android.telecom.cts.MockConnectionService"
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" >
+ <intent-filter>
+ <action android:name="android.telecom.ConnectionService" />
+ </intent-filter>
+ </service>
+
+ <service android:name="android.telecom.cts.MockInCallService"
+ android:permission="android.permission.BIND_INCALL_SERVICE" >
+ <intent-filter>
+ <action android:name="android.telecom.InCallService"/>
+ </intent-filter>
+ </service>
+
+ <activity android:name="android.telecom.cts.MockDialerActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:mimeType="vnd.android.cursor.item/phone" />
+ <data android:mimeType="vnd.android.cursor.item/person" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="voicemail" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="tel" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.telecom2"
+ android:label="CTS tests for android.telecom package">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
+
diff --git a/tests/tests/telecom2/res/.gitignore b/tests/tests/telecom2/res/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/tests/telecom2/res/.gitignore
diff --git a/tests/tests/telecom2/src/android/telecom/cts/DefaultDialerOperationsNoPermissionsTest.java b/tests/tests/telecom2/src/android/telecom/cts/DefaultDialerOperationsNoPermissionsTest.java
new file mode 100644
index 0000000..4ca6bcd
--- /dev/null
+++ b/tests/tests/telecom2/src/android/telecom/cts/DefaultDialerOperationsNoPermissionsTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom.cts;
+
+import android.content.Context;
+import android.telecom.TelecomManager;
+import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
+
+/**
+ * Verifies that certain privileged operations can only be performed by the default dialer.
+ */
+public class DefaultDialerOperationsNoPermissionsTest extends InstrumentationTestCase {
+ private Context mContext;
+ private TelecomManager mTelecomManager;
+ private String mPreviousDefaultDialer = null;
+ private String mSystemDialer = null;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = getInstrumentation().getContext();
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ TestUtils.PACKAGE = mContext.getPackageName();
+ mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
+ // Reset the current dialer to the system dialer, to ensure that we start each test
+ // without being the default dialer.
+ mSystemDialer = TestUtils.getSystemDialer(getInstrumentation());
+ if (!TextUtils.isEmpty(mSystemDialer)) {
+ TestUtils.setDefaultDialer(getInstrumentation(), mSystemDialer);
+ }
+ mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
+ // Restore the default dialer to whatever the default dialer was before the tests
+ // were started. This may or may not be the system dialer.
+ TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
+ }
+ super.tearDown();
+ }
+
+ public void testShowInCallScreenPermissions() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+ @Override
+ public void run() {
+ mTelecomManager.showInCallScreen(false);
+ }
+ }, "showInCallScreen");
+ }
+
+ public void testGetCallCapableAccountsPermissions() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+ @Override
+ public void run() {
+ mTelecomManager.getCallCapablePhoneAccounts();
+ }
+ }, "getCallCapableAccounts");
+ }
+
+ public void testGetDefaultOutgoingPhoneAccount() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+ @Override
+ public void run() {
+ mTelecomManager.getDefaultOutgoingPhoneAccount("tel");
+ }
+ }, "getDefaultOutgoingPhoneAccount");
+ }
+
+ public void testGetLine1Number() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+ @Override
+ public void run() {
+ mTelecomManager.getLine1Number(null);
+ }
+ }, "getLine1Number");
+ }
+
+ public void testGetVoicemailNumber() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+ @Override
+ public void run() {
+ mTelecomManager.getVoiceMailNumber(null);
+ }
+ }, "getVoiceMailNumber");
+ }
+
+ public void testIsVoicemailNumber() throws Exception {
+ verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+ @Override
+ public void run() {
+ mTelecomManager.isVoiceMailNumber(null, null);
+ }
+ }, "isVoiceMailNumber");
+ }
+
+ public void testIsInCall() throws Exception {
+ if (!TestUtils.shouldTestTelecom(mContext)) {
+ return;
+ }
+ verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+ @Override
+ public void run() {
+ mTelecomManager.isInCall();
+ }
+ }, "isInCall");
+ }
+
+ private void verifyForReadPhoneStateOrDefaultDialer(Runnable runnable, String methodName)
+ throws Exception{
+ try {
+ runnable.run();
+ fail("TelecomManager." + methodName + " should throw SecurityException if no "
+ + "READ_PHONE_STATE permission");
+ } catch (SecurityException e) {
+ }
+
+ TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+ runnable.run();
+ }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
new file mode 100644
index 0000000..67d75c9
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+
+public class CarrierConfigManagerTest extends AndroidTestCase {
+ private CarrierConfigManager mConfigManager;
+ private TelephonyManager mTelephonyManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTelephonyManager = (TelephonyManager)
+ getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ mConfigManager = (CarrierConfigManager)
+ getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ }
+
+ private boolean hasValidPhone() {
+ return mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
+ }
+
+ private boolean isSimCardPresent() {
+ return mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE &&
+ mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_ABSENT;
+ }
+
+ private void checkConfig(PersistableBundle config) {
+ if (!hasValidPhone()) {
+ assertNull(config);
+ return;
+ }
+ if (isSimCardPresent()) {
+ assertNotNull(config);
+ } else {
+ // Static default in CarrierConfigManager will be returned when no sim card present.
+ assertEquals(config.getBoolean(CarrierConfigManager.KEY_ADDITIONAL_CALL_SETTING_BOOL),
+ true);
+ }
+ }
+
+ public void testGetConfig() {
+ PersistableBundle config = mConfigManager.getConfig();
+ checkConfig(config);
+ }
+
+ public void testGetConfigForSubId() {
+ PersistableBundle config =
+ mConfigManager.getConfigForSubId(SubscriptionManager.getDefaultSubId());
+ checkConfig(config);
+ }
+
+ /**
+ * Tests the CarrierConfigManager.notifyConfigChangedForSubId() API. This makes a call to
+ * notifyConfigChangedForSubId() API and expects a SecurityException since the test apk is not signed
+ * by certificate on the SIM.
+ */
+ public void testNotifyConfigChangedForSubId() {
+ try {
+ if (isSimCardPresent()) {
+ mConfigManager.notifyConfigChangedForSubId(SubscriptionManager.getDefaultSubId());
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
index f0f977a..18aa23f 100644
--- a/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -22,6 +22,7 @@
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.net.ConnectivityManager;
import android.test.InstrumentationTestCase;
@@ -40,6 +41,7 @@
private boolean mOnMessageWaitingIndicatorChangedCalled;
private boolean mOnServiceStateChangedCalled;
private boolean mOnSignalStrengthChangedCalled;
+ private SignalStrength mSignalStrength;
private TelephonyManager mTelephonyManager;
private PhoneStateListener mListener;
private final Object mLock = new Object();
@@ -152,6 +154,54 @@
assertTrue(mOnSignalStrengthChangedCalled);
}
+ public void testOnSignalStrengthsChanged() throws Throwable {
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+ return;
+ }
+
+ TestThread t = new TestThread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+
+ mListener = new PhoneStateListener() {
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ synchronized(mLock) {
+ mSignalStrength = signalStrength;
+ mLock.notify();
+ }
+ }
+ };
+ mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+
+ Looper.loop();
+ }
+ });
+
+ assertTrue(mSignalStrength == null);
+ t.start();
+
+ synchronized (mLock) {
+ while(mSignalStrength == null) {
+ mLock.wait();
+ }
+ }
+ t.checkException();
+ assertTrue(mSignalStrength != null);
+
+ // Call SignalStrength methods to make sure they do not throw any exceptions
+ mSignalStrength.getCdmaDbm();
+ mSignalStrength.getCdmaEcio();
+ mSignalStrength.getEvdoDbm();
+ mSignalStrength.getEvdoEcio();
+ mSignalStrength.getEvdoSnr();
+ mSignalStrength.getGsmBitErrorRate();
+ mSignalStrength.getGsmSignalStrength();
+ mSignalStrength.isGsm();
+ mSignalStrength.getLevel();
+ }
+
public void testOnMessageWaitingIndicatorChanged() throws Throwable {
if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 2be1dcb..ce3fe78 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -160,7 +160,28 @@
mTelephonyManager.getNeighboringCellInfo();
mTelephonyManager.isNetworkRoaming();
mTelephonyManager.getDeviceId();
+ mTelephonyManager.getDeviceId(mTelephonyManager.getDefaultSim());
mTelephonyManager.getDeviceSoftwareVersion();
+ mTelephonyManager.getPhoneCount();
+ }
+
+ /**
+ * Tests that the phone count returned is valid.
+ */
+ public void testGetPhoneCount() {
+ int phoneCount = mTelephonyManager.getPhoneCount();
+ int phoneType = mTelephonyManager.getPhoneType();
+ switch (phoneType) {
+ case TelephonyManager.PHONE_TYPE_GSM:
+ case TelephonyManager.PHONE_TYPE_CDMA:
+ assertTrue("Phone count should be > 0", phoneCount > 0);
+ break;
+ case TelephonyManager.PHONE_TYPE_NONE:
+ assertTrue("Phone count should be 0", phoneCount == 0);
+ break;
+ default:
+ throw new IllegalArgumentException("Did you add a new phone type? " + phoneType);
+ }
}
/**
@@ -170,6 +191,24 @@
*/
public void testGetDeviceId() {
String deviceId = mTelephonyManager.getDeviceId();
+ verifyDeviceId(deviceId);
+ }
+
+ /**
+ * Tests that the device properly reports either a valid IMEI if
+ * GSM, a valid MEID or ESN if CDMA, or a valid MAC address if
+ * only a WiFi device.
+ */
+ public void testGetDeviceIdForSlotId() {
+ String deviceId = mTelephonyManager.getDeviceId(mTelephonyManager.getDefaultSim());
+ verifyDeviceId(deviceId);
+ // Also verify that no exception is thrown for any slot id (including invalid ones)
+ for (int i = -1; i <= mTelephonyManager.getPhoneCount(); i++) {
+ mTelephonyManager.getDeviceId(i);
+ }
+ }
+
+ private void verifyDeviceId(String deviceId) {
int phoneType = mTelephonyManager.getPhoneType();
switch (phoneType) {
case TelephonyManager.PHONE_TYPE_GSM:
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
index 4895ca9..36b081c 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
@@ -18,10 +18,12 @@
import android.test.AndroidTestCase;
+import android.text.Editable;
import android.text.InputFilter;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
+import android.text.TextWatcher;
import android.text.style.StrikethroughSpan;
import android.text.style.TabStopSpan;
import android.text.style.UnderlineSpan;
@@ -596,4 +598,46 @@
// expected exception
}
}
+
+ private static class MockTextWatcher implements TextWatcher {
+ private int mDepth = 0;
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ SpannableStringBuilder builder = (SpannableStringBuilder)s;
+ mDepth++;
+ assertEquals(mDepth, builder.getTextWatcherDepth());
+ mDepth--;
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ SpannableStringBuilder builder = (SpannableStringBuilder)s;
+ mDepth++;
+ assertEquals(mDepth, builder.getTextWatcherDepth());
+ mDepth--;
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ SpannableStringBuilder builder = (SpannableStringBuilder)s;
+ mDepth++;
+ assertEquals(mDepth, builder.getTextWatcherDepth());
+ if (mDepth <= builder.length()) {
+ // This will recursively call afterTextChanged.
+ builder.replace(mDepth - 1, mDepth, "a");
+ }
+ mDepth--;
+ }
+ }
+
+ public void testGetTextWatcherDepth() {
+ SpannableStringBuilder builder = new SpannableStringBuilder("hello");
+ builder.setSpan(new MockTextWatcher(), 0, builder.length(), 0);
+ assertEquals(0, builder.getTextWatcherDepth());
+ builder.replace(0, 1, "H");
+ assertEquals(0, builder.getTextWatcherDepth());
+ // MockTextWatcher replaces each character with 'a'.
+ assertEquals("aaaaa", builder.toString());
+ }
}
diff --git a/tests/tests/transition/Android.mk b/tests/tests/transition/Android.mk
new file mode 100644
index 0000000..3b48e25
--- /dev/null
+++ b/tests/tests/transition/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsTransitionTestCases
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/transition/AndroidManifest.xml b/tests/tests/transition/AndroidManifest.xml
new file mode 100644
index 0000000..0ce1791
--- /dev/null
+++ b/tests/tests/transition/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.transition">
+ <uses-sdk android:minSdkVersion="11" />
+ <uses-permission android:name="android.permission.INJECT_EVENTS" />
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <application>
+ <activity android:name="android.transition.cts.TransitionActivity"
+ android:label="TransitionActivity"/>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.transition"
+ android:label="CTS tests for android.transition package">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
+
diff --git a/tests/tests/transition/res/layout/scene1.xml b/tests/tests/transition/res/layout/scene1.xml
new file mode 100644
index 0000000..4ed5b03
--- /dev/null
+++ b/tests/tests/transition/res/layout/scene1.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#F00"
+ android:id="@+id/redSquare" />
+ <View android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#0F0"
+ android:id="@+id/greenSquare"
+ android:layout_below="@+id/redSquare" />
+</RelativeLayout>
diff --git a/tests/tests/transition/res/layout/scene2.xml b/tests/tests/transition/res/layout/scene2.xml
new file mode 100644
index 0000000..2033c95
--- /dev/null
+++ b/tests/tests/transition/res/layout/scene2.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#0F0"
+ android:id="@+id/greenSquare" />
+ <View android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#F00"
+ android:id="@+id/redSquare"
+ android:layout_below="@+id/greenSquare" />
+</RelativeLayout>
diff --git a/tests/tests/transition/res/layout/scene3.xml b/tests/tests/transition/res/layout/scene3.xml
new file mode 100644
index 0000000..b0625aa
--- /dev/null
+++ b/tests/tests/transition/res/layout/scene3.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#0F0"
+ android:id="@+id/greenSquare" />
+ <View android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#F00"
+ android:id="@+id/redSquare"
+ android:layout_toRightOf="@+id/greenSquare" />
+</RelativeLayout>
diff --git a/tests/tests/transition/res/layout/transition_main.xml b/tests/tests/transition/res/layout/transition_main.xml
new file mode 100644
index 0000000..64de7dd
--- /dev/null
+++ b/tests/tests/transition/res/layout/transition_main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container"/>
diff --git a/tests/tests/transition/res/values/strings.xml b/tests/tests/transition/res/values/strings.xml
new file mode 100644
index 0000000..579da74
--- /dev/null
+++ b/tests/tests/transition/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="add_button">Add Button</string>
+</resources>
diff --git a/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java b/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
new file mode 100644
index 0000000..4bbfc2c
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/BaseTransitionTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.transition.cts;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.test.ActivityInstrumentationTestCase2;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+import com.android.cts.transition.R;
+
+public class BaseTransitionTest extends ActivityInstrumentationTestCase2<TransitionActivity> {
+ protected TransitionActivity mActivity;
+ protected FrameLayout mSceneRoot;
+ public float mAnimatedValue;
+ protected ArrayList<View> mTargets = new ArrayList<>();
+ protected TestTransition mTransition;
+
+ public BaseTransitionTest() {
+ super(TransitionActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setActivityInitialTouchMode(false);
+ mActivity = getActivity();
+ mSceneRoot = (FrameLayout) mActivity.findViewById(R.id.container);
+ mTargets.clear();
+ mTransition = new TestTransition();
+ }
+
+ protected void waitForStart() throws InterruptedException {
+ waitForStart(mTransition.listener);
+ }
+
+ protected static void waitForStart(SimpleTransitionListener listener) throws InterruptedException {
+ long endTime = System.currentTimeMillis() + 50;
+ synchronized (listener) {
+ while (!listener.started) {
+ long now = System.currentTimeMillis();
+ long waitTime = endTime - now;
+ if (waitTime <= 0) {
+ throw new InterruptedException();
+ }
+ listener.wait(waitTime);
+ }
+ }
+ }
+
+ protected void waitForEnd(long waitMillis) throws InterruptedException {
+ waitForEnd(mTransition.listener, waitMillis);
+ }
+
+ protected static void waitForEnd(SimpleTransitionListener listener, long waitMillis)
+ throws InterruptedException {
+ long endTime = System.currentTimeMillis() + waitMillis;
+ synchronized (listener) {
+ while (!listener.ended) {
+ long now = System.currentTimeMillis();
+ long waitTime = endTime - now;
+ if (waitTime <= 0) {
+ throw new InterruptedException();
+ }
+ listener.wait(waitTime);
+ }
+ }
+ }
+
+ protected void startTransition(final int layoutId) throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Scene scene = Scene.getSceneForLayout(mSceneRoot, layoutId, mActivity);
+ TransitionManager.go(scene, mTransition);
+ }
+ });
+ waitForStart();
+ }
+
+ protected void endTransition() throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.endTransitions(mSceneRoot);
+ }
+ });
+ }
+
+ public class TestTransition extends Visibility {
+ public final SimpleTransitionListener listener = new SimpleTransitionListener();
+
+ public TestTransition() {
+ addListener(listener);
+ setDuration(100);
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ mTargets.add(view);
+ return ObjectAnimator.ofFloat(BaseTransitionTest.this, "mAnimatedValue", 0, 1);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ mTargets.add(view);
+ return ObjectAnimator.ofFloat(BaseTransitionTest.this, "mAnimatedValue", 1, 0);
+ }
+ }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/SimpleTransitionListener.java b/tests/tests/transition/src/android/transition/cts/SimpleTransitionListener.java
new file mode 100644
index 0000000..4d18bbfb
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/SimpleTransitionListener.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.transition.cts;
+
+import android.transition.Transition;
+import android.transition.Transition.TransitionListener;
+
+/**
+ * Listener captures whether each of the methods is called.
+ */
+class SimpleTransitionListener implements TransitionListener {
+
+ public boolean started;
+
+ public boolean ended;
+
+ public boolean canceled;
+
+ public boolean paused;
+
+ public boolean resumed;
+
+ @Override
+ public synchronized void onTransitionStart(Transition transition) {
+ started = true;
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void onTransitionEnd(Transition transition) {
+ ended = true;
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void onTransitionCancel(Transition transition) {
+ canceled = true;
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void onTransitionPause(Transition transition) {
+ paused = true;
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void onTransitionResume(Transition transition) {
+ resumed = true;
+ notifyAll();
+ }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/TransitionActivity.java b/tests/tests/transition/src/android/transition/cts/TransitionActivity.java
new file mode 100644
index 0000000..8236bd5
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/TransitionActivity.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.transition.cts;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
+import android.graphics.Shader;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.OvalShape;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import java.util.ArrayList;
+
+import com.android.cts.transition.R;
+
+public class TransitionActivity extends Activity {
+ @Override
+ public void onCreate(Bundle bundle){
+ super.onCreate(bundle);
+ setContentView(R.layout.transition_main);
+ }
+}
+
diff --git a/tests/tests/transition/src/android/transition/cts/TransitionManagerTest.java b/tests/tests/transition/src/android/transition/cts/TransitionManagerTest.java
new file mode 100644
index 0000000..5d16b19
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/TransitionManagerTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.transition.cts;
+
+import com.android.cts.transition.R;
+
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class TransitionManagerTest extends BaseTransitionTest {
+
+ public TransitionManagerTest() {
+ }
+
+ public void testBeginDelayedTransition() throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+ View view = mActivity.getLayoutInflater().inflate(R.layout.scene1, mSceneRoot,
+ false);
+ mSceneRoot.addView(view);
+ }
+ });
+
+ waitForStart();
+ waitForEnd(150);
+ assertFalse(mTransition.listener.resumed);
+ assertFalse(mTransition.listener.paused);
+ assertFalse(mTransition.listener.canceled);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertNotNull(mActivity.findViewById(R.id.redSquare));
+ assertNotNull(mActivity.findViewById(R.id.greenSquare));
+ }
+ });
+ }
+
+ public void testGo() throws Throwable {
+ startTransition(R.layout.scene1);
+ waitForStart();
+ waitForEnd(150);
+
+ assertFalse(mTransition.listener.resumed);
+ assertFalse(mTransition.listener.paused);
+ assertFalse(mTransition.listener.canceled);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertNotNull(mActivity.findViewById(R.id.redSquare));
+ assertNotNull(mActivity.findViewById(R.id.greenSquare));
+ }
+ });
+ }
+
+ public void testSetTransition1() throws Throwable {
+ final TransitionManager transitionManager = new TransitionManager();
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Scene scene = Scene.getSceneForLayout(mSceneRoot, R.layout.scene1, mActivity);
+ transitionManager.setTransition(scene, mTransition);
+ transitionManager.transitionTo(scene);
+ }
+ });
+
+ waitForStart();
+ waitForEnd(150);
+ assertFalse(mTransition.listener.resumed);
+ assertFalse(mTransition.listener.paused);
+ assertFalse(mTransition.listener.canceled);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertNotNull(mActivity.findViewById(R.id.redSquare));
+ assertNotNull(mActivity.findViewById(R.id.greenSquare));
+ mTransition.listener.started = false;
+ mTransition.listener.ended = false;
+ Scene scene = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, mActivity);
+ transitionManager.transitionTo(scene);
+ }
+ });
+ Thread.sleep(50);
+ assertFalse(mTransition.listener.started);
+ endTransition();
+ }
+
+ public void testSetTransition2() throws Throwable {
+ final TransitionManager transitionManager = new TransitionManager();
+ final Scene[] scenes = new Scene[3];
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ scenes[0] = Scene.getSceneForLayout(mSceneRoot, R.layout.scene1, mActivity);
+ scenes[1] = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, mActivity);
+ scenes[2] = Scene.getSceneForLayout(mSceneRoot, R.layout.scene3, mActivity);
+ transitionManager.setTransition(scenes[0], scenes[1], mTransition);
+ transitionManager.transitionTo(scenes[0]);
+ }
+ });
+ Thread.sleep(50);
+ assertFalse(mTransition.listener.started);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ transitionManager.transitionTo(scenes[1]);
+ }
+ });
+
+ waitForStart();
+ waitForEnd(150);
+ assertFalse(mTransition.listener.resumed);
+ assertFalse(mTransition.listener.paused);
+ assertFalse(mTransition.listener.canceled);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mTransition.listener.started = false;
+ mTransition.listener.ended = false;
+ transitionManager.transitionTo(scenes[2]);
+ }
+ });
+ Thread.sleep(50);
+ assertFalse(mTransition.listener.started);
+ endTransition();
+ }
+
+ public void testEndTransitions() throws Throwable {
+ mTransition.setDuration(400);
+
+ startTransition(R.layout.scene1);
+ waitForStart();
+ endTransition();
+ waitForEnd(50);
+ }
+
+ public void testEndTransitionsBeforeStarted() throws Throwable {
+ mTransition.setDuration(400);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Scene scene = Scene.getSceneForLayout(mSceneRoot, R.layout.scene1, mActivity);
+ TransitionManager.go(scene, mTransition);
+ TransitionManager.endTransitions(mSceneRoot);
+ }
+ });
+ Thread.sleep(50);
+ assertFalse(mTransition.listener.started);
+ assertFalse(mTransition.listener.ended);
+ }
+}
+
diff --git a/tests/tests/transition/src/android/transition/cts/TransitionTest.java b/tests/tests/transition/src/android/transition/cts/TransitionTest.java
new file mode 100644
index 0000000..6ecf8a4
--- /dev/null
+++ b/tests/tests/transition/src/android/transition/cts/TransitionTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.transition.cts;
+
+import com.android.cts.transition.R;
+
+import android.transition.AutoTransition;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+
+public class TransitionTest extends BaseTransitionTest {
+
+ public TransitionTest() {
+ }
+
+ public void testAddListener() throws Throwable {
+ startTransition(R.layout.scene1);
+
+ waitForStart();
+
+ final SimpleTransitionListener listener2 = new SimpleTransitionListener();
+ mTransition.addListener(listener2);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ AutoTransition autoTransition = new AutoTransition();
+ autoTransition.setDuration(100);
+ autoTransition.addListener(listener2);
+ Scene scene = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, mActivity);
+ TransitionManager.go(scene, autoTransition);
+ }
+ });
+
+ waitForStart(listener2);
+
+ assertTrue(mTransition.listener.paused);
+ assertTrue(mTransition.listener.resumed);
+ assertFalse(mTransition.listener.canceled);
+ assertTrue(mTransition.listener.ended);
+ assertTrue(mTransition.listener.started);
+
+ assertTrue(listener2.paused);
+ assertTrue(listener2.resumed);
+ assertFalse(listener2.canceled);
+ assertTrue(listener2.ended);
+ assertTrue(listener2.started);
+ endTransition();
+ }
+
+ public void testRemoveListener() throws Throwable {
+ startTransition(R.layout.scene1);
+ waitForStart();
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mTransition.removeListener(mTransition.listener);
+ }
+ });
+
+ Thread.sleep(150);
+ assertFalse(mTransition.listener.ended);
+ }
+}
+
diff --git a/tests/tests/uirendering/res/drawable-nodpi/black1.png b/tests/tests/uirendering/res/drawable-nodpi/black1.png
index 3487ced..bbfc2c1 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/black1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/black1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/blackitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/blackitalic1.png
index 8fd3b50..6a8a830 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/blackitalic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/blackitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/bold1.png b/tests/tests/uirendering/res/drawable-nodpi/bold1.png
index 199cccc..cd0465b 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/bold1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/bold1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/bolditalic1.png b/tests/tests/uirendering/res/drawable-nodpi/bolditalic1.png
index 985635e..0819c8a 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/bolditalic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/bolditalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensed1.png b/tests/tests/uirendering/res/drawable-nodpi/condensed1.png
index 6889a3a..16f03e2 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/condensed1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensed1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensedbold1.png b/tests/tests/uirendering/res/drawable-nodpi/condensedbold1.png
index 9554dee..98174f0 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/condensedbold1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensedbold1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensedbolditalic1.png b/tests/tests/uirendering/res/drawable-nodpi/condensedbolditalic1.png
index 0483355..8b017ba 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/condensedbolditalic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensedbolditalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condenseditalic1.png b/tests/tests/uirendering/res/drawable-nodpi/condenseditalic1.png
index 6584147..5fc1f9c 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/condenseditalic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/condenseditalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensedlight1.png b/tests/tests/uirendering/res/drawable-nodpi/condensedlight1.png
index 49d01ac..1aca9cf 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/condensedlight1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensedlight1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensedlightitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/condensedlightitalic1.png
index 6fe4a76..47ecd67 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/condensedlightitalic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensedlightitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/hello1.png b/tests/tests/uirendering/res/drawable-nodpi/hello1.png
index 7a4be5a..6c9cb58 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/hello1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/hello1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/italic1.png b/tests/tests/uirendering/res/drawable-nodpi/italic1.png
index a5f9ef2..0afb3ae 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/italic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/italic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/light1.png b/tests/tests/uirendering/res/drawable-nodpi/light1.png
index dfa59da..6822edb 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/light1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/light1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/lightitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/lightitalic1.png
index 283ddc4..b49d2d6 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/lightitalic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/lightitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/medium1.png b/tests/tests/uirendering/res/drawable-nodpi/medium1.png
index e615186..5b187bc 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/medium1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/medium1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/mediumitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/mediumitalic1.png
index 3e15fc8..057edc0 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/mediumitalic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/mediumitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/thin1.png b/tests/tests/uirendering/res/drawable-nodpi/thin1.png
index 9637262..a780f16 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/thin1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/thin1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/thinitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/thinitalic1.png
index 0afbb9a..6ddb52b 100644
--- a/tests/tests/uirendering/res/drawable-nodpi/thinitalic1.png
+++ b/tests/tests/uirendering/res/drawable-nodpi/thinitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/layout/circle_clipped_webview.xml b/tests/tests/uirendering/res/layout/circle_clipped_webview.xml
new file mode 100644
index 0000000..44b65be
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/circle_clipped_webview.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<android.uirendering.cts.testclasses.view.CircleClipFrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height">
+ <WebView
+ android:id="@+id/webview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</android.uirendering.cts.testclasses.view.CircleClipFrameLayout>
diff --git a/tests/tests/uirendering/res/layout/simple_red_layout.xml b/tests/tests/uirendering/res/layout/simple_red_layout.xml
index 2af8db6..1c551d3 100644
--- a/tests/tests/uirendering/res/layout/simple_red_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_red_layout.xml
@@ -16,5 +16,4 @@
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/test_width"
android:layout_height="@dimen/test_height"
- android:id="@+id/test_root"
- android:background="#f00" />
+ android:background="#f00"/>
diff --git a/tests/tests/uirendering/res/layout/test_container.xml b/tests/tests/uirendering/res/layout/test_container.xml
index 94a8eab..deff9de 100644
--- a/tests/tests/uirendering/res/layout/test_container.xml
+++ b/tests/tests/uirendering/res/layout/test_container.xml
@@ -13,12 +13,16 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/test_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ViewStub
- android:id="@+id/test_content_stub"
+ <FrameLayout
android:layout_gravity="center"
+ android:id="@+id/test_content_wrapper"
android:layout_width="@dimen/test_width"
- android:layout_height="@dimen/test_height"/>
+ android:layout_height="@dimen/test_height">
+ <ViewStub
+ android:id="@+id/test_content_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ </FrameLayout>
</FrameLayout>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
index 7f62b3e..aa91c2e 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
@@ -15,27 +15,31 @@
*/
package android.uirendering.cts.bitmapverifiers;
+import android.annotation.ColorInt;
+
/**
* Checks to see if a bitmap is entirely a single color
*/
public class ColorVerifier extends PerPixelBitmapVerifier {
+ @ColorInt
private int mColor;
- public ColorVerifier(int color) {
+ public ColorVerifier(@ColorInt int color) {
this(color, DEFAULT_THRESHOLD);
}
- public ColorVerifier(int color, int colorTolerance) {
+ public ColorVerifier(@ColorInt int color, int colorTolerance) {
super(colorTolerance);
mColor = color;
}
- public ColorVerifier(int color, int colorThreshold, float spatialTolerance) {
+ public ColorVerifier(@ColorInt int color, int colorThreshold, float spatialTolerance) {
super(colorThreshold, spatialTolerance);
mColor = color;
}
@Override
+ @ColorInt
protected int getExpectedColor(int x, int y) {
return mColor;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
index d4a63de..42e8960 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
@@ -25,8 +25,8 @@
private int[] mGoldenBitmapArray;
public GoldenImageVerifier(Bitmap goldenBitmap, BitmapComparer bitmapComparer) {
- mGoldenBitmapArray = new int[goldenBitmap.getWidth() * goldenBitmap.getHeight()];
- goldenBitmap.getPixels(mGoldenBitmapArray, 0, goldenBitmap.getWidth(), 0, 0,
+ mGoldenBitmapArray = new int[ActivityTestBase.TEST_WIDTH * ActivityTestBase.TEST_HEIGHT];
+ goldenBitmap.getPixels(mGoldenBitmapArray, 0, ActivityTestBase.TEST_WIDTH, 0, 0,
ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
mBitmapComparer = bitmapComparer;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
index 2d00db5..8fe75ee 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
@@ -15,6 +15,7 @@
*/
package android.uirendering.cts.bitmapverifiers;
+import android.annotation.ColorInt;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
@@ -47,6 +48,7 @@
mSpatialTolerance = spatialTolerance;
}
+ @ColorInt
protected int getExpectedColor(int x, int y) {
return Color.WHITE;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CanvasStateTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CanvasStateTests.java
new file mode 100644
index 0000000..becc2f7
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CanvasStateTests.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses;
+
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+
+/**
+ * Tests of state query-able from canvas at draw time.
+ *
+ * Although these tests don't verify drawing content, they still make use of ActivityTestBase's
+ * capability to test the hardware accelerated Canvas in the way that it is used by Views.
+ */
+public class CanvasStateTests extends ActivityTestBase {
+
+ @SmallTest
+ public void testClipRectReturnValues() {
+ createTest()
+ .addCanvasClient(new CanvasClient() {
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ canvas.save();
+ boolean isNonEmpty = canvas.clipRect(0, 0, 20, 20);
+ assertTrue("clip state should be non empty", isNonEmpty);
+
+ isNonEmpty = canvas.clipRect(0, 40, 20, 60);
+ assertFalse("clip state should be empty", isNonEmpty);
+ canvas.restore();
+ }
+ })
+ .runWithoutVerification();
+ }
+
+ @SmallTest
+ public void testClipRegionReturnValues() {
+ createTest()
+ .addCanvasClient(new CanvasClient() {
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ canvas.save();
+ RectF clipRectF = new RectF(0, 0, 20, 20);
+
+ assertFalse(canvas.quickReject(0, 0, 20, 20, Canvas.EdgeType.BW));
+ if (!canvas.isHardwareAccelerated()) {
+ // SW canvas may not be in View space, so we offset the clipping region
+ // so it will operate within the canvas client's window.
+ // (Currently, this isn't necessary, since SW layer size == draw area)
+ canvas.getMatrix().mapRect(clipRectF);
+ }
+
+ Region rectRegion = new Region();
+ rectRegion.set((int) clipRectF.left, (int) clipRectF.top,
+ (int) clipRectF.right, (int) clipRectF.bottom);
+
+ boolean isNonEmpty = canvas.clipRegion(rectRegion);
+ assertTrue("clip state should be non empty", isNonEmpty);
+
+ // Note: we don't test that non-intersecting clip regions empty the clip,
+ // For region clipping, the impl is allowed to return true conservatively
+ // in many cases.
+ canvas.restore();
+ }
+ })
+ .runWithoutVerification();
+ }
+
+ @SmallTest
+ public void testClipPathReturnValues() {
+ createTest()
+ .addCanvasClient(new CanvasClient() {
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ canvas.save();
+ Path rectPath = new Path();
+ rectPath.addRect(0, 0, 20, 20, Path.Direction.CW);
+
+ boolean isNonEmpty = canvas.clipPath(rectPath);
+ assertTrue("clip state should be non empty", isNonEmpty);
+
+ rectPath.offset(0, 40);
+ isNonEmpty = canvas.clipPath(rectPath);
+ assertFalse("clip state should be empty", isNonEmpty);
+ canvas.restore();
+ }
+ })
+ .runWithoutVerification();
+ }
+ @SmallTest
+ public void testQuickReject() {
+ createTest()
+ .addCanvasClient(new CanvasClient() {
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ canvas.save();
+ canvas.clipRect(0, 0, 20, 20);
+
+ // not rejected!
+ assertFalse(canvas.quickReject(0, 0, 20, 20, Canvas.EdgeType.BW));
+
+ // rejected!
+ assertTrue(canvas.quickReject(0, 40, 20, 60, Canvas.EdgeType.BW));
+ canvas.restore();
+ }
+ })
+ .runWithoutVerification();
+ }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/FontRenderingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/FontRenderingTests.java
index e7ed7ac..5833f20 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/FontRenderingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/FontRenderingTests.java
@@ -32,17 +32,24 @@
import com.android.cts.uirendering.R;
public class FontRenderingTests extends ActivityTestBase {
- // Threshold is barely loose enough for differences between sw and hw renderers
- static double MSSIM_THRESHOLD = 0.91;
-
- private final BitmapComparer mFuzzyComparer = new MSSIMComparer(MSSIM_THRESHOLD);
+ // Thresholds are barely loose enough for differences between sw and hw renderers.
+ private static final double REGULAR_THRESHOLD = 0.92;
+ private static final double THIN_THRESHOLD = 0.87;
// Representative characters including some from Unicode 7
- private final String mTestString1 = "Hamburg \u20bd";
- private final String mTestString2 = "\u20b9\u0186\u0254\u1e24\u1e43";
+ private static final String sTestString1 = "Hambu";
+ private static final String sTestString2 = "rg \u20bd";
+ private static final String sTestString3 = "\u20b9\u0186\u0254\u1e24\u1e43";
- private void fontTestBody(final Typeface typeface, int id) {
+ private void fontTestBody(String family, int style, int id) {
Bitmap goldenBitmap = BitmapFactory.decodeResource(getActivity().getResources(), id);
+
+ // adjust threshold based on thinness - more variance is expected in thin cases
+ boolean thinTestCase = family.endsWith("-thin") && ((style & Typeface.BOLD) == 0);
+ BitmapComparer comparer = new MSSIMComparer(
+ thinTestCase ? THIN_THRESHOLD : REGULAR_THRESHOLD);
+
+ final Typeface typeface = Typeface.create(family, style);
createTest()
.addCanvasClient(new CanvasClient() {
@Override
@@ -50,174 +57,201 @@
Paint p = new Paint();
p.setAntiAlias(true);
p.setColor(Color.BLACK);
- p.setTextSize(30);
+ p.setTextSize(26);
p.setTypeface(typeface);
- canvas.drawText(mTestString1, 10, 60, p);
- canvas.drawText(mTestString2, 10, 100, p);
+ canvas.drawText(sTestString1, 1, 20, p);
+ canvas.drawText(sTestString2, 1, 50, p);
+ canvas.drawText(sTestString3, 1, 80, p);
}
})
- .runWithVerifier(new GoldenImageVerifier(goldenBitmap, mFuzzyComparer));
+ .runWithVerifier(new GoldenImageVerifier(goldenBitmap, comparer));
}
@SmallTest
public void testDefaultFont() {
- Typeface tf = Typeface.create("sans-serif", Typeface.NORMAL);
- fontTestBody(tf, R.drawable.hello1);
+ fontTestBody("sans-serif",
+ Typeface.NORMAL,
+ R.drawable.hello1);
}
@SmallTest
public void testBoldFont() {
- Typeface tf = Typeface.create("sans-serif", Typeface.BOLD);
- fontTestBody(tf, R.drawable.bold1);
+ fontTestBody("sans-serif",
+ Typeface.BOLD,
+ R.drawable.bold1);
}
@SmallTest
public void testItalicFont() {
- Typeface tf = Typeface.create("sans-serif", Typeface.ITALIC);
- fontTestBody(tf, R.drawable.italic1);
+ fontTestBody("sans-serif",
+ Typeface.ITALIC,
+ R.drawable.italic1);
}
@SmallTest
public void testBoldItalicFont() {
- Typeface tf = Typeface.create("sans-serif", Typeface.BOLD | Typeface.ITALIC);
- fontTestBody(tf, R.drawable.bolditalic1);
+ fontTestBody("sans-serif",
+ Typeface.BOLD | Typeface.ITALIC,
+ R.drawable.bolditalic1);
}
@SmallTest
public void testMediumFont() {
- Typeface tf = Typeface.create("sans-serif-medium", Typeface.NORMAL);
- fontTestBody(tf, R.drawable.medium1);
+ fontTestBody("sans-serif-medium",
+ Typeface.NORMAL,
+ R.drawable.medium1);
}
@SmallTest
public void testMediumBoldFont() {
// bold attribute on medium base font = black
- Typeface tf = Typeface.create("sans-serif-medium", Typeface.BOLD);
- fontTestBody(tf, R.drawable.black1);
+ fontTestBody("sans-serif-medium",
+ Typeface.BOLD,
+ R.drawable.black1);
}
@SmallTest
public void testMediumItalicFont() {
- Typeface tf = Typeface.create("sans-serif-medium", Typeface.ITALIC);
- fontTestBody(tf, R.drawable.mediumitalic1);
+ fontTestBody("sans-serif-medium",
+ Typeface.ITALIC,
+ R.drawable.mediumitalic1);
}
@SmallTest
public void testMediumBoldItalicFont() {
- Typeface tf = Typeface.create("sans-serif-medium", Typeface.BOLD | Typeface.ITALIC);
- fontTestBody(tf, R.drawable.blackitalic1);
+ fontTestBody("sans-serif-medium",
+ Typeface.BOLD | Typeface.ITALIC,
+ R.drawable.blackitalic1);
}
@SmallTest
public void testLightFont() {
- Typeface tf = Typeface.create("sans-serif-light", Typeface.NORMAL);
- fontTestBody(tf, R.drawable.light1);
+ fontTestBody("sans-serif-light",
+ Typeface.NORMAL,
+ R.drawable.light1);
}
@SmallTest
public void testLightBoldFont() {
// bold attribute on light base font = medium
- Typeface tf = Typeface.create("sans-serif-light", Typeface.BOLD);
- fontTestBody(tf, R.drawable.medium1);
+ fontTestBody("sans-serif-light",
+ Typeface.BOLD,
+ R.drawable.medium1);
}
@SmallTest
public void testLightItalicFont() {
- Typeface tf = Typeface.create("sans-serif-light", Typeface.ITALIC);
- fontTestBody(tf, R.drawable.lightitalic1);
+ fontTestBody("sans-serif-light",
+ Typeface.ITALIC,
+ R.drawable.lightitalic1);
}
@SmallTest
public void testLightBoldItalicFont() {
- Typeface tf = Typeface.create("sans-serif-light", Typeface.BOLD | Typeface.ITALIC);
- fontTestBody(tf, R.drawable.mediumitalic1);
+ fontTestBody("sans-serif-light",
+ Typeface.BOLD | Typeface.ITALIC,
+ R.drawable.mediumitalic1);
}
@SmallTest
public void testThinFont() {
- Typeface tf = Typeface.create("sans-serif-thin", Typeface.NORMAL);
- fontTestBody(tf, R.drawable.thin1);
+ fontTestBody("sans-serif-thin",
+ Typeface.NORMAL,
+ R.drawable.thin1);
}
@SmallTest
public void testThinBoldFont() {
// bold attribute on thin base font = normal
- Typeface tf = Typeface.create("sans-serif-thin", Typeface.BOLD);
- fontTestBody(tf, R.drawable.hello1);
+ fontTestBody("sans-serif-thin",
+ Typeface.BOLD,
+ R.drawable.hello1);
}
@SmallTest
public void testThinItalicFont() {
- Typeface tf = Typeface.create("sans-serif-thin", Typeface.ITALIC);
- fontTestBody(tf, R.drawable.thinitalic1);
+ fontTestBody("sans-serif-thin",
+ Typeface.ITALIC,
+ R.drawable.thinitalic1);
}
@SmallTest
public void testThinBoldItalicFont() {
- Typeface tf = Typeface.create("sans-serif-thin", Typeface.BOLD | Typeface.ITALIC);
- fontTestBody(tf, R.drawable.italic1);
+ fontTestBody("sans-serif-thin",
+ Typeface.BOLD | Typeface.ITALIC,
+ R.drawable.italic1);
}
@SmallTest
public void testBlackFont() {
- Typeface tf = Typeface.create("sans-serif-black", Typeface.NORMAL);
- fontTestBody(tf, R.drawable.black1);
+ fontTestBody("sans-serif-black",
+ Typeface.NORMAL,
+ R.drawable.black1);
}
@SmallTest
public void testBlackBoldFont() {
// bold attribute on black base font = black
- Typeface tf = Typeface.create("sans-serif-black", Typeface.BOLD);
- fontTestBody(tf, R.drawable.black1);
+ fontTestBody("sans-serif-black",
+ Typeface.BOLD,
+ R.drawable.black1);
}
@SmallTest
public void testBlackItalicFont() {
- Typeface tf = Typeface.create("sans-serif-black", Typeface.ITALIC);
- fontTestBody(tf, R.drawable.blackitalic1);
+ fontTestBody("sans-serif-black",
+ Typeface.ITALIC,
+ R.drawable.blackitalic1);
}
@SmallTest
public void testBlackBoldItalicFont() {
- Typeface tf = Typeface.create("sans-serif-black", Typeface.BOLD | Typeface.ITALIC);
- fontTestBody(tf, R.drawable.blackitalic1);
+ fontTestBody("sans-serif-black",
+ Typeface.BOLD | Typeface.ITALIC,
+ R.drawable.blackitalic1);
}
/* condensed fonts */
@SmallTest
public void testCondensedFont() {
- Typeface tf = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
- fontTestBody(tf, R.drawable.condensed1);
+ fontTestBody("sans-serif-condensed",
+ Typeface.NORMAL,
+ R.drawable.condensed1);
}
@SmallTest
public void testCondensedBoldFont() {
- Typeface tf = Typeface.create("sans-serif-condensed", Typeface.BOLD);
- fontTestBody(tf, R.drawable.condensedbold1);
+ fontTestBody("sans-serif-condensed",
+ Typeface.BOLD,
+ R.drawable.condensedbold1);
}
@SmallTest
public void testCondensedItalicFont() {
- Typeface tf = Typeface.create("sans-serif-condensed", Typeface.ITALIC);
- fontTestBody(tf, R.drawable.condenseditalic1);
+ fontTestBody("sans-serif-condensed",
+ Typeface.ITALIC,
+ R.drawable.condenseditalic1);
}
@SmallTest
public void testCondensedBoldItalicFont() {
- Typeface tf = Typeface.create("sans-serif-condensed", Typeface.BOLD | Typeface.ITALIC);
- fontTestBody(tf, R.drawable.condensedbolditalic1);
+ fontTestBody("sans-serif-condensed",
+ Typeface.BOLD | Typeface.ITALIC,
+ R.drawable.condensedbolditalic1);
}
@SmallTest
public void testCondensedLightFont() {
- Typeface tf = Typeface.create("sans-serif-condensed-light", Typeface.NORMAL);
- fontTestBody(tf, R.drawable.condensedlight1);
+ fontTestBody("sans-serif-condensed-light",
+ Typeface.NORMAL,
+ R.drawable.condensedlight1);
}
@SmallTest
public void testCondensedLightItalicFont() {
- Typeface tf = Typeface.create("sans-serif-condensed-light", Typeface.ITALIC);
- fontTestBody(tf, R.drawable.condensedlightitalic1);
+ fontTestBody("sans-serif-condensed-light",
+ Typeface.ITALIC,
+ R.drawable.condensedlightitalic1);
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
index c86ff76..dfaaaea 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
@@ -75,7 +75,7 @@
final Rect clipRect = new Rect(0, 0, 50, 50);
ViewInitializer viewInitializer = new ViewInitializer() {
@Override
- public void intializeView(View view) {
+ public void initializeView(View view) {
view.setClipBounds(clipRect);
}
};
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
new file mode 100644
index 0000000..a5f76dd
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses;
+
+import android.annotation.ColorInt;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import com.android.cts.uirendering.R;
+
+public class LayerTests extends ActivityTestBase {
+ @SmallTest
+ public void testLayerPaintAlpha() {
+ // red channel full strength, other channels 75% strength
+ // (since 25% alpha red subtracts from them)
+ @ColorInt
+ final int expectedColor = Color.rgb(255, 191, 191);
+ createTest()
+ .addLayout(R.layout.simple_red_layout, new ViewInitializer() {
+ @Override
+ public void initializeView(View view) {
+ // reduce alpha by 50%
+ Paint paint = new Paint();
+ paint.setAlpha(128);
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
+
+ // reduce alpha by another 50% (ensuring two alphas combine correctly)
+ view.setAlpha(0.5f);
+ }
+ })
+ .runWithVerifier(new ColorVerifier(expectedColor));
+ }
+
+ @SmallTest
+ public void testLayerPaintColorFilter() {
+ // Red, fully desaturated. Note that it's not 255/3 in each channel.
+ // See ColorMatrix#setSaturation()
+ @ColorInt
+ final int expectedColor = Color.rgb(54, 54, 54);
+ createTest()
+ .addLayout(R.layout.simple_red_layout, new ViewInitializer() {
+ @Override
+ public void initializeView(View view) {
+ Paint paint = new Paint();
+ ColorMatrix desatMatrix = new ColorMatrix();
+ desatMatrix.setSaturation(0.0f);
+ paint.setColorFilter(new ColorMatrixColorFilter(desatMatrix));
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
+ }
+ })
+ .runWithVerifier(new ColorVerifier(expectedColor));
+ }
+
+ @SmallTest
+ public void testLayerPaintBlend() {
+ // Red, drawn underneath opaque white, so output should be white.
+ // TODO: consider doing more interesting blending test here
+ @ColorInt
+ final int expectedColor = Color.WHITE;
+ createTest()
+ .addLayout(R.layout.simple_red_layout, new ViewInitializer() {
+ @Override
+ public void initializeView(View view) {
+ Paint paint = new Paint();
+ /* Note that when drawing in SW, we're blending within an otherwise empty
+ * SW layer, as opposed to in the frame buffer (which has a white
+ * background).
+ *
+ * For this reason we use just use DST, which just throws out the SRC
+ * content, regardless of the DST alpha channel.
+ */
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST));
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
+ }
+ })
+ .runWithVerifier(new ColorVerifier(expectedColor));
+ }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 6911cf0..2726dac 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.uirendering.cts.testclasses;
import android.graphics.Canvas;
@@ -14,14 +30,15 @@
import android.uirendering.cts.testinfrastructure.ViewInitializer;
import android.view.View;
import android.view.ViewGroup;
+import android.webkit.WebView;
import com.android.cts.uirendering.R;
public class PathClippingTests extends ActivityTestBase {
// draw circle with hole in it, with stroked circle
- static final CanvasClient sCircleDrawCanvasClient = new CanvasClient() {
+ static final CanvasClient sTorusDrawCanvasClient = new CanvasClient() {
@Override
public String getDebugString() {
- return "StrokedCircleDraw";
+ return "TorusDraw";
}
@Override
@@ -36,10 +53,10 @@
};
// draw circle with hole in it, by path operations + path clipping
- static final CanvasClient sCircleClipCanvasClient = new CanvasClient() {
+ static final CanvasClient sTorusClipCanvasClient = new CanvasClient() {
@Override
public String getDebugString() {
- return "CircleClipDraw";
+ return "TorusClipDraw";
}
@Override
@@ -60,15 +77,15 @@
@SmallTest
public void testCircleWithCircle() {
createTest()
- .addCanvasClient(sCircleDrawCanvasClient, false)
- .addCanvasClient(sCircleClipCanvasClient)
+ .addCanvasClient(sTorusDrawCanvasClient, false)
+ .addCanvasClient(sTorusClipCanvasClient)
.runWithComparer(new MSSIMComparer(0.90));
}
@SmallTest
public void testCircleWithPoints() {
createTest()
- .addCanvasClient(sCircleClipCanvasClient)
+ .addCanvasClient(sTorusClipCanvasClient)
.runWithVerifier(new SamplePointVerifier(
new Point[] {
// inside of circle
@@ -92,7 +109,7 @@
createTest()
.addLayout(R.layout.blue_padded_layout, new ViewInitializer() {
@Override
- public void intializeView(View view) {
+ public void initializeView(View view) {
ViewGroup rootView = (ViewGroup) view;
rootView.setClipChildren(true);
View childView = rootView.getChildAt(0);
@@ -143,4 +160,29 @@
})
.runWithComparer(new MSSIMComparer(0.90));
}
+
+ @SmallTest
+ public void testWebViewClipWithCircle() {
+ createTest()
+ // golden client - draw a simple non-AA circle
+ .addCanvasClient(new CanvasClient() {
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ Paint paint = new Paint();
+ paint.setAntiAlias(false);
+ paint.setColor(Color.BLUE);
+ canvas.drawOval(0, 0, width, height, paint);
+ }
+ }, false)
+ // verify against solid color webview, clipped to its parent oval
+ .addLayout(R.layout.circle_clipped_webview, new ViewInitializer() {
+ @Override
+ public void initializeView(View view) {
+ WebView webview = (WebView)view.findViewById(R.id.webview);
+ assertNotNull(webview);
+ webview.loadData("<body style=\"background-color:blue\">", null, null);
+ }
+ })
+ .runWithComparer(new MSSIMComparer(0.95));
+ }
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
index 32ab0e4c2..71b4f3f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
@@ -345,17 +345,6 @@
null, bitmapVerifiers);
}
- /*
- @SmallTest
- public void testShaderSweeps() {
- int mask = DisplayModifier.Accessor.AA_MASK
- | DisplayModifier.Accessor.SHADER_MASK
- | DisplayModifier.Accessor.XFERMODE_MASK
- | DisplayModifier.Accessor.SHAPES_MASK;
- sweepModifiersForMask(mask, null, DEFAULT_MSSIM_COMPARER, null);
- }
- */
-
protected void sweepModifiersForMask(int mask, final DisplayModifier drawOp,
BitmapComparer[] bitmapComparers, BitmapVerifier[] bitmapVerifiers) {
if ((mask & DisplayModifier.Accessor.ALL_OPTIONS_MASK) == 0) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
index 343228f..f1f7c99 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
@@ -28,13 +28,13 @@
final ViewInitializer BOUNDS_CLIP_INIT = new ViewInitializer() {
@Override
- public void intializeView(View rootView) {
+ public void initializeView(View rootView) {
((ViewGroup)rootView).setClipChildren(true);
}
};
final ViewInitializer PADDING_CLIP_INIT = new ViewInitializer() {
@Override
- public void intializeView(View rootView) {
+ public void initializeView(View rootView) {
ViewGroup child = (ViewGroup) rootView.findViewById(R.id.child);
child.setClipToPadding(true);
child.setWillNotDraw(true);
@@ -43,7 +43,7 @@
};
final ViewInitializer OUTLINE_CLIP_INIT = new ViewInitializer() {
@Override
- public void intializeView(View rootView) {
+ public void initializeView(View rootView) {
View child = rootView.findViewById(R.id.child);
child.setOutlineProvider(new ViewOutlineProvider() {
@Override
@@ -56,7 +56,7 @@
};
final ViewInitializer CLIP_BOUNDS_CLIP_INIT = new ViewInitializer() {
@Override
- public void intializeView(View view) {
+ public void initializeView(View view) {
view.setClipBounds(CLIP_BOUNDS_RECT);
}
};
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/CircleClipFrameLayout.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/CircleClipFrameLayout.java
new file mode 100644
index 0000000..4329c60
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/CircleClipFrameLayout.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class CircleClipFrameLayout extends FrameLayout {
+ final Path mClipPath = new Path();
+ public CircleClipFrameLayout(Context context) {
+ this(context, null);
+ }
+
+ public CircleClipFrameLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CircleClipFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public CircleClipFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ canvas.save();
+
+ mClipPath.reset();
+ mClipPath.addOval(0, 0, getWidth(), getHeight(), Path.Direction.CW);
+ canvas.clipPath(mClipPath);
+ super.dispatchDraw(canvas);
+
+ canvas.restore();
+ }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
index b8935fb..900e14d 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.uirendering.cts.testclasses.view;
import android.content.Context;
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 8c5f245..526f4f9 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -263,6 +263,20 @@
}
}
+ /**
+ * Runs a test where each testcase is run without verification. Should only be used
+ * where custom CanvasClients, Views, or ViewInitializers do their own internal
+ * test assertions.
+ */
+ public void runWithoutVerification() {
+ runWithVerifier(new BitmapVerifier() {
+ @Override
+ public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+ return true;
+ }
+ });
+ }
+
public TestCaseBuilder addWebView(String webViewUrl,
@Nullable ViewInitializer viewInitializer) {
return addWebView(webViewUrl, viewInitializer, false)
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
index b42ac88..99a9ef6 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
@@ -519,14 +519,17 @@
for (Map.Entry<String, LinkedHashMap<String, DisplayModifier>> entry :
mDisplayMap.entrySet()) {
int displayModifierIndex = mIndices[mapIndex];
- mDebugString += "Modification : " + entry.getKey();
+ if (!mDebugString.isEmpty()) {
+ mDebugString += ", ";
+ }
+ mDebugString += entry.getKey();
// Loop until we find the modification we are going to use
for (Map.Entry<String, DisplayModifier> modifierEntry :
entry.getValue().entrySet()) {
// Once we find the modification we want, then we will add it to the list,
// and the last applied modifications
if (displayModifierIndex == 0) {
- mDebugString += " value : " + modifierEntry.getKey() + " ";
+ mDebugString += ": " + modifierEntry.getKey();
modifierArrayList.add(modifierEntry.getValue());
break;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 041fcdd..d585f5f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -39,6 +39,7 @@
private Handler mHandler;
private View mView;
+ private View mViewWrapper;
private boolean mOnTv;
public void onCreate(Bundle bundle){
@@ -93,6 +94,7 @@
int drawCountDelay = 0;
setContentView(R.layout.test_container);
ViewStub stub = (ViewStub) findViewById(R.id.test_content_stub);
+ mViewWrapper = findViewById(R.id.test_content_wrapper);
switch (message.what) {
case LAYOUT_MSG: {
stub.setLayoutResource(message.arg1);
@@ -119,9 +121,12 @@
}
if (mViewInitializer != null) {
- mViewInitializer.intializeView(mView);
+ mViewInitializer.initializeView(mView);
}
- mView.setLayerType(message.arg2, null);
+
+ // set layer on wrapper parent of view, so view initializer
+ // can control layer type of View under test.
+ mViewWrapper.setLayerType(message.arg2, null);
DrawCounterListener onDrawListener = new DrawCounterListener(drawCountDelay);
@@ -147,7 +152,7 @@
mView.postInvalidate();
} else {
synchronized (mLock) {
- mLock.set(mView.getLeft(), mView.getTop());
+ mLock.set(mViewWrapper.getLeft(), mViewWrapper.getTop());
mLock.notify();
}
mView.getViewTreeObserver().removeOnPreDrawListener(this);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java
index 8980df3..9a3cbb1 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java
@@ -21,5 +21,5 @@
* Called after a view is created to set various properties on the view
*/
public abstract class ViewInitializer {
- public abstract void intializeView(View view);
+ public abstract void initializeView(View view);
}
diff --git a/tests/tests/view/res/layout/view_layout.xml b/tests/tests/view/res/layout/view_layout.xml
index e6e1550..b501a4d 100644
--- a/tests/tests/view/res/layout/view_layout.xml
+++ b/tests/tests/view/res/layout/view_layout.xml
@@ -21,7 +21,8 @@
android:id="@+id/viewlayout_root"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:scrollIndicators="left|right">
<android.view.cts.MockView
android:id="@+id/mock_view"
diff --git a/tests/tests/view/src/android/view/cts/ContextThemeWrapperTest.java b/tests/tests/view/src/android/view/cts/ContextThemeWrapperTest.java
index c40f095..012a13d 100644
--- a/tests/tests/view/src/android/view/cts/ContextThemeWrapperTest.java
+++ b/tests/tests/view/src/android/view/cts/ContextThemeWrapperTest.java
@@ -43,11 +43,11 @@
}
public void testConstructor() {
- // new the ContextThemeWrapper instance
new ContextThemeWrapper();
- // new the ContextThemeWrapper instance
new ContextThemeWrapper(getContext(), R.style.TextAppearance);
+
+ new ContextThemeWrapper(getContext(), getContext().getTheme());
}
public void testAccessTheme() {
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 34e4d09..fe3bd60 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -250,6 +250,31 @@
assertSame(parent, view.getParent());
}
+ public void testAccessScrollIndicators() {
+ View view = mActivity.findViewById(R.id.viewlayout_root);
+
+ assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT,
+ view.getScrollIndicators());
+ }
+
+ public void testSetScrollIndicators() {
+ View view = new View(mActivity);
+
+ view.setScrollIndicators(0);
+ assertEquals(0, view.getScrollIndicators());
+
+ view.setScrollIndicators(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT);
+ assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT,
+ view.getScrollIndicators());
+
+ view.setScrollIndicators(View.SCROLL_INDICATOR_TOP, View.SCROLL_INDICATOR_TOP);
+ assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT
+ | View.SCROLL_INDICATOR_TOP, view.getScrollIndicators());
+
+ view.setScrollIndicators(0, view.getScrollIndicators());
+ assertEquals(0, view.getScrollIndicators());
+ }
+
public void testFindViewById() {
View parent = mActivity.findViewById(R.id.viewlayout_root);
assertSame(parent, parent.findViewById(R.id.viewlayout_root));
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index ec02294..a52eabe 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -107,7 +107,8 @@
Log.i(LOG_TAG, String.format("Checking user agent string %s", actualUserAgentString));
final String patternString =
"Mozilla/5\\.0 \\(Linux;( U;)? Android ([^;]+);( (\\w+)-(\\w+);)?" +
- "\\s?(.*)\\sBuild/(.+)\\) AppleWebKit/(\\d+)\\.(\\d+) \\(KHTML, like Gecko\\) " +
+ "\\s?(.*)\\sBuild/(.+); wv\\) AppleWebKit/(\\d+)\\.(\\d+) " +
+ "\\(KHTML, like Gecko\\) " +
"Version/\\d+\\.\\d+ Chrome/\\d+\\.\\d+\\.\\d+\\.\\d+( Mobile)? " +
"Safari/(\\d+)\\.(\\d+)";
// Groups used:
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index dba2243..ee6f4ec 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -415,43 +415,17 @@
}
@UiThreadTest
- public void testSetScrollBarStyle() {
- if (!NullWebViewUtils.isWebViewAvailable()) {
- return;
- }
-
- mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
- assertFalse(mWebView.overlayHorizontalScrollbar());
- assertFalse(mWebView.overlayVerticalScrollbar());
-
- mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
- assertTrue(mWebView.overlayHorizontalScrollbar());
- assertTrue(mWebView.overlayVerticalScrollbar());
-
- mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
- assertFalse(mWebView.overlayHorizontalScrollbar());
- assertFalse(mWebView.overlayVerticalScrollbar());
-
- mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
- assertTrue(mWebView.overlayHorizontalScrollbar());
- assertTrue(mWebView.overlayVerticalScrollbar());
- }
-
- @UiThreadTest
public void testScrollBarOverlay() throws Throwable {
if (!NullWebViewUtils.isWebViewAvailable()) {
return;
}
+ // These functions have no effect; just verify they don't crash
mWebView.setHorizontalScrollbarOverlay(true);
mWebView.setVerticalScrollbarOverlay(false);
+
assertTrue(mWebView.overlayHorizontalScrollbar());
assertFalse(mWebView.overlayVerticalScrollbar());
-
- mWebView.setHorizontalScrollbarOverlay(false);
- mWebView.setVerticalScrollbarOverlay(true);
- assertFalse(mWebView.overlayHorizontalScrollbar());
- assertTrue(mWebView.overlayVerticalScrollbar());
}
@UiThreadTest
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index 4b88c01..bc43106 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -327,6 +327,14 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.cts.DatePickerDialogCtsActivity"
+ android:label="DatePickerDialogCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.app.ActivityGroup"
android:label="ActivityGroup" />
diff --git a/tests/tests/widget/res/layout/switch_layout.xml b/tests/tests/widget/res/layout/switch_layout.xml
new file mode 100644
index 0000000..a34845b
--- /dev/null
+++ b/tests/tests/widget/res/layout/switch_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<Switch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:thumbTint="@android:color/white"
+ android:thumbTintMode="src_over"
+ android:trackTint="@android:color/black"
+ android:trackTintMode="src_atop" />
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index 4fadafc..81a1a4b 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -383,34 +383,6 @@
assertEquals(1, mAdapterView.getSelectedItemId());
assertEquals(1, mAdapterView.getSelectedItemPosition());
assertEquals(FRUIT[1], mAdapterView.getSelectedItem());
-
- // Ensure getSelectedItemZzz() synchronizes after data set change.
- ArrayAdapter<String> adapter = new ArrayAdapter<>(
- getActivity(), android.R.layout.simple_list_item_1);
- adapter.add(FRUIT[0]);
- adapter.add(FRUIT[1]);
-
- ListAdapter previousAdapter = mAdapterView.getAdapter();
- mAdapterView.setAdapter(adapter);
- mAdapterView.setSelection(1);
- assertEquals("Initial getSelectedItemId() is correct",
- 1, mAdapterView.getSelectedItemId());
- assertEquals("Initial getSelectedItemPosition() is correct",
- 1, mAdapterView.getSelectedItemPosition());
-
- adapter.remove(FRUIT[0]);
- assertEquals("Synchronized getSelectedItemId() after data set invalidation",
- 0, mAdapterView.getSelectedItemId());
- assertEquals("Synchronized getSelectedItemPosition() after data set invalidation",
- 0, mAdapterView.getSelectedItemPosition());
-
- adapter.clear();
- assertEquals("Synchronized getSelectedItemId() after data set cleared",
- AdapterView.INVALID_ROW_ID, mAdapterView.getSelectedItemId());
- assertEquals("Synchronized getSelectedItemPosition() after data set cleared",
- AdapterView.INVALID_POSITION, mAdapterView.getSelectedItemPosition());
-
- mAdapterView.setAdapter(previousAdapter);
}
/*
diff --git a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
index 1e17ea7..fdca64c 100644
--- a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
@@ -21,6 +21,7 @@
import java.util.List;
import android.content.Context;
+import android.content.res.Resources.Theme;
import android.database.DataSetObserver;
import android.test.InstrumentationTestCase;
import android.test.UiThreadTest;
@@ -204,6 +205,12 @@
mArrayAdapter.setDropDownViewResource(INVALD_ID);
}
+ public void testAccessDropDownViewTheme() {
+ Theme theme = mContext.getResources().newTheme();
+ mArrayAdapter.setDropDownViewTheme(theme);
+ assertSame(theme, mArrayAdapter.getDropDownViewTheme());
+ }
+
/**
* insert the item to the specific position, notify data changed
* check -1, normal, > count
diff --git a/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java b/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
index bf5382a..2269e00 100644
--- a/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
@@ -188,6 +188,7 @@
// set null drawable
compoundButton = new MockCompoundButton(mContext);
compoundButton.setButtonDrawable(null);
+ assertNull(compoundButton.getButtonDrawable());
// set drawable when checkedTextView is GONE
compoundButton = new MockCompoundButton(mContext);
@@ -197,6 +198,7 @@
assertEquals(StateSet.WILD_CARD, firstDrawable.getState());
compoundButton.setButtonDrawable(firstDrawable);
+ assertSame(firstDrawable, compoundButton.getButtonDrawable());
assertFalse(firstDrawable.isVisible());
// update drawable when checkedTextView is VISIBLE
@@ -206,6 +208,7 @@
assertEquals(StateSet.WILD_CARD, secondDrawable.getState());
compoundButton.setButtonDrawable(secondDrawable);
+ assertSame(secondDrawable, compoundButton.getButtonDrawable());
assertTrue(secondDrawable.isVisible());
// the firstDrawable is not active.
assertFalse(firstDrawable.isVisible());
diff --git a/tests/tests/widget/src/android/widget/cts/CursorAdapterTest.java b/tests/tests/widget/src/android/widget/cts/CursorAdapterTest.java
index 0916e59..7fe016f 100644
--- a/tests/tests/widget/src/android/widget/cts/CursorAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CursorAdapterTest.java
@@ -19,6 +19,7 @@
import java.io.File;
import android.content.Context;
+import android.content.res.Resources.Theme;
import android.cts.util.PollingCheck;
import android.cts.util.ReadElf;
import android.cts.util.TestThread;
@@ -253,6 +254,14 @@
}
@UiThreadTest
+ public void testAccessDropDownViewTheme() {
+ CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, null);
+ Theme theme = mContext.getResources().newTheme();
+ cursorAdapter.setDropDownViewTheme(theme);
+ assertSame(theme, cursorAdapter.getDropDownViewTheme());
+ }
+
+ @UiThreadTest
public void testGetFilter() {
CursorAdapter cursorAdapter = new MockCursorAdapter(mContext, mCursor);
Filter filter = cursorAdapter.getFilter();
diff --git a/tests/tests/widget/src/android/widget/cts/DatePickerDialogCtsActivity.java b/tests/tests/widget/src/android/widget/cts/DatePickerDialogCtsActivity.java
new file mode 100644
index 0000000..ea8c3c4
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerDialogCtsActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * A minimal application for DatePickerDialog test.
+ */
+public class DatePickerDialogCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
new file mode 100644
index 0000000..1477f73
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DatePickerDialog;
+import android.content.Context;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+
+/**
+ * Test {@link DatePickerDialog}.
+ */
+public class DatePickerDialogTest extends
+ ActivityInstrumentationTestCase2<DatePickerDialogCtsActivity> {
+
+ private Activity mActivity;
+
+ public DatePickerDialogTest() {
+ super(DatePickerDialogCtsActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ }
+
+ @UiThreadTest
+ @SuppressWarnings("deprecation")
+ public void testConstructor() {
+ new DatePickerDialog(mActivity, null, 1970, 1, 1);
+
+ new DatePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 1970, 1, 1);
+
+ new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK, null, 1970, 1, 1);
+
+ new DatePickerDialog(mActivity,
+ android.R.style.Theme_Material_Dialog_Alert, null, 1970, 1, 1);
+
+ try {
+ new DatePickerDialog(null, null, 1970, 1, 1);
+ fail("should throw NullPointerException");
+ } catch (Exception e) {
+ }
+ }
+
+ @UiThreadTest
+ public void testShowDismiss() {
+ DatePickerDialog d = createDatePickerDialog();
+
+ d.show();
+ assertTrue("Showing date picker", d.isShowing());
+
+ d.show();
+ assertTrue("Date picker still showing", d.isShowing());
+
+ d.dismiss();
+ assertFalse("Dismissed date picker", d.isShowing());
+
+ d.dismiss();
+ assertFalse("Date picker still dismissed", d.isShowing());
+ }
+
+ private MockDatePickerDialog createDatePickerDialog() {
+ return new MockDatePickerDialog(mActivity, null, 1970, 1, 1);
+ }
+
+ private class MockDatePickerDialog extends DatePickerDialog {
+ public MockDatePickerDialog(Context context, OnDateSetListener callBack,
+ int year, int monthOfYear, int dayOfMonth) {
+ super(context, callBack, year, monthOfYear, dayOfMonth);
+ }
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
new file mode 100644
index 0000000..fa4e8063
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import com.android.cts.widget.R;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ListPopupWindow;
+import android.widget.PopupWindow;
+import android.widget.PopupWindow.OnDismissListener;
+
+public class ListPopupWindowTest extends
+ ActivityInstrumentationTestCase2<MockPopupWindowCtsActivity> {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+
+ /** The list popup window. */
+ private ListPopupWindow mPopupWindow;
+
+ /**
+ * Instantiates a new popup window test.
+ */
+ public ListPopupWindowTest() {
+ super(MockPopupWindowCtsActivity.class);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.test.ActivityInstrumentationTestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ }
+
+ public void testConstructor() {
+ new ListPopupWindow(mActivity);
+
+ new ListPopupWindow(mActivity, null);
+
+ new ListPopupWindow(mActivity, null, android.R.attr.popupWindowStyle);
+
+ new ListPopupWindow(mActivity, null, 0, android.R.style.Widget_Material_ListPopupWindow);
+ }
+
+ public void testAccessBackground() {
+ mPopupWindow = new ListPopupWindow(mActivity);
+
+ Drawable drawable = new ColorDrawable();
+ mPopupWindow.setBackgroundDrawable(drawable);
+ assertSame(drawable, mPopupWindow.getBackground());
+
+ mPopupWindow.setBackgroundDrawable(null);
+ assertNull(mPopupWindow.getBackground());
+ }
+
+ public void testAccessAnimationStyle() {
+ mPopupWindow = new ListPopupWindow(mActivity);
+ assertEquals(0, mPopupWindow.getAnimationStyle());
+
+ mPopupWindow.setAnimationStyle(android.R.style.Animation_Toast);
+ assertEquals(android.R.style.Animation_Toast, mPopupWindow.getAnimationStyle());
+
+ // abnormal values
+ mPopupWindow.setAnimationStyle(-100);
+ assertEquals(-100, mPopupWindow.getAnimationStyle());
+ }
+
+ public void testAccessHeight() {
+ mPopupWindow = new ListPopupWindow(mActivity);
+ assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getHeight());
+
+ int height = getDisplay().getHeight() / 2;
+ mPopupWindow.setHeight(height);
+ assertEquals(height, mPopupWindow.getHeight());
+
+ height = getDisplay().getHeight();
+ mPopupWindow.setHeight(height);
+ assertEquals(height, mPopupWindow.getHeight());
+
+ mPopupWindow.setHeight(0);
+ assertEquals(0, mPopupWindow.getHeight());
+
+ height = getDisplay().getHeight() * 2;
+ mPopupWindow.setHeight(height);
+ assertEquals(height, mPopupWindow.getHeight());
+
+ height = -getDisplay().getHeight() / 2;
+ mPopupWindow.setHeight(height);
+ assertEquals(height, mPopupWindow.getHeight());
+ }
+
+ /**
+ * Gets the display.
+ *
+ * @return the display
+ */
+ private Display getDisplay() {
+ WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
+ return wm.getDefaultDisplay();
+ }
+
+ public void testAccessWidth() {
+ mPopupWindow = new ListPopupWindow(mActivity);
+ assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getWidth());
+
+ int width = getDisplay().getWidth() / 2;
+ mPopupWindow.setWidth(width);
+ assertEquals(width, mPopupWindow.getWidth());
+
+ width = getDisplay().getWidth();
+ mPopupWindow.setWidth(width);
+ assertEquals(width, mPopupWindow.getWidth());
+
+ mPopupWindow.setWidth(0);
+ assertEquals(0, mPopupWindow.getWidth());
+
+ width = getDisplay().getWidth() * 2;
+ mPopupWindow.setWidth(width);
+ assertEquals(width, mPopupWindow.getWidth());
+
+ width = - getDisplay().getWidth() / 2;
+ mPopupWindow.setWidth(width);
+ assertEquals(width, mPopupWindow.getWidth());
+ }
+
+ public void testShow() {
+ int[] anchorXY = new int[2];
+ int[] viewOnScreenXY = new int[2];
+ int[] viewInWindowXY = new int[2];
+
+ mPopupWindow = new ListPopupWindow(mActivity);
+
+ final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
+ mPopupWindow.setAnchorView(upperAnchor);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ mPopupWindow.show();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertTrue(mPopupWindow.isShowing());
+
+ mPopupWindow.getListView().getLocationOnScreen(viewOnScreenXY);
+ upperAnchor.getLocationOnScreen(anchorXY);
+ mPopupWindow.getListView().getLocationInWindow(viewInWindowXY);
+ assertEquals(anchorXY[0] + viewInWindowXY[0], viewOnScreenXY[0]);
+ assertEquals(anchorXY[1] + viewInWindowXY[1] + upperAnchor.getHeight(), viewOnScreenXY[1]);
+
+ dismissPopup();
+ }
+
+ public void testSetWindowLayoutType() {
+ mPopupWindow = new ListPopupWindow(mActivity);
+
+ final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
+ mPopupWindow.setAnchorView(upperAnchor);
+ mPopupWindow.setWindowLayoutType(
+ WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ mPopupWindow.show();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertTrue(mPopupWindow.isShowing());
+
+ WindowManager.LayoutParams p = (WindowManager.LayoutParams)
+ mPopupWindow.getListView().getRootView().getLayoutParams();
+ assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL, p.type);
+
+ dismissPopup();
+ }
+
+ @UiThreadTest
+ public void testDismiss() {
+ mPopupWindow = new ListPopupWindow(mActivity);
+ assertFalse(mPopupWindow.isShowing());
+ View anchorView = mActivity.findViewById(R.id.anchor_upper);
+ mPopupWindow.setAnchorView(anchorView);
+ mPopupWindow.show();
+
+ mPopupWindow.dismiss();
+ assertFalse(mPopupWindow.isShowing());
+
+ mPopupWindow.dismiss();
+ assertFalse(mPopupWindow.isShowing());
+ }
+
+ public void testSetOnDismissListener() {
+ mPopupWindow = new ListPopupWindow(mActivity);
+ mPopupWindow.setOnDismissListener(null);
+
+ MockOnDismissListener onDismissListener = new MockOnDismissListener();
+ mPopupWindow.setOnDismissListener(onDismissListener);
+ showPopup();
+ dismissPopup();
+ assertEquals(1, onDismissListener.getOnDismissCalledCount());
+
+ showPopup();
+ dismissPopup();
+ assertEquals(2, onDismissListener.getOnDismissCalledCount());
+
+ mPopupWindow.setOnDismissListener(null);
+ showPopup();
+ dismissPopup();
+ assertEquals(2, onDismissListener.getOnDismissCalledCount());
+ }
+
+ public void testAccessInputMethodMode() {
+ mPopupWindow = new ListPopupWindow(mActivity);
+ assertEquals(PopupWindow.INPUT_METHOD_NEEDED, mPopupWindow.getInputMethodMode());
+
+ mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE);
+ assertEquals(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE, mPopupWindow.getInputMethodMode());
+
+ mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+ assertEquals(PopupWindow.INPUT_METHOD_NEEDED, mPopupWindow.getInputMethodMode());
+
+ mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ assertEquals(PopupWindow.INPUT_METHOD_NOT_NEEDED, mPopupWindow.getInputMethodMode());
+
+ mPopupWindow.setInputMethodMode(-1);
+ assertEquals(-1, mPopupWindow.getInputMethodMode());
+ }
+
+ /**
+ * The listener interface for receiving OnDismiss events. The class that is
+ * interested in processing a OnDismiss event implements this interface, and
+ * the object created with that class is registered with a component using
+ * the component's <code>setOnDismissListener<code> method. When
+ * the OnDismiss event occurs, that object's appropriate
+ * method is invoked.
+ */
+ private static class MockOnDismissListener implements OnDismissListener {
+
+ /** The Ondismiss called count. */
+ private int mOnDismissCalledCount;
+
+ /**
+ * Gets the onDismiss() called count.
+ *
+ * @return the on dismiss called count
+ */
+ public int getOnDismissCalledCount() {
+ return mOnDismissCalledCount;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.widget.PopupWindow.OnDismissListener#onDismiss()
+ */
+ public void onDismiss() {
+ mOnDismissCalledCount++;
+ }
+
+ }
+
+ /**
+ * Show PopupWindow.
+ */
+ // FIXME: logcat info complains that there is window leakage due to that mPopupWindow is not
+ // clean up. Need to fix it.
+ private void showPopup() {
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ if (mPopupWindow == null || mPopupWindow.isShowing()) {
+ return;
+ }
+ View anchor = mActivity.findViewById(R.id.anchor_upper);
+ mPopupWindow.setAnchorView(anchor);
+ mPopupWindow.show();
+ assertTrue(mPopupWindow.isShowing());
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Dismiss PopupWindow.
+ */
+ private void dismissPopup() {
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ if (mPopupWindow == null || !mPopupWindow.isShowing())
+ return;
+ mPopupWindow.dismiss();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ListViewTest.java b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
index 5f0967a..60db540 100644
--- a/tests/tests/widget/src/android/widget/cts/ListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
@@ -32,15 +32,18 @@
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.AttributeSet;
+import android.util.Pair;
import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LayoutAnimationController;
+import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
@@ -361,6 +364,53 @@
assertEquals(2, mListView.getHeaderViewsCount());
}
+ public void testHeaderFooterType() throws Throwable {
+ final TextView headerView = new TextView(getActivity());
+ final List<Pair<View, View>> mismatch = new ArrayList<Pair<View, View>>();
+ final ArrayAdapter adapter = new ArrayAdapter<String>(mActivity,
+ android.R.layout.simple_list_item_1, mNameList) {
+ @Override
+ public int getItemViewType(int position) {
+ return position == 0 ? AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER :
+ super.getItemViewType(position - 1);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (position == 0) {
+ if (convertView != null && convertView != headerView) {
+ mismatch.add(new Pair<View, View>(headerView, convertView));
+ }
+ return headerView;
+ } else {
+ return super.getView(position - 1, convertView, parent);
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return super.getCount() + 1;
+ }
+ };
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mListView.setAdapter(adapter);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ adapter.notifyDataSetChanged();
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+
+ assertEquals(0, mismatch.size());
+ }
+
public void testAccessDivider() {
mInstrumentation.runOnMainSync(new Runnable() {
public void run() {
diff --git a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
new file mode 100644
index 0000000..2dff4cb
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.cts;
+
+import com.android.cts.widget.R;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.Gravity;
+import android.widget.PopupMenu;
+
+
+public class PopupMenuTest extends
+ ActivityInstrumentationTestCase2<MockPopupWindowCtsActivity> {
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+
+ public PopupMenuTest() {
+ super("com.android.cts.widget", MockPopupWindowCtsActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ }
+
+ public void testAccessGravity() {
+ PopupMenu popupMenu = new PopupMenu(mActivity,
+ mActivity.findViewById(R.id.anchor_middle_left));
+ assertEquals(Gravity.NO_GRAVITY, popupMenu.getGravity());
+ popupMenu.setGravity(Gravity.TOP);
+ assertEquals(Gravity.TOP, popupMenu.getGravity());
+ }
+
+ public void testOnDismissListener() {
+ final PopupMenu popupMenu = new PopupMenu(mActivity,
+ mActivity.findViewById(R.id.anchor_middle_left));
+ TestPopupDismissListener listener = new TestPopupDismissListener();
+ popupMenu.setOnDismissListener(listener);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ popupMenu.show();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ assertEquals(0, listener.getDismissCount());
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ popupMenu.dismiss();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ assertEquals(1, listener.getDismissCount());
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ popupMenu.dismiss();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ assertEquals(1, listener.getDismissCount());
+ }
+
+ private class TestPopupDismissListener implements PopupMenu.OnDismissListener {
+ int mDismissCount;
+
+ @Override
+ public void onDismiss(PopupMenu menu) {
+ mDismissCount++;
+ }
+
+ int getDismissCount() {
+ return mDismissCount;
+ }
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 706ad8d..1131e02 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -29,6 +29,8 @@
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
+import android.transition.Transition;
+import android.transition.TransitionValues;
import android.view.Display;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -334,6 +336,41 @@
dismissPopup();
}
+ public void testOverlapAnchor() {
+ int[] anchorXY = new int[2];
+ int[] viewOnScreenXY = new int[2];
+ int[] viewInWindowXY = new int[2];
+
+ mPopupWindow = createPopupWindow(createPopupContent());
+ final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
+ upperAnchor.getLocationOnScreen(anchorXY);
+
+ assertFalse(mPopupWindow.getOverlapAnchor());
+ mPopupWindow.setOverlapAnchor(true);
+ assertTrue(mPopupWindow.getOverlapAnchor());
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ mPopupWindow.showAsDropDown(upperAnchor, 0, 0);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY);
+ mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
+ assertEquals(anchorXY[0] + viewInWindowXY[0], viewOnScreenXY[0]);
+ assertEquals(anchorXY[1] + viewInWindowXY[1], viewOnScreenXY[1]);
+ }
+
+ public void testAccessWindowLayoutType() {
+ mPopupWindow = createPopupWindow(createPopupContent());
+ assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
+ mPopupWindow.getWindowLayoutType());
+ mPopupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+ assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
+ mPopupWindow.getWindowLayoutType());
+ }
+
public void testGetMaxAvailableHeight() {
mPopupWindow = createPopupWindow(createPopupContent());
@@ -447,6 +484,36 @@
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM & p.flags);
}
+ public void testEnterExitTransition() {
+ mPopupWindow = createPopupWindow(createPopupContent());
+ final View anchorView = mActivity.findViewById(R.id.anchor_upper);
+
+ final MockTransition enterTransition = new MockTransition();
+ final MockTransition exitTransition = new MockTransition();
+ mPopupWindow.setEnterTransition(enterTransition);
+ mPopupWindow.setExitTransition(exitTransition);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ mPopupWindow.showAsDropDown(anchorView, 0, 0);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(1, enterTransition.getTransitionCount());
+ assertEquals(0, exitTransition.getTransitionCount());
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ public void run() {
+ mPopupWindow.dismiss();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(1, enterTransition.getTransitionCount());
+ assertEquals(1, exitTransition.getTransitionCount());
+ }
+
public void testUpdatePositionAndDimension() {
int[] fstXY = new int[2];
int[] sndXY = new int[2];
@@ -823,6 +890,28 @@
}
}
+ private static class MockTransition extends Transition {
+ private int mTransitionCount;
+
+ private MockTransition() {
+ addListener(new Transition.TransitionListenerAdapter() {
+ public void onTransitionEnd(Transition transition) {
+ mTransitionCount++;
+ }
+ });
+ }
+
+ public void captureStartValues(TransitionValues transitionValues) {
+ }
+
+ public void captureEndValues(TransitionValues transitionValues) {
+ }
+
+ int getTransitionCount() {
+ return mTransitionCount;
+ }
+ }
+
private View createPopupContent() {
View popupView = new View(mActivity);
popupView.setLayoutParams(new ViewGroup.LayoutParams(50, 50));
diff --git a/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java b/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java
new file mode 100644
index 0000000..cfd61a2
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.test.InstrumentationTestCase;
+import android.test.UiThreadTest;
+import android.widget.QuickContactBadge;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class QuickContactBadgeTest extends InstrumentationTestCase {
+
+ @UiThreadTest
+ public void testPrioritizedMimetype() throws InterruptedException {
+ final String plainMimeType = "text/plain";
+ final Uri nonExistentContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 0);
+ final CountDownLatch latch = new CountDownLatch(1);
+ Context context = new ContextWrapper(getInstrumentation().getContext()) {
+ @Override
+ public void startActivity(Intent intent) {
+ testCallback(intent);
+ }
+
+ @Override
+ public void startActivityAsUser(Intent intent, UserHandle user) {
+ testCallback(intent);
+ }
+
+ @Override
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+ testCallback(intent);
+ }
+
+ private void testCallback(Intent intent) {
+ assertEquals(plainMimeType, intent.getStringExtra(
+ ContactsContract.QuickContact.EXTRA_PRIORITIZED_MIMETYPE));
+ latch.countDown();
+ }
+ };
+
+ // Execute: create QuickContactBadge with a prioritized mimetype and click on it
+ QuickContactBadge badge = new QuickContactBadge(context);
+ badge.setPrioritizedMimeType(plainMimeType);
+ badge.assignContactUri(nonExistentContactUri);
+ badge.onClick(badge);
+
+ // Verify: the QuickContactBadge attempts to start an activity, and sets the
+ // prioritized mimetype. We don't know which method will be used to start the activity,
+ // so we check all options.
+ assertTrue(latch.await(1, TimeUnit.SECONDS));
+ }
+}
+
diff --git a/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java b/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
index b5ce5c9..a8d7f54 100644
--- a/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
@@ -318,6 +318,18 @@
assertFalse(myRelativeLayout.checkLayoutParams(p3));
}
+ public void testGetRule() {
+ RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(0, 0);
+ p.addRule(RelativeLayout.LEFT_OF, R.id.abslistview_root);
+ p.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
+
+ assertEquals("Get resource ID rule", R.id.abslistview_root,
+ p.getRule(RelativeLayout.LEFT_OF));
+ assertEquals("Get boolean rule", RelativeLayout.TRUE,
+ p.getRule(RelativeLayout.CENTER_IN_PARENT));
+ assertEquals("Get missing rule", 0, p.getRule(RelativeLayout.ABOVE));
+ }
+
private class MyRelativeLayout extends RelativeLayout {
public MyRelativeLayout(Context context) {
super(context);
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
index c530293..5cbf524 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleAdapterTest.java
@@ -20,6 +20,7 @@
import android.content.Context;
+import android.content.res.Resources.Theme;
import android.cts.util.WidgetTestUtils;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
@@ -260,6 +261,12 @@
}
}
+ public void testAccessDropDownViewTheme() {
+ Theme theme = mContext.getResources().newTheme();
+ mSimpleAdapter.setDropDownViewTheme(theme);
+ assertSame(theme, mSimpleAdapter.getDropDownViewTheme());
+ }
+
public void testAccessViewBinder() {
// no binder default
assertNull(mSimpleAdapter.getViewBinder());
diff --git a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
index 1989626..599d308 100644
--- a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java
@@ -23,8 +23,10 @@
import android.app.Dialog;
import android.content.Context;
import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
+import android.view.ContextThemeWrapper;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
@@ -52,6 +54,17 @@
new Spinner(mTargetContext, null, com.android.internal.R.attr.spinnerStyle);
+ new Spinner(mTargetContext, Spinner.MODE_DIALOG);
+
+ new Spinner(mTargetContext, null, com.android.internal.R.attr.spinnerStyle,
+ Spinner.MODE_DIALOG);
+
+ new Spinner(mTargetContext, null, com.android.internal.R.attr.spinnerStyle, 0,
+ Spinner.MODE_DIALOG);
+
+ new Spinner(mTargetContext, null, com.android.internal.R.attr.spinnerStyle, 0,
+ Spinner.MODE_DIALOG, mTargetContext.getTheme());
+
Spinner spinner = (Spinner) getActivity().findViewById(R.id.spinner1);
assertEquals(mTargetContext.getString(R.string.text_view_hello), spinner.getPrompt());
}
@@ -156,6 +169,18 @@
// TODO: find the dialog and get its title to assert whether setPromptId() takes effect?
}
+ @UiThreadTest
+ public void testGetPopupContext() {
+ Theme theme = mTargetContext.getResources().newTheme();
+ Spinner themeSpinner = new Spinner(mTargetContext, null,
+ com.android.internal.R.attr.spinnerStyle, 0, Spinner.MODE_DIALOG, theme);
+ assertNotSame(mTargetContext, themeSpinner.getPopupContext());
+ assertSame(theme, themeSpinner.getPopupContext().getTheme());
+
+ ContextThemeWrapper context = (ContextThemeWrapper)themeSpinner.getPopupContext();
+ assertSame(mTargetContext, context.getBaseContext());
+ }
+
public void testOnLayout() {
// onLayout() is implementation details, do NOT test
}
diff --git a/tests/tests/widget/src/android/widget/cts/SwitchTest.java b/tests/tests/widget/src/android/widget/cts/SwitchTest.java
new file mode 100644
index 0000000..164e7bf
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/SwitchTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import com.android.cts.widget.R;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.XmlResourceParser;
+import android.cts.util.WidgetTestUtils;
+import android.graphics.Color;
+import android.graphics.PorterDuff.Mode;
+import android.test.AndroidTestCase;
+import android.util.Xml;
+import android.widget.Switch;
+
+import java.io.IOException;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Test {@link Switch}.
+ */
+public class SwitchTest extends AndroidTestCase {
+ public void testConstructor() throws XmlPullParserException, IOException {
+ new Switch(mContext);
+
+ XmlResourceParser parser = mContext.getResources().getLayout(R.layout.switch_layout);
+ WidgetTestUtils.beginDocument(parser, "Switch");
+
+ new Switch(mContext, parser);
+
+ new Switch(mContext, parser, 0);
+
+ new Switch(mContext, parser, 0, 0);
+ }
+
+ public void testAccessThumbTint() throws XmlPullParserException, IOException {
+ XmlResourceParser parser = mContext.getResources().getLayout(R.layout.switch_layout);
+ WidgetTestUtils.beginDocument(parser, "Switch");
+ Switch aSwitch = new Switch(mContext, parser);
+ assertEquals(Color.WHITE, aSwitch.getThumbTintList().getDefaultColor());
+ assertEquals(Mode.SRC_OVER, aSwitch.getThumbTintMode());
+
+ ColorStateList colors = ColorStateList.valueOf(Color.RED);
+ aSwitch.setThumbTintList(colors);
+ aSwitch.setThumbTintMode(Mode.XOR);
+
+ assertSame(colors, aSwitch.getThumbTintList());
+ assertEquals(Mode.XOR, aSwitch.getThumbTintMode());
+ }
+
+ public void testAccessTrackTint() throws XmlPullParserException, IOException {
+ XmlResourceParser parser = mContext.getResources().getLayout(R.layout.switch_layout);
+ WidgetTestUtils.beginDocument(parser, "Switch");
+ Switch aSwitch = new Switch(mContext, parser);
+ assertEquals(Color.BLACK, aSwitch.getTrackTintList().getDefaultColor());
+ assertEquals(Mode.SRC_ATOP, aSwitch.getTrackTintMode());
+
+ ColorStateList colors = ColorStateList.valueOf(Color.RED);
+ aSwitch.setTrackTintList(colors);
+ aSwitch.setTrackTintMode(Mode.XOR);
+
+ assertSame(colors, aSwitch.getTrackTintList());
+ assertEquals(Mode.XOR, aSwitch.getTrackTintMode());
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 3562fc8..ec098f8 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -33,6 +33,7 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
@@ -77,12 +78,16 @@
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.ActionMode;
import android.view.Gravity;
import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
@@ -2614,6 +2619,88 @@
assertTrue(mTextView.getHeight() <= maxLines * mTextView.getLineHeight());
}
+ public int calculateTextWidth(String text) {
+ mTextView = findTextView(R.id.textview_text);
+
+ // Set the TextView width as the half of the whole text.
+ float[] widths = new float[text.length()];
+ mTextView.getPaint().getTextWidths(text, widths);
+ float textfieldWidth = 0.0f;
+ for (int i = 0; i < text.length(); ++i) {
+ textfieldWidth += widths[i];
+ }
+ return (int)textfieldWidth;
+
+ }
+
+ @UiThreadTest
+ public void testHyphenationNotHappen_frequencyNone() {
+ final int[] BREAK_STRATEGIES = {
+ Layout.BREAK_STRATEGY_SIMPLE, Layout.BREAK_STRATEGY_HIGH_QUALITY,
+ Layout.BREAK_STRATEGY_BALANCED };
+
+ mTextView = findTextView(R.id.textview_text);
+
+ for (int breakStrategy : BREAK_STRATEGIES) {
+ for (int charWidth = 10; charWidth < 120; charWidth += 5) {
+ // Change the text view's width to charWidth width.
+ mTextView.setWidth(calculateTextWidth(LONG_TEXT.substring(0, charWidth)));
+
+ mTextView.setText(LONG_TEXT);
+ mTextView.setBreakStrategy(breakStrategy);
+
+ mTextView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
+
+ mTextView.requestLayout();
+ mTextView.onPreDraw(); // For freezing the layout.
+ Layout layout = mTextView.getLayout();
+
+ final int lineCount = layout.getLineCount();
+ for (int line = 0; line < lineCount; ++line) {
+ final int lineEnd = layout.getLineEnd(line);
+ // In any width, any break strategy, hyphenation should not happen if
+ // HYPHENATION_FREQUENCY_NONE is specified.
+ assertTrue(lineEnd == LONG_TEXT.length() ||
+ Character.isWhitespace(LONG_TEXT.charAt(lineEnd - 1)));
+ }
+ }
+ }
+ }
+
+ @UiThreadTest
+ public void testHyphenationNotHappen_breakStrategySimple() {
+ final int[] HYPHENATION_FREQUENCIES = {
+ Layout.HYPHENATION_FREQUENCY_NORMAL, Layout.HYPHENATION_FREQUENCY_FULL,
+ Layout.HYPHENATION_FREQUENCY_NONE };
+
+ mTextView = findTextView(R.id.textview_text);
+
+ for (int hyphenationFrequency: HYPHENATION_FREQUENCIES) {
+ for (int charWidth = 10; charWidth < 120; charWidth += 5) {
+ // Change the text view's width to charWidth width.
+ mTextView.setWidth(calculateTextWidth(LONG_TEXT.substring(0, charWidth)));
+
+ mTextView.setText(LONG_TEXT);
+ mTextView.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
+
+ mTextView.setHyphenationFrequency(hyphenationFrequency);
+
+ mTextView.requestLayout();
+ mTextView.onPreDraw(); // For freezing the layout.
+ Layout layout = mTextView.getLayout();
+
+ final int lineCount = layout.getLineCount();
+ for (int line = 0; line < lineCount; ++line) {
+ final int lineEnd = layout.getLineEnd(line);
+ // In any width, any hyphenation frequency, hyphenation should not happen if
+ // BREAK_STRATEGY_SIMPLE is specified.
+ assertTrue(lineEnd == LONG_TEXT.length() ||
+ Character.isWhitespace(LONG_TEXT.charAt(lineEnd - 1)));
+ }
+ }
+ }
+ }
+
@UiThreadTest
public void testSetMaxLinesException() {
mTextView = new TextView(mActivity);
@@ -2831,7 +2918,7 @@
assertEquals(40, mTextView.getPaddingBottom());
}
- public void testSetTextAppearance() {
+ public void testDeprecatedSetTextAppearance() {
mTextView = new TextView(mActivity);
mTextView.setTextAppearance(mActivity, R.style.TextAppearance_All);
@@ -2860,10 +2947,49 @@
assertEquals(null, mTextView.getTypeface());
}
+ public void testSetTextAppearance() {
+ mTextView = new TextView(mActivity);
+
+ mTextView.setTextAppearance(R.style.TextAppearance_All);
+ assertEquals(mActivity.getResources().getColor(R.drawable.black),
+ mTextView.getCurrentTextColor());
+ assertEquals(20f, mTextView.getTextSize(), 0.01f);
+ assertEquals(Typeface.BOLD, mTextView.getTypeface().getStyle());
+ assertEquals(mActivity.getResources().getColor(R.drawable.red),
+ mTextView.getCurrentHintTextColor());
+ assertEquals(mActivity.getResources().getColor(R.drawable.blue),
+ mTextView.getLinkTextColors().getDefaultColor());
+
+ mTextView.setTextAppearance(R.style.TextAppearance_Colors);
+ assertEquals(mActivity.getResources().getColor(R.drawable.black),
+ mTextView.getCurrentTextColor());
+ assertEquals(mActivity.getResources().getColor(R.drawable.blue),
+ mTextView.getCurrentHintTextColor());
+ assertEquals(mActivity.getResources().getColor(R.drawable.yellow),
+ mTextView.getLinkTextColors().getDefaultColor());
+
+ mTextView.setTextAppearance(R.style.TextAppearance_NotColors);
+ assertEquals(17f, mTextView.getTextSize(), 0.01f);
+ assertEquals(Typeface.NORMAL, mTextView.getTypeface().getStyle());
+
+ mTextView.setTextAppearance(R.style.TextAppearance_Style);
+ assertEquals(null, mTextView.getTypeface());
+ }
+
public void testOnPreDraw() {
// Do not test. Implementation details.
}
+ public void testAccessCompoundDrawableTint() {
+ mTextView = new TextView(mActivity);
+
+ ColorStateList colors = ColorStateList.valueOf(Color.RED);
+ mTextView.setCompoundDrawableTintList(colors);
+ mTextView.setCompoundDrawableTintMode(PorterDuff.Mode.XOR);
+ assertSame(colors, mTextView.getCompoundDrawableTintList());
+ assertEquals(PorterDuff.Mode.XOR, mTextView.getCompoundDrawableTintMode());
+ }
+
public void testSetHorizontallyScrolling() {
// make the text view has more than one line
mTextView = findTextView(R.id.textview_text);
@@ -4301,38 +4427,135 @@
assertEquals(Layout.HYPHENATION_FREQUENCY_FULL, tv.getHyphenationFrequency());
}
- public void testSetGetIndents() {
- TextView tv = new TextView(mActivity);
- {
- // Null.
- tv.setIndents(null, null);
- assertNull(tv.getLeftIndents());
- assertNull(tv.getRightIndents());
- }
- {
- // Empty arrays.
- final int[] emptyArray = {};
- tv.setIndents(emptyArray, emptyArray);
- assertEquals(0, tv.getLeftIndents().length);
- assertEquals(0, tv.getRightIndents().length);
- }
- {
- final int[] leftIndents = { 10, 20, 30, 40 };
- final int[] rightIndents = { 15, 25, 35, 45 };
- tv.setIndents(leftIndents, rightIndents);
- MoreAsserts.assertEquals(leftIndents, tv.getLeftIndents());
- MoreAsserts.assertEquals(rightIndents, tv.getRightIndents());
- }
- {
- // Negative values.
- final int[] leftIndents = { -10, 20, -30, 40 };
- final int[] rightIndents = { 15, -25, 35, -45 };
- tv.setIndents(leftIndents, rightIndents);
- MoreAsserts.assertEquals(leftIndents, tv.getLeftIndents());
- MoreAsserts.assertEquals(rightIndents, tv.getRightIndents());
- }
+ public void testSetAndGetCustomSelectionActionModeCallback() {
+ final String text = "abcde";
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ mTextView = new EditText(mActivity);
+ mActivity.setContentView(mTextView);
+ mTextView.setText(text, BufferType.SPANNABLE);
+ mTextView.setTextIsSelectable(true);
+ mTextView.requestFocus();
+ mTextView.setSelected(true);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // Check default value.
+ assertNull(mTextView.getCustomSelectionActionModeCallback());
+
+ MockActionModeCallback callbackBlockActionMode = new MockActionModeCallback(false);
+ mTextView.setCustomSelectionActionModeCallback(callbackBlockActionMode);
+ assertEquals(callbackBlockActionMode,
+ mTextView.getCustomSelectionActionModeCallback());
+
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ // Set selection and try to start action mode.
+ final Bundle args = new Bundle();
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length());
+ mTextView.performAccessibilityActionInternal(
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(1, callbackBlockActionMode.getCreateCount());
+
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ // Remove selection and stop action mode.
+ mTextView.onTextContextMenuItem(android.R.id.copy);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // Action mode was blocked.
+ assertEquals(0, callbackBlockActionMode.getDestroyCount());
+
+ // Overwrite callback.
+ MockActionModeCallback callbackStartActionMode = new MockActionModeCallback(true);
+ mTextView.setCustomSelectionActionModeCallback(callbackStartActionMode);
+ assertEquals(callbackStartActionMode, mTextView.getCustomSelectionActionModeCallback());
+
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ // Set selection and try to start action mode.
+ final Bundle args = new Bundle();
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+ args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length());
+ mTextView.performAccessibilityActionInternal(
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
+
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(1, callbackStartActionMode.getCreateCount());
+
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ // Remove selection and stop action mode.
+ mTextView.onTextContextMenuItem(android.R.id.copy);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // Action mode was started
+ assertEquals(1, callbackStartActionMode.getDestroyCount());
}
+ public void testSetAndGetCustomInseltionActionMode() {
+ initTextViewForTyping();
+ // Check default value.
+ assertNull(mTextView.getCustomInsertionActionModeCallback());
+
+ MockActionModeCallback callback = new MockActionModeCallback(false);
+ mTextView.setCustomInsertionActionModeCallback(callback);
+ assertEquals(callback, mTextView.getCustomInsertionActionModeCallback());
+ // TODO(Bug: 22033189): Tests the set callback is actually used.
+ }
+
+ private static class MockActionModeCallback implements ActionMode.Callback {
+ private int mCreateCount = 0;
+ private int mDestroyCount = 0;
+ private final boolean mAllowToStartActionMode;
+
+ public MockActionModeCallback(boolean allowToStartActionMode) {
+ mAllowToStartActionMode = allowToStartActionMode;
+ }
+
+ public int getCreateCount() {
+ return mCreateCount;
+ }
+
+ public int getDestroyCount() {
+ return mDestroyCount;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ mDestroyCount++;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mCreateCount++;
+ return mAllowToStartActionMode;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+ };
+
private static class MockOnEditorActionListener implements OnEditorActionListener {
private boolean isOnEditorActionCalled;
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerTest.java b/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
index fcf787a..1ce2844 100644
--- a/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
@@ -167,6 +167,37 @@
assertEquals(Integer.valueOf(23), mTimePicker.getCurrentHour());
}
+ public void testAccessHour() {
+ mTimePicker = new TimePicker(mContext);
+
+ // AM/PM mode
+ mTimePicker.setIs24HourView(false);
+
+ mTimePicker.setHour(0);
+ assertEquals(0, mTimePicker.getHour());
+
+ mTimePicker.setHour(12);
+ assertEquals(12, mTimePicker.getHour());
+
+ mTimePicker.setHour(13);
+ assertEquals(13, mTimePicker.getHour());
+
+ mTimePicker.setHour(23);
+ assertEquals(23, mTimePicker.getHour());
+
+ // for 24 hour mode
+ mTimePicker.setIs24HourView(true);
+
+ mTimePicker.setHour(0);
+ assertEquals(0, mTimePicker.getHour());
+
+ mTimePicker.setHour(13);
+ assertEquals(13, mTimePicker.getHour());
+
+ mTimePicker.setHour(23);
+ assertEquals(23, mTimePicker.getHour());
+ }
+
public void testAccessIs24HourView() {
mTimePicker = new TimePicker(mContext);
assertFalse(mTimePicker.is24HourView());
@@ -194,6 +225,22 @@
assertEquals(Integer.valueOf(59), mTimePicker.getCurrentMinute());
}
+ public void testAccessMinute() {
+ mTimePicker = new TimePicker(mContext);
+
+ mTimePicker.setMinute(0);
+ assertEquals(0, mTimePicker.getMinute());
+
+ mTimePicker.setMinute(12);
+ assertEquals(12, mTimePicker.getMinute());
+
+ mTimePicker.setMinute(33);
+ assertEquals(33, mTimePicker.getMinute());
+
+ mTimePicker.setMinute(59);
+ assertEquals(59, mTimePicker.getMinute());
+ }
+
public void testGetBaseline() {
mTimePicker = new TimePicker(mContext);
assertEquals(-1, mTimePicker.getBaseline());
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
index 833bf69..328b855 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
@@ -20,6 +20,7 @@
import vogar.Expectation;
import vogar.ExpectationStore;
+import vogar.Result;
import java.io.File;
import java.io.FileOutputStream;
@@ -228,7 +229,8 @@
}
public static boolean isKnownFailure(ExpectationStore expectationStore, String testName) {
- return expectationStore != null && expectationStore.get(testName) != Expectation.SUCCESS;
+ return expectationStore != null
+ && expectationStore.get(testName).getResult() != Result.SUCCESS;
}
// Returns the list of ABIs supported by this TestCase on this architecture.
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index 95b77f2..83c451e 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -447,6 +447,9 @@
expectations,
testClassName,
testName);
+ int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectations,
+ testClassName,
+ testName);
TestClass testClass;
if (testCases.containsKey(testClassName)) {
testClass = testCases.get(testClassName);
@@ -456,7 +459,7 @@
}
testClass.mCases.add(new TestMethod(testName, "", "", supportedAbis,
- knownFailure, false, false));
+ knownFailure, false, false, timeoutInMinutes));
}
private static boolean isJunit3Test(Class<?> klass) {
diff --git a/tools/utils/DescriptionGenerator.java b/tools/utils/DescriptionGenerator.java
index 09e1118..1e2542f 100644
--- a/tools/utils/DescriptionGenerator.java
+++ b/tools/utils/DescriptionGenerator.java
@@ -86,6 +86,7 @@
static final String ATTRIBUTE_NAME = "name";
static final String ATTRIBUTE_ABIS = "abis";
static final String ATTRIBUTE_HOST_CONTROLLER = "HostController";
+ static final String ATTRIBUTE_TIMEOUT = "timeout";
static final String XML_OUTPUT_PATH = "./description.xml";
@@ -438,6 +439,10 @@
if ((caze.mController != null) && (caze.mController.length() != 0)) {
setAttribute(caseNode, ATTRIBUTE_HOST_CONTROLLER, caze.mController);
}
+ if (caze.mTimeoutInMinutes != 0) {
+ setAttribute(caseNode, ATTRIBUTE_TIMEOUT,
+ Integer.toString(caze.mTimeoutInMinutes));
+ }
if (caze.mDescription != null && !caze.mDescription.equals("")) {
caseNode.appendChild(mDoc.createElement(TAG_DESCRIPTION))
@@ -573,9 +578,10 @@
VogarUtils.buildFullTestName(clazz.toString(), name));
Set<String> supportedAbis =
VogarUtils.extractSupportedAbis(architecture, expectation);
+ int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectation);
cases.add(new TestMethod(
name, method.commentText(), controller, supportedAbis,
- knownFailure, isBroken, isSuppressed));
+ knownFailure, isBroken, isSuppressed, timeoutInMinutes));
}
}
@@ -635,6 +641,7 @@
String mKnownFailure;
boolean mIsBroken;
boolean mIsSuppressed;
+ int mTimeoutInMinutes; // zero to use default timeout.
/**
* Construct an test case object.
@@ -644,7 +651,10 @@
* @param knownFailure The reason of known failure.
*/
TestMethod(String name, String description, String controller, Set<String> abis,
- String knownFailure, boolean isBroken, boolean isSuppressed) {
+ String knownFailure, boolean isBroken, boolean isSuppressed, int timeoutInMinutes) {
+ if (timeoutInMinutes < 0) {
+ throw new IllegalArgumentException("timeoutInMinutes < 0: " + timeoutInMinutes);
+ }
mName = name;
mDescription = description;
mController = controller;
@@ -652,6 +662,7 @@
mKnownFailure = knownFailure;
mIsBroken = isBroken;
mIsSuppressed = isSuppressed;
+ mTimeoutInMinutes = timeoutInMinutes;
}
}
}
diff --git a/tools/utils/VogarUtils.java b/tools/utils/VogarUtils.java
index 5e8b944..8e77e7c 100644
--- a/tools/utils/VogarUtils.java
+++ b/tools/utils/VogarUtils.java
@@ -19,6 +19,7 @@
import vogar.Expectation;
import vogar.ExpectationStore;
import vogar.ModeId;
+import vogar.Result;
import java.io.File;
import java.io.FilenameFilter;
@@ -52,14 +53,14 @@
}
String fullTestName = buildFullTestName(testClassName, testMethodName);
Expectation expectation = expectationStore.get(fullTestName);
- if (expectation == Expectation.SUCCESS) {
+ if (expectation.getResult() == Result.SUCCESS) {
return false;
}
String description = expectation.getDescription();
boolean foundAbi = AbiUtils.parseAbiList(description).size() > 0;
- return expectation != Expectation.SUCCESS && !foundAbi;
+ return expectation.getResult() != Result.SUCCESS && !foundAbi;
}
public static ExpectationStore provideExpectationStore(String dir) throws IOException {
@@ -119,7 +120,7 @@
String className,
String testName) {
- String fullTestName = VogarUtils.buildFullTestName(className, testName);
+ String fullTestName = buildFullTestName(className, testName);
Set<String> supportedAbiSet = AbiUtils.getAbisForArch(architecture);
for (ExpectationStore expectationStore : expectationStores) {
Expectation expectation = expectationStore.get(fullTestName);
@@ -128,4 +129,44 @@
return supportedAbiSet;
}
+
+ /**
+ * Returns the greatest timeout in minutes for the test in all
+ * expectation stores, or 0 if no timeout was found.
+ */
+ public static int timeoutInMinutes(ExpectationStore[] expectationStores,
+ final String testClassName,
+ final String testMethodName) {
+ int timeoutInMinutes = 0;
+ for (ExpectationStore expectationStore : expectationStores) {
+ timeoutInMinutes = Math.max(timeoutInMinutes,
+ timeoutInMinutes(expectationStore,
+ testClassName,
+ testMethodName));
+ }
+ return timeoutInMinutes;
+ }
+
+ /**
+ * Returns the timeout in minutes for the test in the expectation
+ * stores, or 0 if no timeout was found.
+ */
+ public static int timeoutInMinutes(ExpectationStore expectationStore,
+ final String testClassName,
+ final String testMethodName) {
+ if (expectationStore == null) {
+ return 0;
+ }
+ String fullTestName = buildFullTestName(testClassName, testMethodName);
+ return timeoutInMinutes(expectationStore.get(fullTestName));
+ }
+
+ /**
+ * Returns the timeout in minutes for the expectation. Currently a
+ * tag of large results in a 60 minute timeout, otherwise 0 is
+ * returned to indicate a default timeout should be used.
+ */
+ public static int timeoutInMinutes(Expectation expectation) {
+ return expectation.getTags().contains("large") ? 60 : 0;
+ }
}