Merge "DO NOT MERGE Add comment for false-negative test on emulator" into nougat-cts-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 3411f47..7815c33 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.verifier"
android:versionCode="5"
- android:versionName="7.0_r3">
+ android:versionName="7.0_r5">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="24"/>
@@ -1965,7 +1965,7 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_other" />
<meta-data android:name="test_excluded_features"
- android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
</activity>
<activity android:name=".tv.MockTvInputSetupActivity">
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
index abc8033..142ffff 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
@@ -1,18 +1,11 @@
<?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.
--->
+<!-- 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. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
@@ -24,54 +17,285 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/scrollView"
- >
+ android:id="@+id/scrollView">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- >
+ android:orientation="vertical">
- <TextView
+ <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:gravity="bottom"
- android:id="@+id/audio_frequency_unprocessed_defined"/>
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:id="@+id/audio_frequency_unprocessed_progress_bar" />
+ android:id="@+id/audio_frequency_unprocessed_defined" />
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:id="@+id/audio_frequency_unprocessed_layout_test1"
+ android:id="@+id/unprocessed_layout_test_tone"
>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_unprocessed_instructions2"
- android:id="@+id/audio_frequency_unprocessed_instructions2" />
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_unprocessed_test1_btn"
- android:id="@+id/audio_frequency_unprocessed_test1_btn" />
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:colorAccent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/audio_frequency_unprocessed_results_text"
- android:id="@+id/audio_frequency_unprocessed_results1_text" />
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:text="@string/unprocessed_test_tone_instructions"
+ android:id="@+id/unprocessed_test_tone_instructions" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="2">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/unprocessed_test_tone_btn"
+ android:id="@+id/unprocessed_test_tone_btn" />
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:id="@+id/unprocessed_test_tone_progress_bar" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:background="?android:colorAccent" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="1">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/unprocessed_play"
+ android:id="@+id/unprocessed_play_tone_btn" />
+ </LinearLayout>
+ </LinearLayout>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/unprocessed_test_tone_result"
+ android:id="@+id/unprocessed_test_tone_result" />
</LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/unprocessed_layout_test_noise">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:colorAccent" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:text="@string/unprocessed_test_noise_instructions"
+ android:id="@+id/unprocessed_test_noise_instructions" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="2">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/unprocessed_test_noise_btn"
+ android:id="@+id/unprocessed_test_noise_btn" />
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:id="@+id/unprocessed_test_noise_progress_bar" />
+ </LinearLayout>
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:background="?android:colorAccent" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="1">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/unprocessed_play"
+ android:id="@+id/unprocessed_play_noise_btn" />
+ </LinearLayout>
+ </LinearLayout>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/unprocessed_test_noise_result"
+ android:id="@+id/unprocessed_test_noise_result" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/unprocessed_layout_test_usb_background">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:colorAccent" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:text="@string/unprocessed_test_usb_background_instructions"
+ android:id="@+id/unprocessed_test_usb_background_instructions" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="2">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/unprocessed_test_usb_background_btn"
+ android:id="@+id/unprocessed_test_usb_background_btn" />
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:id="@+id/unprocessed_test_usb_background_progress_bar" />
+ </LinearLayout>
+ </LinearLayout>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/unprocessed_test_usb_background_result"
+ android:id="@+id/unprocessed_test_usb_background_result" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/unprocessed_layout_test_usb_noise">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:colorAccent" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:text="@string/unprocessed_test_usb_noise_instructions"
+ android:id="@+id/unprocessed_test_usb_noise_instructions" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="2">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/unprocessed_test_usb_noise_btn"
+ android:id="@+id/unprocessed_test_usb_noise_btn" />
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:id="@+id/unprocessed_test_usb_noise_progress_bar" />
+ </LinearLayout>
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:background="?android:colorAccent" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_weight="1">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/unprocessed_play"
+ android:id="@+id/unprocessed_play_usb_noise_btn" />
+ </LinearLayout>
+ </LinearLayout>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/unprocessed_test_usb_noise_result"
+ android:id="@+id/unprocessed_test_usb_noise_result" />
+ </LinearLayout>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:colorAccent" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/unprocessed_test_global_result"
+ android:id="@+id/unprocessed_test_global_result" />
<include layout="@layout/pass_fail_buttons" />
</LinearLayout>
diff --git a/apps/CtsVerifier/res/raw/onekhztone.mp3 b/apps/CtsVerifier/res/raw/onekhztone.mp3
new file mode 100644
index 0000000..c0e6660
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/onekhztone.mp3
Binary files differ
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
old mode 100644
new mode 100755
index c197a25..6cb4778
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -342,7 +342,7 @@
<string name="ble_low">Low</string>
<string name="ble_medium">Medium</string>
<string name="ble_high">High</string>
- <string name="ble_scanner_power_level_instruction">Count: Ultra low < low < medium < high\nRssi: Ultra low < low < medium < high\nDistance to see count freezing: Ultra low < low < medium < high\nA common error is ultra low, low and medium behave similarly, with similar rssi, freeze at similar distance.\n\n All power level receive a different mac address. After 15 mins, a green text "Get a new Mac address" will show up.</string>
+ <string name="ble_scanner_power_level_instruction">Count: Ultra low < low < medium < high\nRssi: Ultra low < low < medium < high\nDistance to see count freezing: Ultra low < low < medium < high\nA common error is ultra low, low and medium behave similarly, with similar rssi, freeze at similar distance.\n\n All power level receive a mac address.</string>
<string name="ble_scanner_scan_filter_name">BLE Hardware Scan Filter</string>
<string name="ble_scanner_scan_filter_info">Lock the screen of scanner, and connect to monsoon. It will not wake up when advertiser is advertising unscannable, and scanner is scanning with filter.</string>
<string name="ble_scanner_scan_filter_instruction">Scan filter is to scan data with service UUID = 0x6666 only. If you scan without scan filter, data with service UUID = 0x5555 and 0x6666 will show up on screen.\nFor monsoon test:\n\tClick scan with filter, lock the screen, connect to monsoon. It will not wake up when advertiser is advertising unscannable data packets, but will show a peak in power usage when advertiser is advertising scannable data.\nFor logcat test:\n\tClick scan with filter, logcat the scanner. No data will be received by GattService when advertiser is advertising unscannable data.</string>
@@ -2252,10 +2252,6 @@
This was expected.\n
Mark this test as passed.\n
</string>
- <string name="device_owner_vpn_connection_canceled">
- Cannot establish a VPN connection.\n
- Connection canceled by user.\n
- </string>
<string name="device_owner_vpn_test">Check VPN</string>
<string name="device_owner_vpn_info_default">Vpn test message</string>
@@ -2269,7 +2265,8 @@
- You cannot edit, add or remove any existing VPNs.\n
- Trying to perform any of the above actions will trigger a support message.\n\n
2. Press Check VPN to check programmatic Vpn test.\n
- - Check Vpn setup\n\n
+ - Check Vpn setup is not allowed\n
+ - If prompted to allow a VPN connection, press OK.\n\n
Use the Back button to return to this page.
</string>
<string name="device_owner_user_vpn_restriction_set">Set VPN restriction</string>
@@ -2469,7 +2466,7 @@
<string name="password_quality_complex">Complex</string>
<string name="set_permitted_accessibility_services">Set permitted accessibility services</string>
<string name="permitted_accessibility_services_set_step">
- Disallow \'Dummy Accessibility service\' from permitted accessibility services by turning on
+ Check that \'Dummy Accessibility service\' is not enabled in Settings and disallow \'Dummy Accessibility service\' from permitted accessibility services by turning on
the switch below.
</string>
<string name="set_permitted_accessibility_services_action">
@@ -2480,7 +2477,7 @@
</string>
<string name="set_permitted_input_methods">Set permitted input methods</string>
<string name="permitted_input_methods_set_step">
- Disallow \'Dummy Input method\' from permitted input methods by turning on the switch below.
+ Check that \'Dummy Input method\' is not enabled in Settings and disallow \'Dummy Input method\' from permitted input methods by turning on the switch below.
</string>
<string name="set_permitted_input_methods_action">
Enabling \'Dummy Input Method\' in the list of accessibility services
@@ -2741,7 +2738,7 @@
<string name="audio_general_headset_yes">Yes</string>
<string name="audio_general_deficiency_found">WARNING: Some results show potential deficiencies on the system.
Please consider addressing them for a future release.</string>
- <string name="audio_general_test_passed">Test Successful</string>
+ <string name="audio_general_test_passed">Test Result: Successful</string>
<string name="audio_general_test_failed">Test Result: Not Optimal</string>
<string name="audio_general_default_false_string">false</string>
<string name="audio_general_default_true_string">true</string>
@@ -2835,17 +2832,39 @@
<!-- Audio Frequency Unprocessed Test -->
<string name="audio_frequency_unprocessed_test">Audio Frequency Unprocessed Test</string>
<string name="audio_frequency_unprocessed_info">
- This test requires an external broadband noise source (or click/impulse).
- Please be prepared to activate the noise source when asked to.
- The system will measure frequency response of the built in microphone using the UNPROCESSED
- audio source.
+ This test requires an external USB reference microphone, external speakers and a Sound Pressure Level meter.
+ You can play the test signals from the device under test or from a secondary device.
+ Follow the instructions on the screen to measure the frequency response for the built in microphone
+ using UNPROCESSED audio source.
+ If the Audio Frequency Unprocessed feature is defined in this system, success in all tests is mandatory to pass.
+ If the feature is not defined, measurements are still needed, but success in all of them is not mandatory to pass.
</string>
- <string name="audio_frequency_unprocessed_defined">Audio Frequency Unprocessed feature is defined. Test is mandatory</string>
- <string name="audio_frequency_unprocessed_not_defined">Audio Frequency Unprocessed feature is NOT defined. Test is optional</string>
- <string name="audio_frequency_unprocessed_instructions2">
- Once you press the [TEST] button, you have 5 seconds to play a broadband sound (click or white noise).
- </string>
- <string name="audio_frequency_unprocessed_test1_btn">Test 1</string>
- <string name="audio_frequency_unprocessed_results_text">Results...</string>
+ <string name="audio_frequency_unprocessed_defined">Audio Frequency Unprocessed feature is defined. Success in all tests is mandatory to pass</string>
+ <string name="audio_frequency_unprocessed_not_defined">Audio Frequency Unprocessed feature is NOT defined. Success in all test is NOT mandatory to pass</string>
+
+ <string name="unprocessed_play">Play</string>
+ <string name="unprocessed_stop">Stop</string>
+
+ <string name="unprocessed_test_tone_instructions">TEST TONE: Press [PLAY] to play tone at 1 Khz. Measure sound SPL to be 94 dB
+ right next to microphone under test. Press [TEST]</string>
+ <string name="unprocessed_test_tone_btn">Test</string>
+ <string name="unprocessed_test_tone_result">Results...</string>
+
+ <string name="unprocessed_test_noise_instructions">TEST NOISE: Position speakers 40 cms from device under test.
+ Press [PLAY] to play broadband white noise. Press [TEST]</string>
+ <string name="unprocessed_test_noise_btn">Test</string>
+ <string name="unprocessed_test_noise_result">Results...</string>
+
+ <string name="unprocessed_test_usb_background_instructions">TEST USB BACKGROUND: Connect USB microphone and position it right next to microphone under test.
+ No source of noise should be active during this test. Press [TEST]</string>
+ <string name="unprocessed_test_usb_background_btn">Test</string>
+ <string name="unprocessed_test_usb_background_result">Results...</string>
+
+ <string name="unprocessed_test_usb_noise_instructions">TEST USB NOISE: Connect USB microphone and position it right next to microphone under test.
+ Position speakers 40 cms from device under test. Press [PLAY] to play broadband white noise. Press [TEST]</string>
+ <string name="unprocessed_test_usb_noise_btn">Test</string>
+ <string name="unprocessed_test_usb_noise_result">Results...</string>
+
+ <string name="unprocessed_test_global_result">Global Results...</string>
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
index 56275c5..009dd58 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
@@ -22,11 +22,11 @@
import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
+
import android.content.Context;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
-
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
@@ -34,17 +34,13 @@
import android.media.AudioTrack;
import android.media.AudioRecord;
import android.media.MediaRecorder;
-
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
-
import android.util.Log;
-
import android.view.View;
import android.view.View.OnClickListener;
-
import android.widget.Button;
import android.widget.TextView;
import android.widget.SeekBar;
@@ -59,25 +55,63 @@
private static final String TAG = "AudioFrequencyUnprocessedActivity";
private static final int TEST_STARTED = 900;
- private static final int TEST_ENDED = 901;
- private static final int TEST_MESSAGE = 902;
- private static final int TEST1_MESSAGE = 903;
- private static final int TEST1_ENDED = 904;
- private static final double MIN_ENERGY_BAND_1 = -50.0; //dB Full Scale
- private static final double MAX_ENERGY_BAND_1_BASE = -60.0; //dB Full Scale
- private static final double MIN_FRACTION_POINTS_IN_BAND = 0.3;
+ private static final int TEST_MESSAGE = 903;
+ private static final int TEST_ENDED = 904;
+ private static final int TEST_ENDED_ERROR = 905;
+ private static final double MIN_FRACTION_POINTS_IN_BAND = 0.5;
+
+ private static final double TONE_RMS_EXPECTED = -36.0;
+ private static final double TONE_RMS_MAX_ERROR = 3.0;
+
private static final double MAX_VAL = Math.pow(2, 15);
private static final double CLIP_LEVEL = (MAX_VAL-10) / MAX_VAL;
+ private static final int SOURCE_TONE = 0;
+ private static final int SOURCE_NOISE = 1;
+
+ private static final int TEST_NONE = -1;
+ private static final int TEST_TONE = 0;
+ private static final int TEST_NOISE = 1;
+ private static final int TEST_USB_BACKGROUND = 2;
+ private static final int TEST_USB_NOISE = 3;
+ private static final int TEST_COUNT = 4;
+ private int mCurrentTest = TEST_NONE;
+ private boolean mTestsDone[] = new boolean[TEST_COUNT];
+
+ private static final int TEST_DURATION_DEFAULT = 2000;
+ private static final int TEST_DURATION_TONE = TEST_DURATION_DEFAULT;
+ private static final int TEST_DURATION_NOISE = TEST_DURATION_DEFAULT;
+ private static final int TEST_DURATION_USB_BACKGROUND = TEST_DURATION_DEFAULT;
+ private static final int TEST_DURATION_USB_NOISE = TEST_DURATION_DEFAULT;
+
final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
Context mContext;
- Button mTest1Button; //execute test 1
- Button mTest2Button; //user to start test
- String mUsbDevicesInfo; //usb device info for report
- LinearLayout mLayoutTest1;
- TextView mTest1Result;
- ProgressBar mProgressBar;
+ LinearLayout mLayoutTestTone;
+ Button mButtonTestTone;
+ ProgressBar mProgressTone;
+ TextView mResultTestTone;
+ Button mButtonPlayTone;
+
+ LinearLayout mLayoutTestNoise;
+ Button mButtonTestNoise;
+ ProgressBar mProgressNoise;
+ TextView mResultTestNoise;
+ Button mButtonPlayNoise;
+
+ LinearLayout mLayoutTestUsbBackground;
+ Button mButtonTestUsbBackground;
+ ProgressBar mProgressUsbBackground;
+ TextView mResultTestUsbBackground;
+
+ LinearLayout mLayoutTestUsbNoise;
+ Button mButtonTestUsbNoise;
+ ProgressBar mProgressUsbNoise;
+ TextView mResultTestUsbNoise;
+ Button mButtonPlayUsbNoise;
+
+ TextView mGlobalResultText;
+ TextView mTextViewUnprocessedStatus;
private boolean mIsRecording = false;
private final Object mRecordingLock = new Object();
@@ -95,6 +129,8 @@
PipeShort mPipe = new PipeShort(65536);
+ SoundPlayerObject mSPlayer;
+
private boolean mSupportsUnprocessed = false;
private DspBufferComplex mC;
@@ -102,25 +138,27 @@
private DspWindow mWindow;
private DspFftServer mFftServer;
- private VectorAverage mFreqAverageMain = new VectorAverage();
- private VectorAverage mFreqAverageBuiltIn = new VectorAverage();
+ private VectorAverage mFreqAverageTone = new VectorAverage();
+ private VectorAverage mFreqAverageNoise = new VectorAverage();
+ private VectorAverage mFreqAverageUsbBackground = new VectorAverage();
+ private VectorAverage mFreqAverageUsbNoise = new VectorAverage();
- int mBands = 4;
- AudioBandSpecs[] bandSpecsArray = new AudioBandSpecs[mBands];
- private TextView mTextViewUnprocessedStatus;
+ //RMS for tone:
+ private double mRMS;
+ private double mRMSMax;
- private class OnBtnClickListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.audio_frequency_unprocessed_test1_btn:
- setMaxLevel();
- testMaxLevel();
- startTest1();
- break;
- }
- }
- }
+ private double mRMSTone;
+ private double mRMSMaxTone;
+
+ int mBands = 3;
+ int mBandsTone = 3;
+ int mBandsBack = 3;
+ AudioBandSpecs[] mBandSpecsMic = new AudioBandSpecs[mBands];
+ AudioBandSpecs[] mBandSpecsTone = new AudioBandSpecs[mBandsTone];
+ AudioBandSpecs[] mBandSpecsBack = new AudioBandSpecs[mBandsBack];
+ private Results mResultsMic;
+ private Results mResultsTone;
+ private Results mResultsBack;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -139,13 +177,51 @@
getResources().getText(R.string.audio_frequency_unprocessed_not_defined));
}
- mTest1Button = (Button)findViewById(R.id.audio_frequency_unprocessed_test1_btn);
- mTest1Button.setOnClickListener(mBtnClickListener);
- mTest1Result = (TextView)findViewById(R.id.audio_frequency_unprocessed_results1_text);
- mLayoutTest1 = (LinearLayout) findViewById(R.id.audio_frequency_unprocessed_layout_test1);
- mProgressBar = (ProgressBar)findViewById(R.id.audio_frequency_unprocessed_progress_bar);
- showWait(false);
- enableLayout(mLayoutTest1, true);
+ mSPlayer = new SoundPlayerObject();
+ playerSetSource(SOURCE_TONE);
+
+ // Test tone
+ mLayoutTestTone = (LinearLayout) findViewById(R.id.unprocessed_layout_test_tone);
+ mButtonTestTone = (Button) findViewById(R.id.unprocessed_test_tone_btn);
+ mButtonTestTone.setOnClickListener(mBtnClickListener);
+ mProgressTone = (ProgressBar) findViewById(R.id.unprocessed_test_tone_progress_bar);
+ mResultTestTone = (TextView) findViewById(R.id.unprocessed_test_tone_result);
+ mButtonPlayTone = (Button) findViewById(R.id.unprocessed_play_tone_btn);
+ mButtonPlayTone.setOnClickListener(mBtnClickListener);
+ showWait(mProgressTone, false);
+
+ //Test Noise
+ mLayoutTestNoise = (LinearLayout) findViewById(R.id.unprocessed_layout_test_noise);
+ mButtonTestNoise = (Button) findViewById(R.id.unprocessed_test_noise_btn);
+ mButtonTestNoise.setOnClickListener(mBtnClickListener);
+ mProgressNoise = (ProgressBar) findViewById(R.id.unprocessed_test_noise_progress_bar);
+ mResultTestNoise = (TextView) findViewById(R.id.unprocessed_test_noise_result);
+ mButtonPlayNoise = (Button) findViewById(R.id.unprocessed_play_noise_btn);
+ mButtonPlayNoise.setOnClickListener(mBtnClickListener);
+ showWait(mProgressNoise, false);
+
+ //USB Background
+ mLayoutTestUsbBackground = (LinearLayout)
+ findViewById(R.id.unprocessed_layout_test_usb_background);
+ mButtonTestUsbBackground = (Button) findViewById(R.id.unprocessed_test_usb_background_btn);
+ mButtonTestUsbBackground.setOnClickListener(mBtnClickListener);
+ mProgressUsbBackground = (ProgressBar)
+ findViewById(R.id.unprocessed_test_usb_background_progress_bar);
+ mResultTestUsbBackground = (TextView)
+ findViewById(R.id.unprocessed_test_usb_background_result);
+ showWait(mProgressUsbBackground, false);
+
+ mLayoutTestUsbNoise = (LinearLayout) findViewById(R.id.unprocessed_layout_test_usb_noise);
+ mButtonTestUsbNoise = (Button) findViewById(R.id.unprocessed_test_usb_noise_btn);
+ mButtonTestUsbNoise.setOnClickListener(mBtnClickListener);
+ mProgressUsbNoise = (ProgressBar)findViewById(R.id.unprocessed_test_usb_noise_progress_bar);
+ mResultTestUsbNoise = (TextView) findViewById(R.id.unprocessed_test_usb_noise_result);
+ mButtonPlayUsbNoise = (Button) findViewById(R.id.unprocessed_play_usb_noise_btn);
+ mButtonPlayUsbNoise.setOnClickListener(mBtnClickListener);
+ showWait(mProgressUsbNoise, false);
+
+ setButtonPlayStatus(-1);
+ mGlobalResultText = (TextView) findViewById(R.id.unprocessed_test_global_result);
//Init FFT stuff
mAudioShortArray2 = new short[mBlockSizeSamples*2];
@@ -162,29 +238,141 @@
setInfoResources(R.string.audio_frequency_unprocessed_test,
R.string.audio_frequency_unprocessed_info, -1);
- //Init bands for BuiltIn/Reference test
- bandSpecsArray[0] = new AudioBandSpecs(
- 2, 500, /* frequency start,stop */
- 30.0, -50, /* start top,bottom value */
- 30.0, -4.0 /* stop top,bottom value */);
+ //Init bands for Mic test
+ mBandSpecsMic[0] = new AudioBandSpecs(
+ 5, 100, /* frequency start,stop */
+ 20.0, -20.0, /* start top,bottom value */
+ 20.0, -20.0 /* stop top,bottom value */);
- bandSpecsArray[1] = new AudioBandSpecs(
- 500,4000, /* frequency start,stop */
- 4.0, -4.0, /* start top,bottom value */
- 4.0, -4.0 /* stop top,bottom value */);
+ mBandSpecsMic[1] = new AudioBandSpecs(
+ 100, 7000, /* frequency start,stop */
+ 10.0, -10.0, /* start top,bottom value */
+ 10.0, -10.0 /* stop top,bottom value */);
- bandSpecsArray[2] = new AudioBandSpecs(
- 4000, 12000, /* frequency start,stop */
- 4.0, -4.0, /* start top,bottom value */
- 5.0, -5.0 /* stop top,bottom value */);
+ mBandSpecsMic[2] = new AudioBandSpecs(
+ 7000, 20000, /* frequency start,stop */
+ 30.0, -30.0, /* start top,bottom value */
+ 30.0, -30.0 /* stop top,bottom value */);
- bandSpecsArray[3] = new AudioBandSpecs(
- 12000, 20000, /* frequency start,stop */
- 5.0, -5.0, /* start top,bottom value */
- 5.0, -30.0 /* stop top,bottom value */);
+ //Init bands for Tone test
+ mBandSpecsTone[0] = new AudioBandSpecs(
+ 5, 900, /* frequency start,stop */
+ -10.0, -100.0, /* start top,bottom value */
+ -10.0, -100.0 /* stop top,bottom value */);
+
+ mBandSpecsTone[1] = new AudioBandSpecs(
+ 900, 1100, /* frequency start,stop */
+ 10.0, -50.0, /* start top,bottom value */
+ 10.0, -10.0 /* stop top,bottom value */);
+
+ mBandSpecsTone[2] = new AudioBandSpecs(
+ 1100, 20000, /* frequency start,stop */
+ -30.0, -120.0, /* start top,bottom value */
+ -30.0, -120.0 /* stop top,bottom value */);
+
+ //Init bands for Background test
+ mBandSpecsBack[0] = new AudioBandSpecs(
+ 5, 100, /* frequency start,stop */
+ 10.0, -120.0, /* start top,bottom value */
+ -10.0, -120.0 /* stop top,bottom value */);
+
+ mBandSpecsBack[1] = new AudioBandSpecs(
+ 100, 7000, /* frequency start,stop */
+ -10.0, -120.0, /* start top,bottom value */
+ -50.0, -120.0 /* stop top,bottom value */);
+
+ mBandSpecsBack[2] = new AudioBandSpecs(
+ 7000, 20000, /* frequency start,stop */
+ -50.0, -120.0, /* start top,bottom value */
+ -50.0, -120.0 /* stop top,bottom value */);
mSupportsUnprocessed = supportsUnprocessed();
Log.v(TAG, "Supports unprocessed: " + mSupportsUnprocessed);
+
+ mResultsMic = new Results("mic_response", mBands);
+ mResultsTone = new Results("tone_response", mBandsTone);
+ mResultsBack = new Results("background_response", mBandsBack);
+ }
+
+ private void playerToggleButton(int buttonId, int sourceId) {
+ if (playerIsPlaying()) {
+ playerStopAll();
+ } else {
+ playerSetSource(sourceId);
+ playerTransport(true);
+ setButtonPlayStatus(buttonId);
+ }
+ }
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ switch (id) {
+ case R.id.unprocessed_test_tone_btn:
+ startTest(TEST_TONE);
+ break;
+ case R.id.unprocessed_play_tone_btn:
+ playerToggleButton(id, SOURCE_TONE);
+ break;
+ case R.id.unprocessed_test_noise_btn:
+ startTest(TEST_NOISE);
+ break;
+ case R.id.unprocessed_play_noise_btn:
+ playerToggleButton(id, SOURCE_NOISE);
+ break;
+ case R.id.unprocessed_test_usb_background_btn:
+ startTest(TEST_USB_BACKGROUND);
+ break;
+ case R.id.unprocessed_test_usb_noise_btn:
+ startTest(TEST_USB_NOISE);
+ break;
+ case R.id.unprocessed_play_usb_noise_btn:
+ playerToggleButton(id, SOURCE_NOISE);
+ break;
+ }
+ }
+ }
+
+ private void setButtonPlayStatus(int playResId) {
+ String play = getResources().getText(R.string.unprocessed_play).toString();
+ String stop = getResources().getText(R.string.unprocessed_stop).toString();
+
+ mButtonPlayTone.setText(playResId == R.id.unprocessed_play_tone_btn ? stop : play);
+ mButtonPlayNoise.setText(playResId == R.id.unprocessed_play_noise_btn ? stop : play);
+ mButtonPlayUsbNoise.setText(playResId ==
+ R.id.unprocessed_play_usb_noise_btn ? stop : play);
+ }
+
+ private void playerSetSource(int sourceIndex) {
+ switch (sourceIndex) {
+ case SOURCE_TONE:
+ mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.onekhztone);
+ break;
+ default:
+ case SOURCE_NOISE:
+ mSPlayer.setSoundWithResId(getApplicationContext(),
+ R.raw.stereo_mono_white_noise_48);
+ break;
+ }
+ }
+
+ private void playerTransport(boolean play) {
+ if (!mSPlayer.isAlive()) {
+ mSPlayer.start();
+ }
+ mSPlayer.play(play);
+ }
+
+ private boolean playerIsPlaying() {
+ return mSPlayer.isPlaying();
+ }
+
+ private void playerStopAll() {
+ if (mSPlayer.isAlive() && mSPlayer.isPlaying()) {
+ mSPlayer.play(false);
+ setButtonPlayStatus(-1);
+ }
}
/**
@@ -197,14 +385,66 @@
}
}
- /**
- * show active progress bar
- */
- private void showWait(boolean show) {
+ private void showWait(ProgressBar pb, boolean show) {
if (show) {
- mProgressBar.setVisibility(View.VISIBLE);
+ pb.setVisibility(View.VISIBLE);
} else {
- mProgressBar.setVisibility(View.INVISIBLE);
+ pb.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private String getTestString(int testId) {
+ String name = "undefined";
+ switch(testId) {
+ case TEST_TONE:
+ name = "BuiltIn_tone";
+ break;
+ case TEST_NOISE:
+ name = "BuiltIn_noise";
+ break;
+ case TEST_USB_BACKGROUND:
+ name = "USB_background";
+ break;
+ case TEST_USB_NOISE:
+ name = "USB_noise";
+ break;
+ }
+ return name;
+ }
+
+ private void showWait(int testId, boolean show) {
+ switch(testId) {
+ case TEST_TONE:
+ showWait(mProgressTone, show);
+ break;
+ case TEST_NOISE:
+ showWait(mProgressNoise, show);
+ break;
+ case TEST_USB_BACKGROUND:
+ showWait(mProgressUsbBackground, show);
+ break;
+ case TEST_USB_NOISE:
+ showWait(mProgressUsbNoise, show);
+ break;
+ }
+ }
+
+ private void showMessage(int testId, String msg) {
+ if (msg != null && msg.length() > 0) {
+ switch(testId) {
+ case TEST_TONE:
+ mResultTestTone.setText(msg);
+ break;
+ case TEST_NOISE:
+ mResultTestNoise.setText(msg);
+ break;
+ case TEST_USB_BACKGROUND:
+ mResultTestUsbBackground.setText(msg);
+ break;
+ case TEST_USB_NOISE:
+ mResultTestUsbNoise.setText(msg);
+ break;
+ }
}
}
@@ -224,47 +464,320 @@
return unprocessedSupport;
}
- /**
- * Start the loopback audio test
- */
- private void startTest1() {
+ private void computeAllResults() {
+ StringBuilder sb = new StringBuilder();
+
+ boolean allDone = true;
+
+ for (int i=0; i<TEST_COUNT; i++) {
+ allDone = allDone & mTestsDone[i];
+ sb.append(String.format("%s : %s\n", getTestString(i),
+ mTestsDone[i] ? "DONE" :" NOT DONE"));
+ }
+
+ if (allDone) {
+ sb.append(computeResults());
+ } else {
+ sb.append("Please execute all tests for results\n");
+ }
+ mGlobalResultText.setText(sb.toString());
+ }
+
+ private void processSpectrum(Results results, AudioBandSpecs[] bandsSpecs, int anchorBand) {
+ int points = results.mValuesLog.length;
+ int bandCount = bandsSpecs.length;
+ int currentBand = 0;
+ for (int i = 0; i < points; i++) {
+ double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
+ if (freq > bandsSpecs[currentBand].mFreqStop) {
+ currentBand++;
+ if (currentBand >= bandCount)
+ break;
+ }
+
+ if (freq >= bandsSpecs[currentBand].mFreqStart) {
+ results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
+ results.mPointsPerBand[currentBand]++;
+ }
+ }
+
+ for (int b = 0; b < bandCount; b++) {
+ if (results.mPointsPerBand[b] > 0) {
+ results.mAverageEnergyPerBand[b] =
+ results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
+ }
+ }
+
+ //set offset relative to band anchor band level
+ for (int b = 0; b < bandCount; b++) {
+ if (anchorBand > -1 && anchorBand < bandCount) {
+ bandsSpecs[b].setOffset(results.mAverageEnergyPerBand[anchorBand]);
+ } else {
+ bandsSpecs[b].setOffset(0);
+ }
+ }
+
+ //test points in band.
+ currentBand = 0;
+ for (int i = 0; i < points; i++) {
+ double freq = (double) mSamplingRate * i / (double) mBlockSizeSamples;
+ if (freq > bandsSpecs[currentBand].mFreqStop) {
+ currentBand++;
+ if (currentBand >= bandCount)
+ break;
+ }
+
+ if (freq >= bandsSpecs[currentBand].mFreqStart) {
+ double value = results.mValuesLog[i];
+ if (bandsSpecs[currentBand].isInBounds(freq, value)) {
+ results.mInBoundPointsPerBand[currentBand]++;
+ }
+ }
+ }
+ }
+
+ private String computeResults() {
+ StringBuilder sb = new StringBuilder();
+
+ int points = mFreqAverageNoise.getSize();
+ //mFreqAverageNoise size is determined by the latest data writen to it.
+ //Currently, this data is always mBlockSizeSamples/2.
+ if (points < 1) {
+ return "error: not enough points";
+ }
+
+ double[] tone = new double[points];
+ double[] noise = new double[points];
+ double[] reference = new double[points];
+ double[] background = new double[points];
+
+ mFreqAverageTone.getData(tone, false);
+ mFreqAverageNoise.getData(noise, false);
+ mFreqAverageUsbNoise.getData(reference, false);
+ mFreqAverageUsbBackground.getData(background, false);
+
+ //Convert to dB
+ double[] toneDb = new double[points];
+ double[] noiseDb = new double[points];
+ double[] referenceDb = new double[points];
+ double[] backgroundDb = new double[points];
+
+ double[] compensatedNoiseDb = new double[points];
+
+ for (int i = 0; i < points; i++) {
+ toneDb[i] = 20 * Math.log10(tone[i]);
+ noiseDb[i] = 20 * Math.log10(noise[i]);
+ referenceDb[i] = 20 * Math.log10(reference[i]);
+ backgroundDb[i] = 20 * Math.log10(background[i]);
+
+ compensatedNoiseDb[i] = noiseDb[i] - referenceDb[i];
+ }
+
+ mResultsMic.reset();
+ mResultsTone.reset();
+ mResultsBack.reset();
+
+ mResultsMic.mValuesLog = compensatedNoiseDb;
+ mResultsTone.mValuesLog = toneDb;
+ mResultsBack.mValuesLog = backgroundDb;
+
+ processSpectrum(mResultsMic, mBandSpecsMic, 1);
+ processSpectrum(mResultsTone, mBandSpecsTone, 1);
+ processSpectrum(mResultsBack, mBandSpecsBack, -1); //no reference for offset
+
+ //Tone test
+ boolean toneTestSuccess = true;
+ {
+ //rms level should be -36 dbfs +/- 3 db?
+ double rmsMaxDb = 20 * Math.log10(mRMSMaxTone);
+ sb.append(String.format("RMS level of tone: %.2f dBFS\n", rmsMaxDb));
+ sb.append(String.format("Target RMS level: %.2f dBFS +/- %.2f dB\n",
+ TONE_RMS_EXPECTED,
+ TONE_RMS_MAX_ERROR));
+ if (Math.abs(rmsMaxDb - TONE_RMS_EXPECTED) > TONE_RMS_MAX_ERROR) {
+ toneTestSuccess = false;
+ sb.append("RMS level test FAILED\n");
+ } else {
+ sb.append(" RMS level test SUCCESSFUL\n");
+ }
+ //check the spectrum is really a tone around 1 khz
+ }
+
+ sb.append("\n");
+ sb.append(mResultsTone.toString());
+ if (mResultsTone.testAll()) {
+ sb.append(" 1 Khz Tone Frequency Response Test SUCCESSFUL\n");
+ } else {
+ sb.append(" 1 Khz Tone Frequency Response Test FAILED\n");
+ }
+ sb.append("\n");
+
+ sb.append("\n");
+ sb.append(mResultsBack.toString());
+ if (mResultsBack.testAll()) {
+ sb.append(" Background environment Test SUCCESSFUL\n");
+ } else {
+ sb.append(" Background environment Test FAILED\n");
+ }
+
+ sb.append("\n");
+ sb.append(mResultsMic.toString());
+ if (mResultsMic.testAll()) {
+ sb.append(" Frequency Response Test SUCCESSFUL\n");
+ } else {
+ sb.append(" Frequency Response Test FAILED\n");
+ }
+ sb.append("\n");
+
+ recordTestResults(mResultsTone);
+ recordTestResults(mResultsMic);
+
+ boolean allTestsPassed = false;
+ if (mResultsMic.testAll() && mResultsTone.testAll() && toneTestSuccess &&
+ mResultsBack.testAll()) {
+ allTestsPassed = true;
+ String strSuccess = getResources().getString(R.string.audio_general_test_passed);
+ sb.append(strSuccess);
+ } else {
+ String strFailed = getResources().getString(R.string.audio_general_test_failed);
+ sb.append(strFailed);
+ }
+
+ sb.append("\n");
+ if (mSupportsUnprocessed) { //test is mandatory
+ sb.append(getResources().getText(
+ R.string.audio_frequency_unprocessed_defined).toString());
+ if (allTestsPassed) {
+ getPassButton().setEnabled(true);
+ } else {
+ getPassButton().setEnabled(false);
+ }
+ } else {
+ //test optional
+ sb.append(getResources().getText(
+ R.string.audio_frequency_unprocessed_not_defined).toString());
+ getPassButton().setEnabled(true);
+ }
+ return sb.toString();
+ }
+
+ Thread mTestThread;
+ private void startTest(int testId) {
if (mTestThread != null && !mTestThread.isAlive()) {
mTestThread = null; //kill it.
}
if (mTestThread == null) {
+ mRMS = 0;
+ mRMSMax = 0;
Log.v(TAG,"Executing test Thread");
- mTestThread = new Thread(mTest1Runnable);
+ switch(testId) {
+ case TEST_TONE:
+ mTestThread = new Thread(new TestRunnable(TEST_TONE) {
+ public void run() {
+ super.run();
+ if (!mUsbMicConnected) {
+ sendMessage(mTestId, TEST_MESSAGE,
+ "Testing Built in Microphone: Tone");
+ mRMSTone = 0;
+ mRMSMaxTone = 0;
+ mFreqAverageTone.reset();
+ mFreqAverageTone.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
+ record(TEST_DURATION_TONE);
+ sendMessage(mTestId, TEST_ENDED, "Testing Completed");
+ mTestsDone[mTestId] = true;
+ } else {
+ sendMessage(mTestId, TEST_ENDED_ERROR,
+ "Please Unplug USB Microphone");
+ mTestsDone[mTestId] = false;
+ }
+ }
+ });
+ break;
+ case TEST_NOISE:
+ mTestThread = new Thread(new TestRunnable(TEST_NOISE) {
+ public void run() {
+ super.run();
+ if (!mUsbMicConnected) {
+ sendMessage(mTestId, TEST_MESSAGE,
+ "Testing Built in Microphone: Noise");
+ mFreqAverageNoise.reset();
+ mFreqAverageNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
+ record(TEST_DURATION_NOISE);
+ sendMessage(mTestId, TEST_ENDED, "Testing Completed");
+ mTestsDone[mTestId] = true;
+ } else {
+ sendMessage(mTestId, TEST_ENDED_ERROR,
+ "Please Unplug USB Microphone");
+ mTestsDone[mTestId] = false;
+ }
+ }
+ });
+ break;
+ case TEST_USB_BACKGROUND:
+ playerStopAll();
+ mTestThread = new Thread(new TestRunnable(TEST_USB_BACKGROUND) {
+ public void run() {
+ super.run();
+ if (mUsbMicConnected) {
+ sendMessage(mTestId, TEST_MESSAGE,
+ "Testing USB Microphone: background");
+ mFreqAverageUsbBackground.reset();
+ mFreqAverageUsbBackground.setCaptureType(
+ VectorAverage.CAPTURE_TYPE_AVERAGE);
+ record(TEST_DURATION_USB_BACKGROUND);
+ sendMessage(mTestId, TEST_ENDED, "Testing Completed");
+ mTestsDone[mTestId] = true;
+ } else {
+ sendMessage(mTestId, TEST_ENDED_ERROR,
+ "USB Microphone not detected.");
+ mTestsDone[mTestId] = false;
+ }
+ }
+ });
+ break;
+ case TEST_USB_NOISE:
+ mTestThread = new Thread(new TestRunnable(TEST_USB_NOISE) {
+ public void run() {
+ super.run();
+ if (mUsbMicConnected) {
+ sendMessage(mTestId, TEST_MESSAGE, "Testing USB Microphone: Noise");
+ mFreqAverageUsbNoise.reset();
+ mFreqAverageUsbNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
+ record(TEST_DURATION_USB_NOISE);
+ sendMessage(mTestId, TEST_ENDED, "Testing Completed");
+ mTestsDone[mTestId] = true;
+ } else {
+ sendMessage(mTestId, TEST_ENDED_ERROR,
+ "USB Microphone not detected.");
+ mTestsDone[mTestId] = false;
+ }
+ }
+ });
+ break;
+ }
mTestThread.start();
} else {
Log.v(TAG,"test Thread already running.");
}
}
-
- Thread mTestThread;
- Runnable mTest1Runnable = new Runnable() {
- public void run() {
- Message msg = Message.obtain();
- msg.what = TEST_STARTED;
- mMessageHandler.sendMessage(msg);
-
- sendMessage("Testing Built in Microphone");
- mFreqAverageBuiltIn.reset();
- mFreqAverageBuiltIn.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
- play();
-
- sendMessage("Testing Completed");
-
- Message msg2 = Message.obtain();
- msg2.what = TEST1_ENDED;
- mMessageHandler.sendMessage(msg2);
+ public class TestRunnable implements Runnable {
+ public int mTestId;
+ public boolean mUsbMicConnected;
+ TestRunnable(int testId) {
+ Log.v(TAG,"New TestRunnable");
+ mTestId = testId;
}
-
- private void play() {
+ public void run() {
+ mCurrentTest = mTestId;
+ sendMessage(mTestId, TEST_STARTED,"");
+ mUsbMicConnected =
+ UsbMicrophoneTester.getIsMicrophoneConnected(getApplicationContext());
+ };
+ public void record(int durationMs) {
startRecording();
-
try {
- Thread.sleep(5000);
+ Thread.sleep(durationMs);
} catch (InterruptedException e) {
e.printStackTrace();
//restore interrupted status
@@ -272,41 +785,40 @@
}
stopRecording();
}
-
- private void sendMessage(String str) {
+ public void sendMessage(int testId, int msgType, String str) {
Message msg = Message.obtain();
- msg.what = TEST1_MESSAGE;
+ msg.what = msgType;
msg.obj = str;
+ msg.arg1 = testId;
mMessageHandler.sendMessage(msg);
}
- };
+ }
private Handler mMessageHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
+ int testId = msg.arg1; //testId
+ String str = (String) msg.obj;
switch (msg.what) {
case TEST_STARTED:
- showWait(true);
- getPassButton().setEnabled(false);
+ showWait(testId, true);
+// getPassButton().setEnabled(false);
+ break;
+ case TEST_MESSAGE:
+ showMessage(testId, str);
break;
case TEST_ENDED:
- showWait(false);
-// computeTest2Results();
+ showWait(testId, false);
+ playerStopAll();
+ showMessage(testId, str);
+ appendResultsToScreen(testId, "test finished");
+ computeAllResults();
break;
- case TEST1_MESSAGE: {
- String str = (String)msg.obj;
- if (str != null) {
- mTest1Result.setText(str);
- }
- }
- break;
- case TEST1_ENDED:
- showWait(false);
- computeTest1Results();
- break;
- case TEST_MESSAGE: {
- }
- break;
+ case TEST_ENDED_ERROR:
+ showWait(testId, false);
+ playerStopAll();
+ showMessage(testId, str);
+ computeAllResults();
default:
Log.e(TAG, String.format("Unknown message: %d", msg.what));
}
@@ -314,21 +826,32 @@
};
private class Results {
+ private int mBandCount;
private String mLabel;
public double[] mValuesLog;
- int[] mPointsPerBand = new int[mBands];
- double[] mAverageEnergyPerBand = new double[mBands];
- int[] mInBoundPointsPerBand = new int[mBands];
- public Results(String label) {
+ int[] mPointsPerBand; // = new int[mBands];
+ double[] mAverageEnergyPerBand;// = new double[mBands];
+ int[] mInBoundPointsPerBand;// = new int[mBands];
+ public Results(String label, int bandCount) {
mLabel = label;
+ mBandCount = bandCount;
+ mPointsPerBand = new int[mBandCount];
+ mAverageEnergyPerBand = new double[mBandCount];
+ mInBoundPointsPerBand = new int[mBandCount];
+ }
+ public void reset() {
+ for (int i = 0; i < mBandCount; i++) {
+ mPointsPerBand[i] = 0;
+ mAverageEnergyPerBand[i] = 0;
+ mInBoundPointsPerBand[i] = 0;
+ }
}
//append results
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Channel %s\n", mLabel));
- sb.append("Level in Band 1 : " + (testLevel() ? "OK" :"Not Optimal") + "\n");
- for (int b = 0; b < mBands; b++) {
+ for (int b = 0; b < mBandCount; b++) {
double percent = 0;
if (mPointsPerBand[b] > 0) {
percent = 100.0 * (double) mInBoundPointsPerBand[b] / mPointsPerBand[b];
@@ -344,15 +867,8 @@
return sb.toString();
}
- public boolean testLevel() {
- if (mAverageEnergyPerBand[1] >= MIN_ENERGY_BAND_1) {
- return true;
- }
- return false;
- }
-
public boolean testInBand(int b) {
- if (b >= 0 && b < mBands && mPointsPerBand[b] > 0) {
+ if (b >= 0 && b < mBandCount && mPointsPerBand[b] > 0) {
if ((double) mInBoundPointsPerBand[b] / mPointsPerBand[b] >
MIN_FRACTION_POINTS_IN_BAND) {
return true;
@@ -362,10 +878,7 @@
}
public boolean testAll() {
- if (!testLevel()) {
- return false;
- }
- for (int b = 0; b < mBands; b++) {
+ for (int b = 0; b < mBandCount; b++) {
if (!testInBand(b)) {
return false;
}
@@ -375,98 +888,13 @@
}
- /**
- * compute test1 results
- */
- private void computeTest1Results() {
- Results resultsBuiltIn = new Results("BuiltIn");
- if (computeResultsForVector(mFreqAverageBuiltIn, resultsBuiltIn, bandSpecsArray)) {
- appendResultsToScreen(resultsBuiltIn.toString(), mTest1Result);
- recordTestResults(resultsBuiltIn);
- }
- if (mSupportsUnprocessed) { //test is mandatory
- appendResultsToScreen(getResources().getText(
- R.string.audio_frequency_unprocessed_defined).toString(), mTest1Result);
- if (resultsBuiltIn.testAll()) {
- //tst passed
- getPassButton().setEnabled(true);
- String strSuccess = getResources().getString(R.string.audio_general_test_passed);
- appendResultsToScreen(strSuccess, mTest1Result);
- } else {
- getPassButton().setEnabled(false);
- String strFailed = getResources().getString(R.string.audio_general_test_failed);
- appendResultsToScreen(strFailed, mTest1Result);
- }
- } else {
- //test optional
- appendResultsToScreen(getResources().getText(
- R.string.audio_frequency_unprocessed_not_defined).toString(), mTest1Result);
- getPassButton().setEnabled(true);
- }
- }
-
- private boolean computeResultsForVector(VectorAverage freqAverage, Results results,
- AudioBandSpecs[] bandSpecs) {
-
- int points = freqAverage.getSize();
- if (points > 0) {
- //compute vector in db
- double[] values = new double[points];
- freqAverage.getData(values, false);
- results.mValuesLog = new double[points];
- for (int i = 0; i < points; i++) {
- results.mValuesLog[i] = 20 * Math.log10(values[i]);
- }
-
- int currentBand = 0;
- for (int i = 0; i < points; i++) {
- double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
- if (freq > bandSpecs[currentBand].mFreqStop) {
- currentBand++;
- if (currentBand >= mBands)
- break;
- }
-
- if (freq >= bandSpecs[currentBand].mFreqStart) {
- results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
- results.mPointsPerBand[currentBand]++;
- }
- }
-
- for (int b = 0; b < mBands; b++) {
- if (results.mPointsPerBand[b] > 0) {
- results.mAverageEnergyPerBand[b] =
- results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
- }
- }
-
- //set offset relative to band 1 level
- for (int b = 0; b < mBands; b++) {
- bandSpecs[b].setOffset(results.mAverageEnergyPerBand[1]);
- }
-
- //test points in band.
- currentBand = 0;
- for (int i = 0; i < points; i++) {
- double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
- if (freq > bandSpecs[currentBand].mFreqStop) {
- currentBand++;
- if (currentBand >= mBands)
- break;
- }
-
- if (freq >= bandSpecs[currentBand].mFreqStart) {
- double value = results.mValuesLog[i];
- if (bandSpecs[currentBand].isInBounds(freq, value)) {
- results.mInBoundPointsPerBand[currentBand]++;
- }
- }
- }
- return true;
- } else {
- return false;
- }
- }
+// /**
+// * compute test results
+// */
+// private void computeTestResults(int testId) {
+// String testName = getTestString(testId);
+// appendResultsToScreen(testId, "test finished");
+// }
//append results
private void appendResultsToScreen(String str, TextView text) {
@@ -474,6 +902,24 @@
text.setText(currentText + "\n" + str);
}
+ private void appendResultsToScreen(int testId, String str) {
+ switch(testId) {
+ case TEST_TONE:
+ appendResultsToScreen(str, mResultTestTone);
+ showToneRMS();
+ break;
+ case TEST_NOISE:
+ appendResultsToScreen(str, mResultTestNoise);
+ break;
+ case TEST_USB_BACKGROUND:
+ appendResultsToScreen(str, mResultTestUsbBackground);
+ break;
+ case TEST_USB_NOISE:
+ appendResultsToScreen(str, mResultTestUsbNoise);
+ break;
+ }
+ }
+
/**
* Store test results in log
*/
@@ -615,6 +1061,12 @@
mRecorder.setPositionNotificationPeriod(mBlockSizeSamples / 2);
return true;
}
+ private void showToneRMS() {
+ String str = String.format("RMS: %.3f dBFS. Max RMS: %.3f dBFS",
+ 20 * Math.log10(mRMSTone),
+ 20 * Math.log10(mRMSMaxTone));
+ showMessage(TEST_TONE, str);
+ }
// ---------------------------------------------------------
// Implementation of AudioRecord.OnPeriodicNotificationListener
@@ -627,9 +1079,10 @@
//compute stuff.
int clipcount = 0;
- double sum = 0;
+// double sum = 0;
double maxabs = 0;
int i;
+ double rmsTempSum = 0;
for (i = 0; i < samplesNeeded; i++) {
double value = mAudioShortArray2[i] / MAX_VAL;
@@ -643,10 +1096,18 @@
clipcount++;
}
- sum += value * value;
+ rmsTempSum += value * value;
//fft stuff
mData.mData[i] = value;
}
+ double rms = Math.sqrt(rmsTempSum / samplesNeeded);
+
+ double alpha = 0.9;
+ double total_rms = rms * alpha + mRMS *(1-alpha);
+ mRMS = total_rms;
+ if (mRMS > mRMSMax) {
+ mRMSMax = mRMS;
+ }
//for the current frame, compute FFT and send to the viewer.
@@ -660,8 +1121,25 @@
halfMagnitude[i] = Math.sqrt(mC.mReal[i] * mC.mReal[i] + mC.mImag[i] * mC.mImag[i]);
}
- mFreqAverageMain.setData(halfMagnitude, false); //average all of them!
- mFreqAverageBuiltIn.setData(halfMagnitude, false);
+ switch(mCurrentTest) {
+ case TEST_TONE: {
+ mFreqAverageTone.setData(halfMagnitude, false);
+ //Update realtime info on screen
+ mRMSTone = mRMS;
+ mRMSMaxTone = mRMSMax;
+ showToneRMS();
+ }
+ break;
+ case TEST_NOISE:
+ mFreqAverageNoise.setData(halfMagnitude, false);
+ break;
+ case TEST_USB_BACKGROUND:
+ mFreqAverageUsbBackground.setData(halfMagnitude, false);
+ break;
+ case TEST_USB_NOISE:
+ mFreqAverageUsbNoise.setData(halfMagnitude, false);
+ break;
+ }
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
old mode 100644
new mode 100755
index bf3484e..c0097ce
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
@@ -30,7 +30,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
-import android.os.CountDownTimer;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
@@ -46,10 +45,6 @@
private Map<Integer, Integer> mCount;
private int[] mPowerLevel;
- private TextView mTimerText;
- private CountDownTimer mTimer;
- private static final long REFRESH_MAC_TIME = 930000; // 15.5 min
-
private static final int[] POWER_DBM = {-21, -15, -7, 1, 9};
@Override
@@ -59,23 +54,6 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.ble_power_level_name,
R.string.ble_power_level_info, -1);
- getPassButton().setEnabled(false);
-
- mTimerText = (TextView)findViewById(R.id.ble_timer);
- mTimer = new CountDownTimer(REFRESH_MAC_TIME, 1000) {
- @Override
- public void onTick(long millis) {
- int min = (int)millis / 60000;
- int sec = ((int)millis / 1000) % 60;
- mTimerText.setText(min + ":" + sec);
- }
-
- @Override
- public void onFinish() {
- mTimerText.setTextColor(getResources().getColor(R.color.red));
- mTimerText.setText("Time is up!");
- }
- };
mRssiText = new HashMap<Integer, TextView>();
mCountText = new HashMap<Integer, TextView>();
@@ -168,7 +146,6 @@
for (int i : mPowerLevel) {
mCount.put(i, 0);
}
- mTimer.start();
}
Integer t = mCount.get(powerLevelBit) + 1;
mCount.put(powerLevelBit, t);
@@ -188,10 +165,6 @@
case BleScannerService.BLE_PRIVACY_NEW_MAC_RECEIVE:
Toast.makeText(context, "New MAC address detected", Toast.LENGTH_SHORT)
.show();
- mTimerText.setTextColor(getResources().getColor(R.color.green));
- mTimerText.append(" Get new MAC address.");
- mTimer.cancel();
- getPassButton().setEnabled(true);
break;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 6aacea8..9af0840 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -399,7 +399,6 @@
adapter.add(mCredSettingsVisibleTest);
adapter.add(mAppSettingsVisibleTest);
adapter.add(mLocationSettingsVisibleTest);
- adapter.add(mWiFiDataUsageSettingsVisibleTest);
adapter.add(mCellularDataUsageSettingsVisibleTest);
adapter.add(mPrintSettingsVisibleTest);
@@ -420,6 +419,10 @@
adapter.add(mParentProfilePassword);
adapter.add(mPolicyTransparencyTest);
+ if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ adapter.add(mWiFiDataUsageSettingsVisibleTest);
+ }
+
if (canResolveIntent(new Intent(Settings.ACTION_APPLICATION_SETTINGS))) {
adapter.add(mDisallowAppsControlTest);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 1349694..cf8ec89 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -97,8 +97,10 @@
}
} break;
case COMMAND_DISALLOW_KEYGUARD_UNREDACTED_NOTIFICATIONS: {
- mDpm.setKeyguardDisabledFeatures(mAdmin,
- DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+ boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false);
+ mDpm.setKeyguardDisabledFeatures(mAdmin, enforced
+ ? DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS
+ : 0);
} break;
case COMMAND_SET_AUTO_TIME_REQUIRED: {
mDpm.setAutoTimeRequired(mAdmin,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DisallowAppsControlActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DisallowAppsControlActivity.java
index 985cbf6..3cabbd3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DisallowAppsControlActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DisallowAppsControlActivity.java
@@ -67,11 +67,8 @@
@Override
public void finish() {
// Pass and fail buttons are known to call finish() when clicked, and this is when we want to
- // clear the password.
- final ComponentName adminComponent = getAdminComponent();
- if (mDpm.isAdminActive(adminComponent)) {
- allowAppsControl();
- }
+ // clear the restriction.
+ allowAppsControl();
super.finish();
}
@@ -120,9 +117,4 @@
setupCheckDisabledForceStopTest(adapter);
setupCheckDisabledAppStorageButtonsTest(adapter);
}
-
- @Override
- protected void clearRemainingState(final DialogTestListItem test) {
- allowAppsControl();
- }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
index 2f7619c..e767f7f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -52,6 +53,7 @@
private static final String NFC_BEAM_ACTIVITY = "com.android.nfc.BeamShareActivity";
private static final String SAMPLE_IMAGE_FILENAME = "image_to_share.jpg";
private static final String SAMPLE_IMAGE_CONTENT = "sample image";
+ private static final String SAMPLE_TEXT = "sample text";
private static final int MARGIN = 80;
private static final int TEXT_SIZE = 200;
@@ -76,11 +78,28 @@
UserManager.DISALLOW_OUTGOING_BEAM);
}
- final Uri uri = createUriForImage(SAMPLE_IMAGE_FILENAME, SAMPLE_IMAGE_CONTENT);
- Uri[] uris = new Uri[] { uri };
-
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
- mNfcAdapter.setBeamPushUris(uris, this);
+
+ final Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+ // Sending a large amount of data requires hand-over to bluetooth, so determine here
+ // if supported by the device. If bluetooth is not supported, a simple text message
+ // will be transferred instead.
+ final boolean hasBluetooth =
+ getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+ if (hasBluetooth) {
+ final Uri uri = createUriForImage(SAMPLE_IMAGE_FILENAME, SAMPLE_IMAGE_CONTENT);
+ Uri[] uris = new Uri[]{uri};
+
+ mNfcAdapter.setBeamPushUris(uris, this);
+
+ shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ shareIntent.setType("image/jpg");
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ shareIntent.putExtra(Intent.EXTRA_TEXT, SAMPLE_TEXT);
+ }
findViewById(R.id.manual_beam_button).setOnClickListener(new OnClickListener() {
@Override
@@ -91,10 +110,6 @@
findViewById(R.id.intent_share_button).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
- shareIntent.setType("image/jpg");
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Specify the package name of NfcBeamActivity so that the tester don't need to
// select the activity manually.
shareIntent.setClassName(NFC_BEAM_PACKAGE, NFC_BEAM_ACTIVITY);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
index 9261284..5eaf862 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
@@ -266,7 +266,8 @@
return mTestId;
}
- public class DummyAccessibilityService extends AccessibilityService {
+ public static class DummyAccessibilityService extends AccessibilityService {
+
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
// Do nothing
@@ -278,7 +279,7 @@
}
}
- public class DummyInputMethod extends InputMethodService {
+ public static class DummyInputMethod extends InputMethodService {
@Override
public boolean onEvaluateFullscreenMode() {
return false;
@@ -306,4 +307,4 @@
this.command = command;
}
}
-}
\ No newline at end of file
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
index 88f4f18..c5b4a93 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
@@ -18,6 +18,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.provider.Settings;
@@ -121,7 +122,8 @@
private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
for (String restriction : UserRestrictions.getUserRestrictions()) {
final Intent intent = UserRestrictions.getUserRestrictionTestIntent(this, restriction);
- if (!mIsDeviceOwner && !UserRestrictions.isValidForPO(restriction)) {
+ if (!UserRestrictions.isRestrictionValid(this, restriction) ||
+ (!mIsDeviceOwner && !UserRestrictions.isValidForPO(restriction))) {
continue;
}
final String title = UserRestrictions.getRestrictionLabel(this, restriction);
@@ -133,7 +135,8 @@
for (Pair<Intent, Integer> policy : POLICIES) {
final Intent intent = policy.first;
String test = intent.getStringExtra(PolicyTransparencyTestActivity.EXTRA_TEST);
- if (!mIsDeviceOwner && !ALSO_VALID_FOR_PO.contains(test)) {
+ if (!isPolicyValid(test) ||
+ (!mIsDeviceOwner && !ALSO_VALID_FOR_PO.contains(test))) {
continue;
}
final String title = getString(policy.second);
@@ -144,6 +147,16 @@
}
}
+ private boolean isPolicyValid(String test) {
+ final PackageManager pm = getPackageManager();
+ switch (test) {
+ case PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_INPUT_METHOD:
+ return pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS);
+ default:
+ return true;
+ }
+ }
+
private void setSupportMsgButtonClickListeners() {
findViewById(R.id.short_msg_button).setOnClickListener(this);
findViewById(R.id.long_msg_button).setOnClickListener(this);
@@ -178,4 +191,4 @@
: CommandReceiverActivity.COMMAND_PROFILE_OWNER_CLEAR_POLICIES);
startActivity(intent);
}
-}
\ No newline at end of file
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index 06d0381..935a1b8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.UserManager;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -174,6 +175,43 @@
item.intentAction);
}
+ public static boolean isRestrictionValid(Context context, String restriction) {
+ final PackageManager pm = context.getPackageManager();
+ switch (restriction) {
+ case UserManager.DISALLOW_ADD_USER:
+ case UserManager.DISALLOW_REMOVE_USER:
+ return UserManager.supportsMultipleUsers();
+ case UserManager.DISALLOW_ADJUST_VOLUME:
+ return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
+ case UserManager.DISALLOW_CONFIG_CELL_BROADCASTS:
+ // Get com.android.internal.R.bool.config_cellBroadcastAppLinks
+ final int resId = context.getResources().getIdentifier(
+ "config_cellBroadcastAppLinks", "bool", "android");
+ boolean isCellBroadcastAppLinkEnabled = context.getResources().getBoolean(resId);
+ try {
+ if (isCellBroadcastAppLinkEnabled) {
+ if (pm.getApplicationEnabledSetting("com.android.cellbroadcastreceiver")
+ == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+ isCellBroadcastAppLinkEnabled = false; // CMAS app disabled
+ }
+ }
+ } catch (IllegalArgumentException ignored) {
+ isCellBroadcastAppLinkEnabled = false; // CMAS app not installed
+ }
+ return isCellBroadcastAppLinkEnabled;
+ case UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS:
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+ case UserManager.DISALLOW_CONFIG_WIFI:
+ return pm.hasSystemFeature(PackageManager.FEATURE_WIFI);
+ case UserManager.DISALLOW_OUTGOING_BEAM:
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+ case UserManager.DISALLOW_SHARE_LOCATION:
+ return pm.hasSystemFeature(PackageManager.FEATURE_LOCATION);
+ default:
+ return true;
+ }
+ }
+
private static class UserRestrictionItem {
final int label;
final int userAction;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java
index 49c0c20..b7c10bb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java
@@ -56,7 +56,7 @@
private DevicePolicyManager mDevicePolicyManager;
private UserManager mUserManager;
private static final String TAG = "DeviceOwnerPositiveTestActivity";
- private static final int REQUEST_VPN_CODE = 1;
+ private static final int REQUEST_VPN_CODE = 54321;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -81,12 +81,13 @@
@Override
protected void onActivityResult(int requestCode, int result, Intent data) {
- if (requestCode == REQUEST_VPN_CODE && result == RESULT_OK) {
+ if (requestCode == REQUEST_VPN_CODE) {
+ // We don't care about the result - ideally it should automatically cancel, but if
+ // some custom component doesn't do that, try to establish the connection anyway
+ // and see what happens.
establishVpn();
} else {
- // vpn connection canceled by user
- Log.w(TAG, "Test failed, canceled by user");
- populateInfo(R.string.device_owner_vpn_connection_canceled);
+ Log.w(TAG, "Unexpected request code: " + requestCode);
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DynamicSensorDiscoveryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DynamicSensorDiscoveryTestActivity.java
index dd9c752..df3a67c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DynamicSensorDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DynamicSensorDiscoveryTestActivity.java
@@ -88,7 +88,7 @@
showUserMessage(String.format("Please connect an external sensor to device in %d seconds.",
CONNECTION_TIMEOUT_SEC));
- Assert.assertTrue("Cannot detect sensor connection.", mCallback.waitForConnection());
+ Assert.assertTrue("Cannot detect sensor connection.", mCallback.waitForConnection(null));
mSensorConnected = true;
mSensorId = mCallback.getSensorId();
return "OnConnect: Success";
@@ -143,7 +143,8 @@
showUserMessage(String.format("Please connect the same sensor that was previously " +
"connected in %d seconds", CONNECTION_TIMEOUT_SEC));
- Assert.assertTrue("Cannot detect sensor reconnection.", mCallback.waitForConnection());
+ Assert.assertTrue("Cannot detect sensor reconnection.",
+ mCallback.waitForConnection(mSensorId));
Integer sensorId = mCallback.getSensorId();
boolean match = mSensorId != null && sensorId != null &&
@@ -155,16 +156,19 @@
private class Callback extends SensorManager.DynamicSensorCallback {
private Sensor mSensor = null;
+ private Integer mExpectSensorId = null;
private CountDownLatch mConnectLatch;
private CountDownLatch mDisconnectLatch;
@Override
public void onDynamicSensorConnected(Sensor sensor) {
- mSensor = sensor;
- Log.d(TAG, "Sensor Connected: " + mSensor);
+ Log.d(TAG, "Sensor Connected: " + sensor);
- if (mConnectLatch != null) {
- mConnectLatch.countDown();
+ if (mExpectSensorId == null || mExpectSensorId == sensor.getId()) {
+ mSensor = sensor;
+ if (mConnectLatch != null) {
+ mConnectLatch.countDown();
+ }
}
}
@@ -178,8 +182,9 @@
}
}
- public boolean waitForConnection() {
+ public boolean waitForConnection(Integer sensorId) {
boolean ret;
+ mExpectSensorId = sensorId;
mConnectLatch = new CountDownLatch(1);
try {
ret = mConnectLatch.await(CONNECTION_TIMEOUT_SEC, TimeUnit.SECONDS);
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
index ef95b66..9e0d3c5 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
@@ -204,7 +204,13 @@
static protected int[] getIntValues(int fieldId, int count) throws IllegalAccessException{
int[] resultInts = new int[count];
- GLES20.glGetIntegerv(fieldId, resultInts, 0);
+ // The JNI wrapper layer has a piece of code that defines
+ // the expected array length. It defaults to 1 and looks
+ // like it's missing GLES3 variables. So, we won't be
+ // querying if the array has zero lenght.
+ if (count > 0) {
+ GLES20.glGetIntegerv(fieldId, resultInts, 0);
+ }
return resultInts;
}
}
@@ -318,7 +324,7 @@
}
}
- // NOTE: Changes to the types of the variables will carry over to
+ // NOTE: Changes to the types of the variables will carry over to
// GraphicsDeviceInfo proto via GraphicsDeviceInfo. See
// go/edi-userguide for details.
static ImplementationVariable[] GLES2_IMPLEMENTATION_VARIABLES = {
diff --git a/common/host-side/tradefed/res/report/compatibility_result.xsl b/common/host-side/tradefed/res/report/compatibility_result.xsl
index b0e40b1..b86107d 100644
--- a/common/host-side/tradefed/res/report/compatibility_result.xsl
+++ b/common/host-side/tradefed/res/report/compatibility_result.xsl
@@ -180,7 +180,7 @@
<xsl:template name="filteredResultTestReport">
<xsl:param name="header" />
<xsl:param name="resultFilter" />
- <xsl:variable name="numMatching" select="count(Result/Module/Test[@result=$resultFilter])" />
+ <xsl:variable name="numMatching" select="count(Result/Module/TestCase/Test[@result=$resultFilter])" />
<xsl:if test="$numMatching > 0">
<h2 align="center"><xsl:value-of select="$header" /> (<xsl:value-of select="$numMatching"/>)</h2>
<xsl:call-template name="detailedTestReport">
@@ -194,7 +194,7 @@
<div>
<xsl:for-each select="Result/Module">
<xsl:if test="$resultFilter=''
- or count(Test[@result=$resultFilter]) > 0">
+ or count(TestCase/Test[@result=$resultFilter]) > 0">
<table class="testdetails">
<tr>
@@ -214,7 +214,7 @@
<xsl:variable name="TestCase" select="."/>
<!-- test -->
<xsl:for-each select="Test">
- <xsl:if test="$resultFilter='' or $resultFilter=@result">
+ <xsl:if test="$resultFilter='' or @result=$resultFilter">
<tr>
<td class="testname"> <xsl:value-of select="$TestCase/@name"/>#<xsl:value-of select="@name"/></td>
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index ed9150e..10fc053 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -121,6 +121,10 @@
}
}
+ public String getRecentCommandLineArgs() {
+ return mBuildInfo.getBuildAttributes().get(COMMAND_LINE_ARGS);
+ }
+
public String getSuiteBuild() {
return mBuildInfo.getBuildAttributes().get(SUITE_BUILD);
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index 6a9a9ab..05f7ff9 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -18,6 +18,7 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest.RetryType;
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.IModuleResult;
@@ -91,6 +92,13 @@
importance = Importance.IF_UNSET)
private Integer mRetrySessionId = null;
+ @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
+ description = "used with " + CompatibilityTest.RETRY_OPTION
+ + ", retry tests of a certain status. Possible values include \"failed\" and "
+ + "\"not_executed\".",
+ importance = Importance.IF_UNSET)
+ private RetryType mRetryType = null;
+
@Option(name = "result-server", description = "Server to publish test results.")
private String mResultServer;
@@ -125,6 +133,13 @@
private int mCurrentTestNum;
private int mTotalTestsInModule;
+
+ // Whether modules can be marked done for this invocation. Initialized in invocationStarted()
+ // Visible for unit testing
+ protected boolean mCanMarkDone;
+ // Whether the current module has previously been marked done
+ private boolean mModuleWasDone;
+
// Nullable. If null, "this" is considered the master and must handle
// result aggregation and reporting. When not null, it should forward events
// to the master.
@@ -157,6 +172,7 @@
if (mDeviceSerial == null && buildInfo.getDeviceSerial() != null) {
mDeviceSerial = buildInfo.getDeviceSerial();
}
+ mCanMarkDone = canMarkDone(mBuildHelper.getRecentCommandLineArgs());
}
if (isShardResultReporter()) {
@@ -253,13 +269,14 @@
mTotalTestsInModule +=
Math.max(0, numTests - mCurrentModuleResult.getNotExecuted());
}
- mCurrentModuleResult.setDone(false);
} else {
mCurrentModuleResult = mResult.getOrCreateModule(id);
mTotalTestsInModule = numTests;
// Reset counters
mCurrentTestNum = 0;
}
+ mModuleWasDone = mCurrentModuleResult.isDone();
+ mCurrentModuleResult.setDone(false);
}
/**
@@ -344,8 +361,12 @@
@Override
public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
mCurrentModuleResult.addRuntime(elapsedTime);
- // Expect them to be equal, but greater than to be safe.
- mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
+ if (mCanMarkDone || mModuleWasDone) {
+ // Only mark module done if status of the invocation allows it (mCanMarkDone) or module
+ // was previously marked done (mModuleWasDone) and all expected tests are collected.
+ // Expect mCurrentTestNum = mTotalTestsInModule, but use >= to be safe
+ mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
+ }
mCurrentModuleResult.setNotExecuted(Math.max(mTotalTestsInModule - mCurrentTestNum, 0));
if (isShardResultReporter()) {
// Forward module results to the master.
@@ -408,7 +429,6 @@
// NOTE: Everything after this line only applies to the master ResultReporter.
-
synchronized(this) {
// The master ResultReporter tracks the progress of all invocations across
// shard ResultReporters. Writing results should not proceed until all
@@ -460,16 +480,16 @@
long startTime = mResult.getStartTime();
try {
+ // Zip the full test results directory.
+ copyDynamicConfigFiles(mBuildHelper.getDynamicConfigFiles(), mResultDir);
+ copyFormattingFiles(mResultDir);
+
File resultFile = ResultHandler.writeResults(mBuildHelper.getSuiteName(),
mBuildHelper.getSuiteVersion(), mBuildHelper.getSuitePlan(),
mBuildHelper.getSuiteBuild(), mResult, mResultDir, startTime,
elapsedTime + startTime, mReferenceUrl, getLogUrl(),
mBuildHelper.getCommandLineArgs());
info("Test Result: %s", resultFile.getCanonicalPath());
-
- // Zip the full test results directory.
- copyDynamicConfigFiles(mBuildHelper.getDynamicConfigFiles(), mResultDir);
- copyFormattingFiles(mResultDir);
File zippedResults = zipResults(mResultDir);
info("Full Result: %s", zippedResults.getCanonicalPath());
@@ -614,6 +634,28 @@
}
/**
+ * Returns whether it is safe to mark modules as "done", given the invocation command-line
+ * arguments. Returns true unless this is a retry and specific filtering techniques are applied
+ * on the command-line, such as:
+ * --retry-type failed
+ * --include-filter
+ * --exclude-filter
+ * -t/--test
+ * --subplan
+ */
+ private boolean canMarkDone(String args) {
+ if (mRetrySessionId == null) {
+ return true; // always allow modules to be marked done if not retry
+ }
+ return !(RetryType.FAILED.equals(mRetryType)
+ || args.contains(CompatibilityTest.INCLUDE_FILTER_OPTION)
+ || args.contains(CompatibilityTest.EXCLUDE_FILTER_OPTION)
+ || args.contains(CompatibilityTest.SUBPLAN_OPTION)
+ || args.matches(String.format(".* (-%s|--%s) .*",
+ CompatibilityTest.TEST_OPTION_SHORT_NAME, CompatibilityTest.TEST_OPTION)));
+ }
+
+ /**
* Copy the xml formatting files stored in this jar to the results directory
*
* @param resultsDir
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
index ff28263..6b9b5e4 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
@@ -57,12 +57,20 @@
public static final String FAILED = "failed";
public static final String NOT_EXECUTED = "not_executed";
// static mapping of result types to TestStatuses
- private static final Map<String, TestStatus> mStatusMap;
+ private static final Map<String, TestStatus> STATUS_MAP;
static {
Map<String, TestStatus> statusMap = new HashMap<String, TestStatus>();
statusMap.put(PASSED, TestStatus.PASS);
statusMap.put(FAILED, TestStatus.FAIL);
- mStatusMap = Collections.unmodifiableMap(statusMap);
+ STATUS_MAP = Collections.unmodifiableMap(statusMap);
+ }
+
+ // TODO(aaronholden): remove this temporary workaround for b/33090757
+ private static final Set<String> MULTITEST_MODULES;
+ static {
+ Set<String> multiTestModuleSet = new HashSet<String>();
+ multiTestModuleSet.add("CtsDeqpTestCases");
+ MULTITEST_MODULES = Collections.unmodifiableSet(multiTestModuleSet);
}
@Option (name = "name", shortName = 'n', description = "the name of the subplan to create",
@@ -183,10 +191,51 @@
if (mModuleName != null) {
subPlan.addIncludeFilter(new TestFilter(mAbiName, mModuleName, mTestName).toString());
}
+ Set<TestStatus> statusesToRun = getStatusesToRun();
for (IModuleResult module : mResult.getModules()) {
+
+ // TODO(aaronholden): remove this special case from SubPlanCreator, and filter
+ // individual tests only when the module should run. Tracked by b/33211104
+ if (MULTITEST_MODULES.contains(module.getName())) {
+ // cannot check module.isDone() since this value is not accurate for modules
+ // with multiple test configs. If we should run not-executed tests, include module
+ // and exclude tests with status not in mResultTypes.
+ TestFilter moduleFilter =
+ new TestFilter(module.getAbi(), module.getName(), null /*test*/);
+ if (mResultTypes.contains(NOT_EXECUTED)) {
+ subPlan.addIncludeFilter(moduleFilter.toString());
+ for (ICaseResult caseResult : module.getResults()) {
+ for (ITestResult testResult : caseResult.getResults()) {
+ if (!statusesToRun.contains(testResult.getResultStatus())) {
+ TestFilter testExclude = new TestFilter(module.getAbi(),
+ module.getName(), testResult.getFullName());
+ subPlan.addExcludeFilter(testExclude.toString());
+ }
+ }
+ }
+ } else {
+ // not running not-executed tests, only include executed tests
+ if (shouldRunModule(module)) {
+ // at least some executed tests will be included
+ for (ICaseResult caseResult : module.getResults()) {
+ for (ITestResult testResult : caseResult.getResults()) {
+ if (statusesToRun.contains(testResult.getResultStatus())) {
+ TestFilter testInclude = new TestFilter(module.getAbi(),
+ module.getName(), testResult.getFullName());
+ subPlan.addIncludeFilter(testInclude.toString());
+ }
+ }
+ }
+ } else {
+ // no executed tests will be included, so exclude entire module
+ subPlan.addExcludeFilter(moduleFilter.toString());
+ }
+ }
+ continue;
+ }
+
if (shouldRunModule(module)) {
- Set<TestStatus> statusesToRun = getStatusesToRun();
TestFilter moduleInclude =
new TestFilter(module.getAbi(), module.getName(), null /*test*/);
if (shouldRunEntireModule(module)) {
@@ -280,7 +329,7 @@
for (String resultType : mResultTypes) {
// no test status exists for not-executed tests
if (resultType != NOT_EXECUTED) {
- statusesToRun.add(mStatusMap.get(resultType));
+ statusesToRun.add(STATUS_MAP.get(resultType));
}
}
return statusesToRun;
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/PreconditionPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/PreconditionPreparer.java
index fc25e03..27ef658 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/PreconditionPreparer.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/PreconditionPreparer.java
@@ -19,14 +19,20 @@
import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
import com.android.ddmlib.Log;
import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.ConfigurationException;
import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.targetprep.BuildError;
import com.android.tradefed.targetprep.ITargetPreparer;
import com.android.tradefed.targetprep.TargetSetupError;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* An {@link ITargetPreparer} that performs checks and/or tasks to ensure the
* the device is ready to run the test suite.
@@ -38,16 +44,38 @@
description = "Whether preconditions should be skipped")
private boolean mSkipPreconditions = false;
+ @Option(name = CompatibilityTest.PRECONDITION_ARG_OPTION,
+ description = "the arguments to pass to a precondition. The expected format is"
+ + "\"<arg-name>:<arg-value>\"")
+ private List<String> mPreconditionArgs = new ArrayList<>();
+
protected final String LOG_TAG = getClass().getSimpleName();
@Override
public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
BuildError, DeviceNotAvailableException {
if (!mSkipPreconditions) {
+ for (String preconditionArg : mPreconditionArgs) {
+ String[] parts = preconditionArg.split(":");
+ String argName = parts[0];
+ // If arg-value is not supplied, set to "true"
+ String argValue = (parts.length > 1) ? parts[1] : Boolean.toString(true);
+ setOption(argName, argValue);
+ }
run(device, buildInfo);
}
}
+ private void setOption(String option, String value) {
+ try {
+ OptionSetter setter = new OptionSetter(this);
+ setter.setOptionValue(option, value);
+ } catch (ConfigurationException e) {
+ CLog.i("Value %s for option %s not applicable for class %s", value, option,
+ this.getClass().getName());
+ }
+ }
+
public abstract void run(ITestDevice device, IBuildInfo buildInfo)
throws TargetSetupError, BuildError, DeviceNotAvailableException;
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index bc7a29b..891a0ae 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -85,9 +85,11 @@
public static final String INCLUDE_FILTER_OPTION = "include-filter";
public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
private static final String PLAN_OPTION = "plan";
- private static final String SUBPLAN_OPTION = "subplan";
+ public static final String SUBPLAN_OPTION = "subplan";
public static final String MODULE_OPTION = "module";
public static final String TEST_OPTION = "test";
+ public static final String PRECONDITION_ARG_OPTION = "precondition-arg";
+ public static final char TEST_OPTION_SHORT_NAME = 't';
private static final String MODULE_ARG_OPTION = "module-arg";
private static final String TEST_ARG_OPTION = "test-arg";
public static final String RETRY_OPTION = "retry";
@@ -135,11 +137,17 @@
private String mModuleName = null;
@Option(name = TEST_OPTION,
- shortName = 't',
+ shortName = TEST_OPTION_SHORT_NAME,
description = "the test run.",
importance = Importance.IF_UNSET)
private String mTestName = null;
+ @Option(name = PRECONDITION_ARG_OPTION,
+ description = "the arguments to pass to a precondition. The expected format is"
+ + "\"<arg-name>:<arg-value>\"",
+ importance = Importance.ALWAYS)
+ private List<String> mPreconditionArgs = new ArrayList<>();
+
@Option(name = MODULE_ARG_OPTION,
description = "the arguments to pass to a module. The expected format is"
+ "\"<module-name>:<arg-name>:<arg-value>\"",
@@ -376,7 +384,7 @@
module.setBuild(mBuildHelper.getBuildInfo());
module.setDevice(mDevice);
module.setPreparerWhitelist(mPreparerWhitelist);
- isPrepared &= (module.prepare(mSkipPreconditions));
+ isPrepared &= (module.prepare(mSkipPreconditions, mPreconditionArgs));
}
mModuleRepo.setPrepared(isPrepared);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
index 14427ca..c7acdcd 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
@@ -23,6 +23,7 @@
import com.android.tradefed.testtype.IRuntimeHintProvider;
import java.io.File;
+import java.util.List;
import java.util.Set;
/**
@@ -66,8 +67,12 @@
/**
* Runs the module's precondition checks and setup tasks.
+ * @param skipPrep whether preparation should be skipped
+ * @param preconditionArgs arguments to set on precondition preparers for the module, taking
+ * format arg-name:arg-value. If "arg-value" is unset, the value will default to "true".
* @return whether preparation succeeded.
*/
- boolean prepare(boolean skipPrep) throws DeviceNotAvailableException;
+ boolean prepare(boolean skipPrep, List<String> preconditionArgs)
+ throws DeviceNotAvailableException;
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
index 2c3f96b..5ed6870 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
@@ -268,7 +268,8 @@
* {@inheritDoc}
*/
@Override
- public boolean prepare(boolean skipPrep) throws DeviceNotAvailableException {
+ public boolean prepare(boolean skipPrep, List<String> preconditionArgs)
+ throws DeviceNotAvailableException {
for (ITargetPreparer preparer : mPreconditions) {
CLog.d("Preparer: %s", preparer.getClass().getSimpleName());
if (preparer instanceof IAbiReceiver) {
@@ -276,6 +277,9 @@
}
setOption(preparer, CompatibilityTest.SKIP_PRECONDITIONS_OPTION,
Boolean.toString(skipPrep));
+ for (String preconditionArg : preconditionArgs) {
+ setOption(preparer, CompatibilityTest.PRECONDITION_ARG_OPTION, preconditionArg);
+ }
try {
preparer.setUp(mDevice, mBuild);
} catch (BuildError e) {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index a7a31f6..b8b7858 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -17,6 +17,7 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelperTest;
import com.android.compatibility.common.tradefed.command.CompatibilityConsoleTest;
+import com.android.compatibility.common.tradefed.result.ChecksumReporterTest;
import com.android.compatibility.common.tradefed.result.ConsoleReporterTest;
import com.android.compatibility.common.tradefed.result.ResultReporterTest;
import com.android.compatibility.common.tradefed.result.SubPlanCreatorTest;
@@ -45,6 +46,7 @@
addTestSuite(CompatibilityConsoleTest.class);
addTestSuite(CompatibilityTestTest.class);
addTestSuite(ConsoleReporterTest.class);
+ addTestSuite(ChecksumReporterTest.class);
addTestSuite(ResultReporterTest.class);
addTestSuite(CompatibilityTestTest.class);
addTestSuite(OptionHelperTest.class);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
new file mode 100644
index 0000000..0646385
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.compatibility.common.tradefed.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
+import com.android.compatibility.common.tradefed.result.ResultReporter;
+import com.android.compatibility.common.util.ChecksumReporter;
+import com.android.compatibility.common.util.ChecksumReporter.ChecksumValidationException;
+import com.android.compatibility.common.util.ICaseResult;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.IModuleResult;
+import com.android.compatibility.common.util.ITestResult;
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.TestStatus;
+import com.android.tradefed.build.BuildInfo;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.util.FileUtil;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class ChecksumReporterTest extends TestCase {
+
+ private static final String ROOT_PROPERTY = "TESTS_ROOT";
+ private static final String ROOT_DIR_NAME = "root";
+ private static final String SUITE_NAME = "TESTS";
+ private static final String BUILD_NUMBER = "2";
+ private static final String SUITE_PLAN = "cts";
+ private static final String BASE_DIR_NAME = "android-tests";
+ private static final String TESTCASES = "testcases";
+ private static final String DYNAMIC_CONFIG_URL = "";
+ private static final long START_TIME = 123456L;
+
+ private ChecksumReporter mReporter;
+ private File mRoot = null;
+ private IBuildInfo mBuildInfo;
+ private CompatibilityBuildHelper mBuildHelper;
+ private ReportLog mReportLog = null;
+ private IInvocationResult mInvocationResult;
+ private IModuleResult mModuleResult;
+ private ITestResult mFailedTest;
+
+ @Override
+ public void setUp() throws Exception {
+ mReporter = new ChecksumReporter(100, .001, (short)1);
+ mRoot = FileUtil.createTempDir(ROOT_DIR_NAME);
+ File baseDir = new File(mRoot, BASE_DIR_NAME);
+ baseDir.mkdirs();
+ File testDir = new File(baseDir, TESTCASES);
+ testDir.mkdirs();
+ System.setProperty(ROOT_PROPERTY, mRoot.getAbsolutePath());
+
+ ResultReporter resultReporter = new ResultReporter();
+ OptionSetter setter = new OptionSetter(resultReporter);
+ mBuildInfo = new BuildInfo(BUILD_NUMBER, "", "");
+ mBuildHelper = new CompatibilityBuildHelper(mBuildInfo);
+ mBuildHelper.init(SUITE_PLAN, DYNAMIC_CONFIG_URL, START_TIME);
+ resultReporter.invocationStarted(mBuildInfo);
+ mInvocationResult = resultReporter.getResult();
+ mModuleResult = mInvocationResult.getOrCreateModule("Module-1");
+ mModuleResult.setDone(true);
+ mModuleResult.setNotExecuted(0);
+ ICaseResult caseResult = mModuleResult.getOrCreateResult("Case-1");
+ ITestResult test1 = caseResult.getOrCreateResult("Test1");
+ test1.passed(mReportLog);
+ mFailedTest = caseResult.getOrCreateResult("Test2");
+ mFailedTest.failed("stack-trace - error happened");
+
+ IModuleResult moduleResult2 = mInvocationResult.getOrCreateModule("Module-2");
+ ICaseResult caseResult2 = moduleResult2.getOrCreateResult("Case-2");
+ mModuleResult.setDone(false);
+ mModuleResult.setNotExecuted(1);
+ ITestResult test3 = caseResult2.getOrCreateResult("Test3");
+ test3.passed(mReportLog);
+
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mReporter = null;
+ }
+
+ public void testStoreAndRetrieveTestResults() {
+ mReporter.addInvocation(mInvocationResult);
+ VerifyInvocationResults(mInvocationResult, mReporter);
+ }
+
+ /***
+ * By definition this test is flaky since the checksum has a false positive probability of .1%
+ */
+ public void testInvalidChecksums() {
+ mReporter.addInvocation(mInvocationResult);
+ IModuleResult module = mInvocationResult.getModules().get(1);
+ module.setDone(!module.isDone());
+ String fingerprint = mInvocationResult.getBuildFingerprint();
+ assertFalse("Checksum should contain module: " + module.getName(),
+ mReporter.containsModuleResult(module, fingerprint));
+
+ mFailedTest.setResultStatus(TestStatus.PASS);
+ assertFalse("Checksum should not contain test: " + mFailedTest.getName(),
+ mReporter.containsTestResult(mFailedTest, mModuleResult, fingerprint));
+ assertFalse("Module checksum should verify number of tests",
+ mReporter.containsModuleResult(mModuleResult, fingerprint));
+ }
+
+ public void testFileSerialization()
+ throws IOException, ClassNotFoundException, ChecksumValidationException {
+ mReporter.addInvocation(mInvocationResult);
+
+ File file1 = new File(mRoot, "file1.txt");
+ try (FileWriter fileWriter = new FileWriter(file1, false)) {
+ fileWriter.append("This is a test file");
+ }
+
+ mReporter.addDirectory(mRoot);
+ mReporter.saveToFile(mRoot);
+
+ ChecksumReporter storedChecksum = ChecksumReporter.load(mRoot);
+ VerifyInvocationResults(mInvocationResult, storedChecksum);
+ assertTrue("Serializing checksum maintains file hash",
+ storedChecksum.containsFile(file1, mRoot.getName()));
+ }
+
+ public void testFileCRCOperations() throws IOException {
+ File subDirectory = new File(mRoot, "child");
+ subDirectory.mkdir();
+ File file1 = new File(mRoot, "file1.txt");
+ try (FileWriter fileWriter = new FileWriter(file1, false)) {
+ fileWriter.append("This is a test file");
+ }
+
+ File file2 = new File(subDirectory, "file2.txt");
+ try (FileWriter fileWriter = new FileWriter(file2, false)) {
+ fileWriter.append("This is another test file with a different crc");
+ }
+
+ mReporter.addDirectory(mRoot);
+ String folderName = mRoot.getName();
+ assertTrue(mReporter.containsFile(file1, folderName));
+ assertTrue(mReporter.containsFile(file2, folderName + "/child"));
+ assertFalse("Should not contain non-existent file",
+ mReporter.containsFile(new File(mRoot, "fake.txt"), folderName));
+
+ File file3 = new File(mRoot, "file3.txt");
+ try (FileWriter fileWriter = new FileWriter(file3, false)) {
+ fileWriter.append("This is a test file added after crc calculated");
+ }
+ assertFalse("Should not contain file created after crc calculated",
+ mReporter.containsFile(file3, folderName));
+
+ }
+
+ private void VerifyInvocationResults(IInvocationResult invocation, ChecksumReporter reporter) {
+ for (IModuleResult module : invocation.getModules()) {
+ String buildFingerprint = invocation.getBuildFingerprint();
+ assertTrue("Checksum should contain module: " + module.getName(),
+ reporter.containsModuleResult(module, buildFingerprint));
+ for (ICaseResult caseResult : module.getResults()) {
+ for (ITestResult result : caseResult.getResults()) {
+ assertTrue("Checksum should contain test: " + result.getName(),
+ reporter.containsTestResult(result, module, buildFingerprint));
+ }
+ }
+ }
+ }
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
index 0df6ad0..9e76e54 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
@@ -273,6 +273,84 @@
finalTestResult2.getResultStatus());
}
+ public void testRetryCanSetDone() throws Exception {
+ mReporter.invocationStarted(mBuildInfo);
+ // Set mCanMarkDone directly (otherwise we must build result directory, write XML, and
+ // perform actual retry)
+ mReporter.mCanMarkDone = true;
+ // Set up IInvocationResult with existing results from previous session
+ IInvocationResult invocationResult = mReporter.getResult();
+ IModuleResult moduleResult = invocationResult.getOrCreateModule(ID);
+ moduleResult.setDone(false);
+ ICaseResult caseResult = moduleResult.getOrCreateResult(CLASS);
+ ITestResult testResult1 = caseResult.getOrCreateResult(METHOD_1);
+ testResult1.setResultStatus(TestStatus.PASS);
+ testResult1.setRetry(true);
+ ITestResult testResult2 = caseResult.getOrCreateResult(METHOD_2);
+ testResult2.setResultStatus(TestStatus.FAIL);
+ testResult2.setStackTrace(STACK_TRACE);
+ testResult2.setRetry(true);
+
+ // Assume no additional filtering is applied to retry, and all tests for the module have
+ // been collected. Thus, module "done" value should switch.
+ mReporter.testRunStarted(ID, 1);
+
+ TestIdentifier test2 = new TestIdentifier(CLASS, METHOD_2);
+ mReporter.testStarted(test2);
+ mReporter.testEnded(test2, new HashMap<String, String>());
+
+ mReporter.testRunEnded(10, new HashMap<String, String>());
+ mReporter.invocationEnded(10);
+
+ // Verification that results have been overwritten.
+ IInvocationResult result = mReporter.getResult();
+ assertEquals("Expected 2 pass", 2, result.countResults(TestStatus.PASS));
+ assertEquals("Expected 0 failures", 0, result.countResults(TestStatus.FAIL));
+ List<IModuleResult> modules = result.getModules();
+ assertEquals("Expected 1 module", 1, modules.size());
+ IModuleResult module = modules.get(0);
+ assertTrue("Module should be marked done", module.isDone());
+ }
+
+ public void testRetryCannotSetDone() throws Exception {
+ mReporter.invocationStarted(mBuildInfo);
+ // Set mCanMarkDone directly (otherwise we must build result directory, write XML, and
+ // perform actual retry)
+ mReporter.mCanMarkDone = false;
+ // Set up IInvocationResult with existing results from previous session
+ IInvocationResult invocationResult = mReporter.getResult();
+ IModuleResult moduleResult = invocationResult.getOrCreateModule(ID);
+ moduleResult.setDone(false);
+ ICaseResult caseResult = moduleResult.getOrCreateResult(CLASS);
+ ITestResult testResult1 = caseResult.getOrCreateResult(METHOD_1);
+ testResult1.setResultStatus(TestStatus.PASS);
+ testResult1.setRetry(true);
+ ITestResult testResult2 = caseResult.getOrCreateResult(METHOD_2);
+ testResult2.setResultStatus(TestStatus.FAIL);
+ testResult2.setStackTrace(STACK_TRACE);
+ testResult2.setRetry(true);
+
+ // Since using retry-type failed option, we only run previously failed test
+ // and don't run any non-executed tests, so module "done" value should not switch.
+ mReporter.testRunStarted(ID, 1);
+
+ TestIdentifier test2 = new TestIdentifier(CLASS, METHOD_2);
+ mReporter.testStarted(test2);
+ mReporter.testEnded(test2, new HashMap<String, String>());
+
+ mReporter.testRunEnded(10, new HashMap<String, String>());
+ mReporter.invocationEnded(10);
+
+ // Verification that results have been overwritten.
+ IInvocationResult result = mReporter.getResult();
+ assertEquals("Expected 2 pass", 2, result.countResults(TestStatus.PASS));
+ assertEquals("Expected 0 failures", 0, result.countResults(TestStatus.FAIL));
+ List<IModuleResult> modules = result.getModules();
+ assertEquals("Expected 1 module", 1, modules.size());
+ IModuleResult module = modules.get(0);
+ assertFalse("Module should not be marked done", module.isDone());
+ }
+
public void testResultReporting_moduleNotDone() throws Exception {
mReporter.invocationStarted(mBuildInfo);
mReporter.testRunStarted(ID, 2);
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java b/common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java
new file mode 100644
index 0000000..faac61f
--- /dev/null
+++ b/common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import com.android.annotations.Nullable;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import com.google.common.hash.BloomFilter;
+import com.google.common.hash.Funnels;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/***
+ * Calculate and store checksum values for files and test results
+ */
+public final class ChecksumReporter implements Serializable {
+
+ public static final String NAME = "checksum.data";
+ public static final String PREV_NAME = "checksum.previous.data";
+
+ private static final double DEFAULT_FPP = 0.05;
+ private static final String SEPARATOR = "/";
+ private static final String ID_SEPARATOR = "@";
+ private static final String NAME_SEPARATOR = ".";
+
+ private static final short CURRENT_VERSION = 1;
+ // Serialized format Id (ie magic number) used to identify serialized data.
+ static final short SERIALIZED_FORMAT_CODE = 650;
+
+ private final BloomFilter<CharSequence> mResultChecksum;
+ private final HashMap<String, byte[]> mFileChecksum;
+ private final short mVersion;
+
+ /***
+ * Calculate checksum of test results and files in result directory and write to disk
+ * @param dir test results directory
+ * @param result test results
+ * @return true if successful, false if unable to calculate or store the checksum
+ */
+ public static boolean tryCreateChecksum(File dir, IInvocationResult result) {
+ try {
+ int totalCount = countTestResults(result);
+ ChecksumReporter checksumReporter =
+ new ChecksumReporter(totalCount, DEFAULT_FPP, CURRENT_VERSION);
+ checksumReporter.addInvocation(result);
+ checksumReporter.addDirectory(dir);
+ checksumReporter.saveToFile(dir);
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ /***
+ * Create Checksum Reporter from data saved on disk
+ * @param directory
+ * @return
+ * @throws ChecksumValidationException
+ */
+ public static ChecksumReporter load(File directory) throws ChecksumValidationException {
+ ChecksumReporter reporter = new ChecksumReporter(directory);
+ if (reporter.getCapacity() > 1.1) {
+ throw new ChecksumValidationException("Capacity exceeded.");
+ }
+ return reporter;
+ }
+
+ /***
+ * Deserialize checksum from file
+ * @param directory the parent directory containing the checksum file
+ * @throws ChecksumValidationException
+ */
+ public ChecksumReporter(File directory) throws ChecksumValidationException {
+ File file = new File(directory, ChecksumReporter.NAME);
+ try (FileInputStream fileStream = new FileInputStream(file);
+ InputStream outputStream = new BufferedInputStream(fileStream);
+ ObjectInput objectInput = new ObjectInputStream(outputStream)) {
+ short magicNumber = objectInput.readShort();
+ switch (magicNumber) {
+ case SERIALIZED_FORMAT_CODE:
+ mVersion = objectInput.readShort();
+ mResultChecksum = (BloomFilter<CharSequence>) objectInput.readObject();
+ mFileChecksum = (HashMap<String, byte[]>) objectInput.readObject();
+ break;
+ default:
+ throw new ChecksumValidationException("Unknown format of serialized data.");
+ }
+ } catch (Exception e) {
+ throw new ChecksumValidationException("Unable to load checksum from file", e);
+ }
+ if (mVersion > CURRENT_VERSION) {
+ throw new ChecksumValidationException(
+ "File contains a newer version of ChecksumReporter");
+ }
+ }
+
+ /***
+ * Create new instance of ChecksumReporter
+ * @param testCount the number of test results that will be stored
+ * @param fpp the false positive percentage for result lookup misses
+ */
+ public ChecksumReporter(int testCount, double fpp, short version) {
+ mResultChecksum = BloomFilter.create(Funnels.unencodedCharsFunnel(),
+ testCount, fpp);
+ mFileChecksum = new HashMap<>();
+ mVersion = version;
+ }
+
+ /***
+ * Add each test result from each module and test case
+ */
+ public void addInvocation(IInvocationResult invocationResult) {
+ for (IModuleResult module : invocationResult.getModules()) {
+ String buildFingerprint = invocationResult.getBuildFingerprint();
+ addModuleResult(module, buildFingerprint);
+ for (ICaseResult caseResult : module.getResults()) {
+ for (ITestResult testResult : caseResult.getResults()) {
+ addTestResult(testResult, module, buildFingerprint);
+ }
+ }
+ }
+ }
+
+ /***
+ * Calculate CRC of file and store the result
+ * @param file crc calculated on this file
+ * @param path part of the key to identify the files crc
+ */
+ public void addFile(File file, String path) {
+ byte[] crc;
+ try {
+ crc = calculateFileChecksum(file);
+ } catch (ChecksumValidationException e) {
+ crc = new byte[0];
+ }
+ String key = path + SEPARATOR + file.getName();
+ mFileChecksum.put(key, crc);
+ }
+
+ @VisibleForTesting
+ public boolean containsFile(File file, String path) {
+ String key = path + SEPARATOR + file.getName();
+ if (mFileChecksum.containsKey(key))
+ {
+ try {
+ byte[] crc = calculateFileChecksum(file);
+ return Arrays.equals(mFileChecksum.get(key), crc);
+ } catch (ChecksumValidationException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /***
+ * Adds all child files recursively through all sub directories
+ * @param directory target that is deeply searched for files
+ */
+ public void addDirectory(File directory) {
+ addDirectory(directory, directory.getName());
+ }
+
+ /***
+ * @param path the relative path to the current directory from the base directory
+ */
+ private void addDirectory(File directory, String path) {
+ for(String childName : directory.list()) {
+ File child = new File(directory, childName);
+ if (child.isDirectory()) {
+ addDirectory(child, path + SEPARATOR + child.getName());
+ } else {
+ addFile(child, path);
+ }
+ }
+ }
+
+ /***
+ * Calculate checksum of test result and store the value
+ * @param testResult the target of the checksum
+ * @param moduleResult the module that contains the test result
+ * @param buildFingerprint the fingerprint the test execution is running against
+ */
+ public void addTestResult(
+ ITestResult testResult, IModuleResult moduleResult, String buildFingerprint) {
+
+ String signature = generateTestResultSignature(testResult, moduleResult, buildFingerprint);
+ mResultChecksum.put(signature);
+ }
+
+ @VisibleForTesting
+ public boolean containsTestResult(
+ ITestResult testResult, IModuleResult moduleResult, String buildFingerprint) {
+
+ String signature = generateTestResultSignature(testResult, moduleResult, buildFingerprint);
+ return mResultChecksum.mightContain(signature);
+ }
+
+ /***
+ * Calculate checksm of module result and store value
+ * @param moduleResult the target of the checksum
+ * @param buildFingerprint the fingerprint the test execution is running against
+ */
+ public void addModuleResult(IModuleResult moduleResult, String buildFingerprint) {
+ mResultChecksum.put(
+ generateModuleResultSignature(moduleResult, buildFingerprint));
+ mResultChecksum.put(
+ generateModuleSummarySignature(moduleResult, buildFingerprint));
+ }
+
+ @VisibleForTesting
+ public Boolean containsModuleResult(IModuleResult moduleResult, String buildFingerprint) {
+ return mResultChecksum.mightContain(
+ generateModuleResultSignature(moduleResult, buildFingerprint));
+ }
+
+ /***
+ * Write the checksum data to disk.
+ * Overwrites existing file
+ * @param directory
+ * @throws IOException
+ */
+ public void saveToFile(File directory) throws IOException {
+ File file = new File(directory, NAME);
+
+ try (FileOutputStream fileStream = new FileOutputStream(file, false);
+ OutputStream outputStream = new BufferedOutputStream(fileStream);
+ ObjectOutput objectOutput = new ObjectOutputStream(outputStream)) {
+ objectOutput.writeShort(SERIALIZED_FORMAT_CODE);
+ objectOutput.writeShort(mVersion);
+ objectOutput.writeObject(mResultChecksum);
+ objectOutput.writeObject(mFileChecksum);
+ }
+ }
+
+ @VisibleForTesting
+ double getCapacity() {
+ // If default FPP changes:
+ // increment the CURRENT_VERSION and set the denominator based on this.mVersion
+ return mResultChecksum.expectedFpp() / DEFAULT_FPP;
+ }
+
+ static String generateTestResultSignature(ITestResult testResult, IModuleResult module,
+ String buildFingerprint) {
+ StringBuilder sb = new StringBuilder();
+ String stacktrace = testResult.getStackTrace();
+
+ stacktrace = stacktrace == null ? "" : stacktrace.trim();
+ // Line endings for stacktraces are somewhat unpredictable and there is no need to
+ // actually read the result they are all removed for consistency.
+ stacktrace = stacktrace.replaceAll("\\r?\\n|\\r", "");
+ sb.append(buildFingerprint).append(SEPARATOR)
+ .append(module.getId()).append(SEPARATOR)
+ .append(testResult.getFullName()).append(SEPARATOR)
+ .append(testResult.getResultStatus().getValue()).append(SEPARATOR)
+ .append(stacktrace).append(SEPARATOR);
+ return sb.toString();
+ }
+
+ static String generateTestResultSignature(
+ String packageName, String suiteName, String caseName, String testName, String abi,
+ String status,
+ String stacktrace,
+ String buildFingerprint) {
+
+ String testId = buildTestId(suiteName, caseName, testName, abi);
+ StringBuilder sb = new StringBuilder();
+
+ stacktrace = stacktrace == null ? "" : stacktrace.trim();
+ // Line endings for stacktraces are somewhat unpredictable and there is no need to
+ // actually read the result they are all removed for consistency.
+ stacktrace = stacktrace.replaceAll("\\r?\\n|\\r", "");
+ sb.append(buildFingerprint)
+ .append(SEPARATOR)
+ .append(packageName)
+ .append(SEPARATOR)
+ .append(testId)
+ .append(SEPARATOR)
+ .append(status)
+ .append(SEPARATOR)
+ .append(stacktrace)
+ .append(SEPARATOR);
+ return sb.toString();
+ }
+
+ private static String buildTestId(
+ String suiteName, String caseName, String testName, @Nullable String abi) {
+ String name = Joiner.on(NAME_SEPARATOR).skipNulls().join(
+ Strings.emptyToNull(suiteName),
+ Strings.emptyToNull(caseName),
+ Strings.emptyToNull(testName));
+ return Joiner.on(ID_SEPARATOR).skipNulls().join(
+ Strings.emptyToNull(name),
+ Strings.emptyToNull(abi));
+ }
+
+
+ private static String generateModuleResultSignature(IModuleResult module,
+ String buildFingerprint) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(buildFingerprint).append(SEPARATOR)
+ .append(module.getId()).append(SEPARATOR)
+ .append(module.isDone()).append(SEPARATOR)
+ .append(module.getNotExecuted()).append(SEPARATOR)
+ .append(module.countResults(TestStatus.FAIL));
+ return sb.toString();
+ }
+
+ private static String generateModuleSummarySignature(IModuleResult module,
+ String buildFingerprint) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(buildFingerprint).append(SEPARATOR)
+ .append(module.getId()).append(SEPARATOR)
+ .append(module.countResults(TestStatus.FAIL));
+ return sb.toString();
+ }
+
+ static byte[] calculateFileChecksum(File file) throws ChecksumValidationException {
+
+ try (FileInputStream fis = new FileInputStream(file);
+ InputStream inputStream = new BufferedInputStream(fis)) {
+ MessageDigest hashSum = MessageDigest.getInstance("SHA-256");
+ int cnt;
+ int bufferSize = 8192;
+ byte [] buffer = new byte[bufferSize];
+ while ((cnt = inputStream.read(buffer)) != -1) {
+ hashSum.update(buffer, 0, cnt);
+ }
+
+ byte[] partialHash = new byte[32];
+ hashSum.digest(partialHash, 0, 32);
+ return partialHash;
+ } catch (NoSuchAlgorithmException e) {
+ throw new ChecksumValidationException("Unable to hash file.", e);
+ } catch (IOException e) {
+ throw new ChecksumValidationException("Unable to hash file.", e);
+ } catch (DigestException e) {
+ throw new ChecksumValidationException("Unable to hash file.", e);
+ }
+ }
+
+
+ private static int countTestResults(IInvocationResult invocation) {
+ int count = 0;
+ for (IModuleResult module : invocation.getModules()) {
+ // Two entries per module (result & summary)
+ count += 2;
+ for (ICaseResult caseResult : module.getResults()) {
+ count += caseResult.getResults().size();
+ }
+ }
+ return count;
+ }
+
+ public static class ChecksumValidationException extends Exception {
+ public ChecksumValidationException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ public ChecksumValidationException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+ }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java
similarity index 79%
rename from common/util/src/com/android/compatibility/common/util/ResultHandler.java
rename to common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java
index 435f515..d04ed8e 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -15,6 +15,10 @@
*/
package com.android.compatibility.common.util;
+import com.android.compatibility.common.util.ChecksumReporter.ChecksumValidationException;
+
+import com.google.common.base.Strings;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
@@ -28,6 +32,9 @@
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
@@ -98,12 +105,17 @@
* @param resultsDir
*/
public static List<IInvocationResult> getResults(File resultsDir) {
+ return getResults(resultsDir, false);
+ }
+
+ /**
+ * @param resultsDir
+ * @param useChecksum
+ */
+ public static List<IInvocationResult> getResults(
+ File resultsDir, Boolean useChecksum) {
List<IInvocationResult> results = new ArrayList<>();
- File[] files = resultsDir.listFiles();
- if (files == null || files.length == 0) {
- // No results, just return the empty list
- return results;
- }
+ List<File> files = getResultDirectories(resultsDir);;
for (File resultDir : files) {
if (!resultDir.isDirectory()) {
continue;
@@ -113,7 +125,20 @@
if (!resultFile.exists()) {
continue;
}
+ Boolean invocationUseChecksum = useChecksum;
IInvocationResult invocation = new InvocationResult();
+ invocation.setRetryDirectory(resultDir);
+ ChecksumReporter checksumReporter = null;
+ if (invocationUseChecksum) {
+ try {
+ checksumReporter = ChecksumReporter.load(resultDir);
+ invocation.setRetryChecksumStatus(RetryChecksumStatus.RetryWithChecksum);
+ } catch (ChecksumValidationException e) {
+ // Unable to read checksum form previous execution
+ invocation.setRetryChecksumStatus(RetryChecksumStatus.RetryWithoutChecksum);
+ invocationUseChecksum = false;
+ }
+ }
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();
parser.setInput(new FileReader(resultFile));
@@ -155,7 +180,7 @@
int notExecuted =
Integer.parseInt(parser.getAttributeValue(NS, NOT_EXECUTED_ATTR));
module.setNotExecuted(notExecuted);
- int runtime = Integer.parseInt(parser.getAttributeValue(NS, RUNTIME_ATTR));
+ long runtime = Long.parseLong(parser.getAttributeValue(NS, RUNTIME_ATTR));
module.addRuntime(runtime);
while (parser.nextTag() == XmlPullParser.START_TAG) {
parser.require(XmlPullParser.START_TAG, NS, CASE_TAG);
@@ -194,10 +219,22 @@
}
}
parser.require(XmlPullParser.END_TAG, NS, TEST_TAG);
+ Boolean checksumMismatch = invocationUseChecksum
+ && !checksumReporter.containsTestResult(
+ test, module, invocation.getBuildFingerprint());
+ if (checksumMismatch) {
+ test.removeResult();
+ }
}
parser.require(XmlPullParser.END_TAG, NS, CASE_TAG);
}
parser.require(XmlPullParser.END_TAG, NS, MODULE_TAG);
+ Boolean checksumMismatch = invocationUseChecksum
+ && !checksumReporter.containsModuleResult(
+ module, invocation.getBuildFingerprint());
+ if (checksumMismatch) {
+ module.setDone(false);
+ }
}
parser.require(XmlPullParser.END_TAG, NS, RESULT_TAG);
results.add(invocation);
@@ -296,6 +333,10 @@
serializer.startTag(NS, BUILD_TAG);
for (Entry<String, String> entry : result.getInvocationInfo().entrySet()) {
serializer.attribute(NS, entry.getKey(), entry.getValue());
+ if (Strings.isNullOrEmpty(result.getBuildFingerprint()) &&
+ entry.getKey().equals(BUILD_FINGERPRINT)) {
+ result.setBuildFingerprint(entry.getValue());
+ }
}
serializer.endTag(NS, BUILD_TAG);
@@ -370,20 +411,59 @@
serializer.endTag(NS, MODULE_TAG);
}
serializer.endDocument();
+ createChecksum(resultDir, result);
return resultFile;
}
+ private static void createChecksum(File resultDir, IInvocationResult invocationResult) {
+ RetryChecksumStatus retryStatus = invocationResult.getRetryChecksumStatus();
+ switch (retryStatus) {
+ case NotRetry: case RetryWithChecksum:
+ // Do not disrupt the process if there is a problem generating checksum.
+ ChecksumReporter.tryCreateChecksum(resultDir, invocationResult);
+ break;
+ case RetryWithoutChecksum:
+ // If the previous run has an invalid checksum file,
+ // copy it into current results folder for future troubleshooting
+ File retryDirectory = invocationResult.getRetryDirectory();
+ Path retryChecksum = FileSystems.getDefault().getPath(
+ retryDirectory.getAbsolutePath(), ChecksumReporter.NAME);
+ if (!retryChecksum.toFile().exists()) {
+ // if no checksum file, check for a copy from a previous retry
+ retryChecksum = FileSystems.getDefault().getPath(
+ retryDirectory.getAbsolutePath(), ChecksumReporter.PREV_NAME);
+ }
+
+ if (retryChecksum.toFile().exists()) {
+ File checksumCopy = new File(resultDir, ChecksumReporter.PREV_NAME);
+ try (FileOutputStream stream = new FileOutputStream(checksumCopy)) {
+ Files.copy(retryChecksum, stream);
+ } catch (IOException e) {
+ // Do not disrupt the process if there is a problem copying checksum
+ }
+ }
+ }
+ }
+
/**
* Find the IInvocationResult for the given sessionId.
*/
public static IInvocationResult findResult(File resultsDir, Integer sessionId)
throws FileNotFoundException {
+ return findResult(resultsDir, sessionId, true);
+ }
+
+ /**
+ * Find the IInvocationResult for the given sessionId.
+ */
+ private static IInvocationResult findResult(
+ File resultsDir, Integer sessionId, Boolean useChecksum) throws FileNotFoundException {
if (sessionId < 0) {
throw new IllegalArgumentException(
String.format("Invalid session id [%d] ", sessionId));
}
- List<IInvocationResult> results = getResults(resultsDir);
+ List<IInvocationResult> results = getResults(resultsDir, useChecksum);
if (results == null || sessionId >= results.size()) {
throw new RuntimeException(String.format("Could not find session [%d]", sessionId));
}
@@ -391,6 +471,33 @@
}
/**
+ * Get a list of child directories that contain test invocation results
+ * @param resultsDir the root test result directory
+ * @return
+ */
+ public static List<File> getResultDirectories(File resultsDir) {
+ List<File> directoryList = new ArrayList<>();
+ File[] files = resultsDir.listFiles();
+ if (files == null || files.length == 0) {
+ // No results, just return the empty list
+ return directoryList;
+ }
+ for (File resultDir : files) {
+ if (!resultDir.isDirectory()) {
+ continue;
+ }
+ // Only include if it contain results file
+ File resultFile = new File(resultDir, TEST_RESULT_FILE_NAME);
+ if (!resultFile.exists()) {
+ continue;
+ }
+ directoryList.add(resultDir);
+ }
+ Collections.sort(directoryList, (d1, d2) -> d1.getName().compareTo(d2.getName()));
+ return directoryList;
+ }
+
+ /**
* Return the given time as a {@link String} suitable for displaying.
* <p/>
* Example: Fri Aug 20 15:13:03 PDT 2010
diff --git a/common/host-side/util/tests/Android.mk b/common/host-side/util/tests/Android.mk
index a0e9a3b..4a78835 100644
--- a/common/host-side/util/tests/Android.mk
+++ b/common/host-side/util/tests/Android.mk
@@ -18,7 +18,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := compatibility-host-util junit json-prebuilt
+LOCAL_JAVA_LIBRARIES := compatibility-host-util junit json-prebuilt tradefed-prebuilt
LOCAL_MODULE := compatibility-host-util-tests
diff --git a/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
index 4b47bf3..169cfdb 100644
--- a/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
+++ b/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
@@ -28,6 +28,7 @@
public HostUnitTests() {
super();
addTestSuite(DynamicConfigHandlerTest.class);
+ addTestSuite(ResultHandlerTest.class);
}
public static Test suite() {
diff --git a/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
similarity index 100%
rename from common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
rename to common/host-side/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
diff --git a/common/util/src/com/android/compatibility/common/util/IInvocationResult.java b/common/util/src/com/android/compatibility/common/util/IInvocationResult.java
index 739dd48..2b75371 100644
--- a/common/util/src/com/android/compatibility/common/util/IInvocationResult.java
+++ b/common/util/src/com/android/compatibility/common/util/IInvocationResult.java
@@ -114,4 +114,24 @@
* Return the number of completed test modules for this invocation.
*/
int getModuleCompleteCount();
+
+ /**
+ * Return status of checksum from previous session
+ */
+ RetryChecksumStatus getRetryChecksumStatus();
+
+ /**
+ * Set status of checksum from previous session
+ */
+ void setRetryChecksumStatus(RetryChecksumStatus retryStatus);
+
+ /**
+ * Return the directory of the previous sessions results
+ */
+ File getRetryDirectory();
+
+ /**
+ * Set the directory of the previous sessions results
+ */
+ void setRetryDirectory(File resultDir);
}
diff --git a/common/util/src/com/android/compatibility/common/util/ITestResult.java b/common/util/src/com/android/compatibility/common/util/ITestResult.java
index c35b997..24ee1aa 100644
--- a/common/util/src/com/android/compatibility/common/util/ITestResult.java
+++ b/common/util/src/com/android/compatibility/common/util/ITestResult.java
@@ -142,4 +142,8 @@
*/
boolean isRetry();
+ /**
+ * Clear the existing result and default to 'failed'
+ */
+ void removeResult();
}
diff --git a/common/util/src/com/android/compatibility/common/util/InvocationResult.java b/common/util/src/com/android/compatibility/common/util/InvocationResult.java
index f74c61d..83f1dac 100644
--- a/common/util/src/com/android/compatibility/common/util/InvocationResult.java
+++ b/common/util/src/com/android/compatibility/common/util/InvocationResult.java
@@ -15,6 +15,7 @@
*/
package com.android.compatibility.common.util;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -37,7 +38,8 @@
private String mTestPlan;
private String mCommandLineArgs;
private int mNotExecuted = 0;
-
+ private RetryChecksumStatus mRetryChecksumStatus = RetryChecksumStatus.NotRetry;
+ private File mRetryDirectory = null;
/**
* {@inheritDoc}
*/
@@ -201,4 +203,36 @@
}
return completeModules;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public RetryChecksumStatus getRetryChecksumStatus() {
+ return mRetryChecksumStatus;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setRetryChecksumStatus(RetryChecksumStatus retryStatus) {
+ mRetryChecksumStatus = retryStatus;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public File getRetryDirectory() {
+ return mRetryDirectory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setRetryDirectory(File resultDir) {
+ mRetryDirectory = resultDir;
+ }
}
diff --git a/common/util/src/com/android/compatibility/common/util/RetryChecksumStatus.java b/common/util/src/com/android/compatibility/common/util/RetryChecksumStatus.java
new file mode 100644
index 0000000..a86ab37
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/RetryChecksumStatus.java
@@ -0,0 +1,8 @@
+package com.android.compatibility.common.util;
+
+
+public enum RetryChecksumStatus {
+ NotRetry,
+ RetryWithChecksum,
+ RetryWithoutChecksum
+}
diff --git a/common/util/src/com/android/compatibility/common/util/TestResult.java b/common/util/src/com/android/compatibility/common/util/TestResult.java
index ba378d0..36b6e5a 100644
--- a/common/util/src/com/android/compatibility/common/util/TestResult.java
+++ b/common/util/src/com/android/compatibility/common/util/TestResult.java
@@ -242,6 +242,15 @@
* {@inheritDoc}
*/
@Override
+ public void removeResult() {
+ setResultStatus(TestStatus.FAIL);
+ setStackTrace("");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public int compareTo(ITestResult another) {
return getName().compareTo(another.getName());
}
diff --git a/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java b/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
index 967b8a8..e6c6a87 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
@@ -37,7 +37,6 @@
addTestSuite(StatTest.class);
addTestSuite(TestFilterTest.class);
addTestSuite(TestResultTest.class);
- addTestSuite(ResultHandlerTest.class);
}
public static Test suite() {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index 1032c62..b77eef2 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -293,8 +293,18 @@
}
private String getAdoptionDisk() throws Exception {
- final String disks = getDevice().executeShellCommand("sm list-disks adoptable");
- if (disks == null || disks.length() == 0) {
+ // In the case where we run multiple test we cleanup the state of the device. This
+ // results in the execution of sm forget all which causes the MountService to "reset"
+ // all its knowledge about available drives. This can cause the adoptable drive to
+ // become temporarily unavailable.
+ int attempt = 0;
+ String disks = getDevice().executeShellCommand("sm list-disks adoptable");
+ while ((disks == null || disks.isEmpty()) && attempt++ < 15) {
+ Thread.sleep(1000);
+ disks = getDevice().executeShellCommand("sm list-disks adoptable");
+ }
+
+ if (disks == null || disks.isEmpty()) {
throw new AssertionError("Devices that claim to support adoptable storage must have "
+ "adoptable media inserted during CTS to verify correct behavior");
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index fd1bc4d..a4686aa 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -129,6 +129,7 @@
public void doDirectBootTest(String mode) throws Exception {
int[] users = {};
+ boolean doTest = true;
try {
users = createUsersForTest();
@@ -150,17 +151,22 @@
// Reboot system into known state with keys ejected
if (MODE_EMULATED.equals(mode)) {
- getDevice().executeShellCommand("sm set-emulate-fbe true");
+ final String res = getDevice().executeShellCommand("sm set-emulate-fbe true");
+ if (res != null && res.contains("Emulation not supported")) {
+ doTest = false;
+ }
getDevice().waitForDeviceOnline();
} else {
getDevice().rebootUntilOnline();
}
waitForBootCompleted();
- if (MODE_NONE.equals(mode)) {
- runDeviceTests(PKG, CLASS, "testVerifyUnlockedAndDismiss", users);
- } else {
- runDeviceTests(PKG, CLASS, "testVerifyLockedAndDismiss", users);
+ if (doTest) {
+ if (MODE_NONE.equals(mode)) {
+ runDeviceTests(PKG, CLASS, "testVerifyUnlockedAndDismiss", users);
+ } else {
+ runDeviceTests(PKG, CLASS, "testVerifyLockedAndDismiss", users);
+ }
}
} finally {
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index 250a198..d0453fb 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -262,7 +262,7 @@
Assert.assertNotNull("Permissions label should be present", permLabelView);
AccessibilityNodeInfo permItemView = findCollectionItem(permLabelView);
- Assert.assertNotNull("Permissions item should be present", permLabelView);
+ Assert.assertNotNull("Permissions item should be present", permItemView);
click(permItemView);
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
index 3bfa905..7dd303f 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
@@ -33,7 +33,7 @@
if (dpm.isAdminActive(cn)) {
dpm.removeActiveAdmin(cn);
- for (int i = 0; i < 1000 && dpm.isAdminActive(cn); i++) {
+ for (int i = 0; i < 6000 && dpm.isAdminActive(cn); i++) {
Thread.sleep(10);
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
index 8e42279..71aa205 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
@@ -16,9 +16,12 @@
package com.android.cts.deviceowner;
import android.app.admin.SecurityLog.SecurityEvent;
-import android.os.UserHandle;
+import android.os.Parcel;
+import android.os.SystemClock;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.TimeUnit;
public class SecurityLoggingTest extends BaseDeviceOwnerTest {
@@ -62,11 +65,72 @@
}
/**
- * Test: Test enabling and disabling of security logging.
+ * Test: retrieving security logs. This test has should be called when security logging is
+ * enabled and directly after reboot with device owner installed so that security logging
+ * actually takes place and fetching the logs isn't subject to rate limiting.
*/
- public void testEnablingAndDisablingSecurityLogging() {
+ public void testGetSecurityLogs() {
+ List<SecurityEvent> events = mDevicePolicyManager.retrieveSecurityLogs(getWho());
+
+ // There must be at least some events, e.g. PackageManager logs all process launches.
+ assertTrue("Unable to get events", events != null && events.size() > 0);
+
+ // We don't know much about the events, so just call public API methods and do a simple
+ // sanity check of timestamps.
+
+ // Check that timestamps are between system start and current time.
+ long systemStartedNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis())
+ - SystemClock.elapsedRealtimeNanos();
+ long nowNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis() + 1);
+
+ for (int i = 0; i < events.size(); i++) {
+ SecurityEvent event = events.get(i);
+ long currentTimestampNanos = event.getTimeNanos();
+ assertTrue("Logged event predates boot", currentTimestampNanos >= systemStartedNanos);
+ assertTrue("Last logged event is in future", currentTimestampNanos <= nowNanos);
+
+ // Test parcelling: flatten to a parcel.
+ Parcel p = Parcel.obtain();
+ event.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ // Restore from parcel and check contents.
+ SecurityEvent restored = SecurityEvent.CREATOR.createFromParcel(p);
+ p.recycle();
+
+ // For some events data is encapsulated into Object array.
+ if (event.getData() instanceof Object[]) {
+ assertTrue("Parcelling changed the array returned by getData",
+ Arrays.equals((Object[]) event.getData(), (Object[]) restored.getData()));
+ } else {
+ assertEquals("Parcelling changed the result of getData",
+ event.getData(), restored.getData());
+ }
+ assertEquals("Parcelling changed the result of getTag",
+ event.getTag(), restored.getTag());
+ assertEquals("Parcelling changed the result of getTimeNanos",
+ event.getTimeNanos(), restored.getTimeNanos());
+ assertEquals("Parcelling changed the result of describeContents",
+ event.describeContents(), restored.describeContents());
+ }
+ }
+
+ /**
+ * Test: Test enabling security logging. This test should be executed after installing a device
+ * owner so that we check that logging is not enabled by default. This test has a side effect:
+ * security logging is enabled after its execution.
+ */
+ public void testEnablingSecurityLogging() {
+ assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), true);
assertTrue(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
+ }
+
+ /**
+ * Test: Test disabling security logging. This test has a side effect: security logging is
+ * disabled after its execution.
+ */
+ public void testDisablingSecurityLogging() {
mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), false);
assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index a2c7219..2a992a8 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -19,6 +19,7 @@
<uses-sdk android:minSdkVersion="20"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WifiTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WifiTest.java
index 685ad82..124ebe8 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WifiTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WifiTest.java
@@ -23,6 +23,7 @@
import android.os.SystemClock;
import android.test.AndroidTestCase;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.android.compatibility.common.util.WifiConfigCreator.ACTION_CREATE_WIFI_CONFIG;
@@ -50,10 +51,27 @@
// Shared WifiManager instance.
private WifiManager mWifiManager;
+ // Original setting of WifiManager.isWifiEnabled() before setup.
+ private boolean mWifiEnabled;
+
@Override
public void setUp() throws Exception {
super.setUp();
mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+ mWifiEnabled = mWifiManager.isWifiEnabled();
+ if (!mWifiEnabled) {
+ mWifiManager.setWifiEnabled(true);
+ awaitWifiEnabledState(true);
+ }
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (!mWifiEnabled) {
+ mWifiManager.setWifiEnabled(false);
+ awaitWifiEnabledState(false);
+ }
+ super.tearDown();
}
/**
@@ -92,7 +110,6 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
}
-
assertTrue(awaitNetworkState(NETWORK_SSID, /* exists */ false));
}
@@ -123,6 +140,23 @@
}
/**
+ * Block until {@link WifiManager#isWifiEnabled()} returns {@param enabled}. Wait for up to
+ * {@link WifiTest#UPDATE_TIMEOUT_MS} milliseconds, in increments of
+ * {@link WifiTest#UPDATE_INTERVAL_MS}.
+ */
+ private void awaitWifiEnabledState(boolean enabled) throws RuntimeException {
+ for (int probes = 0; probes * UPDATE_INTERVAL_MS <= UPDATE_TIMEOUT_MS; probes++) {
+ if (probes != 0) {
+ SystemClock.sleep(UPDATE_INTERVAL_MS);
+ }
+ if (mWifiManager.isWifiEnabled() == enabled) {
+ return;
+ }
+ }
+ throw new RuntimeException("Waited too long for wifi enabled state = " + enabled);
+ }
+
+ /**
* Internal method to find an existing {@link WifiConfiguration} with the given SSID.
*
* @return A {@link WifiConfiguration} matching the specification, or {@code null} if no such
@@ -132,9 +166,12 @@
if (!ssid.startsWith("\"")) {
ssid = '"' + ssid + '"';
}
- for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
- if (ssid.equals(config.SSID)) {
- return config;
+ final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ if (configs != null) {
+ for (WifiConfiguration config : configs) {
+ if (ssid.equals(config.SSID)) {
+ return config;
+ }
}
}
return null;
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
index 1bb797f..98a47f2 100644
--- a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
@@ -40,8 +40,7 @@
.pkg("com.android.settings");
private static final BySelector INSTALL_BUTTON_SELECTOR = By
.clazz(android.widget.Button.class.getName())
- .res("com.android.packageinstaller:id/ok_button")
- .pkg("com.google.android.packageinstaller");
+ .res("com.android.packageinstaller:id/ok_button");
public void testManualInstallSucceeded() throws Exception {
assertInstallPackage();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 76e4c73..d5aec5c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -248,6 +248,12 @@
return !runResult.hasFailedTests() && runResult.getNumTestsInState(TestStatus.PASSED) > 0;
}
+ /** Reboots the device and block until the boot complete flag is set. */
+ protected void rebootAndWaitUntilReady() throws DeviceNotAvailableException {
+ getDevice().rebootUntilOnline();
+ assertTrue("Device failed to boot", getDevice().waitForBootComplete(60000));
+ }
+
/** Returns true if the system supports the split between system and primary user. */
protected boolean hasUserSplit() throws DeviceNotAvailableException {
return getBooleanSystemProperty("ro.fw.system_user_split", false);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 70f5218..9cf6fc8 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -474,7 +474,7 @@
String command = "rm " + TEST_APP_LOCATION + apk.getName();
getDevice().executeShellCommand(command);
getDevice().uninstallPackage(TEST_APP_PKG);
- getDevice().uninstallPackage(PACKAGE_INSTALLER_APK);
+ getDevice().uninstallPackage(PACKAGE_INSTALLER_PKG);
if (unknownSourceSetting != null) {
putSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, unknownSourceSetting,
mUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 4748b35..c373f3f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -17,6 +17,7 @@
package com.android.cts.devicepolicy;
import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
/**
* Set of tests for Device Owner use cases.
@@ -62,6 +63,7 @@
DEVICE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mPrimaryUserId,
/*expectFailure*/ false)) {
removeAdmin(DEVICE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mPrimaryUserId);
+ getDevice().uninstallPackage(DEVICE_OWNER_PKG);
fail("Failed to set device owner");
}
}
@@ -101,7 +103,7 @@
}
public void testWifi() throws Exception {
- if (hasDeviceFeature("android.hardware.wifi")) {
+ if (!hasDeviceFeature("android.hardware.wifi")) {
return;
}
executeDeviceOwnerTest("WifiTest");
@@ -310,7 +312,18 @@
}
executeDeviceTestMethod(".SecurityLoggingTest",
"testRetrievingSecurityLogsNotPossibleImmediatelyAfterPreviousSuccessfulRetrieval");
- executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingAndDisablingSecurityLogging");
+ try {
+ executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+ rebootAndWaitUntilReady();
+ // Sleep for ~1 minute so that SecurityLogMonitor fetches the security events. This is
+ // an implementation detail we shouldn't rely on but there is no other way to do it
+ // currently.
+ Thread.sleep(TimeUnit.SECONDS.toMillis(70));
+ executeDeviceTestMethod(".SecurityLoggingTest", "testGetSecurityLogs");
+ } finally {
+ // Always attempt to disable security logging to bring the device to initial state.
+ executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+ }
}
public void testLockTask() throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 42ff41a..a164428 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -36,9 +36,13 @@
mUserId = mPrimaryUserId;
installAppAsUser(DEVICE_ADMIN_APK, mUserId);
- assertTrue("Failed to set device owner", setDeviceOwner(
+ if (!setDeviceOwner(
DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
- /*expectFailure*/ false));
+ /*expectFailure*/ false)) {
+ removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+ getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
+ fail("Failed to set device owner");
+ }
}
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
index f65a245..e1d50bd 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
@@ -30,7 +30,13 @@
mUserId = mPrimaryUserId;
installAppAsUser(DEVICE_ADMIN_APK, mUserId);
- setProfileOwnerOrFail(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+ if (!setProfileOwner(
+ DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
+ /*expectFailure*/ false)) {
+ removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+ getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
+ fail("Failed to set profile owner");
+ }
}
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
index 4105012..aedabc2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
@@ -36,9 +36,13 @@
if (mHasFeature) {
installAppAsUser(PROFILE_OWNER_APK, mUserId);
- assertTrue("Failed to set profile owner",
- setProfileOwner(PROFILE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
- /* expectFailure */ false));
+ if (!setProfileOwner(
+ PROFILE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
+ /* expectFailure */ false)) {
+ removeAdmin(PROFILE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+ getDevice().uninstallPackage(PROFILE_OWNER_PKG);
+ fail("Failed to set profile owner");
+ }
}
}
@@ -53,13 +57,6 @@
super.tearDown();
}
- public void testWifi() throws Exception {
- if (hasDeviceFeature("android.hardware.wifi")) {
- return;
- }
- executeProfileOwnerTest("WifiTest");
- }
-
private void executeProfileOwnerTest(String testClassName) throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTestApi23.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTestApi23.java
index 4c1477e..33aa1d2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTestApi23.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTestApi23.java
@@ -34,7 +34,13 @@
mUserId = USER_OWNER;
installAppAsUser(DEVICE_ADMIN_APK, mUserId);
- setProfileOwnerOrFail(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+ if (!setProfileOwner(
+ DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
+ /*expectFailure*/ false)) {
+ removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+ getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
+ fail("Failed to set profile owner");
+ }
}
}
@@ -43,6 +49,7 @@
if (mHasFeature) {
assertTrue("Failed to remove profile owner.",
removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId));
+ getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
}
super.tearDown();
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
index 41e7864..bc61a81 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
@@ -44,13 +44,15 @@
assertTrue("Failed to clear owner",
removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
mDeviceOwnerUserId));
+ assertTrue("Some user restrictions are still set",
+ runTests("userrestrictions.CheckNoOwnerRestrictionsTest",
+ mDeviceOwnerUserId));
}
- assertTrue("Some user restrictions are still set",
- runTests("userrestrictions.CheckNoOwnerRestrictionsTest", mDeviceOwnerUserId));
// DO/PO might have set DISALLOW_REMOVE_USER, so it needs to be done after removing
// them.
removeTestUsers();
+ getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
}
super.tearDown();
}
diff --git a/hostsidetests/security/Android.mk b/hostsidetests/security/Android.mk
index cc04431..418ce38 100644
--- a/hostsidetests/security/Android.mk
+++ b/hostsidetests/security/Android.mk
@@ -18,6 +18,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := res
+
LOCAL_MODULE_TAGS := optional
# tag this module as a cts test artifact
diff --git a/hostsidetests/security/res/add-debug.apk b/hostsidetests/security/res/add-debug.apk
new file mode 100644
index 0000000..dff9cb6
--- /dev/null
+++ b/hostsidetests/security/res/add-debug.apk
Binary files differ
diff --git a/hostsidetests/security/res/crash_mod.apk b/hostsidetests/security/res/crash_mod.apk
new file mode 100644
index 0000000..4991294
--- /dev/null
+++ b/hostsidetests/security/res/crash_mod.apk
Binary files differ
diff --git a/hostsidetests/security/res/poc b/hostsidetests/security/res/poc
new file mode 100644
index 0000000..8b6f012
--- /dev/null
+++ b/hostsidetests/security/res/poc
Binary files differ
diff --git a/hostsidetests/security/res/test-case b/hostsidetests/security/res/test-case
new file mode 100644
index 0000000..bac4af1
--- /dev/null
+++ b/hostsidetests/security/res/test-case
Binary files differ
diff --git a/hostsidetests/security/src/android/security/cts/AdbUtils.java b/hostsidetests/security/src/android/security/cts/AdbUtils.java
new file mode 100644
index 0000000..a3018fa
--- /dev/null
+++ b/hostsidetests/security/src/android/security/cts/AdbUtils.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Scanner;
+
+public class AdbUtils {
+
+ /** Runs a commandline on the specified device
+ *
+ * @param command the command to be ran
+ * @param device device for the command to be ran on
+ * @return the console output from running the command
+ */
+ public static String runCommandLine(String command, ITestDevice device) throws Exception
+ {
+ return device.executeShellCommand(command);
+ }
+
+ /**
+ * Pushes and runs a binary to the selected device
+ *
+ * @param pathToPoc a string path to poc from the /res folder
+ * @param device device to be ran on
+ * @return the console output from the binary
+ */
+ public static String runPoc(String pathToPoc, ITestDevice device) throws Exception {
+ String fullResourceName = pathToPoc;
+ File pocFile = File.createTempFile("poc", "");
+ try {
+ pocFile = extractResource(fullResourceName, pocFile);
+ device.pushFile(pocFile, "/data/local/tmp/poc");
+ device.executeShellCommand("chmod +x /data/local/tmp/poc");
+ return device.executeShellCommand("/data/local/tmp/poc");
+ } finally {
+ pocFile.delete();
+ }
+ }
+
+ /**
+ * Pushes and installs an apk to the selected device
+ *
+ * @param pathToApk a string path to apk from the /res folder
+ * @param device device to be ran on
+ * @return the output from attempting to install the apk
+ */
+ public static String installApk(String pathToApk, ITestDevice device) throws Exception {
+
+ String fullResourceName = pathToApk;
+ File apkFile = File.createTempFile("apkFile", ".apk");
+ try {
+ apkFile = extractResource(fullResourceName, apkFile);
+ return device.installPackage(apkFile, true);
+ } finally {
+ apkFile.delete();
+ }
+ }
+
+ /**
+ * Extracts the binary data from a resource and writes it to a temp file
+ */
+ private static File extractResource(String fullResourceName, File file) throws Exception {
+ try (InputStream in = AdbUtils.class.getResourceAsStream(fullResourceName);
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
+ if (in == null) {
+ throw new IllegalArgumentException("Resource not found: " + fullResourceName);
+ }
+ byte[] buf = new byte[65536];
+ int chunkSize;
+ while ((chunkSize = in.read(buf)) != -1) {
+ out.write(buf, 0, chunkSize);
+ }
+ return file;
+ }
+
+ }
+}
diff --git a/hostsidetests/security/src/android/security/cts/SecurityTestCase.java b/hostsidetests/security/src/android/security/cts/SecurityTestCase.java
new file mode 100644
index 0000000..b6599c1
--- /dev/null
+++ b/hostsidetests/security/src/android/security/cts/SecurityTestCase.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Scanner;
+
+public class SecurityTestCase extends DeviceTestCase {
+
+ private long kernelStartTime;
+
+ /**
+ * Waits for device to be online, marks the most recent boottime of the device
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ kernelStartTime = System.currentTimeMillis()/1000 -
+ Integer.parseInt(getDevice().executeShellCommand("cut -f1 -d. /proc/uptime").trim());
+ //TODO:(badash@): Watch for other things to track.
+ // Specifically time when app framework starts
+ }
+
+ /**
+ * Takes a device and runs a root command. There is a more robust version implemented by
+ * NativeDevice, but due to some other changes it isnt trivially acessible, but I can get
+ * that implementation fairly easy if we think it is a better idea.
+ */
+ public void enableAdbRoot(ITestDevice mDevice) throws DeviceNotAvailableException {
+ boolean isUserDebug =
+ "userdebug".equals(mDevice.executeShellCommand("getprop ro.build.type").trim());
+ if (!isUserDebug) {
+ //TODO(badash@): This would Noop once cl: ag/1594311 is in
+ return;
+ }
+ mDevice.executeAdbCommand("root");
+ }
+
+ /**
+ * Makes sure the phone is online, and the ensure the current boottime is within 2 seconds
+ * (due to rounding) of the previous boottime to check if The phone has crashed.
+ */
+ @Override
+ public void tearDown() throws Exception {
+ getDevice().waitForDeviceOnline(60 * 1000);
+ assertTrue("Phone has had a hard reset",
+ (System.currentTimeMillis()/1000 -
+ Integer.parseInt(getDevice().executeShellCommand("cut -f1 -d. /proc/uptime").trim())
+ - kernelStartTime < 2));
+ //TODO(badash@): add ability to catch runtime restart
+ getDevice().executeAdbCommand("unroot");
+ }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
index 6f527ed..534e2ea 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
@@ -38,6 +38,11 @@
executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
mAmWmState.waitForHomeActivityVisible(mDevice);
+ /* TODO: Find a proper way to wait until launcher activity
+ * becomes fully visible. It appears that both VisibleBehindActivity
+ * and home activity are visible at some point before the former
+ * disappears.*/
+ Thread.sleep(3000);
mAmWmState.computeState(mDevice, null);
mAmWmState.assertContainsStack(
"Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
@@ -123,7 +128,9 @@
public void testTranslucentActivityOverDockedStack() throws Exception {
launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+ mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME});
launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME, TEST_ACTIVITY_NAME});
launchActivityInStack(TRANSLUCENT_ACTIVITY_NAME, DOCKED_STACK_ID);
mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME, DOCKED_ACTIVITY_NAME,
TRANSLUCENT_ACTIVITY_NAME}, false /* compareTaskAndStackBounds */);
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
index 8268cb5..ae22720 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
@@ -59,8 +59,9 @@
public void testLaunchToSide() throws Exception {
launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
- launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
+ launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
+ mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
mAmWmState.assertContainsStack(
"Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
@@ -71,6 +72,7 @@
launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
final String[] waitForFirstVisible = new String[] {TEST_ACTIVITY_NAME};
final String[] waitForSecondVisible = new String[] {NO_RELAUNCH_ACTIVITY_NAME};
+ mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
// Launch activity to side.
launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
@@ -103,7 +105,10 @@
public void testLaunchToSideMultiple() throws Exception {
launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
- final String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
+ mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
+
+ final String[] waitForActivitiesVisible =
+ new String[] {TEST_ACTIVITY_NAME, LAUNCH_TO_SIDE_ACTIVITY_NAME};
// Launch activity to side.
launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
@@ -142,11 +147,16 @@
private void launchTargetToSide(String targetActivityName,
boolean taskCountMustIncrement) throws Exception {
launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
- final String[] waitForActivitiesVisible = new String[] {targetActivityName};
+ mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
+
+ final String[] waitForActivitiesVisible =
+ new String[] {targetActivityName, LAUNCH_TO_SIDE_ACTIVITY_NAME};
// Launch activity to side with data.
launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME, true, false, targetActivityName);
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.assertContainsStack(
+ "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
.getTasks().size();
mAmWmState.assertNotNull("Launched to side activity must be in fullscreen stack.",
@@ -192,7 +202,9 @@
public void testLaunchToSideMultipleWithFlag() throws Exception {
launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
- final String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
+ mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
+ final String[] waitForActivitiesVisible =
+ new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME, TEST_ACTIVITY_NAME};
// Launch activity to side.
launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
@@ -219,15 +231,17 @@
public void testRotationWhenDocked() throws Exception {
launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
+ mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
- final String[] waitForActivitiesVisible = new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME};
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
mAmWmState.assertContainsStack(
"Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
// Rotate device single steps (90°) 0-1-2-3.
// Each time we compute the state we implicitly assert valid bounds.
+ String[] waitForActivitiesVisible =
+ new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME, TEST_ACTIVITY_NAME};
setDeviceRotation(0);
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
setDeviceRotation(1);
@@ -252,14 +266,16 @@
public void testRotationWhenDockedWhileLocked() throws Exception {
launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
+ mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
- final String[] waitForActivitiesVisible = new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME};
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
mAmWmState.assertSanity();
mAmWmState.assertContainsStack(
"Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
+ String[] waitForActivitiesVisible =
+ new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME, TEST_ACTIVITY_NAME};
lockDevice();
setDeviceRotation(0);
unlockDevice();
@@ -283,7 +299,9 @@
public void testResizeDockedStack() throws Exception {
launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+ mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME});
launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
resizeDockedStack(STACK_SIZE, STACK_SIZE, TASK_SIZE, TASK_SIZE);
mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME, DOCKED_ACTIVITY_NAME},
false /* compareTaskAndStackBounds */);
@@ -298,12 +316,14 @@
}
public void testActivityLifeCycleOnResizeDockedStack() throws Exception {
+ final String[] waitTestActivityName = new String[] {TEST_ACTIVITY_NAME};
executeShellCommand(getAmStartCmd(TEST_ACTIVITY_NAME));
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(mDevice, waitTestActivityName);
final Rectangle fullScreenBounds =
mAmWmState.getWmState().getStack(FULLSCREEN_WORKSPACE_STACK_ID).getBounds();
moveActivityToDockStack(TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(mDevice, waitTestActivityName);
launchActivityInStack(NO_RELAUNCH_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
mAmWmState.computeState(mDevice,
@@ -315,15 +335,15 @@
Rectangle newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
+ mAmWmState.computeState(mDevice,
+ new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
// We resize twice to make sure we cross an orientation change threshold for both
// activities.
newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, false);
resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
-
mAmWmState.computeState(mDevice,
new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
-
assertActivityLifecycle(TEST_ACTIVITY_NAME, true);
assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false);
}
diff --git a/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java b/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
index 7b3a30c..683aded 100644
--- a/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
+++ b/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
@@ -157,7 +157,6 @@
}
private void setUpEnvironment() throws Exception {
- device.disconnectFromWifi();
dhryMin = Double.MAX_VALUE;
dhryMax = Double.MIN_VALUE;
Thread.sleep(600000);
@@ -240,15 +239,21 @@
device.executeShellCommand("settings put global airplane_mode_on 0");
device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
- double perfdegradapp = (appResultsWithMode.get(1) - appResultsWithoutMode.get(1))*100/appResultsWithMode.get(1);
- double perfdegraddhry = (dhrystoneResultsWithoutMode.get(0) - dhrystoneResultsWithMode.get(0))*100/dhrystoneResultsWithoutMode.get(0);
+ double resDhry = dhrystoneResultsWithMode.get(2);
+ double resApp = appResultsWithMode.get(2);
+
+ /* Report if performance is below 5% margin for both dhrystone and shader */
+ if ((resDhry > 5) || (resApp > 5)) {
+ Log.w("SustainedPerformanceHostTests",
+ "Sustainable mode results, Dhrystone: " + resDhry + " App: " + resApp);
+ }
/*
- * Checks if the performance in the mode is consistent with
- * 5% error margin.
+ * Error if the performance in the mode is not consistent with
+ * 5% error margin for shader and 10% error margin for dhrystone.
*/
assertFalse("Results in the mode are not sustainable",
- (dhrystoneResultsWithMode.get(2) > 10) ||
- (appResultsWithMode.get(2)) > 5);
+ (resDhry > 15) ||
+ (resApp > 5));
}
}
diff --git a/hostsidetests/theme/assets/22/hdpi.zip b/hostsidetests/theme/assets/22/hdpi.zip
index 0fa67b7..2b23d67 100644
--- a/hostsidetests/theme/assets/22/hdpi.zip
+++ b/hostsidetests/theme/assets/22/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/xhdpi.zip b/hostsidetests/theme/assets/22/xhdpi.zip
index de6e2e1..fbde98a 100644
--- a/hostsidetests/theme/assets/22/xhdpi.zip
+++ b/hostsidetests/theme/assets/22/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/xxhdpi.zip b/hostsidetests/theme/assets/22/xxhdpi.zip
index 9f0d778..ed7036e 100644
--- a/hostsidetests/theme/assets/22/xxhdpi.zip
+++ b/hostsidetests/theme/assets/22/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/24/hdpi.zip b/hostsidetests/theme/assets/24/hdpi.zip
new file mode 100644
index 0000000..d9dc466
--- /dev/null
+++ b/hostsidetests/theme/assets/24/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/24/tvdpi.zip b/hostsidetests/theme/assets/24/tvdpi.zip
new file mode 100644
index 0000000..b9ef65a
--- /dev/null
+++ b/hostsidetests/theme/assets/24/tvdpi.zip
Binary files differ
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
index b4f839e..bbda333 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
@@ -104,8 +104,11 @@
waitForIdle();
// Clean up.
+ // The GLOBAL_ACTION_HOME is needed in the case where additional
+ // notifications showing up, we need to clear them as well for the upcoming
+ // new tests to work properly.
getInstrumentation().getUiAutomation().performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_BACK);
+ AccessibilityService.GLOBAL_ACTION_HOME);
// Sleep a bit so the UI is settled.
waitForIdle();
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 543a385..deb1854 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -671,11 +671,13 @@
}
/**
- * Test that managed provisioning is pre-installed if and only if the device declares the
- * device admin feature.
+ * Test that managed provisioning is pre-installed if the device declares the device admin
+ * feature.
*/
public void testManagedProvisioningPreInstalled() throws Exception {
- assertEquals(mDeviceAdmin, isPackageInstalledOnSystemImage(MANAGED_PROVISIONING_PKG));
+ if (mDeviceAdmin) {
+ assertTrue(isPackageInstalledOnSystemImage(MANAGED_PROVISIONING_PKG));
+ }
}
private void assertDeviceOwnerMessage(String message) {
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index cad4922..38443b9 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -457,6 +457,10 @@
}
public void testUsbAccessory() {
+ // USB accessory mode is only a requirement for devices with USB ports supporting
+ // peripheral mode. As there is no public API to distinguish a device with only host
+ // mode support from having both peripheral and host support, the test may have
+ // false negatives.
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) &&
!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION) &&
!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
@@ -464,23 +468,18 @@
}
}
- public void testWifiFeature() throws Exception {
+ public void testWifiFeature() throws Exception {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
// no WiFi, skip the test
return;
}
boolean enabled = mWifiManager.isWifiEnabled();
try {
- // WifiManager is hard-coded to return true,
- // the case without WiFi is already handled,
- // so this case MUST have WiFi.
- if (mWifiManager.setWifiEnabled(true)) {
- assertAvailable(PackageManager.FEATURE_WIFI);
- }
+ // assert wifimanager can toggle wifi from current sate
+ assertTrue(mWifiManager.setWifiEnabled(!enabled));
+
} finally {
- if (!enabled) {
- mWifiManager.setWifiEnabled(false);
- }
+ mWifiManager.setWifiEnabled(enabled);
}
}
diff --git a/tests/backup/src/android/backup/cts/BackupQuotaTest.java b/tests/backup/src/android/backup/cts/BackupQuotaTest.java
index 6122ad0..01c368a 100644
--- a/tests/backup/src/android/backup/cts/BackupQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/BackupQuotaTest.java
@@ -147,10 +147,6 @@
out.append(str);
}
return out.toString();
- } finally {
- if (br != null) {
- closeQuietly(br);
- }
}
}
@@ -158,7 +154,7 @@
String command) throws Exception {
final ParcelFileDescriptor pfd =
instrumentation.getUiAutomation().executeShellCommand(command);
- return new FileInputStream(pfd.getFileDescriptor());
+ return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
}
private static void closeQuietly(AutoCloseable closeable) {
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index db687bf..5b75bf2 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -1079,7 +1079,7 @@
// Check get unknown value fails
uint32_t badTag = (uint32_t) ACAMERA_VENDOR_START - 1;
- ret = ACameraMetadata_getConstEntry(chars, ACAMERA_VENDOR_START, &entry);
+ ret = ACameraMetadata_getConstEntry(chars, badTag, &entry);
if (ret == ACAMERA_OK) {
LOG_ERROR(errorString, "Error: get unknown tag should fail!");
goto cleanup;
diff --git a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
index 2969753..bbc4c75 100644
--- a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
@@ -494,6 +494,10 @@
});
stopCapture();
+ if (VERBOSE) Log.v(TAG, "Cleanup Renderscript cache");
+ scriptGraph.close();
+ RenderScriptSingleton.clearContext();
+ RenderScriptSingleton.setContext(getContext());
}
}
});
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 67c08fe..11071fc 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -149,11 +149,13 @@
assertNotNull("Can't get lens facing info", lensFacing);
if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
assertTrue("System doesn't have front camera feature",
- mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) ||
- mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
} else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
assertTrue("System doesn't have back camera feature",
mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA));
+ } else if (lensFacing == CameraCharacteristics.LENS_FACING_EXTERNAL) {
+ assertTrue("System doesn't have external camera feature",
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
} else {
fail("Unknown camera lens facing " + lensFacing.toString());
}
@@ -166,10 +168,11 @@
assertTrue("Missing system feature: FEATURE_CAMERA_ANY",
ids.length == 0
|| mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
- assertTrue("Missing system feature: FEATURE_CAMERA or FEATURE_CAMERA_FRONT",
+ assertTrue("Missing system feature: FEATURE_CAMERA, FEATURE_CAMERA_FRONT or FEATURE_CAMERA_EXTERNAL",
ids.length == 0
|| mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
- || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
+ || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)
+ || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
}
// Test: that properties can be queried from each device, without exceptions.
diff --git a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index df89297..07e810f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -714,7 +714,7 @@
*/
public int[] getAvailableToneMapModesChecked() {
Key<int[]> key = CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES;
- int[] modes = getValueFromKeyNonNull(key);
+ int[] modes = mCharacteristics.get(key);
if (modes == null) {
return new int[0];
diff --git a/tests/leanbackjank/app/res/values/strings.xml b/tests/leanbackjank/app/res/values/strings.xml
index 17edf95..c90bbbc 100644
--- a/tests/leanbackjank/app/res/values/strings.xml
+++ b/tests/leanbackjank/app/res/values/strings.xml
@@ -20,8 +20,6 @@
<string name="browse_title"><![CDATA[Leanback launcher jank test application]]></string>
<string name="error">Error</string>
<string name="ok">OK</string>
- <string name="grid_item_template">Item %1$d</string>
- <string name="settings">Settings</string>
<!-- Error messages -->
<string name="error_fragment_message">An error occurred</string>
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
index f237438..bedbc5d 100644
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
+++ b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
@@ -30,8 +30,8 @@
* It contains an Image CardView
*/
public class CardPresenter extends Presenter {
- private static int CARD_WIDTH = 313;
- private static int CARD_HEIGHT = 176;
+ private static int CARD_WIDTH = 560;
+ private static int CARD_HEIGHT = 320;
private static int sSelectedBackgroundColor;
private static int sDefaultBackgroundColor;
private Drawable mDefaultCardImage;
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/GridItemPresenter.java b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/GridItemPresenter.java
deleted file mode 100644
index 1ab7e8d..0000000
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/GridItemPresenter.java
+++ /dev/null
@@ -1,59 +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.leanbackjank.app.presenter;
-
-import android.graphics.Color;
-import android.support.v17.leanback.widget.Presenter;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import android.leanbackjank.app.R;
-import android.leanbackjank.app.ui.MainFragment;
-
-public class GridItemPresenter extends Presenter {
- private static int GRID_ITEM_WIDTH = 200;
- private static int GRID_ITEM_HEIGHT = 200;
-
- private MainFragment mainFragment;
-
- public GridItemPresenter(MainFragment mainFragment) {
- this.mainFragment = mainFragment;
- }
-
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent) {
- TextView view = new TextView(parent.getContext());
- view.setLayoutParams(new ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT));
- view.setFocusable(true);
- view.setFocusableInTouchMode(true);
- view.setBackgroundColor(
- mainFragment.getResources().getColor(R.color.default_background, null));
- view.setTextColor(Color.WHITE);
- view.setGravity(Gravity.CENTER);
- return new ViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(ViewHolder viewHolder, Object item) {
- ((TextView) viewHolder.view).setText((String) item);
- }
-
- @Override
- public void onUnbindViewHolder(ViewHolder viewHolder) {
- }
-}
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java b/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
index 78032c2..399681c 100644
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
+++ b/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
@@ -21,7 +21,6 @@
import android.leanbackjank.app.data.VideoProvider;
import android.leanbackjank.app.model.Movie;
import android.leanbackjank.app.presenter.CardPresenter;
-import android.leanbackjank.app.presenter.GridItemPresenter;
import android.leanbackjank.app.presenter.IconHeaderItemPresenter;
import android.os.Bundle;
import android.os.Handler;
@@ -184,15 +183,6 @@
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}
- HeaderItem gridHeader = new HeaderItem(i, getString(R.string.settings));
-
- GridItemPresenter gridPresenter = new GridItemPresenter(this);
- ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);
- for (int j = 0; j < 10; j++) {
- gridRowAdapter.add(getString(R.string.grid_item_template, j));
- }
- mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
-
setAdapter(mRowsAdapter);
}
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
index bd96d76..2c3c2ad 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -26,6 +26,9 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.TrafficStats;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
@@ -53,6 +56,12 @@
private static final long MINUTE = 1000 * 60;
private static final int TIMEOUT_MILLIS = 15000;
+ private static final String CHECK_CONNECTIVITY_URL = "http://www.265.com/";
+ private static final String CHECK_CALLBACK_URL = CHECK_CONNECTIVITY_URL;
+
+ private static final int NETWORK_TAG = 0xf00d;
+ private static final long THRESHOLD_BYTES = 2 * 1024 * 1024; // 2 MB
+
private interface NetworkInterfaceToTest {
int getNetworkType();
int getTransportType();
@@ -119,7 +128,7 @@
private String mWriteSettingsMode;
private String mUsageStatsMode;
- private void exerciseRemoteHost(Network network) throws Exception {
+ private void exerciseRemoteHost(Network network, URL url) throws Exception {
NetworkInfo networkInfo = mCm.getNetworkInfo(network);
if (networkInfo == null) {
Log.w(LOG_TAG, "Network info is null");
@@ -131,8 +140,8 @@
String originalKeepAlive = System.getProperty("http.keepAlive");
System.setProperty("http.keepAlive", "false");
try {
- urlc = (HttpURLConnection) network.openConnection(new URL(
- "http://www.265.com/"));
+ TrafficStats.setThreadStatsTag(NETWORK_TAG);
+ urlc = (HttpURLConnection) network.openConnection(url);
urlc.setConnectTimeout(TIMEOUT_MILLIS);
urlc.setUseCaches(false);
urlc.connect();
@@ -147,6 +156,7 @@
} catch (Exception e) {
Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
} finally {
+ TrafficStats.clearThreadStatsTag();
if (in != null) {
try {
in.close();
@@ -229,10 +239,12 @@
private class NetworkCallback extends ConnectivityManager.NetworkCallback {
private long mTolerance;
+ private URL mUrl;
public boolean success;
- NetworkCallback(long tolerance) {
+ NetworkCallback(long tolerance, URL url) {
mTolerance = tolerance;
+ mUrl = url;
success = false;
}
@@ -240,7 +252,7 @@
public void onAvailable(Network network) {
try {
mStartTime = System.currentTimeMillis() - mTolerance;
- exerciseRemoteHost(network);
+ exerciseRemoteHost(network, mUrl);
mEndTime = System.currentTimeMillis() + mTolerance;
success = true;
synchronized(NetworkUsageStatsTest.this) {
@@ -260,7 +272,7 @@
if (!hasFeature) {
return false;
}
- NetworkCallback callback = new NetworkCallback(tolerance);
+ NetworkCallback callback = new NetworkCallback(tolerance, new URL(CHECK_CONNECTIVITY_URL));
mCm.requestNetwork(new NetworkRequest.Builder()
.addTransportType(sNetworkInterfacesToTest[networkTypeIndex].getTransportType())
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -520,10 +532,99 @@
}
}
+ public void testTagDetails() throws Exception {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
+ // Relatively large tolerance to accommodate for history bucket size.
+ if (!shouldTestThisNetworkType(i, MINUTE * 120)) {
+ continue;
+ }
+ setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
+ NetworkStats result = null;
+ try {
+ result = mNsm.queryDetailsForUidTag(
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
+ mStartTime, mEndTime, Process.myUid(), NETWORK_TAG);
+ assertTrue(result != null);
+ NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ long totalTxPackets = 0;
+ long totalRxPackets = 0;
+ long totalTxBytes = 0;
+ long totalRxBytes = 0;
+ while (result.hasNextBucket()) {
+ assertTrue(result.getNextBucket(bucket));
+ assertTimestamps(bucket);
+ assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL);
+ assertEquals(bucket.getUid(), Process.myUid());
+ if (bucket.getTag() == NETWORK_TAG) {
+ totalTxPackets += bucket.getTxPackets();
+ totalRxPackets += bucket.getRxPackets();
+ totalTxBytes += bucket.getTxBytes();
+ totalRxBytes += bucket.getRxBytes();
+ }
+ }
+ assertTrue("No Rx bytes tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+ + " for uid " + Process.myUid(), totalRxBytes > 0);
+ assertTrue("No Rx packets tagged with " + Integer.toHexString(NETWORK_TAG)
+ + " for uid " + Process.myUid(), totalRxPackets > 0);
+ assertTrue("No Tx bytes tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+ + " for uid " + Process.myUid(), totalTxBytes > 0);
+ assertTrue("No Tx packets tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+ + " for uid " + Process.myUid(), totalTxPackets > 0);
+ } catch (SecurityException e) {
+ fail("testUidDetails fails with exception: " + e.toString());
+ } finally {
+ if (result != null) {
+ result.close();
+ }
+ }
+ setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
+ try {
+ result = mNsm.queryDetailsForUidTag(
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
+ mStartTime, mEndTime, Process.myUid(), NETWORK_TAG);
+ fail("negative testUidDetails fails: no exception thrown.");
+ } catch (SecurityException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ public void testCallback() throws Exception {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
+ // Relatively large tolerance to accommodate for history bucket size.
+ if (!shouldTestThisNetworkType(i, MINUTE/2)) {
+ continue;
+ }
+ setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
+
+ TestUsageCallback usageCallback = new TestUsageCallback();
+ HandlerThread thread = new HandlerThread("callback-thread");
+ thread.start();
+ Handler handler = new Handler(thread.getLooper());
+ mNsm.registerUsageCallback(sNetworkInterfacesToTest[i].getNetworkType(),
+ getSubscriberId(i), THRESHOLD_BYTES, usageCallback, handler);
+
+ // TODO: Force traffic and check whether the callback is invoked.
+ // Right now the test only covers whether the callback can be registered, but not
+ // whether it is invoked upon data usage since we don't have a scalable way of
+ // storing files of >2MB in CTS.
+
+ mNsm.unregisterUsageCallback(usageCallback);
+ }
+ }
+
private void assertTimestamps(final NetworkStats.Bucket bucket) {
assertTrue("Start timestamp " + bucket.getStartTimeStamp() + " is less than " +
mStartTime, bucket.getStartTimeStamp() >= mStartTime);
assertTrue("End timestamp " + bucket.getEndTimeStamp() + " is greater than " +
mEndTime, bucket.getEndTimeStamp() <= mEndTime);
}
+
+ private static class TestUsageCallback extends NetworkStatsManager.UsageCallback {
+ @Override
+ public void onThresholdReached(int networkType, String subscriberId) {
+ Log.v(LOG_TAG, "Called onThresholdReached for networkType=" + networkType
+ + " subscriberId=" + subscriberId);
+ }
+ }
}
diff --git a/tests/tests/drm/src/android/drm/cts/DrmInfoRequestTest.java b/tests/tests/drm/src/android/drm/cts/DrmInfoRequestTest.java
index 02894d4..cedc2d5 100644
--- a/tests/tests/drm/src/android/drm/cts/DrmInfoRequestTest.java
+++ b/tests/tests/drm/src/android/drm/cts/DrmInfoRequestTest.java
@@ -31,6 +31,8 @@
public static void testInvalidInfoTypes() throws Exception {
checkInvalidInfoType(DrmInfoRequest.TYPE_REGISTRATION_INFO - 1);
+ checkInvalidInfoType(
+ DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO + 1);
}
public static void testValidInfoTypes() throws Exception {
diff --git a/tests/tests/drm/src/android/drm/cts/DrmInfoStatusTest.java b/tests/tests/drm/src/android/drm/cts/DrmInfoStatusTest.java
index cecd558..103bd6e 100644
--- a/tests/tests/drm/src/android/drm/cts/DrmInfoStatusTest.java
+++ b/tests/tests/drm/src/android/drm/cts/DrmInfoStatusTest.java
@@ -30,6 +30,7 @@
DrmInfoRequest.TYPE_REGISTRATION_INFO;
public static void testInvalidStatusCodes() throws Exception {
+ checkInvalidStatusCode(DrmInfoStatus.STATUS_ERROR + 1);
checkInvalidStatusCode(DrmInfoStatus.STATUS_OK - 1);
}
diff --git a/tests/tests/drm/src/android/drm/cts/DrmInfoTest.java b/tests/tests/drm/src/android/drm/cts/DrmInfoTest.java
index 7a498a6..656df5b 100644
--- a/tests/tests/drm/src/android/drm/cts/DrmInfoTest.java
+++ b/tests/tests/drm/src/android/drm/cts/DrmInfoTest.java
@@ -33,6 +33,8 @@
public static void testInvalidInfoTypes() throws Exception {
checkInvalidInfoType(DrmInfoRequest.TYPE_REGISTRATION_INFO - 1);
+ checkInvalidInfoType(
+ DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO + 1);
}
public static void testValidInfoTypes() throws Exception {
diff --git a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
index 77c6da1..5a76816 100644
--- a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -25,6 +25,7 @@
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.microedition.khronos.egl.EGL10;
@@ -61,20 +62,16 @@
int detectedMajorVersion = getDetectedMajorVersion();
int reportedVersion = getVersionFromActivityManager(mActivity);
- assertEquals("Detected OpenGL ES major version " + detectedMajorVersion
- + " but Activity Manager is reporting " + getMajorVersion(reportedVersion)
- + " (Check ro.opengles.version)",
- detectedMajorVersion, getMajorVersion(reportedVersion));
assertEquals("Reported OpenGL ES version from ActivityManager differs from PackageManager",
reportedVersion, getVersionFromPackageManager(mActivity));
- assertGlVersionString(1);
+ assertGlVersionString(1, 1);
if (detectedMajorVersion == 2) {
restartActivityWithClientVersion(2);
- assertGlVersionString(2);
+ assertGlVersionString(2, getMinorVersion(reportedVersion));
} else if (detectedMajorVersion == 3) {
restartActivityWithClientVersion(3);
- assertGlVersionString(3);
+ assertGlVersionString(3, getMinorVersion(reportedVersion));
}
}
@@ -123,14 +120,11 @@
restartActivityWithClientVersion(3);
String extensions = mActivity.getExtensionsString();
- if (!hasExtension(extensions, "ANDROID_extension_pack_es31a")) {
- assertFalse("FEATURE_OPENGLES_EXTENSION_PACK is available but ANDROID_extension_pack_es31a isn't in the extension list",
- hasAepFeature);
- return;
- }
-
- assertTrue("ANDROID_extension_pack_es31a is present, but support is incomplete",
- mActivity.getAepEs31Support());
+ boolean hasAepExtension = hasExtension(extensions, "GL_ANDROID_extension_pack_es31a");
+ assertEquals("System feature FEATURE_OPENGLES_EXTENSION_PACK is "
+ + (hasAepFeature ? "" : "not ") + "available, but extension GL_ANDROID_extension_pack_es31a is "
+ + (hasAepExtension ? "" : "not ") + "in the OpenGL ES extension list.",
+ hasAepFeature, hasAepExtension);
}
public void testOpenGlEsVersionForVrHighPerformance() throws InterruptedException {
@@ -288,15 +282,21 @@
}
/**
- * Check that the version string has some form of "Open GL ES X.Y" in it where X is the major
- * version and Y must be some digit.
+ * Check that the version string has the form "OpenGL ES(-CM)? (\d+)\.(\d+)", where the two
+ * numbers match the major and minor parameters.
*/
- private void assertGlVersionString(int majorVersion) throws InterruptedException {
- String versionString = "" + majorVersion;
- String message = "OpenGL version string '" + mActivity.getVersionString()
- + "' is not " + majorVersion + ".0+.";
- assertTrue(message, Pattern.matches(".*OpenGL.*ES.*" + versionString + "\\.\\d.*",
- mActivity.getVersionString()));
+ private void assertGlVersionString(int major, int minor) throws InterruptedException {
+ Matcher matcher = Pattern.compile("OpenGL ES(?:-CM)? (\\d+)\\.(\\d+).*")
+ .matcher(mActivity.getVersionString());
+ assertTrue("OpenGL ES version string is not of the required form "
+ + "'OpenGL ES(-CM)? (\\d+)\\.(\\d+).*'",
+ matcher.matches());
+ int stringMajor = Integer.parseInt(matcher.group(1));
+ int stringMinor = Integer.parseInt(matcher.group(2));
+ assertEquals("GL_VERSION string doesn't match ActivityManager major version (check ro.opengles.version property)",
+ major, stringMajor);
+ assertEquals("GL_VERSION string doesn't match ActivityManager minor version (check ro.opengles.version property)",
+ minor, stringMinor);
}
/** Restart {@link GLSurfaceViewCtsActivity} with a specific client version. */
diff --git a/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java b/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
index 01776e5..647fc30 100644
--- a/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
+++ b/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
@@ -55,13 +55,7 @@
Assert.assertTrue(mTextureView.isOpaque());
mWidth = width;
mHeight = height;
- PackageManager packageManager = getPackageManager();
- boolean hasRearCamera = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
- boolean hasFrontCamera =
- packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
- if (hasRearCamera) {
- mCamera = Camera.open();
- } else if (hasFrontCamera) {
+ if (Camera.getNumberOfCameras() > 0) {
mCamera = Camera.open(0);
} else {
// no camera, and no frame update, so just complete here.
@@ -84,8 +78,10 @@
}
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- mCamera.stopPreview();
- mCamera.release();
+ if (mCamera != null) {
+ mCamera.stopPreview();
+ mCamera.release();
+ }
return true;
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 9cb3710..92c0ede 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -141,6 +141,14 @@
}
/**
+ * If value lies outside the boundary limit, then return the nearer bound value.
+ * Otherwise, return the value unchanged.
+ */
+ public static <TValue extends Number> double clamp(TValue val, TValue min, TValue max) {
+ return Math.min(max.doubleValue(), Math.max(min.doubleValue(), val.doubleValue()));
+ }
+
+ /**
* @return The magnitude (norm) represented by the given array of values.
*/
public static double getMagnitude(float[] values) {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
index 2f4777b..4639b59 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
@@ -75,7 +75,10 @@
sensor.getName(),
sensor.getMinDelay(),
sensor.getMaxDelay()));
+
+ double minDelayUs = sensor.getMinDelay();
double maxDelayUs = sensor.getMaxDelay();
+
if (maxDelayUs <= 0) {
// This sensor didn't report its maxDelay.
// It might be only capable of producing its max rate.
@@ -83,22 +86,32 @@
Log.w(LOG_TAG, "This sensor (" + sensor.getName() + ") didn't report its maxDelay."
+ "Please update it in the sensor HAL to avoid failures in coming "
+ "android releases.");
- maxDelayUs = sensor.getMinDelay();
+ maxDelayUs = minDelayUs;
}
if (environment.isSensorSamplingRateOverloaded()) {
- maxDelayUs = sensor.getMinDelay();
+ maxDelayUs = minDelayUs;
}
// Convert the rateUs parameter into a delay in microseconds and rate in Hz.
double delayUs = environment.getRequestedSamplingPeriodUs();
- // When rateUs > maxDelay, the sensor can do as if we requested maxDelay.
- double upperExpectedHz = SensorCtsHelper.getFrequency(
- Math.min(delayUs, maxDelayUs), TimeUnit.MICROSECONDS);
- // When rateUs < minDelay, the sensor can do as if we requested minDelay
- double lowerExpectedHz = SensorCtsHelper.getFrequency(
- Math.max(delayUs, sensor.getMinDelay()), TimeUnit.MICROSECONDS);
+ double lowerExpectedHz = 0;
+ double upperExpectedHz = 0;
+
+ // If sensor reports its maxDelay, then
+ // lowerExpectedHz = upperExpectedHz and minRate <= expectedHz <= maxRate.
+ // If sensor do not report its maxDelay, then
+ // lowerExpectedHz is as per request (obviously <= maxRate) & upperExpectedHz = maxRate.
+ if (sensor.getMaxDelay() > 0) {
+ delayUs = SensorCtsHelper.clamp(delayUs, minDelayUs, maxDelayUs);
+ double expectedHz = SensorCtsHelper.getFrequency(delayUs, TimeUnit.MICROSECONDS);
+ lowerExpectedHz = upperExpectedHz = expectedHz;
+ } else {
+ delayUs = Math.max(minDelayUs, delayUs);
+ lowerExpectedHz = SensorCtsHelper.getFrequency(delayUs, TimeUnit.MICROSECONDS);
+ upperExpectedHz = SensorCtsHelper.getFrequency(minDelayUs, TimeUnit.MICROSECONDS);
+ }
// Set the pass thresholds based on default multipliers.
double lowerThresholdHz = lowerExpectedHz * DEFAULT_LOWER_THRESHOLD / 100;
diff --git a/tests/tests/location/src/android/location/cts/GeocoderTest.java b/tests/tests/location/src/android/location/cts/GeocoderTest.java
index d96b73f..62d9d25 100644
--- a/tests/tests/location/src/android/location/cts/GeocoderTest.java
+++ b/tests/tests/location/src/android/location/cts/GeocoderTest.java
@@ -17,6 +17,9 @@
package android.location.cts;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.location.Geocoder;
import android.test.AndroidTestCase;
@@ -43,7 +46,21 @@
public void testIsPresent() {
Geocoder geocoder = new Geocoder(getContext());
- assertTrue(geocoder.isPresent());
+ if (isServiceMissing()) {
+ assertFalse(geocoder.isPresent());
+ } else {
+ assertTrue(geocoder.isPresent());
+ }
+ }
+
+ private boolean isServiceMissing() {
+ Context context = getContext();
+ PackageManager pm = context.getPackageManager();
+
+ final Intent intent = new Intent("com.android.location.service.GeocodeProvider");
+ final int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ return pm.queryIntentServices(intent, flags).isEmpty();
}
public void testGetFromLocation() throws IOException, InterruptedException {
diff --git a/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java b/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
index 0f4775e..32c54c9 100644
--- a/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
@@ -135,7 +135,13 @@
List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
Log.i(TAG, "Number of GPS measurement events received = " + events.size());
- // Ensure that after getting a few (at least 2) measurements, that we still don't have
+ if (events.isEmpty()) {
+ SoftAssert.failOrWarning(isMeasurementTestStrict(), "No measurement events received",
+ false);
+ return; // All of the following checks rely on there being measurements
+ }
+
+ // Ensure that after getting a few (at least 2) measurement events, that we still don't have
// location (i.e. that we got measurements before location.) Fail, if strict, warn, if not.
SoftAssert.failOrWarning(isMeasurementTestStrict(),
"Location was received before " + events.size() +
@@ -149,9 +155,8 @@
return; // allow a (passing) return, if not strict, otherwise continue
}
- // If device is not indoors, verify
- // 1) that we receive GPS measurements before being able to calculate the position solution
- // 2) that mandatory fields of GnssMeasurement are in expected ranges.
+ // If device has received measurements also verify
+ // that mandatory fields of GnssMeasurement are in expected ranges.
GnssMeasurementsEvent firstEvent = events.get(0);
Collection<GnssMeasurement> gpsMeasurements = firstEvent.getMeasurements();
int satelliteCount = gpsMeasurements.size();
diff --git a/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java b/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
index 6988a3f..d69be8b 100644
--- a/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
@@ -79,13 +79,19 @@
mTestLocationManager
.registerGnssNavigationMessageCallback(mTestGnssNavigationMessageListener);
- mTestGnssNavigationMessageListener.await();
+ boolean success = mTestGnssNavigationMessageListener.await();
+
if (!mTestGnssNavigationMessageListener.verifyState()) {
return;
}
+ SoftAssert.failOrWarning(isMeasurementTestStrict(),
+ "Time elapsed without getting enough navigation messages."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success);
+
List<GnssNavigationMessage> events = mTestGnssNavigationMessageListener.getEvents();
- assertTrue("No Gps Navigation Message received.", !events.isEmpty());
// Verify mandatory GnssNavigationMessage field values.
TestMeasurementUtil.verifyGnssNavMessageMandatoryField(mTestLocationManager, events);
diff --git a/tests/tests/location/src/android/location/cts/SoftAssert.java b/tests/tests/location/src/android/location/cts/SoftAssert.java
index 3913149..4349f07 100644
--- a/tests/tests/location/src/android/location/cts/SoftAssert.java
+++ b/tests/tests/location/src/android/location/cts/SoftAssert.java
@@ -156,7 +156,9 @@
if (testIsStrict) {
Assert.assertTrue(message, condition);
} else {
- failAsWarning("", message);
+ if (!condition) {
+ failAsWarning("", message);
+ }
}
}
diff --git a/tests/tests/location/src/android/location/cts/TestGnssMeasurementListener.java b/tests/tests/location/src/android/location/cts/TestGnssMeasurementListener.java
index 1809bfb..4d95a52 100644
--- a/tests/tests/location/src/android/location/cts/TestGnssMeasurementListener.java
+++ b/tests/tests/location/src/android/location/cts/TestGnssMeasurementListener.java
@@ -58,7 +58,9 @@
// Only count measurement events with more than one actual Measurement in them
// (not just clock)
if (event.getMeasurements().size() > 0) {
- mMeasurementsEvents.add(event);
+ synchronized(mMeasurementsEvents) {
+ mMeasurementsEvents.add(event);
+ }
mCountDownLatch.countDown();
}
}
@@ -120,11 +122,15 @@
}
/**
- * Get list of GPS Measurements Events.
+ * Get the current list of GPS Measurements Events.
*
- * @return mMeasurementsEvents list of GPS Measurements Events
+ * @return the current list of GPS Measurements Events
*/
public List<GnssMeasurementsEvent> getEvents() {
- return mMeasurementsEvents;
+ synchronized(mMeasurementsEvents) {
+ List<GnssMeasurementsEvent> clone = new ArrayList<>();
+ clone.addAll(mMeasurementsEvents);
+ return clone;
+ }
}
}
diff --git a/tests/tests/location/src/android/location/cts/TestGnssNavigationMessageListener.java b/tests/tests/location/src/android/location/cts/TestGnssNavigationMessageListener.java
index 068c4cb..a2f12de 100644
--- a/tests/tests/location/src/android/location/cts/TestGnssNavigationMessageListener.java
+++ b/tests/tests/location/src/android/location/cts/TestGnssNavigationMessageListener.java
@@ -21,8 +21,8 @@
import android.location.GnssNavigationMessage;
import android.util.Log;
-import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -45,7 +45,7 @@
mTag = tag;
mCountDownLatch = new CountDownLatch(1);
mEventsToCollect = eventsToCollect;
- mEvents = new ArrayList<>(eventsToCollect);
+ mEvents = new CopyOnWriteArrayList<GnssNavigationMessage>();
}
@Override
diff --git a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
index ec0ceae..662a1fd 100644
--- a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
+++ b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
@@ -28,8 +28,11 @@
import junit.framework.Assert;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.Set;
/**
* Helper class for GnssMeasurement Tests.
@@ -54,6 +57,21 @@
private static final int YEAR_2016 = 2016;
+ // The valid Gnss navigation message type as listed in
+ // android/hardware/libhardware/include/hardware/gps.h
+ public static final Set<Integer> GNSS_NAVIGATION_MESSAGE_TYPE =
+ new HashSet<Integer>(Arrays.asList(
+ GnssNavigationMessage.TYPE_GPS_L1CA,
+ GnssNavigationMessage.TYPE_GPS_L2CNAV,
+ GnssNavigationMessage.TYPE_GPS_L5CNAV,
+ GnssNavigationMessage.TYPE_GPS_CNAV2,
+ GnssNavigationMessage.TYPE_GLO_L1CA,
+ GnssNavigationMessage.TYPE_BDS_D1,
+ GnssNavigationMessage.TYPE_BDS_D2,
+ GnssNavigationMessage.TYPE_GAL_I,
+ GnssNavigationMessage.TYPE_GAL_F
+ ));
+
/**
* Check if test can be run on the current device.
*
@@ -65,15 +83,12 @@
String testTag,
int minHardwareYear,
boolean isCtsVerifier) {
- // TODO(sumitk): Enable this check once api 24 for N is avaiable.
- /*
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Log.i(TAG, "This test is designed to work on N or newer. " +
"Test is being skipped because the platform version is being run in " +
Build.VERSION.SDK_INT);
return false;
}
- */
// If device does not have a GPS, skip the test.
PackageManager pm = testLocationManager.getContext().getPackageManager();
@@ -674,9 +689,9 @@
SoftAssert softAssert = new SoftAssert(TAG);
for (GnssNavigationMessage message : events) {
int type = message.getType();
- softAssert.assertTrue("Gnss Navigation Message Type:expected [0x0101 - 0x0104]," +
- " actual = " + type,
- type >= 0x0101 && type <= 0x0104);
+ softAssert.assertTrue("Gnss Navigation Message Type:expected [" +
+ getGnssNavMessageTypes() + "] actual = " + type,
+ GNSS_NAVIGATION_MESSAGE_TYPE.contains(type));
int gnssYearOfHardware = testLocationManager.getLocationManager().getGnssYearOfHardware();
if (gnssYearOfHardware >= YEAR_2016) {
@@ -701,4 +716,14 @@
}
softAssert.assertAll();
}
+
+ private static String getGnssNavMessageTypes() {
+ StringBuilder typesStr = new StringBuilder();
+ for (int type : GNSS_NAVIGATION_MESSAGE_TYPE) {
+ typesStr.append(String.format("0x%04X", type));
+ typesStr.append(", ");
+ }
+
+ return typesStr.length() > 2 ? typesStr.substring(0, typesStr.length() - 2) : "";
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 23fa502..4b5b1d7 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -1092,7 +1092,9 @@
boolean sawOutputEos = false;
int deadDecoderCounter = 0;
ArrayList<String> frames = new ArrayList<String>();
- while ((waitForEos && !sawOutputEos) || frameIx < frameEndIx) {
+ String buf = null;
+ // After all input buffers are queued, dequeue as many output buffers as possible.
+ while ((waitForEos && !sawOutputEos) || frameIx < frameEndIx || buf != null) {
if (frameIx < frameEndIx) {
if (queueInputBuffer(
media,
@@ -1103,7 +1105,7 @@
}
}
- String buf = dequeueAndReleaseOutputBuffer(info);
+ buf = dequeueAndReleaseOutputBuffer(info);
if (buf != null) {
// Some decoders output a 0-sized buffer at the end. Disregard those.
if (info.size > 0) {
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
index 7d5742e..69f3ee8 100644
--- a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -344,24 +344,25 @@
return;
}
- IConnectionStatus wifiStatus = new WifiStatus(mContext);
- if (!wifiStatus.isEnabled()) {
- throw new Error("Wifi is not enabled, please enable Wifi to run tests.");
+ IConnectionStatus connectionStatus = new ConnectionStatus(mContext);
+ if (!connectionStatus.isAvailable()) {
+ throw new Error("Network is not available, reason: " +
+ connectionStatus.getNotConnectedReason());
}
- // If Wifi is not connected, recheck the status a few times.
+
+ // If device is not online, recheck the status a few times.
int retries = 0;
- while (!wifiStatus.isConnected()) {
+ while (!connectionStatus.isConnected()) {
if (retries++ >= CONNECTION_RETRIES) {
- wifiStatus.printConnectionInfo();
- throw new Error("Wifi is not connected, reason: " +
- wifiStatus.getNotConnectedReason());
+ throw new Error("Device is not online, reason: " +
+ connectionStatus.getNotConnectedReason());
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
- wifiStatus.testConnection(videoUrl);
+ connectionStatus.testConnection(videoUrl);
mSessionId = openSession(drm);
mMediaCodecPlayer = new MediaCodecClearKeyPlayer(
diff --git a/tests/tests/media/src/android/media/cts/WifiStatus.java b/tests/tests/media/src/android/media/cts/ConnectionStatus.java
similarity index 81%
rename from tests/tests/media/src/android/media/cts/WifiStatus.java
rename to tests/tests/media/src/android/media/cts/ConnectionStatus.java
index c2577f1..407e553 100644
--- a/tests/tests/media/src/android/media/cts/WifiStatus.java
+++ b/tests/tests/media/src/android/media/cts/ConnectionStatus.java
@@ -20,8 +20,6 @@
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
import android.util.Log;
import java.io.BufferedReader;
@@ -32,19 +30,17 @@
/**
* A class that implements IConnectionStatus interface
- * to report and test Wifi connection.
+ * to report and test connection status.
*/
-public class WifiStatus implements IConnectionStatus {
+public class ConnectionStatus implements IConnectionStatus {
- private static final String TAG = "WifiStatus";
+ private static final String TAG = "ConnectionStatus";
private ConnectivityManager mConnectivityManager;
- private WifiManager mWifiManager;
- public WifiStatus(Context context) {
+ public ConnectionStatus(Context context) {
mConnectivityManager =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
- mWifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
}
public String getNotConnectedReason() {
@@ -52,7 +48,7 @@
if (networkInfo != null) {
return networkInfo.getReason();
} else {
- return "Cannot get network info";
+ return "Network info is not available.";
}
}
@@ -66,22 +62,6 @@
return (networkInfo != null) && networkInfo.isConnected();
}
- public boolean isEnabled() {
- return mWifiManager.isWifiEnabled();
- }
-
- public void printConnectionInfo() {
- WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- if (wifiInfo == null) {
- throw new Error("Fail to get Wifi connection info");
- }
-
- Log.d(TAG, "ssid=" + wifiInfo.getSSID());
- Log.d(TAG, "frequency=" + wifiInfo.getFrequency() + " " + WifiInfo.FREQUENCY_UNITS);
- Log.d(TAG, "rssi=" + wifiInfo.getRssi() + " dBm");
- Log.d(TAG, "link speed=" + wifiInfo.getLinkSpeed() + " " + WifiInfo.LINK_SPEED_UNITS);
- }
-
/**
* Print lines.
*
diff --git a/tests/tests/media/src/android/media/cts/IConnectionStatus.java b/tests/tests/media/src/android/media/cts/IConnectionStatus.java
index 39e2781..17d5ff7 100644
--- a/tests/tests/media/src/android/media/cts/IConnectionStatus.java
+++ b/tests/tests/media/src/android/media/cts/IConnectionStatus.java
@@ -29,10 +29,6 @@
public boolean isConnected();
- public boolean isEnabled();
-
- public void printConnectionInfo();
-
public void testConnection(Uri uri);
}
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index dffb653..32d9eaa 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -424,17 +424,6 @@
return 1;
}
- public void testRecordAudioFromAudioSourceUnprocessed() throws Exception {
- if (!hasMicrophone()) {
- return; // skip
- }
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.UNPROCESSED);
- mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
- mMediaRecorder.setOutputFile(OUTPUT_PATH);
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
- recordMedia(MAX_FILE_SIZE, mOutFile);
- }
-
public void testGetAudioSourceMax() throws Exception {
final int max = MediaRecorder.getAudioSourceMax();
assertTrue(MediaRecorder.AudioSource.DEFAULT <= max);
diff --git a/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java b/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
index a9aabcf..f5d633b 100644
--- a/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
+++ b/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
@@ -314,8 +314,8 @@
BuildError, DeviceNotAvailableException {
if (mSkipMediaDownload) {
- // skip this precondition
- return;
+ logInfo("Skipping media preparation");
+ return; // skip this precondition
}
setMountPoint(device);
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/res/xml/network_security_config.xml b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/res/xml/network_security_config.xml
index 6d8565c..d36462b 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/res/xml/network_security_config.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/res/xml/network_security_config.xml
@@ -6,6 +6,7 @@
</base-config>
<domain-config>
<domain>android.com</domain>
+ <domain>www.android.com</domain>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
diff --git a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
index 4608f22..4531aa6 100644
--- a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
+++ b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
@@ -32,7 +32,7 @@
private static final String SECURITY_PATCH_DATE_ERROR =
"ro.build.version.security_patch should be \"%d-%02d\" or later. Found \"%s\"";
private static final int SECURITY_PATCH_YEAR = 2016;
- private static final int SECURITY_PATCH_MONTH = 8;
+ private static final int SECURITY_PATCH_MONTH = 12;
private boolean mSkipTests = false;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index 5ec765c..485b39c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -228,6 +228,9 @@
private Uri insertVideo(Context context) throws IOException {
File file = new File(Environment.getExternalStorageDirectory(), "testVideo.3gp");
+ // clean up any potential left over entries from a previous aborted run
+ cleanExternalMediaFile(file.getAbsolutePath());
+
new FileCopyHelper(context).copyToExternalStorage(R.raw.testvideo, file);
ContentValues values = new ContentValues();
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index 1f7b601..f2b4470 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -55,7 +55,8 @@
libstagefright_foundation
LOCAL_SRC_FILES := $(call all-java-files-under, src)\
- src/android/security/cts/activity/ISecureRandomService.aidl
+ src/android/security/cts/activity/ISecureRandomService.aidl\
+ aidl/android/security/cts/IIsolatedService.aidl
LOCAL_PACKAGE_NAME := CtsSecurityTestCases
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 7a67b08..7468d68 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -35,6 +35,10 @@
android:isolatedProcess="true"
android:exported="true"/>
+ <service android:name="android.security.cts.IsolatedService"
+ android:process=":Isolated"
+ android:isolatedProcess="true"/>
+
<service android:name="android.security.cts.activity.SecureRandomService"
android:process=":secureRandom"/>
</application>
diff --git a/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
new file mode 100644
index 0000000..16db0fe
--- /dev/null
+++ b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+interface IIsolatedService {
+ String[] getCachedSystemServices();
+ IBinder getSystemService(String serviceName);
+}
diff --git a/tests/tests/security/res/raw/bug_27855419.mp4 b/tests/tests/security/res/raw/bug_27855419.mp4
new file mode 100644
index 0000000..51bfa74
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_27855419.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
new file mode 100644
index 0000000..ac264d0
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.security.cts.IIsolatedService;
+import android.security.cts.IsolatedService;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
+
+public class IsolatedProcessTest extends AndroidTestCase {
+ static final String TAG = IsolatedProcessTest.class.getSimpleName();
+
+ private static final long BIND_SERVICE_TIMEOUT = 5000;
+
+ // No service other than these should be visible to an isolated process
+ private static final String[] SERVICES_ALLOWED_TO_ISOLATED_PROCESS = {
+ "package",
+ Context.ACTIVITY_SERVICE
+ };
+ // Arbitrary set of services to test accessibility from an isolated process
+ private static final String[] RESTRICTED_SERVICES_TO_TEST = {
+ Context.ALARM_SERVICE,
+ Context.WINDOW_SERVICE,
+ Context.POWER_SERVICE
+ };
+
+ private CountDownLatch mLatch;
+ private IIsolatedService mService;
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.e(TAG, "Isolated service " + name + " died abruptly");
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = IIsolatedService.Stub.asInterface(service);
+ mLatch.countDown();
+ }
+ };
+
+ @Override
+ public void setUp() throws InterruptedException {
+ mLatch = new CountDownLatch(1);
+ Intent serviceIntent = new Intent(mContext, IsolatedService.class);
+ mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ Assert.assertTrue("Timed out while waiting to bind to isolated service",
+ mLatch.await(BIND_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ public void testGetCachedServicesFromIsolatedService() throws RemoteException {
+ String[] cachedServices = mService.getCachedSystemServices();
+ for (String serviceName : cachedServices) {
+ Assert.assertTrue(serviceName + " should not be accessbible from an isolated process",
+ ArrayUtils.contains(SERVICES_ALLOWED_TO_ISOLATED_PROCESS, serviceName));
+ }
+ }
+
+ public void testGetServiceFromIsolatedService() throws RemoteException {
+ for (String serviceName : RESTRICTED_SERVICES_TO_TEST) {
+ IBinder service = mService.getSystemService(serviceName);
+ Assert.assertNull(serviceName + " should not be accessible from an isolated process",
+ service);
+ }
+ }
+
+ @Override
+ public void tearDown() {
+ mContext.unbindService(mServiceConnection);
+ }
+
+}
diff --git a/tests/tests/security/src/android/security/cts/IsolatedService.java b/tests/tests/security/src/android/security/cts/IsolatedService.java
new file mode 100644
index 0000000..28d2a65
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IsolatedService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class IsolatedService extends Service {
+
+ private static final String TAG = IsolatedService.class.getSimpleName();
+ private static final String SERVICE_MANAGER_CLASS_NAME = "android.os.ServiceManager";
+ private static final String SERVICE_MANAGER_INTERNAL_CACHE_NAME = "sCache";
+ private static final String GET_SERVICE_METHOD_NAME = "getService";
+
+ private String[] getServicesCachedInServiceManager() {
+ ArrayList<String> cachedServices = new ArrayList<String>();
+ try {
+ Class<?> serviceManager = Class.forName(SERVICE_MANAGER_CLASS_NAME);
+ Field cacheField = serviceManager.getDeclaredField(SERVICE_MANAGER_INTERNAL_CACHE_NAME);
+ cacheField.setAccessible(true);
+ HashMap<String, IBinder> sCache = (HashMap<String, IBinder>) cacheField.get(null);
+ for (Map.Entry<String, IBinder> serviceEntry : sCache.entrySet()) {
+ if (serviceEntry.getValue() != null) {
+ cachedServices.add(serviceEntry.getKey());
+ }
+ }
+ } catch (ClassCastException | ReflectiveOperationException exc) {
+ Log.w(TAG, "Unable to retrieve service manager cache via reflection ", exc);
+ }
+ return cachedServices.toArray(new String[cachedServices.size()]);
+ }
+
+ private IBinder getServiceFromServiceManager(String serviceName) {
+ try {
+ Class<?> serviceManager = Class.forName(SERVICE_MANAGER_CLASS_NAME);
+ Method getServiceMethod =
+ serviceManager.getDeclaredMethod(GET_SERVICE_METHOD_NAME, String.class);
+ IBinder service = (IBinder) getServiceMethod.invoke(null, serviceName);
+ return service;
+ } catch (ClassCastException | ReflectiveOperationException exc) {
+ Log.w(TAG, "Unable to call ServiceManager.getService() ", exc);
+ }
+ return null;
+ }
+
+ private final IIsolatedService.Stub mBinder = new IIsolatedService.Stub() {
+
+ public String[] getCachedSystemServices() {
+ return getServicesCachedInServiceManager();
+ }
+
+ public IBinder getSystemService(String serviceName) {
+ return getServiceFromServiceManager(serviceName);
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index f939eab..8205c22 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -162,6 +162,10 @@
doStagefrightTestMediaPlayer(R.raw.bug_14388161);
}
+ public void testStagefright_bug_27855419_CVE_2016_2463() throws Exception {
+ doStagefrightTest(R.raw.bug_27855419);
+ }
+
private void doStagefrightTest(final int rid) throws Exception {
doStagefrightTestMediaPlayer(rid);
doStagefrightTestMediaCodec(rid);
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index add19d2..3f3a5a2 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -73,7 +73,7 @@
.addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
.build();
- private static int sCounter = 9999;
+ private static int sCounter = 5549999;
Context mContext;
TelecomManager mTelecomManager;
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 458f69b..ae915db 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -125,7 +125,6 @@
}
public Bitmap takeScreenshot(Point testOffset) {
- getInstrumentation().waitForIdleSync();
Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot();
return Bitmap.createBitmap(source, testOffset.x, testOffset.y, TEST_WIDTH, TEST_HEIGHT);
}
diff --git a/tests/tests/view/src/android/view/cts/FrameMetricsListenerTest.java b/tests/tests/view/src/android/view/cts/FrameMetricsListenerTest.java
index 651bfbc..dac3a1922 100644
--- a/tests/tests/view/src/android/view/cts/FrameMetricsListenerTest.java
+++ b/tests/tests/view/src/android/view/cts/FrameMetricsListenerTest.java
@@ -27,6 +27,7 @@
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
+import android.view.cts.util.ViewTestUtils;
import android.view.FrameMetrics;
import android.view.View;
import android.view.Window;
@@ -88,16 +89,8 @@
}
});
- mInstrumentation.waitForIdleSync();
+ scrollView.postInvalidate();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- scrollView.fling(-100);
- }
- });
-
- mInstrumentation.waitForIdleSync();
new PollingCheck() {
@Override
protected boolean check() {
@@ -109,21 +102,17 @@
@Override
public void run() {
mActivity.getWindow().removeOnFrameMetricsAvailableListener(listener);
- data.clear();
}
});
- mInstrumentation.waitForIdleSync();
+ data.clear();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- scrollView.fling(100);
- assertEquals(0, data.size());
- }
- });
-
- mInstrumentation.waitForIdleSync();
+ // Produce 5 frames and assert no metric listeners were invoked
+ for (int i = 0; i < 5; i++) {
+ ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mActivity,
+ () -> { scrollView.invalidate(); });
+ }
+ assertEquals(0, data.size());
}
public void testMultipleListeners() throws Throwable {
@@ -245,5 +234,3 @@
});
}
}
-
-
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
old mode 100644
new mode 100755
index 3e071b6..b896229
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -25,7 +25,10 @@
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
import android.test.AndroidTestCase;
+import android.text.TextUtils;
import android.util.Printer;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
@@ -34,7 +37,10 @@
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -243,6 +249,11 @@
return;
}
+ if (!TextUtils.equals("native", getFbeMode())) {
+ // Skip the test unless the device is in native FBE mode.
+ return;
+ }
+
final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
final List<InputMethodInfo> imis = imm.getInputMethodList();
boolean hasEncryptionAwareInputMethod = false;
@@ -268,4 +279,22 @@
public void println(String x) {
}
}
+
+ private String getFbeMode() {
+ try (ParcelFileDescriptor.AutoCloseInputStream in =
+ new ParcelFileDescriptor.AutoCloseInputStream(
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .executeShellCommand("sm get-fbe-mode"))) {
+ try (BufferedReader br =
+ new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
+ // Assume that the output of "sm get-fbe-mode" is always one-line.
+ final String line = br.readLine();
+ return line != null ? line.trim() : "";
+ }
+ } catch (IOException e) {
+ return "";
+ }
+ }
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index e3730a6..9151783 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -115,6 +115,14 @@
* assumed that no scrolling will happen.
*/
private static final long MIN_SCROLL_WAIT_MS = 1000;
+
+ /**
+ * This is the minimum number of milliseconds to wait for findAll to
+ * find all the matches. If matches are not found, the Listener would
+ * call findAll again until it times out.
+ */
+ private static final long MIN_FIND_WAIT_MS = 3000;
+
/**
* Once scrolling has started, this is the interval that scrolling
* is checked to see if there is a change. If no scrolling change
@@ -1456,18 +1464,34 @@
private static class WaitForFindResultsListener extends FutureTask<Integer>
implements WebView.FindListener {
- public WaitForFindResultsListener() {
+ private final WebViewOnUiThread mWebViewOnUiThread;
+ private final int mMatchesWanted;
+ private final String mStringWanted;
+ private final boolean mRetry;
+
+ public WaitForFindResultsListener(
+ WebViewOnUiThread wv, String wanted, int matches, boolean retry) {
super(new Runnable() {
@Override
public void run() { }
}, null);
+ mWebViewOnUiThread = wv;
+ mMatchesWanted = matches;
+ mStringWanted = wanted;
+ mRetry = retry;
}
@Override
public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
boolean isDoneCounting) {
if (isDoneCounting) {
- set(numberOfMatches);
+ //If mRetry set to true and matches aren't equal, call findAll again
+ if (mRetry && numberOfMatches != mMatchesWanted) {
+ mWebViewOnUiThread.findAll(mStringWanted);
+ }
+ else {
+ set(numberOfMatches);
+ }
}
}
}
@@ -1488,15 +1512,10 @@
mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
+ "</body></html>", "text/html", null);
- WaitForFindResultsListener l = new WaitForFindResultsListener();
- int previousScrollY = mOnUiThread.getScrollY();
- mOnUiThread.pageDown(true);
- // Wait for content fully loaded.
- waitForScrollingComplete(previousScrollY);
+ WaitForFindResultsListener l = new WaitForFindResultsListener(mOnUiThread, "find", 2, true);
mOnUiThread.setFindListener(l);
mOnUiThread.findAll("find");
-
- assertEquals(2, l.get().intValue());
+ assertEquals(2, l.get(MIN_FIND_WAIT_MS, TimeUnit.MILLISECONDS).intValue());
}
public void testFindNext() throws Throwable {
@@ -1513,10 +1532,13 @@
"Find all instances of a word on the page and highlight them.</p>";
mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p + p + "</body></html>", "text/html", null);
+ WaitForFindResultsListener l = new WaitForFindResultsListener(mOnUiThread, "all", 2, true);
+ mOnUiThread.setFindListener(l);
- // highlight all the strings found
+ // highlight all the strings found and wait for all the matches to be found
mOnUiThread.findAll("all");
- getInstrumentation().waitForIdleSync();
+ l.get(MIN_FIND_WAIT_MS, TimeUnit.MILLISECONDS);
+ mOnUiThread.setFindListener(null);
int previousScrollY = mOnUiThread.getScrollY();
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java b/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
index e627c2b..4d976eb 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
@@ -18,6 +18,7 @@
import android.cts.util.PollingCheck;
import android.test.ActivityInstrumentationTestCase2;
+import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -38,8 +39,10 @@
private static final String LONG_LTR_STRING = "start start1 middle middle1 end end1";
private static final String SHORT_RTL_STRING = "ت";
private static final String SHORT_LTR_STRING = "s";
- public static final int ANY_PADDING = 15;
- public static final int ANY_FADE_LENGTH = 60;
+ public static final int ANY_PADDING = 20;
+ public static final int ANY_FADE_LENGTH = 20;
+ public static final int TEXT_SIZE = 20;
+ public static final int WIDTH = 100;
private static final TestCase[] TEST_DATA = {
// no fade - fading disabled
@@ -144,10 +147,10 @@
int gravity, int textAlignment, boolean scrollToMiddle) {
final MockTextView textView = new MockTextView(getActivity());
textView.setSingleLine(true);
- textView.setTextSize(30);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, TEXT_SIZE);
textView.setPadding(ANY_PADDING, ANY_PADDING, ANY_PADDING, ANY_PADDING);
textView.setFadingEdgeLength(ANY_FADE_LENGTH);
- textView.setLayoutParams(new ViewGroup.LayoutParams(300,
+ textView.setLayoutParams(new ViewGroup.LayoutParams(WIDTH,
ViewGroup.LayoutParams.WRAP_CONTENT));
textView.setHorizontalFadingEdgeEnabled(horizontalFadingEnabled);
textView.setText(text);
diff --git a/tools/cts-media/get_achievable_rates.py b/tools/cts-media/get_achievable_rates.py
index 9dde743..369a982 100755
--- a/tools/cts-media/get_achievable_rates.py
+++ b/tools/cts-media/get_achievable_rates.py
@@ -14,36 +14,9 @@
# limitations under the License.
#
-import argparse, math, re, sys
+import argparse, json, math, re, sys, zipfile
import xml.etree.ElementTree as ET
from collections import defaultdict, namedtuple
-import itertools
-
-
-def createLookup(values, key):
- """Creates a lookup table for a collection of values based on keys.
-
- Arguments:
- values: a collection of arbitrary values. Must be iterable.
- key: a function of one argument that returns the key for a value.
-
- Returns:
- A dict mapping keys (as generated by the key argument) to lists of
- values. All values in the lists have the same key, and are in the order
- they appeared in the collection.
- """
- lookup = defaultdict(list)
- for v in values:
- lookup[key(v)].append(v)
- return lookup
-
-
-def _intify(value):
- """Returns a value converted to int if possible, else the original value."""
- try:
- return int(value)
- except ValueError:
- return value
class Size(namedtuple('Size', ['width', 'height'])):
@@ -51,43 +24,50 @@
def __str__(self):
return '%dx%d' % (self.width, self.height)
+def nicekey(v):
+ """Returns a nicer sort key for sorting strings.
-class _VideoResultBase(object):
- """Helper methods for results. Not for use by applications.
+ This sorts using lower case, with numbers in numerical order first."""
+ key = []
+ num = False
+ for p in re.split('(\d+)', v.lower()):
+ if num:
+ key.append(('0', int(p)))
+ elif p:
+ key.append((p, 0))
+ num = not num
+ return key + [(v, 0)]
- Attributes:
- codec: The name of the codec (string) or None
- size: Size representing the video size or None
- mime: The mime-type of the codec (string) or None
- rates: The measured achievable frame rates
- is_decoder: True iff codec is a decoder.
- """
+def nice(v):
+ """Returns a nicer representation for objects in debug messages.
- def __init__(self, is_decoder):
- self.codec = None
- self.mime = None
- self.size = None
- self._rates_from_failure = []
- self._rates_from_message = []
- self.is_decoder = is_decoder
+ Dictionaries are sorted, size is WxH, unicode removed, and floats have 1 digit precision."""
+ if isinstance(v, dict):
+ return 'dict(' + ', '.join(k + '=' + nice(v) for k, v in sorted(v.items(), key=lambda i: nicekey(i[0]))) + ')'
+ if isinstance(v, str):
+ return repr(v)
+ if isinstance(v, int):
+ return str(v)
+ if isinstance(v, Size):
+ return repr(str(v))
+ if isinstance(v, float):
+ return '%.1f' % v
+ if isinstance(v, type(u'')):
+ return repr(str(v))
+ raise ValueError(v)
- def _inited(self):
- """Returns true iff codec, mime and size was set."""
- return None not in (self.codec, self.mime, self.size)
-
- def __len__(self):
- # don't report any result if codec name, mime type and size is unclear
- if not self._inited():
- return 0
- return len(self.rates)
-
- @property
- def rates(self):
- return self._rates_from_failure or self._rates_from_message
+class ResultParser:
+ @staticmethod
+ def _intify(value):
+ """Returns a value converted to int if possible, else the original value."""
+ try:
+ return int(value)
+ except ValueError:
+ return value
def _parseDict(self, value):
"""Parses a MediaFormat from its string representation sans brackets."""
- return dict((k, _intify(v))
+ return dict((k, self._intify(v))
for k, v in re.findall(r'([^ =]+)=([^ [=]+(?:|\[[^\]]+\]))(?:, |$)', value))
def _cleanFormat(self, format):
@@ -110,293 +90,270 @@
if key.endswith('Format'):
self._cleanFormat(value)
else:
- value = _intify(value)
+ value = self._intify(value)
return key, value
- def _parseValuesFromBracket(self, line):
- """Returns the values enclosed in brackets without the brackets.
- Parses a line matching the pattern "<tag>: [<values>]" and returns <values>.
+def perc(data, p, fn=round):
+ """Returns a percentile value from a sorted array.
- Raises:
- ValueError: if the line does not match the pattern.
- """
- try:
- return re.match(r'^[^:]+: *\[(?P<values>.*)\]\.$', line).group('values')
- except AttributeError:
- raise ValueError('line does not match "tag: [value]": %s' % line)
-
- def _parseRawData(self, line):
- """Parses the raw data line for video performance tests.
-
- Yields:
- Dict objects corresponding to parsed results, mapping string keys to
- int, string or dict values.
- """
- try:
- values = self._parseValuesFromBracket(line)
- result = {}
- for m in re.finditer(self.MESSAGE_PATTERN + r'(?P<sep>,? +|$)', values):
- key, value = self._parsePartialResult(m)
- result[key] = value
- if m.group('sep') != ' ':
- yield result
- result = {}
- except ValueError:
- print >> sys.stderr, 'could not parse line %s' % repr(line)
-
- def _tryParseMeasuredFrameRate(self, line):
- """Parses a line starting with 'Measured frame rate:'."""
- if line.startswith('Measured frame rate: '):
- try:
- values = self._parseValuesFromBracket(line)
- values = re.split(r' *, *', values)
- self._rates_from_failure = list(map(float, values))
- except ValueError:
- print >> sys.stderr, 'could not parse line %s' % repr(line)
-
- def parse(self, test):
- """Parses the ValueArray and FailedScene lines of a test result.
-
- Arguments:
- test: An ElementTree <Test> element.
- """
- failure = test.find('FailedScene')
- if failure is not None:
- trace = failure.find('StackTrace')
- if trace is not None:
- for line in re.split(r'[\r\n]+', trace.text):
- self._parseFailureLine(line)
- details = test.find('Details')
- if details is not None:
- for array in details.iter('ValueArray'):
- message = array.get('message')
- self._parseMessage(message, array)
-
- def _parseFailureLine(self, line):
- raise NotImplementedError
-
- def _parseMessage(self, message, array):
- raise NotImplementedError
-
- def getData(self):
- """Gets the parsed test result data.
-
- Yields:
- Result objects containing at least codec, size, mime and rates attributes."""
- yield self
+ Arguments:
+ data: sorted data
+ p: percentile value (0-100)
+ fn: method used for rounding the percentile to an integer index in data
+ """
+ return data[int(fn((len(data) - 1) * p / 100))]
-class VideoEncoderDecoderTestResult(_VideoResultBase):
- """Represents a result from a VideoEncoderDecoderTest performance case."""
+def genXml(data, A=None):
+ yield '<?xml version="1.0" encoding="utf-8" ?>'
+ yield '<!-- Copyright 2016 The Android Open Source Project'
+ yield ''
+ yield ' Licensed under the Apache License, Version 2.0 (the "License");'
+ yield ' you may not use this file except in compliance with the License.'
+ yield ' You may obtain a copy of the License at'
+ yield ''
+ yield ' http://www.apache.org/licenses/LICENSE-2.0'
+ yield ''
+ yield ' Unless required by applicable law or agreed to in writing, software'
+ yield ' distributed under the License is distributed on an "AS IS" BASIS,'
+ yield ' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.'
+ yield ' See the License for the specific language governing permissions and'
+ yield ' limitations under the License.'
+ yield '-->'
+ yield ''
+ yield '<MediaCodecs>'
+ last_section = None
+ from collections import namedtuple
+ Comp = namedtuple('Comp', 'is_decoder google mime name')
+ Result = namedtuple('Result', 'mn mx p95 med geo p5')
+ for comp_, cdata in sorted(data.items()):
+ comp = Comp(*comp_)
+ section = 'Decoders' if comp.is_decoder else 'Encoders'
+ if section != last_section:
+ if last_section:
+ yield ' </%s>' % last_section
+ yield ' <%s>' % section
+ last_section = section
+ yield ' <MediaCodec name="%s" type="%s" update="true">' % (comp.name, comp.mime)
+ for size, sdata in sorted(cdata.items()):
+ data = sorted(sdata)
+ N = len(data)
+ mn, mx = data[0], data[-1]
- def __init__(self, unused_m):
- super(VideoEncoderDecoderTestResult, self).__init__(is_decoder=False)
+ if N < 20 and not A.ignore:
+ raise ValueError("need at least 20 data points for %s size %s; have %s" %
+ (comp.name, size, N))
- # If a VideoEncoderDecoderTest succeeds, it provides the results in the
- # message of a ValueArray. If fails, it provides the results in the failure
- # using raw data. (For now it also includes some data in the ValueArrays even
- # if it fails, which we ignore.)
+ TO = 2.2 # tolerance with margin
+ T = TO / 1.1 # tolerance without margin
- def _parseFailureLine(self, line):
- """Handles parsing a line from the failure log."""
- self._tryParseMeasuredFrameRate(line)
- if line.startswith('Raw data: '):
- for result in self._parseRawData(line):
- fmt = result['EncOutputFormat']
- self.size = Size(fmt['width'], fmt['height'])
- self.codec = result['codec']
- self.mime = fmt['mime']
+ Final = namedtuple('Final', 'comment c2 var qual')
+ lastFinal = None
+ for RG in (10, 15, 20, 25, 30, 40, 50):
+ P = 50./RG
+ quality = 0
+ p95, med, p5 = perc(data, P, math.floor), perc(data, 50, round), perc(data, 100 - P, math.ceil)
+ geo = math.sqrt(p5 * p95)
+ comment = ''
+ pub_lo, pub_hi = min(int(p95 * T), round(geo)), max(math.ceil(p5 / T), round(geo))
+ if pub_lo > med:
+ if pub_lo > med * 1.1:
+ quality += 0.5
+ comment += ' SLOW'
+ pub_lo = int(med)
+ if N < 2 * RG:
+ comment += ' N=%d' % N
+ quality += 2
+ RGVAR = False
+ if p5 / p95 > T ** 3:
+ quality += 3
+ RGVAR = True
+ if pub_hi > pub_lo * TO:
+ quality += 1
+ if RG == 10:
+ # find best pub_lo and pub_hi
+ for i in range(N / 2):
+ pub_lo_, pub_hi_ = min(int(data[N / 2 - i - 1] * T), round(geo), int(med)), max(math.ceil(data[N / 2 + i] / T), round(geo))
+ if pub_hi_ > pub_lo_ * TO:
+ # ???
+ pub_lo = min(pub_lo, math.ceil(pub_hi_ / TO))
+ break
+ pub_lo, pub_hi = pub_lo_, pub_hi_
+ if mn < pub_lo / T or mx > pub_hi * T or pub_lo <= pub_hi / T:
+ quality += 1
+ comment += ' FLAKY('
+ if round(mn, 1) < pub_lo / T:
+ comment += 'mn=%.1f < ' % mn
+ comment += 'RANGE'
+ if round(mx, 1) > pub_hi * T:
+ comment += ' < mx=%.1f' % mx
+ comment += ')'
+ if False:
+ comment += ' DATA(mn=%1.f p%d=%1.f accept=%1.f-%1.f p50=%1.f p%d=%1.f mx=%1.f)' % (
+ mn, 100-P, p95, pub_lo / T, pub_hi * T, med, P, p5, mx)
+ var = math.sqrt(p5/p95)
+ if p95 < geo / T or p5 > geo * T:
+ if RGVAR:
+ comment += ' RG.VARIANCE:%.1f' % ((p5/p95) ** (1./3))
+ else:
+ comment += ' variance:%.1f' % var
+ comment = comment.replace('RANGE', '%d - %d' % (math.ceil(pub_lo / T), int(pub_hi * T)))
+ c2 = ''
+ if N >= 2 * RG:
+ c2 += ' N=%d' % N
+ if var <= T or p5 / p95 > T ** 3:
+ c2 += ' v%d%%=%.1f' % (round(100 - 2 * P), var)
+ if A and A.dbg:
+ c2 += ' E=%s' % (str(quality))
+ if c2:
+ c2 = ' <!--%s -->' % c2
- def _parseMessage(self, message, array):
- """Handles parsing a message from ValueArrays."""
- if message.startswith('codec='):
- result = dict(self._parsePartialResult(m)
- for m in re.finditer(self.MESSAGE_PATTERN + '(?: |$)', message))
- if 'EncInputFormat' in result:
- self.codec = result['codec']
- fmt = result['EncInputFormat']
- self.size = Size(fmt['width'], fmt['height'])
- self.mime = result['EncOutputFormat']['mime']
- self._rates_from_message.append(1000000./result['min'])
+ if comment:
+ comment = ' <!-- measured %d%%:%d-%d med:%d%s -->' % (round(100 - 2 * P), int(p95), math.ceil(p5), int(round(med)), comment)
+ if A and A.dbg: yield '<!-- --> %s%s' % (comment, c2)
+ c2 = ' <Limit name="measured-frame-rate-%s" range="%d-%d" />%s' % (size, pub_lo, pub_hi, c2)
+ final = Final(comment, c2, var, quality)
+ if lastFinal and final.var > lastFinal.var * math.sqrt(1.3):
+ if A and A.dbg: yield '<!-- RANGE JUMP -->'
+ break
+ elif not lastFinal or quality <= lastFinal.qual:
+ lastFinal = final
+ if N < 2 * RG or quality >= 4:
+ break
+ comment, c2, var, quality = lastFinal
+
+ if comment:
+ yield comment
+ yield c2
+ yield ' </MediaCodec>'
+ if last_section:
+ yield ' </%s>' % last_section
+ yield '</MediaCodecs>'
-class VideoDecoderPerfTestResult(_VideoResultBase):
- """Represents a result from a VideoDecoderPerfTest performance case."""
-
- # If a VideoDecoderPerfTest succeeds, it provides the results in the message
- # of a ValueArray. If fails, it provides the results in the failure only
- # using raw data.
-
- def __init__(self, unused_m):
- super(VideoDecoderPerfTestResult, self).__init__(is_decoder=True)
-
- def _parseFailureLine(self, line):
- """Handles parsing a line from the failure log."""
- self._tryParseMeasuredFrameRate(line)
- # if the test failed, we can only get the codec/size/mime from the raw data.
- if line.startswith('Raw data: '):
- for result in self._parseRawData(line):
- fmt = result['DecOutputFormat']
- self.size = Size(fmt['width'], fmt['height'])
- self.codec = result['codec']
- self.mime = result['mime']
-
- def _parseMessage(self, message, array):
- """Handles parsing a message from ValueArrays."""
- if message.startswith('codec='):
- result = dict(self._parsePartialResult(m)
- for m in re.finditer(self.MESSAGE_PATTERN + '(?: |$)', message))
- if result.get('decodeto') == 'surface':
- self.codec = result['codec']
- fmt = result['DecOutputFormat']
- self.size = Size(fmt['width'], fmt['height'])
- self.mime = result['mime']
- self._rates_from_message.append(1000000. / result['min'])
-
-
-class Results(object):
- """Container that keeps all test results."""
+class Data:
def __init__(self):
- self._results = [] # namedtuples
- self._device = None
+ self.data = set()
+ self.kind = {}
+ self.devices = set()
+ self.parser = ResultParser()
- VIDEO_ENCODER_DECODER_TEST_REGEX = re.compile(
- 'test(.*)(\d{4})x(\d{4})(Goog|Other)$')
+ def summarize(self, A=None):
+ devs = sorted(self.devices)
+ # device > (not encoder,goog,mime,codec) > size > fps
+ xmlInfo = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
- VIDEO_DECODER_PERF_TEST_REGEX = re.compile(
- 'test(VP[89]|H26[34]|MPEG4|HEVC)(\d+)x(\d+)(.*)$')
+ for mime, encoder, goog in sorted(set(self.kind.values())):
+ for dev, build, codec, size, num, std, avg, p0, p5, p10, p20, p30, p40, p50, p60, p70, p80, p90, p95, p100 in self.data:
+ if self.kind[codec] != (mime, encoder, goog):
+ continue
- TestCaseSpec = namedtuple('TestCaseSpec', 'package path class_ regex result_class')
+ if p95 > 2: # ignore measurements at or below 2fps
+ xmlInfo[dev][(not encoder, goog, mime, codec)][size].append(p95)
+ else:
+ print >> sys.stderr, "warning: p95 value is suspiciously low: %s" % (
+ nice(dict(config=dict(dev=dev, codec=codec, size=str(size), N=num),
+ data=dict(std=std, avg=avg, p0=p0, p5=p5, p10=p10, p20=p20, p30=p30, p40=p40,
+ p50=p50, p60=p60, p70=p70, p80=p80, p90=p90, p95=p95, p100=p100))))
+ for dev, ddata in xmlInfo.items():
+ outFile = '{}.media_codecs_performance.xml'.format(dev)
+ print >> sys.stderr, "generating", outFile
+ with open(outFile, "wt") as out:
+ for l in genXml(ddata, A=A):
+ out.write(l + '\n')
+ print l
+ print >> sys.stderr, "generated", outFile
- def _getTestCases(self):
- return [
- self.TestCaseSpec(package='CtsDeviceVideoPerf',
- path='TestSuite/TestSuite/TestSuite/TestSuite/TestCase',
- class_='VideoEncoderDecoderTest',
- regex=self.VIDEO_ENCODER_DECODER_TEST_REGEX,
- result_class=VideoEncoderDecoderTestResult),
- self.TestCaseSpec(package='CtsMediaTestCases',
- path='TestSuite/TestSuite/TestSuite/TestCase',
- class_='VideoDecoderPerfTest',
- regex=self.VIDEO_DECODER_PERF_TEST_REGEX,
- result_class=VideoDecoderPerfTestResult)
- ]
+ def parse_fmt(self, fmt):
+ return self.parser._parseDict(fmt)
- def _verifyDeviceInfo(self, device):
- assert self._device in (None, device), "expected %s device" % self._device
- self._device = device
+ def parse_perf(self, a, device, build):
+ def rateFn(i):
+ if i is None:
+ return i
+ elif i == 0:
+ return 1e6
+ return 1000. / i
- def importXml(self, xml):
- self._verifyDeviceInfo(xml.find('DeviceInfo/BuildInfo').get('buildName'))
+ points = ('avg', 'min', 'p5', 'p10', 'p20', 'p30', 'p40', 'p50', 'p60', 'p70', 'p80', 'p90', 'p95', 'max')
+ a = dict(a)
+ codec = a['codec_name'] + ''
+ mime = a['mime_type']
+ size = Size(a['width'], a['height'])
+ if 'decode_to' in a:
+ fmt = self.parse_fmt(a['output_format'])
+ ofmt = self.parse_fmt(a['input_format'])
+ else:
+ fmt = self.parse_fmt(a['input_format'])
+ ofmt = self.parse_fmt(a['output_format'])
+ size = Size(max(fmt['width'], ofmt['width']), max(fmt['height'], ofmt['height']))
- packages = createLookup(self._getTestCases(), lambda tc: tc.package)
-
- for pkg in xml.iter('TestPackage'):
- tests_in_package = packages.get(pkg.get('name'))
- if not tests_in_package:
- continue
- paths = createLookup(tests_in_package, lambda tc: tc.path)
- for path, tests_in_path in paths.items():
- classes = createLookup(tests_in_path, lambda tc: tc.class_)
- for tc in pkg.iterfind(path):
- tests_in_class = classes.get(tc.get('name'))
- if not tests_in_class:
- continue
- for test in tc.iter('Test'):
- for tc in tests_in_class:
- m = tc.regex.match(test.get('name'))
- if m:
- result = tc.result_class(m)
- result.parse(test)
- self._results.append(result)
-
- def importFile(self, path):
- print >> sys.stderr, 'Importing "%s"...' % path
try:
- return self.importXml(ET.parse(path))
- except ET.ParseError:
- raise ValueError('not a valid XML file')
+ prefix = 'time_avg_stats_'
+ if prefix + 'stdev' in a and a[prefix + 'avg']:
+ stdev = (a[prefix + 'stdev'] * 1e3 / a[prefix + 'avg'] ** 2)
+ data = ((device, build, codec, size, a[prefix + 'num'], stdev) +
+ tuple(rateFn(a.get(prefix + i)) for i in points))
+ self.data.add(data)
+ self.kind[codec] = (mime, 'decode_to' not in a, codec.lower().startswith('omx.google.'))
+ self.devices.add(data[0])
+ except (KeyError, ZeroDivisionError):
+ print >> sys.stderr, a
+ raise
- def getData(self):
- for result in self._results:
- for data in result.getData():
- yield data
-
- def dumpXml(self, results):
- yield '<?xml version="1.0" encoding="utf-8" ?>'
- yield '<!-- Copyright 2015 The Android Open Source Project'
- yield ''
- yield ' Licensed under the Apache License, Version 2.0 (the "License");'
- yield ' you may not use this file except in compliance with the License.'
- yield ' You may obtain a copy of the License at'
- yield ''
- yield ' http://www.apache.org/licenses/LICENSE-2.0'
- yield ''
- yield ' Unless required by applicable law or agreed to in writing, software'
- yield ' distributed under the License is distributed on an "AS IS" BASIS,'
- yield ' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.'
- yield ' See the License for the specific language governing permissions and'
- yield ' limitations under the License.'
- yield '-->'
- yield ''
- yield '<MediaCodecs>'
- last_section = None
- Comp = namedtuple('Comp', 'is_decoder google mime name')
- by_comp = createLookup(results,
- lambda e: Comp(is_decoder=e.is_decoder, google='.google.' in e.codec, mime=e.mime, name=e.codec))
- for comp in sorted(by_comp):
- section = 'Decoders' if comp.is_decoder else 'Encoders'
- if section != last_section:
- if last_section:
- yield ' </%s>' % last_section
- yield ' <%s>' % section
- last_section = section
- yield ' <MediaCodec name="%s" type="%s" update="true">' % (comp.name, comp.mime)
- by_size = createLookup(by_comp[comp], lambda e: e.size)
- for size in sorted(by_size):
- values = list(itertools.chain(*(e.rates for e in by_size[size])))
- min_, max_ = min(values), max(values)
- med_ = int(math.sqrt(min_ * max_))
- yield ' <Limit name="measured-frame-rate-%s" range="%d-%d" />' % (size, med_, med_)
- yield ' </MediaCodec>'
- if last_section:
- yield ' </%s>' % last_section
- yield '</MediaCodecs>'
-
-
-class Main(object):
- """Executor of this utility."""
-
- def __init__(self):
- self._result = Results()
-
- self._parser = argparse.ArgumentParser('get_achievable_framerates')
- self._parser.add_argument('result_xml', nargs='+')
-
- def _parseArgs(self):
- self._args = self._parser.parse_args()
-
- def _importXml(self, xml):
- self._result.importFile(xml)
-
- def _report(self):
- for line in self._result.dumpXml(r for r in self._result.getData() if r):
- print line
-
- def run(self):
- self._parseArgs()
- try:
- for xml in self._args.result_xml:
+ def parse_json(self, json, device, build):
+ for test, results in json:
+ if test in ("video_encoder_performance", "video_decoder_performance"):
try:
- self._importXml(xml)
- except (ValueError, IOError, AssertionError) as e:
- print >> sys.stderr, e
- raise KeyboardInterrupt
- self._report()
- except KeyboardInterrupt:
- print >> sys.stderr, 'Interrupted.'
+ if isinstance(results, list) and len(results[0]) and len(results[0][0]) == 2 and len(results[0][0][0]):
+ for result in results:
+ self.parse_perf(result, device, build)
+ else:
+ self.parse_perf(results, device, build)
+ except KeyboardInterrupt:
+ raise
-if __name__ == '__main__':
- Main().run()
+ def parse_result(self, result):
+ device, build = '', ''
+ if not result.endswith('.zip'):
+ print >> sys.stderr, "cannot parse %s" % result
+ return
+ try:
+ with zipfile.ZipFile(result) as zip:
+ resultInfo, testInfos = None, []
+ for info in zip.infolist():
+ if re.search(r'/GenericDeviceInfo.deviceinfo.json$', info.filename):
+ resultInfo = info
+ elif re.search(r'/Cts(Media|Video)TestCases\.reportlog\.json$', info.filename):
+ testInfos.append(info)
+ if resultInfo:
+ try:
+ jsonFile = zip.open(resultInfo)
+ jsonData = json.load(jsonFile, encoding='utf-8')
+ device, build = jsonData['build_device'], jsonData['build_id']
+ except ValueError:
+ print >> sys.stderr, "could not parse %s" % resultInfo.filename
+ for info in testInfos:
+ jsonFile = zip.open(info)
+ try:
+ jsonData = json.load(jsonFile, encoding='utf-8', object_pairs_hook=lambda items: items)
+ except ValueError:
+ print >> sys.stderr, "cannot parse JSON in %s" % info.filename
+ self.parse_json(jsonData, device, build)
+
+ except zipfile.BadZipfile:
+ raise ValueError('bad zipfile')
+
+
+P = argparse.ArgumentParser("gar_v2")
+P.add_argument("--dbg", "-v", action='store_true', help="dump debug info into xml")
+P.add_argument("--ignore", "-I", action='store_true', help="ignore minimum sample count")
+P.add_argument("result_zip", nargs="*")
+A = P.parse_args()
+
+D = Data()
+for res in A.result_zip:
+ D.parse_result(res)
+D.summarize(A=A)
diff --git a/tools/cts-tradefed/Android.mk b/tools/cts-tradefed/Android.mk
index 32babe6..fbf63d7 100644
--- a/tools/cts-tradefed/Android.mk
+++ b/tools/cts-tradefed/Android.mk
@@ -25,7 +25,7 @@
LOCAL_SUITE_TARGET_ARCH := $(TARGET_ARCH)
LOCAL_SUITE_NAME := CTS
LOCAL_SUITE_FULLNAME := "Compatibility Test Suite"
-LOCAL_SUITE_VERSION := 7.0
+LOCAL_SUITE_VERSION := 7.0_r5
LOCAL_MODULE := cts-tradefed