Merge "camera cts: Allow mandatory depth streams to be tested." into rvc-dev
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
index 1505aca..99df613 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
@@ -20,7 +20,9 @@
import android.app.ListActivity;
import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Bundle;
+import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.ListView;
@@ -28,17 +30,26 @@
/** {@link ListActivity} that displays a list of manual tests. */
public abstract class AbstractTestListActivity extends ListActivity {
private static final int LAUNCH_TEST_REQUEST_CODE = 9001;
+ //An invalid value which smaller than the edge of coordinate on the screen.
+ private static final float DEFAULT_CLICKED_COORDINATE = -1;
protected TestListAdapter mAdapter;
- // Start time of test item.
+ // Start time of test case.
protected long mStartTime;
- // End time of test item.
+ // End time of test case.
protected long mEndTime;
+ // X-axis of clicked coordinate when entering a test case.
+ protected float mCoordinateX;
+ // Y-axis of clicked coordinate when entering a test case.
+ protected float mCoornidateY;
+ // Whether test case was executed through automation.
+ protected boolean mIsAutomated;
protected void setTestListAdapter(TestListAdapter adapter) {
mAdapter = adapter;
setListAdapter(mAdapter);
mAdapter.loadTestResults();
+ setOnTouchListenerToListView();
}
private Intent getIntent(int position) {
@@ -82,11 +93,16 @@
}
TestResult testResult = TestResult.fromActivityResult(resultCode, data);
testResult.getHistoryCollection().add(
- testResult.getName(), mStartTime, mEndTime);
+ testResult.getName(), mStartTime, mEndTime, mIsAutomated);
mAdapter.setTestResult(testResult);
}
// Reset end time to avoid keeping same end time in retry.
mEndTime = 0;
+ // Reset mIsAutomated flag to false
+ mIsAutomated = false;
+ // Reset clicked coordinate.
+ mCoordinateX = DEFAULT_CLICKED_COORDINATE;
+ mCoornidateY = DEFAULT_CLICKED_COORDINATE;
}
/** Launch the activity when its {@link ListView} item is clicked. */
@@ -94,6 +110,10 @@
protected final void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
mStartTime = System.currentTimeMillis();
+ //Check whether the clicked coordinate is consistent with the center of the clicked Object.
+ Rect rect = new Rect();
+ view.getGlobalVisibleRect(rect);
+ mIsAutomated = (mCoordinateX == rect.centerX()) && (mCoornidateY == rect.centerY());
handleItemClick(listView, view, position, id);
}
@@ -102,4 +122,23 @@
Intent intent = getIntent(position);
startActivityForResult(intent, LAUNCH_TEST_REQUEST_CODE);
}
+
+ /** Set OnTouchListener to ListView to get the clicked Coordinate*/
+ protected void setOnTouchListenerToListView() {
+ getListView().setOnTouchListener(null);
+ getListView().setOnTouchListener(new View.OnTouchListener(){
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ mCoordinateX = event.getRawX();
+ mCoornidateY = event.getRawY();
+ } else {
+ // Reset clicked coordinate.
+ mCoordinateX = DEFAULT_CLICKED_COORDINATE;
+ mCoornidateY = DEFAULT_CLICKED_COORDINATE;
+ }
+ return false;
+ }
+ });
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultHistoryCollection.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultHistoryCollection.java
index 0e7160c..f92d233 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultHistoryCollection.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultHistoryCollection.java
@@ -3,7 +3,6 @@
import com.android.compatibility.common.util.TestResultHistory;
import java.io.Serializable;
-import java.util.AbstractMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -11,6 +10,7 @@
public class TestResultHistoryCollection implements Serializable {
+ private static final long serialVersionUID = 0L;
private final Set<TestResultHistory> mHistoryCollection = new HashSet<>();
/**
@@ -23,30 +23,32 @@
}
/**
- * Add a test result history with test name, start time and end time.
+ * Add a test result history with test name, start time, end time and isAutomated.
*
* @param test a string of test name.
* @param start start time of a test.
* @param end end time of a test.
+ * @param isAutomated whether test case was executed through automation.
*/
- public void add(String test, long start, long end) {
- Set<Map.Entry> duration = new HashSet<>();
- duration.add(new AbstractMap.SimpleEntry<>(start, end));
- mHistoryCollection.add(new TestResultHistory(test, duration));
+ public void add(String test, long start, long end, boolean isAutomated) {
+ Set<TestResultHistory.ExecutionRecord> executionRecords
+ = new HashSet<TestResultHistory.ExecutionRecord> ();
+ executionRecords.add(new TestResultHistory.ExecutionRecord(start, end, isAutomated));
+ mHistoryCollection.add(new TestResultHistory(test, executionRecords));
}
/**
- * Add test result histories for tests containing test name and a set of execution time.
+ * Add test result histories for tests containing test name and a set of ExecutionRecords
*
* @param test test name.
- * @param durations set of start and end time.
+ * @param executionRecords set of ExecutionRecords.
*/
- public void addAll(String test, Set<Map.Entry> durations) {
- TestResultHistory history = new TestResultHistory(test, durations);
+ public void addAll(String test, Set<TestResultHistory.ExecutionRecord> executionRecords) {
+ TestResultHistory history = new TestResultHistory(test, executionRecords);
boolean match = false;
for (TestResultHistory resultHistory: mHistoryCollection) {
if (resultHistory.getTestName().equals(test)) {
- resultHistory.getDurations().addAll(durations);
+ resultHistory.getExecutionRecords().addAll(executionRecords);
match = true;
break;
}
@@ -63,10 +65,12 @@
* @param resultHistoryCollection a set of test result histories.
*/
public void merge(String prefix, TestResultHistoryCollection resultHistoryCollection) {
- if (resultHistoryCollection != null) {
+ if (resultHistoryCollection != null) {
resultHistoryCollection.asSet().forEach(t-> addAll(
- prefix != null ? prefix + ":" + t.getTestName() : t.getTestName(), t.getDurations()));
- }
+ prefix != null
+ ? prefix + ":" + t.getTestName()
+ : t.getTestName(), t.getExecutionRecords()));
+ }
}
/**
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
index 6e48bcc..fb18b06 100644
--- 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
@@ -19,6 +19,9 @@
* Enum containing metadata for mainline modules.
*/
public enum MainlineModule {
+
+ // Added in Q
+
// Security
MEDIA_SOFTWARE_CODEC("com.google.android.media.swcodec",
true, ModuleType.APEX,
@@ -50,10 +53,6 @@
"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
- TZDATA2("com.google.android.tzdata2",
- true, ModuleType.APEX,
- "48:F3:A2:98:76:1B:6D:46:75:7C:EE:62:43:66:6A:25:B9:15:B9:42:18:A6:C2:82:72:99:BE"
- + ":DA:C9:92:AB:E7"),
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"
@@ -70,6 +69,61 @@
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"),
+
+ // Added in R
+
+ ADBD("com.google.android.adbd",
+ true, ModuleType.APEX,
+ "87:3D:4E:43:58:25:1A:25:1A:2D:9C:18:E1:55:09:45:21:88:A8:1E:FE:9A:83:9D:43:0D:E8"
+ + ":D8:7E:C2:49:4C"),
+ NEURAL_NETWORKS("com.google.android.neuralnetworks",
+ true, ModuleType.APEX,
+ "6F:AB:D5:72:9A:90:02:6B:74:E4:87:79:8F:DF:10:BB:E3:6C:9E:6C:B7:A6:59:04:3C:D8:15"
+ + ":61:6C:9E:60:50"),
+ CELL_BROADCAST("com.google.android.cellbroadcast",
+ true, ModuleType.APEX,
+ "A8:2C:84:7A:A3:9D:DA:19:A5:6C:9E:D3:56:50:1A:76:4F:BD:5D:C9:60:98:66:16:E3:1D:48"
+ + ":EE:27:08:19:70"),
+ EXT_SERVICES("com.google.android.extservices",
+ true, ModuleType.APEX,
+ "10:89:F2:7C:85:6A:83:D4:02:6B:6A:49:97:15:4C:A1:70:9A:F6:93:27:C8:EF:9A:2D:1D:56"
+ + ":AB:69:DE:07:0B"),
+ IPSEC("com.google.android.ipsec",
+ true, ModuleType.APEX,
+ "64:3D:3E:A5:B7:BF:22:E5:94:42:29:77:7C:4B:FF:C6:C8:44:14:64:4D:E0:4B:E4:90:37:57"
+ + ":DE:83:CF:04:8B"),
+ MEDIA_PROVIDER("com.google.android.mediaprovider",
+ true, ModuleType.APEX,
+ "1A:61:93:09:6D:DC:81:58:72:45:EF:2C:07:33:73:6E:8E:FF:9D:E9:0E:51:27:4B:F8:23:AC"
+ + ":F0:F7:49:00:A0"),
+ PERMISSION_CONTROLLER_APEX("com.google.android.permission",
+ true, ModuleType.APEX,
+ "69:AC:92:BF:BA:D5:85:4C:61:8E:AB:AE:85:7F:AB:0B:1A:65:19:44:E9:19:EA:3C:86:DB:D4"
+ + ":07:04:1E:22:C1"),
+ SDK_EXTENSIONS("com.google.android.sdkext",
+ true, ModuleType.APEX,
+ "99:90:29:2B:22:11:D2:78:17:BF:5B:10:98:84:8F:68:44:53:37:16:2B:47:FF:D1:A0:8E:10"
+ + ":CE:65:B1:CC:73"),
+ STATSD("com.google.android.os.statsd",
+ true, ModuleType.APEX,
+ "DA:FE:D6:20:A7:0C:98:05:A9:A2:22:04:55:6B:0E:94:E8:E3:4D:ED:F4:16:EC:58:92:C6:48"
+ + ":86:53:39:B4:7B"),
+ TELEMETRY_TVP("com.google.mainline.telemetry",
+ true, ModuleType.APK,
+ "9D:AC:CC:AE:4F:49:5A:E6:DB:C5:8A:0E:C2:33:C6:E5:2D:31:14:33:AC:57:3C:4D:A1:C7:39"
+ + ":DF:64:03:51:5D"),
+ TETHERING("com.google.android.tethering",
+ true, ModuleType.APEX,
+ "E5:3F:52:F4:14:15:0C:05:BA:E0:E4:CE:E2:07:3D:D0:0F:E6:44:66:1D:5F:9A:0F:BE:49:4A"
+ + ":DC:07:F0:59:93"),
+ TZDATA2("com.google.android.tzdata2",
+ true, ModuleType.APEX,
+ "48:F3:A2:98:76:1B:6D:46:75:7C:EE:62:43:66:6A:25:B9:15:B9:42:18:A6:C2:82:72:99:BE"
+ + ":DA:C9:92:AB:E7"),
+ WIFI("com.google.android.wifi",
+ false, ModuleType.APEX,
+ "B7:A3:DB:7A:86:6D:18:51:3F:97:6C:63:20:BC:0F:E6:E4:01:BA:2F:26:96:B1:C3:94:2A:F0"
+ + ":FE:29:31:98:B1"),
;
public final String packageName;
diff --git a/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java b/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
index bea7506..d5301d4 100644
--- a/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
+++ b/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
@@ -64,6 +64,10 @@
return;
}
+ if (getDevice().isAdbTcp()) { // adb over WiFi, no point checking it
+ return;
+ }
+
ProcessBuilder pb = new ProcessBuilder(check_ms_os_desc.getAbsolutePath());
pb.environment().put("ANDROID_SERIAL", serial);
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
diff --git a/hostsidetests/angle/Android.bp b/hostsidetests/angle/Android.bp
index 3125610..dac193f 100644
--- a/hostsidetests/angle/Android.bp
+++ b/hostsidetests/angle/Android.bp
@@ -22,7 +22,6 @@
"cts",
"vts10",
"general-tests",
- "mts"
],
libs: [
"cts-tradefed",
diff --git a/hostsidetests/angle/app/common/Android.bp b/hostsidetests/angle/app/common/Android.bp
index 508ce2d..fe33a37 100644
--- a/hostsidetests/angle/app/common/Android.bp
+++ b/hostsidetests/angle/app/common/Android.bp
@@ -20,6 +20,5 @@
test_suites: [
"gts",
"ats",
- "mts"
],
}
diff --git a/hostsidetests/angle/app/driverTest/Android.bp b/hostsidetests/angle/app/driverTest/Android.bp
index 1bfc779..9b2adb2 100644
--- a/hostsidetests/angle/app/driverTest/Android.bp
+++ b/hostsidetests/angle/app/driverTest/Android.bp
@@ -22,7 +22,6 @@
test_suites: [
"cts",
"vts10",
- "mts"
],
compile_multilib: "both",
static_libs: [
diff --git a/hostsidetests/angle/app/driverTestSecondary/Android.bp b/hostsidetests/angle/app/driverTestSecondary/Android.bp
index 22f3c2f..fad514e 100644
--- a/hostsidetests/angle/app/driverTestSecondary/Android.bp
+++ b/hostsidetests/angle/app/driverTestSecondary/Android.bp
@@ -24,7 +24,6 @@
test_suites: [
"cts",
"vts10",
- "mts"
],
compile_multilib: "both",
static_libs: [
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/LocationPolicyTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/LocationPolicyTest.java
new file mode 100644
index 0000000..e2ff6c6
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/LocationPolicyTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts;
+
+
+import android.platform.test.annotations.SecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests to verify app location access */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class LocationPolicyTest extends BaseAppSecurityTest {
+
+ private static final String TEST_PKG = "android.appsecurity.cts.locationpolicy";
+ private static final String TEST_APK = "CtsLocationPolicyApp.apk";
+
+ @Before
+ public void setUp() throws Exception {
+ getDevice().uninstallPackage(TEST_PKG);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ getDevice().uninstallPackage(TEST_PKG);
+ }
+
+ @Test
+ @SecurityTest
+ public void testLocationPolicyPermissions() throws Exception {
+ new InstallMultiple(true).addFile(TEST_APK).run();
+ Utils.runDeviceTests(
+ getDevice(), TEST_PKG, ".LocationPolicyTest", "testLocationPolicyPermissions");
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/LocationPolicyApp/Android.bp b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/Android.bp
new file mode 100644
index 0000000..b7174d2
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "CtsLocationPolicyApp",
+ defaults: ["cts_defaults"],
+ libs: [
+ "android.test.runner.stubs",
+ "android.test.base.stubs",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "telephony-common",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ test_suites: [
+ "cts",
+ "vts10",
+ "sts",
+ "general-tests",
+ ],
+ min_sdk_version: "27",
+ target_sdk_version: "28",
+}
diff --git a/hostsidetests/appsecurity/test-apps/LocationPolicyApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/AndroidManifest.xml
new file mode 100644
index 0000000..ed9d1fc
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appsecurity.cts.locationpolicy">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.appsecurity.cts.locationpolicy"
+ android:label="Test to check location policy access for bad sdk."/>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java
new file mode 100644
index 0000000..74ce0c5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts.locationpolicy;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.SecurityTest;
+import android.telephony.TelephonyManager;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class LocationPolicyTest {
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ @Test
+ @SecurityTest
+ public void testLocationPolicyPermissions() throws Exception {
+ assertNotNull(mContext);
+ PackageManager pm = mContext.getPackageManager();
+ assertNotNull(pm);
+ assertNotEquals(
+ PackageManager.PERMISSION_GRANTED,
+ pm.checkPermission(Manifest.permission.ACCESS_FINE_LOCATION,
+ mContext.getPackageName()));
+ assertNotEquals(
+ PackageManager.PERMISSION_GRANTED,
+ pm.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
+ mContext.getPackageName()));
+ TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
+ try {
+ tele.getCellLocation();
+ fail(
+ "ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION Permissions not granted. Should have"
+ + " received a security exception when invoking getCellLocation().");
+ } catch (SecurityException ignore) {
+ // That's what we want!
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/UtilsProvider.java b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/UtilsProvider.java
index d3564f9..39da864 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/UtilsProvider.java
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/UtilsProvider.java
@@ -35,6 +35,7 @@
public static final String ACTION_GRANT_URI = "grantUri";
public static final String ACTION_REVOKE_URI = "revokeUri";
public static final String ACTION_START_ACTIVITY = "startActivity";
+ public static final String ACTION_START_ACTIVITIES = "startActivities";
public static final String ACTION_START_SERVICE = "startService";
public static final String ACTION_VERIFY_OUTGOING_PERSISTED = "verifyOutgoingPersisted";
public static final String ACTION_SET_PRIMARY_CLIP = "setPrimaryClip";
@@ -70,6 +71,11 @@
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(newIntent);
+ } else if (ACTION_START_ACTIVITIES.equals(action)) {
+ final Intent newIntent = intent.getParcelableExtra(EXTRA_INTENT);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivities(new Intent[] { newIntent });
+
} else if (ACTION_START_SERVICE.equals(action)) {
final Intent newIntent = intent.getParcelableExtra(EXTRA_INTENT);
context.startService(newIntent);
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
index 26a2eee..132d7f6 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
@@ -22,7 +22,8 @@
import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
-import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaActivities;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaActivity;
import static junit.framework.Assert.fail;
@@ -85,7 +86,7 @@
// --------------------------------
ReceiveUriActivity.clearStarted();
- grantClipUriPermission(subClip, mode, false);
+ grantClipUriPermissionViaActivities(subClip, mode);
ReceiveUriActivity.waitForStart();
assertAccess(uri, 0);
@@ -99,7 +100,7 @@
// --------------------------------
ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(sub2Clip, mode, false);
+ grantClipUriPermissionViaActivity(sub2Clip, mode);
ReceiveUriActivity.waitForNewIntent();
assertAccess(uri, 0);
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
index 7be059f..444c1bf 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
@@ -22,7 +22,7 @@
import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
-import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaService;
import static junit.framework.Assert.fail;
@@ -79,7 +79,7 @@
// --------------------------------
ReceiveUriService.clearStarted();
- grantClipUriPermission(subClip, mode, true);
+ grantClipUriPermissionViaService(subClip, mode);
ReceiveUriService.waitForStart();
int firstStartId = ReceiveUriService.getCurStartId();
@@ -96,7 +96,7 @@
// Send another Intent to it.
ReceiveUriService.clearStarted();
- grantClipUriPermission(sub2Clip, mode, true);
+ grantClipUriPermissionViaService(sub2Clip, mode);
ReceiveUriService.waitForStart();
assertAccess(uri, 0);
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
index a955dbc..8058d5b 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
@@ -23,7 +23,7 @@
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipNotAllowed;
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipAllowed;
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipNotAllowed;
-import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaActivity;
import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaContext;
import static com.android.cts.usespermissiondiffcertapp.Utils.revokeClipUriPermissionViaContext;
@@ -104,8 +104,8 @@
// Now, let's grant ourselves some access
ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+ grantClipUriPermissionViaActivity(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
ReceiveUriActivity.waitForStart();
// We should now have reading access, even before taking the persistable
@@ -129,9 +129,9 @@
// Launch again giving ourselves persistable read and write access
ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ grantClipUriPermissionViaActivity(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
ReceiveUriActivity.waitForNewIntent();
// Previous persisted grant should be unchanged
@@ -190,8 +190,8 @@
// Give ourselves prefix read access
ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+ grantClipUriPermissionViaActivity(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
ReceiveUriActivity.waitForStart();
// Verify prefix read access
@@ -204,7 +204,7 @@
// Now give ourselves exact write access
ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+ grantClipUriPermissionViaActivity(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
ReceiveUriActivity.waitForNewIntent();
// Verify we have exact write access, but not prefix write
@@ -233,9 +233,9 @@
// Give ourselves prefix read access
ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ grantClipUriPermissionViaActivity(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
ReceiveUriActivity.waitForStart();
// Verify prefix read access
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
index 48cac57..62bad1f 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
@@ -20,6 +20,7 @@
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_GRANT_URI;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_REVOKE_URI;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_SET_PRIMARY_CLIP;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_ACTIVITIES;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_ACTIVITY;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_SERVICE;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_VERIFY_OUTGOING_PERSISTED;
@@ -38,6 +39,8 @@
import com.android.cts.permissiondeclareapp.UtilsProvider;
+import java.util.Objects;
+
public class Utils {
private static Context getContext() {
return InstrumentationRegistry.getTargetContext();
@@ -49,7 +52,19 @@
getContext().getContentResolver().call(UtilsProvider.URI, "", "", extras);
}
- static void grantClipUriPermission(ClipData clip, int mode, boolean service) {
+ static void grantClipUriPermissionViaActivity(ClipData clip, int mode) {
+ grantClipUriPermission(clip, mode, ACTION_START_ACTIVITY);
+ }
+
+ static void grantClipUriPermissionViaActivities(ClipData clip, int mode) {
+ grantClipUriPermission(clip, mode, ACTION_START_ACTIVITIES);
+ }
+
+ static void grantClipUriPermissionViaService(ClipData clip, int mode) {
+ grantClipUriPermission(clip, mode, ACTION_START_SERVICE);
+ }
+
+ private static void grantClipUriPermission(ClipData clip, int mode, String action) {
Intent grantIntent = new Intent();
if (clip.getItemCount() == 1) {
grantIntent.setData(clip.getItemAt(0).getUri());
@@ -65,9 +80,10 @@
}
grantIntent.addFlags(mode);
grantIntent.setClass(getContext(),
- service ? ReceiveUriService.class : ReceiveUriActivity.class);
+ Objects.equals(ACTION_START_SERVICE, action) ? ReceiveUriService.class
+ : ReceiveUriActivity.class);
Intent intent = new Intent();
- intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
+ intent.setAction(action);
intent.putExtra(EXTRA_INTENT, grantIntent);
call(intent);
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
index f6659f3..4321f38 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
@@ -88,12 +88,6 @@
mUiDevice.pressHome();
}
- public void testLockTaskIsActive() throws Exception {
- Log.d(TAG, "testLockTaskIsActive on host-driven test");
- waitAndCheckLockedActivityIsResumed();
- checkLockedActivityIsRunning();
- }
-
/**
* On low-RAM devices, this test can take too long to finish, so the test runner can incorrectly
* assume it's finished. Therefore, only use it once in a given test.
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 048468c..72e8293 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -1226,9 +1226,6 @@
// Wait for the LockTask starting
waitForBroadcastIdle();
- // Make sure that the LockTaskUtilityActivityIfWhitelisted was started.
- executeDeviceTestMethod(".LockTaskHostDrivenTest", "testLockTaskIsActive");
-
// Try to open settings via adb
executeShellCommand("am start -a android.settings.SETTINGS");
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
index c067a1c..08349a1 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -20,8 +20,10 @@
import android.platform.test.annotations.AppModeFull;
+import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
import org.junit.After;
import org.junit.Before;
@@ -46,6 +48,19 @@
}
+ /**
+ * Runs the given phase of ScopedStorageTest by calling into the device with {@code
+ * --no-isolated-storage} flag.
+ * Throws an exception if the test phase fails.
+ */
+ void runDeviceTestWithDisabledIsolatedStorage(String phase) throws Exception {
+ runDeviceTests(new DeviceTestRunOptions("android.scopedstorage.cts")
+ .setDevice(getDevice())
+ .setTestClassName("android.scopedstorage.cts.ScopedStorageTest")
+ .setTestMethodName(phase)
+ .setDisableIsolatedStorage(true));
+ }
+
String executeShellCommand(String cmd) throws Exception {
return getDevice().executeShellCommand(cmd);
}
@@ -431,6 +446,36 @@
runDeviceTest("testWallpaperApisManageExternalStoragePrivileged");
}
+ @Test
+ public void testNoIsolatedStorageInstrumentationFlag() throws Exception {
+ runDeviceTestWithDisabledIsolatedStorage("testNoIsolatedStorageCanCreateFilesAnywhere");
+ runDeviceTestWithDisabledIsolatedStorage(
+ "testNoIsolatedStorageCantReadWriteOtherAppExternalDir");
+ runDeviceTestWithDisabledIsolatedStorage("testNoIsolatedStorageStorageReaddir");
+ runDeviceTestWithDisabledIsolatedStorage("testNoIsolatedStorageQueryOtherAppsFile");
+
+ // Check that appop is revoked after instrumentation is over.
+ runDeviceTest("testCreateFileInAppExternalDir");
+ runDeviceTest("testCreateFileInOtherAppExternalDir");
+ runDeviceTest("testReadWriteFilesInOtherAppExternalDir");
+ }
+
+ @Test
+ public void testRenameFromShell() throws Exception {
+ final ITestDevice device = getDevice();
+ final boolean isAdbRoot = device.isAdbRoot() ? true : false;
+ try {
+ if (isAdbRoot) {
+ device.disableAdbRoot();
+ }
+ runDeviceTest("testRenameFromShell");
+ } finally {
+ if (isAdbRoot) {
+ device.enableAdbRoot();
+ }
+ }
+ }
+
private void grantPermissions(String... perms) throws Exception {
for (String perm : perms) {
executeShellCommand("pm grant android.scopedstorage.cts " + perm);
diff --git a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
index 2422f3b..4596cab 100644
--- a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
+++ b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
@@ -133,6 +133,7 @@
@After
public void teardown() throws Exception {
executeShellCommand("rm " + getShellFile());
+ MediaStore.scanFile(getContentResolver(), getShellFile());
}
/**
@@ -221,6 +222,7 @@
try {
executeShellCommand("touch " + existingFile);
+ MediaStore.scanFile(getContentResolver(), existingFile);
Os.open(existingFile.getPath(), OsConstants.O_RDONLY, /*mode*/ 0);
fail("Opening file for read expected to fail: " + existingFile);
} catch (ErrnoException expected) {
@@ -273,6 +275,7 @@
FileDescriptor fd = null;
try {
executeShellCommand("touch " + existingFile);
+ MediaStore.scanFile(getContentResolver(), existingFile);
fd = Os.open(existingFile.getPath(), OsConstants.O_RDONLY, /*mode*/ 0);
} finally {
if (fd != null) {
@@ -314,6 +317,7 @@
final File shellFile = getShellFile();
executeShellCommand("touch " + getShellFile());
+ MediaStore.scanFile(getContentResolver(), getShellFile());
// can list a non-media file created by other package.
assertThat(Arrays.asList(shellFile.getParentFile().list()))
.contains(shellFile.getName());
@@ -381,6 +385,7 @@
"LegacyFileAccessTest2");
try {
executeShellCommand("touch " + shellFile1);
+ MediaStore.scanFile(getContentResolver(), shellFile1);
// app can't rename shell file.
assertCantRenameFile(shellFile1, shellFile2);
// app can't move shell file to its media directory.
@@ -415,6 +420,7 @@
"LegacyFileAccessTest2");
try {
executeShellCommand("touch " + shellFile1);
+ MediaStore.scanFile(getContentResolver(), shellFile1);
// app can't rename shell file.
assertCantRenameFile(shellFile1, shellFile2);
// app can't move shell file to its media directory.
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index 4bd2344..eb60460 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -781,6 +781,8 @@
} finally {
executeShellCommand("rm " + pdfFile.getAbsolutePath());
executeShellCommand("rm " + videoFile.getAbsolutePath());
+ MediaStore.scanFile(getContentResolver(), pdfFile);
+ MediaStore.scanFile(getContentResolver(), videoFile);
uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -1387,6 +1389,7 @@
videoFile.delete();
topLevelVideoFile.delete();
executeShellCommand("rm " + musicFile.getAbsolutePath());
+ MediaStore.scanFile(getContentResolver(), musicFile);
denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
}
}
@@ -2097,11 +2100,13 @@
// Use shell to create root file because TEST_APP_A is in
// scoped storage.
executeShellCommand("touch " + shellPdfAtRoot.getAbsolutePath());
+ MediaStore.scanFile(getContentResolver(), shellPdfAtRoot);
assertFileAccess_existsOnly(shellPdfAtRoot);
} finally {
deleteFileAsNoThrow(TEST_APP_A, otherAppPdf.getAbsolutePath());
deleteFileAsNoThrow(TEST_APP_A, otherAppImage.getAbsolutePath());
executeShellCommand("rm " + shellPdfAtRoot.getAbsolutePath());
+ MediaStore.scanFile(getContentResolver(), shellPdfAtRoot);
myAppPdf.delete();
uninstallApp(TEST_APP_A);
}
@@ -2225,6 +2230,7 @@
installApp(TEST_APP_A);
assertCreateFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
executeShellCommand("touch " + otherTopLevelFile);
+ MediaStore.scanFile(getContentResolver(), otherTopLevelFile);
allowAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
@@ -2240,6 +2246,7 @@
} finally {
denyAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
executeShellCommand("rm " + otherTopLevelFile);
+ MediaStore.scanFile(getContentResolver(), otherTopLevelFile);
deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
uninstallApp(TEST_APP_A);
}
@@ -2344,6 +2351,7 @@
assertThat(dirInDcim.exists() || dirInDcim.mkdir()).isTrue();
executeShellCommand("touch " + otherAppPdfFile1);
+ MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
installAppWithStoragePermissions(TEST_APP_A);
allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
@@ -2362,6 +2370,8 @@
} finally {
executeShellCommand("rm " + otherAppPdfFile1);
executeShellCommand("rm " + otherAppPdfFile2);
+ MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
+ MediaStore.scanFile(getContentResolver(), otherAppPdfFile2);
otherAppImageFile1.delete();
otherAppImageFile2.delete();
otherAppVideoFile1.delete();
@@ -2637,6 +2647,139 @@
}
}
+ @Test
+ public void testNoIsolatedStorageCanCreateFilesAnywhere() throws Exception {
+ final File topLevelPdf = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
+ final File musicFileInMovies = new File(getMoviesDir(), AUDIO_FILE_NAME);
+ final File imageFileInDcim = new File(getDcimDir(), IMAGE_FILE_NAME);
+ // Nothing special about this, anyone can create an image file in DCIM
+ assertCanCreateFile(imageFileInDcim);
+ // This is where we see the special powers of MANAGE_EXTERNAL_STORAGE, because it can
+ // create a top level file
+ assertCanCreateFile(topLevelPdf);
+ // It can even create a music file in Pictures
+ assertCanCreateFile(musicFileInMovies);
+ }
+
+ @Test
+ public void testNoIsolatedStorageCantReadWriteOtherAppExternalDir() throws Exception {
+ try {
+ // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
+ installAppWithStoragePermissions(TEST_APP_A);
+
+ // Let app A create a file in its data dir
+ final File otherAppExternalDataDir = new File(getExternalFilesDir().getPath().replace(
+ THIS_PACKAGE_NAME, TEST_APP_A.getPackageName()));
+ final File otherAppExternalDataFile = new File(otherAppExternalDataDir,
+ NONMEDIA_FILE_NAME);
+ assertCreateFilesAs(TEST_APP_A, otherAppExternalDataFile);
+
+ // File Manager app gets global access with MANAGE_EXTERNAL_STORAGE permission, however,
+ // file manager app doesn't have access to other app's external files directory
+ assertThat(canOpen(otherAppExternalDataFile, /* forWrite */ false)).isFalse();
+ assertThat(canOpen(otherAppExternalDataFile, /* forWrite */ true)).isFalse();
+ assertThat(otherAppExternalDataFile.delete()).isFalse();
+
+ assertThat(deleteFileAs(TEST_APP_A, otherAppExternalDataFile.getPath())).isTrue();
+
+ assertThrows(IOException.class,
+ () -> { otherAppExternalDataFile.createNewFile(); });
+
+ } finally {
+ uninstallApp(TEST_APP_A); // Uninstalling deletes external app dirs
+ }
+ }
+
+ @Test
+ public void testNoIsolatedStorageStorageReaddir() throws Exception {
+ final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+ final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+ final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+ final File otherTopLevelFile = new File(getExternalStorageDir(),
+ "other" + NONMEDIA_FILE_NAME);
+ try {
+ installApp(TEST_APP_A);
+ assertCreateFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
+ executeShellCommand("touch " + otherTopLevelFile);
+
+ // We can list other apps' files
+ assertDirectoryContains(otherAppPdf.getParentFile(), otherAppPdf);
+ assertDirectoryContains(otherAppImg.getParentFile(), otherAppImg);
+ assertDirectoryContains(otherAppMusic.getParentFile(), otherAppMusic);
+ // We can list top level files
+ assertDirectoryContains(getExternalStorageDir(), otherTopLevelFile);
+
+ // We can also list all top level directories
+ assertDirectoryContains(getExternalStorageDir(), getDefaultTopLevelDirs());
+ } finally {
+ executeShellCommand("rm " + otherTopLevelFile);
+ deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
+ uninstallApp(TEST_APP_A);
+ }
+ }
+
+ @Test
+ public void testNoIsolatedStorageQueryOtherAppsFile() throws Exception {
+ final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+ final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+ final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+ final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
+ try {
+ installApp(TEST_APP_A);
+ // Apps can't query other app's pending file, hence create file and publish it.
+ assertCreatePublishedFilesAs(
+ TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+
+ assertCanQueryAndOpenFile(otherAppPdf, "rw");
+ assertCanQueryAndOpenFile(otherAppImg, "rw");
+ assertCanQueryAndOpenFile(otherAppMusic, "rw");
+ assertCanQueryAndOpenFile(otherHiddenFile, "rw");
+ } finally {
+ deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+ uninstallApp(TEST_APP_A);
+ }
+ }
+
+ @Test
+ public void testRenameFromShell() throws Exception {
+ final File imageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+ final File dir = new File(getMoviesDir(), TEST_DIRECTORY_NAME);
+ final File renamedDir = new File(getMusicDir(), TEST_DIRECTORY_NAME);
+ final File renamedImageFile = new File(dir, IMAGE_FILE_NAME);
+ final File imageFileInRenamedDir = new File(renamedDir, IMAGE_FILE_NAME);
+ try {
+ assertTrue(imageFile.createNewFile());
+ assertThat(getFileRowIdFromDatabase(imageFile)).isNotEqualTo(-1);
+ if (!dir.exists()) {
+ assertThat(dir.mkdir()).isTrue();
+ }
+
+ final String renameFileCommand = String.format("mv %s %s",
+ imageFile.getAbsolutePath(), renamedImageFile.getAbsolutePath());
+ executeShellCommand(renameFileCommand);
+ assertFalse(imageFile.exists());
+ assertThat(getFileRowIdFromDatabase(imageFile)).isEqualTo(-1);
+ assertTrue(renamedImageFile.exists());
+ assertThat(getFileRowIdFromDatabase(renamedImageFile)).isNotEqualTo(-1);
+
+ final String renameDirectoryCommand = String.format("mv %s %s",
+ dir.getAbsolutePath(), renamedDir.getAbsolutePath());
+ executeShellCommand(renameDirectoryCommand);
+ assertFalse(dir.exists());
+ assertFalse(renamedImageFile.exists());
+ assertThat(getFileRowIdFromDatabase(renamedImageFile)).isEqualTo(-1);
+ assertTrue(renamedDir.exists());
+ assertTrue(imageFileInRenamedDir.exists());
+ assertThat(getFileRowIdFromDatabase(imageFileInRenamedDir)).isNotEqualTo(-1);
+ } finally {
+ imageFile.delete();
+ renamedImageFile.delete();
+ imageFileInRenamedDir.delete();
+ dir.delete();
+ renamedDir.delete();
+ }
+ }
+
/**
* Checks restrictions for opening pending and trashed files by different apps. Assumes that
* given {@code testApp} is already installed and has READ_EXTERNAL_STORAGE permission. This
diff --git a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
index ad1fae9..dbafec8 100644
--- a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
+++ b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
@@ -99,8 +99,12 @@
"delete_on_last_lease_delay_ms";
private static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
"total_bytes_per_app_limit_floor";
- public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
+ private static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
"total_bytes_per_app_limit_fraction";
+ private static final String KEY_MAX_ACTIVE_SESSIONS = "max_active_sessions";
+ private static final String KEY_MAX_COMMITTED_BLOBS = "max_committed_blobs";
+ private static final String KEY_MAX_LEASED_BLOBS = "max_leased_blobs";
+ private static final String KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = "max_permitted_pks";
private static final String HELPER_PKG = "com.android.cts.blob.helper";
private static final String HELPER_PKG2 = "com.android.cts.blob.helper2";
@@ -127,9 +131,8 @@
@After
public void tearDown() throws Exception {
- for (BlobHandle blobHandle : mBlobStoreManager.getLeasedBlobs()) {
- mBlobStoreManager.releaseLease(blobHandle);
- }
+ runShellCmd("cmd blob_store clear-all-sessions");
+ runShellCmd("cmd blob_store clear-all-blobs");
}
@Test
@@ -1357,6 +1360,64 @@
}, Pair.create(KEY_SESSION_EXPIRY_TIMEOUT_MS, String.valueOf(waitDurationMs)));
}
+ @Test
+ public void testCreateSession_countLimitExceeded() throws Exception {
+ final DummyBlobData blobData1 = new DummyBlobData.Builder(mContext).build();
+ blobData1.prepare();
+ final DummyBlobData blobData2 = new DummyBlobData.Builder(mContext).build();
+ blobData2.prepare();
+ runWithKeyValues(() -> {
+ mBlobStoreManager.createSession(blobData1.getBlobHandle());
+ assertThrows(LimitExceededException.class,
+ () -> mBlobStoreManager.createSession(blobData2.getBlobHandle()));
+ }, Pair.create(KEY_MAX_ACTIVE_SESSIONS, String.valueOf(1)));
+ }
+
+ @Test
+ public void testCommitSession_countLimitExceeded() throws Exception {
+ final DummyBlobData blobData1 = new DummyBlobData.Builder(mContext).build();
+ blobData1.prepare();
+ final DummyBlobData blobData2 = new DummyBlobData.Builder(mContext).build();
+ blobData2.prepare();
+ runWithKeyValues(() -> {
+ commitBlob(blobData1, null /* accessModifier */, 0 /* expectedResult */);
+ commitBlob(blobData2, null /* accessModifier */, 1 /* expectedResult */);
+ }, Pair.create(KEY_MAX_COMMITTED_BLOBS, String.valueOf(1)));
+ }
+
+ @Test
+ public void testLeaseBlob_countLimitExceeded() throws Exception {
+ final DummyBlobData blobData1 = new DummyBlobData.Builder(mContext).build();
+ blobData1.prepare();
+ final DummyBlobData blobData2 = new DummyBlobData.Builder(mContext).build();
+ blobData2.prepare();
+ runWithKeyValues(() -> {
+ commitBlob(blobData1);
+ commitBlob(blobData2);
+
+ acquireLease(mContext, blobData1.getBlobHandle(), "test desc");
+ assertThrows(LimitExceededException.class,
+ () -> acquireLease(mContext, blobData2.getBlobHandle(), "test desc"));
+ }, Pair.create(KEY_MAX_LEASED_BLOBS, String.valueOf(1)));
+ }
+
+ @Test
+ public void testAllowPackageAccess_countLimitExceeded() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+ runWithKeyValues(() -> {
+ final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ blobData.writeToSession(session);
+
+ session.allowPackageAccess(HELPER_PKG2, HELPER_PKG2_CERT_SHA256);
+ assertThrows(LimitExceededException.class,
+ () -> session.allowPackageAccess(HELPER_PKG3, HELPER_PKG3_CERT_SHA256));
+ }
+ }, Pair.create(KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES, String.valueOf(1)));
+ }
+
private static void runWithKeyValues(ThrowingRunnable runnable,
Pair<String, String>... keyValues) throws Exception {
final Map<String, String> previousValues = new ArrayMap();
@@ -1407,6 +1468,11 @@
private long commitBlob(DummyBlobData blobData,
AccessModifier accessModifier) throws Exception {
+ return commitBlob(blobData, accessModifier, 0 /* expectedResult */);
+ }
+
+ private long commitBlob(DummyBlobData blobData,
+ AccessModifier accessModifier, int expectedResult) throws Exception {
final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
assertThat(sessionId).isGreaterThan(0L);
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
@@ -1418,7 +1484,7 @@
final CompletableFuture<Integer> callback = new CompletableFuture<>();
session.commit(mContext.getMainExecutor(), callback::complete);
assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
- .isEqualTo(0);
+ .isEqualTo(expectedResult);
}
return sessionId;
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
index 8393012..c64eaa3 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
@@ -51,6 +51,7 @@
import android.graphics.PointF;
import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
+import android.view.ViewConfiguration;
import android.widget.TextView;
import androidx.test.InstrumentationRegistry;
@@ -105,7 +106,6 @@
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
-
PackageManager pm = mInstrumentation.getContext().getPackageManager();
mHasTouchscreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
|| pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
@@ -179,7 +179,11 @@
@Test
public void testPanning() {
- if (!mHasTouchscreen) return;
+ //The minimum movement to transit to panningState.
+ final float minSwipeDistance = ViewConfiguration.get(
+ mInstrumentation.getContext()).getScaledTouchSlop();
+ final boolean screenBigEnough = mPan > minSwipeDistance;
+ if (!mHasTouchscreen || !screenBigEnough) return;
assertFalse(isZoomed());
setZoomByTripleTapping(true);
@@ -190,7 +194,8 @@
swipe(mTapLocation2, add(mTapLocation2, -mPan, 0)));
waitOn(mZoomLock,
- () -> (mCurrentZoomCenter.x - oldCenter.x >= mPan / mCurrentScale * 0.9));
+ () -> (mCurrentZoomCenter.x - oldCenter.x
+ >= (mPan - minSwipeDistance) / mCurrentScale * 0.9));
setZoomByTripleTapping(false);
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
index 9224739..56d5240 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -280,7 +280,7 @@
*/
@Test
@AppModeFull
- public void testDoubleTapNoAccessibilityFocus_doesNotPerformClick() {
+ public void testDoubleTapNoFocus_doesNotPerformClick() {
if (!mHasTouchscreen || !mScreenBigEnough) return;
dispatch(doubleTap(mTapLocation));
mHoverListener.assertNonePropagated();
@@ -318,7 +318,7 @@
*/
@Test
@AppModeFull
- public void testDoubleTapAndHoldNoAccessibilityFocus_doesNotPerformLongClick() {
+ public void testDoubleTapAndHoldNoFocus_doesNotPerformLongClick() {
if (!mHasTouchscreen || !mScreenBigEnough) return;
dispatch(doubleTap(mTapLocation));
mHoverListener.assertNonePropagated();
@@ -358,6 +358,63 @@
}
/**
+ * Test the case where we double tap and no item has accessibility focus, so TouchExplorer sends
+ * touch events to the last touch-explored coordinates to simulate a click.
+ */
+ @Test
+ @AppModeFull
+ public void testDoubleTapNoAccessibilityFocus_sendsTouchEvents() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ // Do a single tap so there is a valid last touch-explored location.
+ dispatch(click(mTapLocation));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_EXIT);
+ // We don't really care about these events but we need to make sure all the events we want
+ // to clear have arrived before we clear them.
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ dispatch(doubleTap(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ // The click gets delivered as a series of touch events.
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END, TYPE_VIEW_CLICKED);
+ mClickListener.assertClicked(mView);
+ }
+
+ /**
+ * Test the case where we double tap and hold and no item has accessibility focus, so
+ * TouchExplorer sends touch events to the last touch-explored coordinates to simulate a long
+ * click.
+ */
+ @Test
+ @AppModeFull
+ public void testDoubleTapAndHoldNoAccessibilityFocus_sendsTouchEvents() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ // Do a single tap so there is a valid last touch-explored location.
+ dispatch(click(mTapLocation));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_EXIT);
+ // We don't really care about these events but we need to make sure all the events we want
+ // to clear have arrived before we clear them.
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ dispatch(doubleTapAndHold(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ // The click gets delivered as a series of touch events.
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_VIEW_LONG_CLICKED, TYPE_TOUCH_INTERACTION_END);
+ mLongClickListener.assertLongClicked(mView);
+ }
+
+ /**
* Test the case where we want to double tap using a second finger without triggering touch
* exploration.
*/
diff --git a/tests/app/AppExitTest/AndroidManifest.xml b/tests/app/AppExitTest/AndroidManifest.xml
index 4606ca5..64a1ab8 100644
--- a/tests/app/AppExitTest/AndroidManifest.xml
+++ b/tests/app/AppExitTest/AndroidManifest.xml
@@ -24,6 +24,10 @@
<application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
+ <service android:name="android.app.cts.MemoryConsumerService"
+ android:exported="true"
+ android:isolatedProcess="true">
+ </service>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index bd9e5af..cbded8b 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -30,6 +30,7 @@
import android.externalservice.common.RunningServiceInfo;
import android.externalservice.common.ServiceMessages;
import android.os.AsyncTask;
+import android.os.Binder;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
@@ -44,12 +45,12 @@
import android.os.UserManager;
import android.provider.Settings;
import android.server.wm.settings.SettingsSession;
-import android.system.Os;
import android.system.OsConstants;
import android.test.InstrumentationTestCase;
import android.text.TextUtils;
import android.util.DebugUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.compatibility.common.util.AmMonitor;
import com.android.compatibility.common.util.ShellIdentityUtils;
@@ -58,9 +59,7 @@
import com.android.internal.util.MemInfoReader;
import java.io.BufferedInputStream;
-import java.io.FileDescriptor;
import java.io.IOException;
-import java.nio.DirectByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -131,6 +130,8 @@
private UserHandle mOtherUserHandle;
private DropBoxManager.Entry mAnrEntry;
private SettingsSession<String> mDataAnrSettings;
+ private SettingsSession<String> mHiddenApiSettings;
+ private int mProcSeqNum;
@Override
protected void setUp() throws Exception {
@@ -154,6 +155,11 @@
Settings.Global.DROPBOX_TAG_PREFIX + "data_app_anr"),
Settings.Global::getString, Settings.Global::putString);
mDataAnrSettings.set("enabled");
+ mHiddenApiSettings = new SettingsSession<>(
+ Settings.Global.getUriFor(
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
+ Settings.Global::getString, Settings.Global::putString);
+ mHiddenApiSettings.set("*");
}
private void handleMessagePid(Message msg) {
@@ -217,6 +223,9 @@
if (mDataAnrSettings != null) {
mDataAnrSettings.close();
}
+ if (mHiddenApiSettings != null) {
+ mHiddenApiSettings.close();
+ }
}
private int createUser(String name, boolean guest) throws Exception {
@@ -391,47 +400,37 @@
ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now, now2);
}
- private List<ApplicationExitInfo> fillUpMemoryAndCheck(ArrayList<Long> addresses)
- throws Exception {
- List<ApplicationExitInfo> list = null;
- // Get the meminfo firstly
- MemInfoReader reader = new MemInfoReader();
- reader.readMemInfo();
+ private List<ServiceConnection> fillUpMemoryAndCheck(
+ final MemoryConsumerService.TestFuncInterface testFunc,
+ final List<ApplicationExitInfo> list) throws Exception {
+ final String procNamePrefix = "memconsumer_";
+ final ArrayList<ServiceConnection> memConsumers = new ArrayList<>();
+ Pair<IBinder, ServiceConnection> p = MemoryConsumerService.bindToService(
+ mContext, testFunc, procNamePrefix + mProcSeqNum++);
+ IBinder consumer = p.first;
+ memConsumers.add(p.second);
- long totalMb = (reader.getFreeSizeKb() + reader.getCachedSizeKb()) >> 10;
- final int pageSize = 4096;
- final int oneMb = 1024 * 1024;
+ while (list.size() == 0) {
+ // Get the meminfo firstly
+ MemInfoReader reader = new MemInfoReader();
+ reader.readMemInfo();
- // Create an empty fd -1
- FileDescriptor fd = new FileDescriptor();
-
- // Okay now start a loop to allocate 1MB each time and check if our process is gone.
- for (long i = 0; i < totalMb; i++) {
- long addr = Os.mmap(0, oneMb, OsConstants.PROT_WRITE,
- OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS, fd, 0);
- if (addr == 0) {
- break;
+ long totalMb = (reader.getFreeSizeKb() + reader.getCachedSizeKb()) >> 10;
+ if (!MemoryConsumerService.runOnce(consumer, totalMb) && list.size() == 0) {
+ // Need to create a new consumer (the present one might be running out of space)
+ p = MemoryConsumerService.bindToService(mContext, testFunc,
+ procNamePrefix + mProcSeqNum++);
+ consumer = p.first;
+ memConsumers.add(p.second);
}
- addresses.add(addr);
-
- // We don't have direct access to Memory.pokeByte() though
- DirectByteBuffer buf = new DirectByteBuffer(oneMb, addr, fd, null, false);
-
- // Dirt the buffer
- for (int j = 0; j < oneMb; j += pageSize) {
- buf.put(j, (byte) 0xf);
- }
-
- // Check if we could get the report
- list = ShellIdentityUtils.invokeMethodWithShellPermissions(
- STUB_PACKAGE_NAME, mStubPackagePid, 1,
- mActivityManager::getHistoricalProcessExitReasons,
- android.Manifest.permission.DUMP);
- if (list != null && list.size() == 1) {
+ // make sure we have cached process killed
+ String output = executeShellCmd("dumpsys activity lru");
+ if (output == null && output.indexOf(" cch+") == -1) {
break;
}
}
- return list;
+
+ return memConsumers;
}
public void testLmkdKill() throws Exception {
@@ -444,23 +443,32 @@
// Start a process and do nothing
startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
- final int oneMb = 1024 * 1024;
- ArrayList<Long> addresses = new ArrayList<Long>();
- List<ApplicationExitInfo> list = fillUpMemoryAndCheck(addresses);
+ final ArrayList<IBinder> memConsumers = new ArrayList<>();
+ List<ApplicationExitInfo> list = new ArrayList<>();
+ final MemoryConsumerService.TestFuncInterface testFunc =
+ new MemoryConsumerService.TestFuncInterface(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ List<ApplicationExitInfo> result =
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ STUB_PACKAGE_NAME, mStubPackagePid, 1,
+ mActivityManager::getHistoricalProcessExitReasons,
+ android.Manifest.permission.DUMP);
+ if (result != null && result.size() == 1) {
+ list.add(result.get(0));
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return false;
+ });
- while (list == null || list.size() == 0) {
- // make sure we have cached process killed
- String output = executeShellCmd("dumpsys activity lru");
- if (output == null && output.indexOf(" cch+") == -1) {
- break;
- }
- // try again since the system might have reclaimed some ram
- list = fillUpMemoryAndCheck(addresses);
- }
+ List<ServiceConnection> services = fillUpMemoryAndCheck(testFunc, list);
- // Free all the buffers firstly
- for (int i = addresses.size() - 1; i >= 0; i--) {
- Os.munmap(addresses.get(i), oneMb);
+ // Unbind all the service connections firstly
+ for (int i = services.size() - 1; i >= 0; i--) {
+ mContext.unbindService(services.get(i));
}
long now2 = System.currentTimeMillis();
diff --git a/tests/app/AppExitTest/src/android/app/cts/MemoryConsumerService.java b/tests/app/AppExitTest/src/android/app/cts/MemoryConsumerService.java
new file mode 100644
index 0000000..cca0fac
--- /dev/null
+++ b/tests/app/AppExitTest/src/android/app/cts/MemoryConsumerService.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.cts;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.FileDescriptor;
+import java.nio.DirectByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
+
+public class MemoryConsumerService extends Service {
+ private static final String TAG = MemoryConsumerService.class.getSimpleName();
+
+ private static final int ACTION_RUN_ONCE = IBinder.FIRST_CALL_TRANSACTION;
+ private static final String EXTRA_BUNDLE = "bundle";
+ private static final String EXTRA_TEST_FUNC = "test_func";
+
+ private IBinder mTestFunc;
+
+ private IBinder mBinder = new Binder() {
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case ACTION_RUN_ONCE:
+ reply.writeBoolean(fillUpMemoryAndCheck(data.readLong(), mTestFunc));
+ return true;
+ default:
+ return false;
+ }
+ }
+ };
+
+ static class TestFuncInterface extends Binder {
+ static final int ACTION_TEST = IBinder.FIRST_CALL_TRANSACTION;
+
+ private final BooleanSupplier mTestFunc;
+
+ TestFuncInterface(final BooleanSupplier testFunc) {
+ mTestFunc = testFunc;
+ }
+
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case ACTION_TEST:
+ reply.writeBoolean(mTestFunc.getAsBoolean());
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+
+ private boolean fillUpMemoryAndCheck(final long totalMb, final IBinder testFunc) {
+ final int pageSize = 4096;
+ final int oneMb = 1024 * 1024;
+
+ // Create an empty fd -1
+ FileDescriptor fd = new FileDescriptor();
+
+ // Okay now start a loop to allocate 1MB each time and check if our process is gone.
+ for (long i = 0; i < totalMb; i++) {
+ long addr = 0L;
+ try {
+ addr = Os.mmap(0, oneMb, OsConstants.PROT_WRITE,
+ OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS, fd, 0);
+ } catch (ErrnoException e) {
+ Log.d(TAG, "mmap returns " + e.errno);
+ if (e.errno == OsConstants.ENOMEM) {
+ // Running out of address space?
+ return false;
+ }
+ }
+ if (addr == 0) {
+ return false;
+ }
+
+ // We don't have direct access to Memory.pokeByte() though
+ DirectByteBuffer buf = new DirectByteBuffer(oneMb, addr, fd, null, false);
+
+ // Dirt the buffer
+ for (int j = 0; j < oneMb; j += pageSize) {
+ buf.put(j, (byte) 0xf);
+ }
+
+ // Test to see if we could stop
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ try {
+ testFunc.transact(TestFuncInterface.ACTION_TEST, data, reply, 0);
+ if (reply.readBoolean()) {
+ break;
+ }
+ } catch (RemoteException e) {
+ // Ignore
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ final Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE);
+ mTestFunc = bundle.getBinder(EXTRA_TEST_FUNC);
+ return mBinder;
+ }
+
+ static Pair<IBinder, ServiceConnection> bindToService(final Context context,
+ final TestFuncInterface testFunc, String instanceName) throws Exception {
+ final Intent intent = new Intent();
+ intent.setClass(context, MemoryConsumerService.class);
+ final Bundle bundle = new Bundle();
+ bundle.putBinder(EXTRA_TEST_FUNC, testFunc);
+ intent.putExtra(EXTRA_BUNDLE, bundle);
+ final String keyIBinder = "ibinder";
+ final Bundle holder = new Bundle();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ holder.putBinder(keyIBinder, service);
+ latch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+ context.bindIsolatedService(intent, Context.BIND_AUTO_CREATE,
+ instanceName, AsyncTask.THREAD_POOL_EXECUTOR, conn);
+ latch.await(10_000, TimeUnit.MILLISECONDS);
+ return new Pair<>(holder.getBinder(keyIBinder), conn);
+ }
+
+ static boolean runOnce(final IBinder binder, final long totalMb) {
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+
+ try {
+ data.writeLong(totalMb);
+ binder.transact(ACTION_RUN_ONCE, data, reply, 0);
+ return reply.readBoolean();
+ } catch (RemoteException e) {
+ return false;
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+}
diff --git a/tests/app/src/android/app/cts/UiModeManagerTest.java b/tests/app/src/android/app/cts/UiModeManagerTest.java
index 7174f36..e877350 100644
--- a/tests/app/src/android/app/cts/UiModeManagerTest.java
+++ b/tests/app/src/android/app/cts/UiModeManagerTest.java
@@ -124,6 +124,10 @@
}
public void testNightModeAutoNotPersistedCarMode() {
+ if (mUiModeManager.isNightModeLocked()) {
+ return;
+ }
+
// Reset the mode to no if it is set to another value
setNightMode(UiModeManager.MODE_NIGHT_NO);
mUiModeManager.enableCarMode(0);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
index 22ba3b9..54f391b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
@@ -26,6 +26,8 @@
import android.provider.Settings;
import android.support.test.uiautomator.UiObject2;
+import com.android.compatibility.common.util.FeatureUtil;
+
import org.junit.After;
import org.junit.Test;
@@ -51,8 +53,12 @@
@After
public void killSettings() {
- // Make sure there's no Settings activity left , as it could fail future tests.
- runShellCommand("am force-stop com.android.settings");
+ // Make sure there's no Settings activity left, as it could fail future tests.
+ if (FeatureUtil.isAutomotive()) {
+ runShellCommand("am force-stop com.android.car.settings");
+ } else {
+ runShellCommand("am force-stop com.android.settings");
+ }
}
@Test
@@ -65,6 +71,7 @@
// Asserts services are shown.
mUiBot.assertShownByText(InstrumentedAutoFillService.sServiceLabel);
mUiBot.assertShownByText(InstrumentedAutoFillServiceCompatMode.sServiceLabel);
+ mUiBot.scrollToTextObject(NoOpAutofillService.SERVICE_LABEL);
mUiBot.assertShownByText(NoOpAutofillService.SERVICE_LABEL);
mUiBot.assertNotShowingForSure(BadAutofillService.SERVICE_LABEL);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
index f67bd5c..fb878d3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
@@ -1812,7 +1812,7 @@
// Tapping URLSpan.
final URLSpan span = mUiBot.findFirstUrlSpanWithText("Here is URLSpan");
- span.onClick(/* unused= */ null);
+ mActivity.syncRunOnUiThread(() -> span.onClick(/* unused= */ null));
// Waits for the save UI hided
mUiBot.waitForIdle();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 8babc6c..88173d3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -55,6 +55,9 @@
import android.support.test.uiautomator.SearchCondition;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.text.Html;
import android.text.Spanned;
@@ -1246,4 +1249,15 @@
.getSpans(0, accessibilityTextWithSpan.length(), URLSpan.class);
return spans[0];
}
+
+ public boolean scrollToTextObject(String text) {
+ UiScrollable scroller = new UiScrollable(new UiSelector().scrollable(true));
+ try {
+ // Swipe far away from the edges to avoid triggering navigation gestures
+ scroller.setSwipeDeadZonePercentage(0.25);
+ return scroller.scrollTextIntoView(text);
+ } catch (UiObjectNotFoundException e) {
+ return false;
+ }
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index 72ce477..3ac15bb 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -570,7 +570,6 @@
}
@Test
- @FlakyTest(bugId=159062106)
public void testDisallowMultipleTasksInPinnedStack() throws Exception {
// Launch a test activity so that we have multiple fullscreen tasks
launchActivity(TEST_ACTIVITY);
@@ -1048,7 +1047,6 @@
assertEquals("onPause", 0, lifecycleCounts.getCount(ActivityCallback.ON_PAUSE));
}
- @FlakyTest(bugId = 156003518)
@Test
public void testPinnedStackWithDockedStack() throws Exception {
assumeTrue(supportsSplitScreenMultiWindow());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
index 6299fc8..de7ecc8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
@@ -19,13 +19,16 @@
import static android.server.wm.WindowInsetsAnimationControllerTests.ControlListener.Event.CANCELLED;
import static android.server.wm.WindowInsetsAnimationControllerTests.ControlListener.Event.FINISHED;
import static android.server.wm.WindowInsetsAnimationControllerTests.ControlListener.Event.READY;
-import static android.server.wm.WindowInsetsAnimationTestBase.showImeWithHardKeyboardSetting;
import static android.server.wm.WindowInsetsAnimationUtils.INSETS_EVALUATOR;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
@@ -33,14 +36,17 @@
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.app.Instrumentation;
import android.graphics.Insets;
import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;
@@ -60,6 +66,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -85,7 +95,7 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationControllerTests
*/
-//TODO(b/159038873) @Presubmit
+//TODO(b/159167851) @Presubmit
@RunWith(Parameterized.class)
public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase {
@@ -102,6 +112,13 @@
@Rule
public LimitedErrorCollector mErrorCollector = new LimitedErrorCollector();
+ /**
+ * {@link MockImeSession} used when {@link #mType} is
+ * {@link android.view.WindowInsets.Type#ime()}.
+ */
+ @Nullable
+ private MockImeSession mMockImeSession;
+
@Parameter(0)
public int mType;
@@ -120,15 +137,41 @@
@Before
public void setUp() throws Exception {
super.setUp();
+ final ImeEventStream mockImeEventStream;
+ if (mType == ime()) {
+ final Instrumentation instrumentation = getInstrumentation();
+ assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()),
+ nullValue());
+
+ // For the best test stability MockIme should be selected before launching TestActivity.
+ mMockImeSession = MockImeSession.create(
+ instrumentation.getContext(), instrumentation.getUiAutomation(),
+ new ImeSettings.Builder());
+ mockImeEventStream = mMockImeSession.openEventStream();
+ } else {
+ mockImeEventStream = null;
+ }
+
mActivity = startActivity(TestActivity.class);
mRootView = mActivity.getWindow().getDecorView();
mListener = new ControlListener(mErrorCollector);
- showImeWithHardKeyboardSetting(mObjectTracker);
assumeTestCompatibility();
+
+ if (mockImeEventStream != null) {
+ // TestActivity has a focused EditText. Hence MockIme should receive onStartInput() for
+ // that EditText within a reasonable time.
+ expectEvent(mockImeEventStream,
+ editorMatcher("onStartInput", mActivity.getEditTextMarker()),
+ TimeUnit.SECONDS.toMillis(10));
+ }
}
@After
public void tearDown() throws Throwable {
+ if (mMockImeSession != null) {
+ mMockImeSession.close();
+ mMockImeSession = null;
+ }
runOnUiThread(() -> {}); // Fence to make sure we dispatched everything.
mCallbacks.forEach(VerifyingCallback::assertNoRunningAnimations);
}
@@ -140,6 +183,7 @@
}
}
+ @Presubmit
@Test
public void testControl_andCancel() throws Throwable {
runOnUiThread(() -> {
@@ -172,6 +216,7 @@
mListener.assertWasNotCalled(FINISHED);
}
+ @Presubmit
@Test
public void testControl_immediately_show() throws Throwable {
setVisibilityAndWait(mType, false);
@@ -192,6 +237,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_immediately_hide() throws Throwable {
setVisibilityAndWait(mType, true);
@@ -212,6 +258,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_transition_show() throws Throwable {
setVisibilityAndWait(mType, false);
@@ -230,6 +277,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_transition_hide() throws Throwable {
setVisibilityAndWait(mType, true);
@@ -248,6 +296,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_transition_show_interpolator() throws Throwable {
mInterpolator = new DecelerateInterpolator();
@@ -267,6 +316,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_transition_hide_interpolator() throws Throwable {
mInterpolator = new AccelerateInterpolator();
@@ -309,6 +359,7 @@
mListener.assertWasNotCalled(FINISHED);
}
+ @Presubmit
@Test
public void testImeControl_isntInterruptedByStartingInput() throws Throwable {
if (mType != ime()) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
index 187ed91..373e1e5 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
@@ -19,6 +19,7 @@
import static android.graphics.Insets.NONE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
@@ -32,7 +33,9 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;
+import android.app.Instrumentation;
import android.content.pm.PackageManager;
+import android.graphics.Color;
import android.platform.test.annotations.Presubmit;
import android.view.WindowInsets;
@@ -40,10 +43,9 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.cts.mockime.ImeSettings;
-import com.android.cts.mockime.MockImeSessionRule;
+import com.android.cts.mockime.MockImeSession;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.InOrder;
@@ -56,14 +58,7 @@
@Presubmit
public class WindowInsetsAnimationImeTests extends WindowInsetsAnimationTestBase {
- private static final int KEYBOARD_HEIGHT = 400;
-
- @Rule
- public final MockImeSessionRule mMockImeSessionRule = new MockImeSessionRule(
- InstrumentationRegistry.getInstrumentation().getContext(),
- InstrumentationRegistry.getInstrumentation().getUiAutomation(),
- new ImeSettings.Builder().setInputViewHeight(KEYBOARD_HEIGHT).setDrawsBehindNavBar(true)
- );
+ private static final int KEYBOARD_HEIGHT = 600;
@Before
public void setup() throws Exception {
@@ -71,33 +66,34 @@
assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
mInstrumentation.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_INPUT_METHODS));
+ }
+
+ private void initActivity(boolean useFloating) throws Exception {
+ initMockImeSession(useFloating);
+
mActivity = startActivity(TestActivity.class);
mRootView = mActivity.getWindow().getDecorView();
}
+ private MockImeSession initMockImeSession(boolean useFloating) throws Exception {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ return MockImeSession.create(
+ instrumentation.getContext(), instrumentation.getUiAutomation(),
+ useFloating ? getFloatingImeSettings()
+ : new ImeSettings.Builder().setInputViewHeight(KEYBOARD_HEIGHT)
+ .setDrawsBehindNavBar(true));
+ }
+
@Test
- public void testImeAnimationCallbacksShowAndHide() {
- WindowInsets before = mActivity.mLastWindowInsets;
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().show(ime()));
-
- waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
- commonAnimationAssertions(mActivity, before, true /* show */, ime());
- mActivity.mCallback.animationDone = false;
-
- before = mActivity.mLastWindowInsets;
-
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().hide(ime()));
-
- waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
-
- commonAnimationAssertions(mActivity, before, false /* show */, ime());
+ public void testImeAnimationCallbacksShowAndHide() throws Exception {
+ initActivity(false /* useFloating */);
+ testShowAndHide();
}
@Test
@FlakyTest(detail = "Promote once confirmed non-flaky")
- public void testAnimationCallbacks_overlapping_opposite() {
+ public void testAnimationCallbacks_overlapping_opposite() throws Exception {
+ initActivity(false /* useFloating */);
WindowInsets before = mActivity.mLastWindowInsets;
MultiAnimCallback callbackInner = new MultiAnimCallback();
@@ -155,4 +151,39 @@
callback.imeAnimSteps.get(callback.imeAnimSteps.size() - 1).insets
.getInsets(ime()));
}
+
+ @Test
+ public void testZeroInsetsImeAnimates() throws Exception {
+ initActivity(true /* useFloating */);
+ testShowAndHide();
+ }
+
+ private void testShowAndHide() {
+ WindowInsets before = mActivity.mLastWindowInsets;
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().show(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+ commonAnimationAssertions(mActivity, before, true /* show */, ime());
+ mActivity.mCallback.animationDone = false;
+
+ before = mActivity.mLastWindowInsets;
+
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().hide(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+
+ commonAnimationAssertions(mActivity, before, false /* show */, ime());
+ }
+
+ private static ImeSettings.Builder getFloatingImeSettings() {
+ final ImeSettings.Builder builder = new ImeSettings.Builder();
+ builder.setWindowFlags(0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ // As documented, Window#setNavigationBarColor() is actually ignored when the IME window
+ // does not have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS. We are calling setNavigationBarColor()
+ // to ensure it.
+ builder.setNavigationBarColor(Color.BLACK);
+ return builder;
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
index d4e6472..f51708f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
@@ -20,6 +20,7 @@
import static android.server.wm.WindowInsetsAnimationUtils.requestControlThenTransitionToVisibility;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -43,6 +44,7 @@
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsController;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
@@ -136,8 +138,10 @@
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
getWindow().setDecorFitsSystemWindows(false);
+ getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN);
mTestView = new TestView(this);
mEditText = new EditText(this);
+ mEditText.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN);
mTestView.addView(mEditText);
mTestView.mEvaluator = () -> {
if (mEvaluator != null) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
index 3494c7c..af8ed0b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
@@ -35,9 +35,8 @@
import static org.mockito.Mockito.spy;
import android.os.Bundle;
-import android.provider.Settings;
+import android.os.SystemClock;
import android.server.wm.WindowInsetsAnimationTestBase.AnimCallback.AnimationStep;
-import android.server.wm.settings.SettingsSession;
import android.util.ArraySet;
import android.view.View;
import android.view.WindowInsets;
@@ -46,6 +45,8 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import org.junit.Assert;
import org.mockito.InOrder;
@@ -157,23 +158,6 @@
}
}
- /**
- * Workaround for b/158637229: force the keyboard to show even when there is a hardware keyboard
- * during IME related insets tests to avoid issues when testing on devices that have a hardware
- * keyboard.
- *
- * @param tracker the test's {@link ObjectTracker}, used to clean up the setting override after
- * the test finishes.
- */
- static void showImeWithHardKeyboardSetting(ObjectTracker tracker) {
- final SettingsSession<Integer> showImeWithHardKeyboardSetting = new SettingsSession<>(
- Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
- Settings.Secure::getInt,
- Settings.Secure::putInt);
- tracker.manage(showImeWithHardKeyboardSetting);
- showImeWithHardKeyboardSetting.set(1);
- }
-
public static class AnimCallback extends WindowInsetsAnimation.Callback {
public static class AnimationStep {
@@ -301,6 +285,10 @@
public static class TestActivity extends FocusableActivity {
+ private final String mEditTextMarker =
+ "android.server.wm.WindowInsetsAnimationTestBase.TestActivity"
+ + SystemClock.elapsedRealtimeNanos();
+
AnimCallback mCallback =
spy(new AnimCallback(WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP));
WindowInsets mLastWindowInsets;
@@ -319,6 +307,11 @@
}
}
+ @NonNull
+ String getEditTextMarker() {
+ return mEditTextMarker;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -328,6 +321,7 @@
mView.setOnApplyWindowInsetsListener(mListener);
mChild = new TextView(this);
mEditor = new EditText(this);
+ mEditor.setPrivateImeOptions(mEditTextMarker);
mView.addView(mChild);
mView.addView(mEditor);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
index 55fa3d2..aca3a97 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
@@ -17,7 +17,6 @@
package android.server.wm;
import static android.graphics.PixelFormat.TRANSLUCENT;
-import static android.server.wm.WindowInsetsAnimationTestBase.showImeWithHardKeyboardSetting;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE;
@@ -35,13 +34,19 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -57,10 +62,14 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.test.filters.FlakyTest;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
import org.junit.Rule;
import org.junit.Test;
@@ -181,20 +190,27 @@
}
@Test
- @FlakyTest(detail = "b/159038873")
- public void testImeShowAndHide() {
- showImeWithHardKeyboardSetting(mObjectTracker);
+ public void testImeShowAndHide() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()),
+ nullValue());
+ try (MockImeSession imeSession = MockImeSession.create(instrumentation.getContext(),
+ instrumentation.getUiAutomation(), new ImeSettings.Builder())) {
+ final ImeEventStream stream = imeSession.openEventStream();
- final TestActivity activity = startActivity(TestActivity.class);
- final View rootView = activity.getWindow().getDecorView();
- getInstrumentation().runOnMainSync(() -> {
- rootView.getWindowInsetsController().show(ime());
- });
- PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(ime()));
- getInstrumentation().runOnMainSync(() -> {
- rootView.getWindowInsetsController().hide(ime());
- });
- PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(ime()));
+ final TestActivity activity = startActivity(TestActivity.class);
+ expectEvent(stream, editorMatcher("onStartInput", activity.mEditTextMarker), TIMEOUT);
+
+ final View rootView = activity.getWindow().getDecorView();
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.getWindowInsetsController().show(ime());
+ });
+ PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(ime()));
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.getWindowInsetsController().hide(ime());
+ });
+ PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(ime()));
+ }
}
@Test
@@ -463,15 +479,18 @@
}
@Test
- @FlakyTest(detail = "b/159038873")
public void testShowImeOnCreate() throws Exception {
- showImeWithHardKeyboardSetting(mObjectTracker);
-
- final TestShowOnCreateActivity activity = startActivity(TestShowOnCreateActivity.class);
- final View rootView = activity.getWindow().getDecorView();
- ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
- PollingCheck.waitFor(TIMEOUT,
- () -> rootView.getRootWindowInsets().isVisible(ime()));
+ final Instrumentation instrumentation = getInstrumentation();
+ assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()),
+ nullValue());
+ try (MockImeSession imeSession = MockImeSession.create(instrumentation.getContext(),
+ instrumentation.getUiAutomation(), new ImeSettings.Builder())) {
+ final TestShowOnCreateActivity activity = startActivity(TestShowOnCreateActivity.class);
+ final View rootView = activity.getWindow().getDecorView();
+ ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
+ PollingCheck.waitFor(TIMEOUT,
+ () -> rootView.getRootWindowInsets().isVisible(ime()));
+ }
}
@Test
@@ -619,10 +638,11 @@
}
}
- private static View setViews(Activity activity) {
+ private static View setViews(Activity activity, @Nullable String privateImeOptions) {
LinearLayout layout = new LinearLayout(activity);
View text = new TextView(activity);
EditText editor = new EditText(activity);
+ editor.setPrivateImeOptions(privateImeOptions);
layout.addView(text);
layout.addView(editor);
activity.setContentView(layout);
@@ -631,11 +651,13 @@
}
public static class TestActivity extends FocusableActivity {
+ final String mEditTextMarker =
+ getClass().getName() + "/" + SystemClock.elapsedRealtimeNanos();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setViews(this);
+ setViews(this, mEditTextMarker);
getWindow().setSoftInputMode(SOFT_INPUT_STATE_HIDDEN);
}
}
@@ -645,7 +667,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- View layout = setViews(this);
+ View layout = setViews(this, null /* privateImeOptions */);
ANIMATION_CALLBACK.reset();
getWindow().getDecorView().setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
getWindow().getInsetsController().hide(statusBars());
@@ -654,11 +676,10 @@
}
public static class TestShowOnCreateActivity extends FocusableActivity {
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- View layout = setViews(this);
+ setViews(this, null /* privateImeOptions */);
ANIMATION_CALLBACK.reset();
getWindow().getDecorView().setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
getWindow().getInsetsController().show(ime());
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
index f08633f..1b71ff0 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
@@ -340,4 +340,21 @@
throw new RuntimeException("notExpectEvent failed: " + stream.dump(), e);
}
}
+
+ /**
+ * Clear all events with {@code eventName} in given {@code stream} and returns a forked
+ * {@link ImeEventStream} without events with {@code eventName}.
+ * <p>It is used to make sure previous events influence the test. </p>
+ *
+ * @param stream {@link ImeEventStream} to be cleared
+ * @param eventName The targeted cleared event name
+ * @return A forked {@link ImeEventStream} without event with {@code eventName}
+ */
+ public static ImeEventStream clearAllEvents(@NonNull ImeEventStream stream,
+ @NonNull String eventName) {
+ while (stream.seekToFirst(event -> eventName.equals(event.getEventName())).isPresent()) {
+ stream.skip(1);
+ }
+ return stream.copy();
+ }
}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
index 917f7b3..6731e85 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
@@ -52,6 +52,7 @@
private static final String INLINE_SUGGESTIONS_ENABLED = "InlineSuggestionsEnabled";
private static final String INLINE_SUGGESTION_VIEW_CONTENT_DESC =
"InlineSuggestionViewContentDesc";
+ private static final String STRICT_MODE_ENABLED = "StrictModeEnabled";
@NonNull
private final PersistableBundle mBundle;
@@ -127,6 +128,10 @@
return mBundle.getString(INLINE_SUGGESTION_VIEW_CONTENT_DESC, defaultValue);
}
+ public boolean isStrictModeEnabled() {
+ return mBundle.getBoolean(STRICT_MODE_ENABLED, false);
+ }
+
static Bundle serializeToBundle(@NonNull String eventCallbackActionName,
@Nullable Builder builder) {
final Bundle result = new Bundle();
@@ -280,5 +285,10 @@
return this;
}
+ /** Sets whether to enable {@link android.os.StrictMode} or not. */
+ public Builder setStrictModeEnabled(boolean enabled) {
+ mBundle.putBoolean(STRICT_MODE_ENABLED, enabled);
+ return this;
+ }
}
}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 5be50de..3acf57b 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -36,6 +36,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.ResultReceiver;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
@@ -44,6 +45,7 @@
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -59,9 +61,9 @@
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethod;
import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.HorizontalScrollView;
import android.widget.TextView;
import android.widget.inline.InlinePresentationSpec;
@@ -288,8 +290,7 @@
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
}
case "getDisplayId":
- return getSystemService(WindowManager.class)
- .getDefaultDisplay().getDisplayId();
+ return getDisplay().getDisplayId();
case "verifyLayoutInflaterContext":
return getLayoutInflater().getContext() == this;
case "setHeight":
@@ -299,6 +300,17 @@
case "setInlineSuggestionsExtras":
mInlineSuggestionsExtras = command.getExtras();
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
+ case "verifyGetDisplay":
+ Context configContext = createConfigurationContext(new Configuration());
+ return getDisplay() != null && configContext.getDisplay() != null;
+ case "verifyGetWindowManager":
+ configContext = createConfigurationContext(new Configuration());
+ return getSystemService(WindowManager.class) != null
+ && configContext.getSystemService(WindowManager.class) != null;
+ case "verifyGetViewConfiguration":
+ configContext = createConfigurationContext(new Configuration());
+ return ViewConfiguration.get(this) != null
+ && ViewConfiguration.get(configContext) != null;
}
}
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
@@ -368,6 +380,16 @@
mClientPackageName.set(mSettings.getClientPackageName());
mImeEventActionName.set(mSettings.getEventCallbackActionName());
+ // TODO(b/159593676): consider to detect more violations
+ if (mSettings.isStrictModeEnabled()) {
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder()
+ .detectIncorrectContextUse()
+ .penaltyLog()
+ .build());
+ StrictMode.setViolationLogger(info -> getTracer().onStrictModeViolated(() -> {}));
+ }
+
getTracer().onCreate(() -> {
super.onCreate();
mHandlerThread.start();
@@ -613,12 +635,7 @@
@Override
public void onStartInput(EditorInfo editorInfo, boolean restarting) {
getTracer().onStartInput(editorInfo, restarting,
- () -> {
- super.onStartInput(editorInfo, restarting);
- if (mSettings.getInlineSuggestionsEnabled()) {
- maybeClearExistingInlineSuggestions();
- }
- });
+ () -> super.onStartInput(editorInfo, restarting));
}
@Override
@@ -727,13 +744,6 @@
final AtomicInteger mInflatedViewCount;
final AtomicBoolean mValid = new AtomicBoolean(true);
- PendingInlineSuggestions() {
- mResponse = null;
- mTotalCount = 0;
- mViews = null;
- mInflatedViewCount = null;
- }
-
PendingInlineSuggestions(InlineSuggestionsResponse response) {
mResponse = response;
mTotalCount = response.getInlineSuggestions().size();
@@ -780,7 +790,9 @@
}
mPendingInlineSuggestions = pendingInlineSuggestions;
if (pendingInlineSuggestions.mTotalCount == 0) {
- mView.updateInlineSuggestions(pendingInlineSuggestions);
+ if (mView != null) {
+ mView.updateInlineSuggestions(pendingInlineSuggestions);
+ }
return true;
}
@@ -811,15 +823,6 @@
});
}
- @MainThread
- private void maybeClearExistingInlineSuggestions() {
- if (mPendingInlineSuggestions != null
- && mPendingInlineSuggestions.mTotalCount > 0) {
- mView.updateInlineSuggestions(new PendingInlineSuggestions());
- mPendingInlineSuggestions = null;
- }
- }
-
/**
* Event tracing helper class for {@link MockIme}.
*/
@@ -1059,5 +1062,10 @@
return recordEventInternal("onInlineSuggestionsResponse", supplier::getAsBoolean,
arguments);
}
+
+ void onStrictModeViolated(@NonNull Runnable runnable) {
+ final Bundle arguments = new Bundle();
+ recordEventInternal("onStrictModeViolated", runnable, arguments);
+ }
}
}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
index 699da4c..5a7d94a 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -1006,4 +1006,19 @@
public ImeCommand callSetInlineSuggestionsExtras(@NonNull Bundle bundle) {
return callCommandInternal("setInlineSuggestionsExtras", bundle);
}
+
+ @NonNull
+ public ImeCommand callVerifyGetDisplay() {
+ return callCommandInternal("verifyGetDisplay", new Bundle());
+ }
+
+ @NonNull
+ public ImeCommand callVerifyGetWindowManager() {
+ return callCommandInternal("verifyGetWindowManager", new Bundle());
+ }
+
+ @NonNull
+ public ImeCommand callVerifyGetViewConfiguration() {
+ return callCommandInternal("verifyGetViewConfiguration", new Bundle());
+ }
}
diff --git a/tests/tests/content/src/android/content/pm/cts/FeatureTest.java b/tests/tests/content/src/android/content/pm/cts/FeatureTest.java
index 99d7268..beab1c4 100644
--- a/tests/tests/content/src/android/content/pm/cts/FeatureTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/FeatureTest.java
@@ -76,6 +76,11 @@
return;
}
+ // Skip the tests for low-RAM devices
+ if (mActivityManager.isLowRamDevice()) {
+ return;
+ }
+
fail("Device should support managed profiles, but "
+ PackageManager.FEATURE_MANAGED_USERS + " is not enabled");
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index cb6985f..7e75fb7 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -35,7 +35,6 @@
import static android.media.MediaFormat.MIMETYPE_VIDEO_VP9;
import android.media.MediaPlayer;
import android.os.Build;
-import android.os.SystemProperties;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import android.util.Range;
@@ -697,6 +696,7 @@
private int getActualMax(
boolean isEncoder, String name, String mime, CodecCapabilities caps, int max) {
int flag = isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0;
+ boolean memory_limited = false;
MediaFormat format = createMinFormat(mime, caps);
Log.d(TAG, "Test format " + format);
Vector<MediaCodec> codecs = new Vector<MediaCodec>();
@@ -716,6 +716,7 @@
am.getMemoryInfo(outInfo);
if (outInfo.lowMemory) {
Log.d(TAG, "System is in low memory condition, stopping. max: " + i);
+ memory_limited = true;
break;
}
} catch (IllegalArgumentException e) {
@@ -745,6 +746,10 @@
codecs.get(i).release();
}
codecs.clear();
+ // encode both actual max and whether we ran out of memory
+ if (memory_limited) {
+ actualMax = -actualMax;
+ }
return actualMax;
}
@@ -772,19 +777,21 @@
type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9 ));
}
- private boolean isLowRamDevice() {
- return ((ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE)).
- isLowRamDevice();
- }
-
public void testGetMaxSupportedInstances() {
- final int MAX_INSTANCES = isLowRamDevice() ? 16 : 32;
StringBuilder xmlOverrides = new StringBuilder();
MediaCodecList allCodecs = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ final boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
for (MediaCodecInfo info : allCodecs.getCodecInfos()) {
Log.d(TAG, "codec: " + info.getName());
Log.d(TAG, " isEncoder = " + info.isEncoder());
+ // don't bother testing aliases
+ if (info.isAlias()) {
+ Log.d(TAG, "skipping: " + info.getName() + " is an alias for " +
+ info.getCanonicalName());
+ continue;
+ }
+
String[] types = info.getSupportedTypes();
for (int j = 0; j < types.length; ++j) {
if (!knownTypes(types[j])) {
@@ -793,14 +800,55 @@
}
Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
CodecCapabilities caps = info.getCapabilitiesForType(types[j]);
- int max = caps.getMaxSupportedInstances();
- Log.d(TAG, "getMaxSupportedInstances returns " + max);
- assertTrue(max > 0);
+ int advertised = caps.getMaxSupportedInstances();
+ Log.d(TAG, "getMaxSupportedInstances returns " + advertised);
+ assertTrue(advertised > 0);
+ // see how well the declared max matches against reality
+
+ int tryMax = isLowRam ? 16 : 32;
+ int tryMin = isLowRam ? 4 : 16;
+
+ int trials = Math.min(advertised + 2, tryMax);
int actualMax = getActualMax(
- info.isEncoder(), info.getName(), types[j], caps, MAX_INSTANCES);
- Log.d(TAG, "actualMax " + actualMax + " vs reported max " + max);
- if (actualMax < (int)(max * 0.9) || actualMax > (int) Math.ceil(max * 1.1)) {
+ info.isEncoder(), info.getName(), types[j], caps, trials);
+ Log.d(TAG, "actualMax " + actualMax + " vs advertised " + advertised
+ + " tryMin " + tryMin + " tryMax " + tryMax);
+
+ boolean memory_limited = false;
+ if (actualMax < 0) {
+ memory_limited = true;
+ actualMax = -actualMax;
+ }
+
+ boolean compliant = true;
+ if (info.isHardwareAccelerated()) {
+ // very specific bounds for HW codecs
+ // so the adv+2 above is to see if the HW codec lets us go beyond adv
+ // (it should not)
+ if (actualMax != Math.min(advertised, tryMax)) {
+ Log.d(TAG, "NO: hwcodec " + actualMax + " != min(" + advertised +
+ "," + tryMax + ")");
+ compliant = false;
+ }
+ } else {
+ // sw codecs get a little more relaxation due to memory pressure
+ if (actualMax >= Math.min(advertised, tryMax)) {
+ // no memory issues, and we allocated them all
+ Log.d(TAG, "OK: swcodec " + actualMax + " >= min(" + advertised +
+ "," + tryMax + ")");
+ } else if (actualMax >= Math.min(advertised, tryMin) &&
+ memory_limited) {
+ // memory issues, but we hit our floors
+ Log.d(TAG, "OK: swcodec " + actualMax + " >= min(" + advertised +
+ "," + tryMin + ") + memory limited");
+ } else {
+ Log.d(TAG, "NO: swcodec didn't meet criteria");
+ compliant = false;
+ }
+ }
+
+ if (!compliant) {
String codec = "<MediaCodec name=\"" + info.getName() +
"\" type=\"" + types[j] + "\" >";
String limit = " <Limit name=\"concurrent-instances\" max=\"" +
diff --git a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
index 3f6e690..48466d5 100644
--- a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
@@ -285,6 +285,8 @@
final CountDownLatch successLatch2 = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final CountDownLatch stopLatch = new CountDownLatch(1);
+ final CountDownLatch onReleaseSessionLatch = new CountDownLatch(1);
+
final List<RoutingController> createdControllers = new ArrayList<>();
// Create session with this route
@@ -311,6 +313,16 @@
}
};
+ StubMediaRoute2ProviderService service = mService;
+ if (service != null) {
+ service.setProxy(new StubMediaRoute2ProviderService.Proxy() {
+ @Override
+ public void onReleaseSession(long requestId, String sessionId) {
+ onReleaseSessionLatch.countDown();
+ }
+ });
+ }
+
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
MediaRoute2Info route1 = routes.get(ROUTE_ID1);
MediaRoute2Info route2 = routes.get(ROUTE_ID2);
@@ -337,15 +349,19 @@
RoutingController controller1 = createdControllers.get(0);
RoutingController controller2 = createdControllers.get(1);
- // The first controller is expected to be released.
- assertTrue(controller1.isReleased());
-
assertNotEquals(controller1.getId(), controller2.getId());
assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(
ROUTE_ID1));
assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(
ROUTE_ID2));
+ // Transferred controllers shouldn't be obtainable.
+ assertFalse(mRouter2.getControllers().contains(controller1));
+ assertTrue(mRouter2.getControllers().contains(controller2));
+
+ // Should be able to release transferred controllers.
+ controller1.release();
+ assertTrue(onReleaseSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(createdControllers);
mRouter2.unregisterRouteCallback(routeCallback);
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index a19ba64..34ca9a4 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -80,6 +80,7 @@
import com.android.testutils.ArrayTrackRecord;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.SkipPresubmit;
import org.junit.After;
import org.junit.Before;
@@ -189,6 +190,7 @@
cb.assertNoCallback();
}
+ @SkipPresubmit(reason = "Flaky: b/159718782; add to presubmit after fixing")
@Test
public void testRegisterCallbackWithCarrierPrivileges() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
diff --git a/tests/tests/net/src/android/net/cts/DnsResolverTest.java b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
index e6f75c3..4acbbcf 100644
--- a/tests/tests/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
@@ -47,6 +47,7 @@
import android.util.Log;
import com.android.net.module.util.DnsPacket;
+import com.android.testutils.SkipPresubmit;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -585,6 +586,7 @@
doTestContinuousQueries(mExecutor);
}
+ @SkipPresubmit(reason = "Flaky: b/159762682; add to presubmit after fixing")
public void testContinuousQueriesInline() throws Exception {
doTestContinuousQueries(mExecutorInline);
}
diff --git a/tests/tests/os/Android.bp b/tests/tests/os/Android.bp
index b118392..e04630b 100644
--- a/tests/tests/os/Android.bp
+++ b/tests/tests/os/Android.bp
@@ -26,6 +26,7 @@
"truth-prebuilt",
"guava",
"junit",
+ "CtsMockInputMethodLib"
],
jni_uses_platform_apis: true,
jni_libs: [
diff --git a/tests/tests/os/CtsOsTestCases.xml b/tests/tests/os/CtsOsTestCases.xml
index 3718c59..7ce85d6 100644
--- a/tests/tests/os/CtsOsTestCases.xml
+++ b/tests/tests/os/CtsOsTestCases.xml
@@ -22,6 +22,15 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsOsTestCases.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="force-install-mode" value="FULL"/>
+ <option name="test-file-name" value="CtsMockInputMethod.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.os.cts" />
<option name="runtime-hint" value="3m15s" />
diff --git a/tests/tests/os/src/android/os/cts/SimpleTestActivity.java b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
index 4242315..22c706f 100644
--- a/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
+++ b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
@@ -16,7 +16,30 @@
package android.os.cts;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+
import android.app.Activity;
+import android.os.Bundle;
+import android.widget.EditText;
+import android.widget.LinearLayout;
public class SimpleTestActivity extends Activity {
+ private EditText mEditText;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mEditText = new EditText(this);
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(mEditText);
+ setContentView(layout);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ mEditText.requestFocus();
+ }
}
\ No newline at end of file
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 8ed37ca..b1023f6 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -17,9 +17,15 @@
package android.os.cts;
import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_INPUT_METHODS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.clearAllEvents;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -32,7 +38,7 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
+import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.net.TrafficStats;
@@ -63,10 +69,16 @@
import android.view.ViewConfiguration;
import android.view.WindowManager;
+import androidx.annotation.IntDef;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.cts.mockime.ImeEvent;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -78,6 +90,8 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
@@ -96,10 +110,49 @@
public class StrictModeTest {
private static final String TAG = "StrictModeTest";
private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE";
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10); // 10 seconds
+ private static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
private StrictMode.ThreadPolicy mThreadPolicy;
private StrictMode.VmPolicy mVmPolicy;
+ /**
+ * Verify mode to verifying if APIs violates incorrect context violation.
+ *
+ * @see #VERIFY_MODE_GET_DISPLAY
+ * @see #VERIFY_MODE_GET_WINDOW_MANAGER
+ * @see #VERIFY_MODE_GET_VIEW_CONFIGURATION
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ VERIFY_MODE_GET_DISPLAY,
+ VERIFY_MODE_GET_WINDOW_MANAGER,
+ VERIFY_MODE_GET_VIEW_CONFIGURATION,
+ })
+ private @interface VerifyMode {}
+
+ /**
+ * Verifies if {@link Context#getDisplay} from {@link InputMethodService} and context created
+ * from {@link InputMethodService#createConfigurationContext(Configuration)} violates
+ * incorrect context violation.
+ */
+ private static final int VERIFY_MODE_GET_DISPLAY = 1;
+ /**
+ * Verifies if get {@link android.view.WindowManager} from {@link InputMethodService} and
+ * context created from {@link InputMethodService#createConfigurationContext(Configuration)}
+ * violates incorrect context violation.
+ *
+ * @see Context#getSystemService(String)
+ * @see Context#getSystemService(Class)
+ */
+ private static final int VERIFY_MODE_GET_WINDOW_MANAGER = 2;
+ /**
+ * Verifies if passing {@link InputMethodService} and context created
+ * from {@link InputMethodService#createConfigurationContext(Configuration)} to
+ * {@link android.view.ViewConfiguration#get(Context)} violates incorrect context violation.
+ */
+ private static final int VERIFY_MODE_GET_VIEW_CONFIGURATION = 3;
+
private Context getContext() {
return ApplicationProvider.getApplicationContext();
}
@@ -647,6 +700,9 @@
final Activity activity = InstrumentationRegistry.getInstrumentation()
.startActivitySync(intent);
assertNoViolation(() -> activity.getSystemService(WINDOW_SERVICE));
+
+ // TODO(b/159593676): move the logic to CtsInputMethodTestCases
+ verifyIms(VERIFY_MODE_GET_WINDOW_MANAGER);
}
@Test
@@ -669,10 +725,13 @@
Intent intent = new Intent(getContext(), SimpleTestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
final Activity activity = InstrumentationRegistry.getInstrumentation()
.startActivitySync(intent);
assertNoViolation(() -> activity.getDisplay());
+ // TODO(b/159593676): move the logic to CtsInputMethodTestCases
+ verifyIms(VERIFY_MODE_GET_DISPLAY);
try {
getContext().getApplicationContext().getDisplay();
} catch (UnsupportedOperationException e) {
@@ -691,14 +750,14 @@
final Context baseContext = getContext();
assertViolation(
- "Tried to access UI constants from a non-visual Context.",
+ "Tried to access UI constants from a non-visual Context:",
() -> ViewConfiguration.get(baseContext));
final Display display = baseContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY);
final Context displayContext = baseContext.createDisplayContext(display);
assertViolation(
- "Tried to access UI constants from a non-visual Context.",
+ "Tried to access UI constants from a non-visual Context:",
() -> ViewConfiguration.get(displayContext));
final Context windowContext =
@@ -711,14 +770,55 @@
.startActivitySync(intent);
assertNoViolation(() -> ViewConfiguration.get(activity));
- final TestInputMethodService ims = new TestInputMethodService(getContext());
- assertNoViolation(() -> ViewConfiguration.get(ims));
+ // TODO(b/159593676): move the logic to CtsInputMethodTestCases
+ verifyIms(VERIFY_MODE_GET_VIEW_CONFIGURATION);
}
- private static class TestInputMethodService extends InputMethodService {
- private TestInputMethodService(Context baseContext) {
- attachBaseContext(baseContext);
+ // TODO(b/159593676): move the logic to CtsInputMethodTestCases
+ /**
+ * Verify if APIs violates incorrect context violations by {@code mode}.
+ *
+ * @see VerifyMode
+ */
+ private void verifyIms(@VerifyMode int mode) throws Exception {
+ // If devices do not support installable IMEs, finish the test gracefully. We don't use
+ // assumeTrue here because we do pass some cases, so showing "pass" instead of "skip" makes
+ // sense here.
+ if (!supportsInstallableIme()) {
+ return;
}
+
+ try (final MockImeSession imeSession = MockImeSession.create(getContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder().setStrictModeEnabled(true))) {
+ final ImeEventStream stream = imeSession.openEventStream();
+ expectEvent(stream, event -> "onStartInput".equals(event.getEventName()), TIMEOUT);
+ final ImeEventStream forkedStream = clearAllEvents(stream, "onStrictModeViolated");
+ final ImeEvent imeEvent;
+ switch (mode) {
+ case VERIFY_MODE_GET_DISPLAY:
+ imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetDisplay(),
+ TIMEOUT);
+ break;
+ case VERIFY_MODE_GET_WINDOW_MANAGER:
+ imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetWindowManager(),
+ TIMEOUT);
+ break;
+ case VERIFY_MODE_GET_VIEW_CONFIGURATION:
+ imeEvent = expectCommand(forkedStream,
+ imeSession.callVerifyGetViewConfiguration(), TIMEOUT);
+ break;
+ default:
+ imeEvent = null;
+ }
+ assertTrue(imeEvent.getReturnBooleanValue());
+ notExpectEvent(stream, event -> "onStrictModeViolated".equals(event.getEventName()),
+ NOT_EXPECT_TIMEOUT);
+ }
+ }
+
+ private boolean supportsInstallableIme() {
+ return getContext().getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS);
}
private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
diff --git a/tests/tests/permission/src/android/permission/cts/IgnoreAllTestsRule.java b/tests/tests/permission/src/android/permission/cts/IgnoreAllTestsRule.java
new file mode 100644
index 0000000..45288bd
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/IgnoreAllTestsRule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+/**
+ * A {@link org.junit.Rule} that will cause all tests in the class
+ * to be ignored if the argument to the constructor is true.
+ */
+public class IgnoreAllTestsRule implements MethodRule {
+
+ private boolean mIgnore;
+
+ /**
+ * Creates a new IgnoreAllTestsRule
+ * @param ignore If true, all tests in the class will be ignored. If false, this rule will
+ * do nothing.
+ */
+ public IgnoreAllTestsRule(boolean ignore) {
+ mIgnore = ignore;
+ }
+
+ @Override
+ public Statement apply(Statement base, FrameworkMethod method, Object target) {
+ if (mIgnore) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ }
+ };
+ } else {
+ return base;
+ }
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java b/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
index 656f6ad..5670cc1 100644
--- a/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
@@ -43,6 +43,7 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.concurrent.CompletableFuture;
@@ -70,6 +71,10 @@
private String mOldOneTimePermissionTimeoutValue;
+ @Rule
+ public IgnoreAllTestsRule mIgnoreAutomotive = new IgnoreAllTestsRule(
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
+
@Before
public void wakeUpScreen() {
SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
diff --git a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
index 47c1323..f4f97f71 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
@@ -21,6 +21,11 @@
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+import android.content.pm.PackageManager.MATCH_SYSTEM_ONLY
+import android.content.pm.ResolveInfo
+import android.content.res.Resources
import android.provider.Settings
import android.support.test.uiautomator.By
import android.support.test.uiautomator.BySelector
@@ -52,6 +57,8 @@
protected val uiAutomation: UiAutomation = instrumentation.uiAutomation
protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
protected val packageManager: PackageManager = context.packageManager
+ private val mPermissionControllerResources: Resources = context.createPackageContext(
+ getPermissionControllerPackageName(), 0).resources
@get:Rule
val activityRule = ActivityTestRule(StartForFutureActivity::class.java, false, false)
@@ -87,6 +94,27 @@
pressHome()
}
+ protected fun getPermissionControllerString(res: String): String =
+ mPermissionControllerResources.getString(mPermissionControllerResources
+ .getIdentifier(res, "string", "com.android.permissioncontroller"))
+
+ private fun getPermissionControllerPackageName(): String {
+ val intent = Intent("android.intent.action.MANAGE_PERMISSIONS")
+ intent.addCategory(Intent.CATEGORY_DEFAULT)
+ val packageManager: PackageManager = context.getPackageManager()
+ val matches: List<ResolveInfo> = packageManager.queryIntentActivities(intent,
+ MATCH_SYSTEM_ONLY or MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE)
+ return if (matches.size == 1) {
+ val resolveInfo: ResolveInfo = matches[0]
+ if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) {
+ throw RuntimeException("The permissions manager must be a privileged app")
+ }
+ matches[0].activityInfo.packageName
+ } else {
+ throw RuntimeException("There must be exactly one permissions manager; found $matches")
+ }
+ }
+
protected fun installPackage(
apkPath: String,
reinstall: Boolean = false,
@@ -117,8 +145,8 @@
return UiAutomatorUtils.waitFindObject(selector, timeoutMillis)
}
- protected fun click(selector: BySelector) {
- waitFindObject(selector).click()
+ protected fun click(selector: BySelector, timeoutMillis: Long = 10_000) {
+ waitFindObject(selector, timeoutMillis).click()
waitForIdle()
}
diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
index 1f074fc..25e8962 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
@@ -68,6 +68,13 @@
const val NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON =
"com.android.permissioncontroller:" +
"id/permission_no_upgrade_and_dont_ask_again_button"
+
+ const val ALLOW_BUTTON_TEXT = "grant_dialog_button_allow"
+ const val ALLOW_FOREGROUND_BUTTON_TEXT = "grant_dialog_button_allow_foreground"
+ const val DENY_BUTTON_TEXT = "grant_dialog_button_deny"
+ const val DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT =
+ "grant_dialog_button_deny_and_dont_ask_again"
+ const val NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON_TEXT = "grant_dialog_button_no_upgrade"
}
enum class PermissionState {
@@ -78,6 +85,7 @@
protected val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
protected val isWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ protected val isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
private val platformResources = context.createPackageContext("android", 0).resources
private val permissionToLabelResNameMap =
@@ -192,11 +200,21 @@
protected fun clearTargetSdkWarning() =
click(By.res("android:id/button1"))
- protected fun clickPermissionReviewContinue() =
- click(By.res("com.android.permissioncontroller:id/continue_button"))
+ protected fun clickPermissionReviewContinue() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString("review_button_continue")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/continue_button"))
+ }
+ }
- protected fun clickPermissionReviewCancel() =
- click(By.res("com.android.permissioncontroller:id/cancel_button"))
+ protected fun clickPermissionReviewCancel() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString("review_button_cancel")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/cancel_button"))
+ }
+ }
protected fun approvePermissionReview() {
startAppActivityAndAssertResultCode(Activity.RESULT_OK) {
@@ -300,30 +318,55 @@
block
)
- protected fun clickPermissionRequestAllowButton() =
- click(By.res(ALLOW_BUTTON))
+ protected fun clickPermissionRequestAllowButton() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)))
+ } else {
+ click(By.res(ALLOW_BUTTON))
+ }
+ }
protected fun clickPermissionRequestSettingsLinkAndAllowAlways() {
+ clickPermissionRequestSettingsLink()
eventually({
- clickPermissionRequestSettingsLink()
clickAllowAlwaysInSettings()
}, TIMEOUT_MILLIS * 2)
pressBack()
}
protected fun clickAllowAlwaysInSettings() {
- click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString("app_permission_button_allow_always")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+ }
}
- protected fun clickPermissionRequestAllowForegroundButton() =
- click(By.res(ALLOW_FOREGROUND_BUTTON))
+ protected fun clickPermissionRequestAllowForegroundButton(timeoutMillis: Long = 10_000) {
+ if (isAutomotive) {
+ click(By.text(
+ getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)), timeoutMillis)
+ } else {
+ click(By.res(ALLOW_FOREGROUND_BUTTON), timeoutMillis)
+ }
+ }
- protected fun clickPermissionRequestDenyButton() =
- click(By.res(DENY_BUTTON))
+ protected fun clickPermissionRequestDenyButton() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
+ } else {
+ click(By.res(DENY_BUTTON))
+ }
+ }
protected fun clickPermissionRequestSettingsLinkAndDeny() {
clickPermissionRequestSettingsLink()
- click(By.res("com.android.permissioncontroller:id/deny_radio_button"))
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString("app_permission_button_deny")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/deny_radio_button"))
+ }
+ waitForIdle()
pressBack()
}
@@ -331,9 +374,15 @@
waitForIdle()
eventually {
// UiObject2 doesn't expose CharSequence.
- val node = uiAutomation.rootInActiveWindow.findAccessibilityNodeInfosByViewId(
- "com.android.permissioncontroller:id/detail_message"
- )[0]
+ val node = if (isAutomotive) {
+ uiAutomation.rootInActiveWindow.findAccessibilityNodeInfosByText(
+ "Allow in settings."
+ )[0]
+ } else {
+ uiAutomation.rootInActiveWindow.findAccessibilityNodeInfosByViewId(
+ "com.android.permissioncontroller:id/detail_message"
+ )[0]
+ }
assertTrue(node.isVisibleToUser)
val text = node.text as Spanned
val clickableSpan = text.getSpans(0, text.length, ClickableSpan::class.java)[0]
@@ -343,20 +392,25 @@
waitForIdle()
}
- protected fun clickPermissionRequestDenyAndDontAskAgainButton() =
- click(
- By.res(DENY_AND_DONT_ASK_AGAIN_BUTTON)
- )
+ protected fun clickPermissionRequestDenyAndDontAskAgainButton() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT)))
+ } else {
+ click(By.res(DENY_AND_DONT_ASK_AGAIN_BUTTON))
+ }
+ }
+ // Only used in TV and Watch form factors
protected fun clickPermissionRequestDontAskAgainButton() =
click(By.res("com.android.permissioncontroller:id/permission_deny_dont_ask_again_button"))
- protected fun clickPermissionRequestNoUpgradeButton() =
- click(By.res(NO_UPGRADE_BUTTON))
-
- protected fun clickPermissionRequestNoUpgradeAndDontAskAgainButton() = click(
- By.res(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON)
- )
+ protected fun clickPermissionRequestNoUpgradeAndDontAskAgainButton() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON_TEXT)))
+ } else {
+ click(By.res(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON))
+ }
+ }
protected fun grantAppPermissions(vararg permissions: String, targetSdk: Int = 30) {
setAppPermissionState(*permissions, state = PermissionState.ALLOWED, isLegacyApp = false,
@@ -412,6 +466,10 @@
}
val wasGranted = if (isTv) {
false
+ } else if (isAutomotive) {
+ // Automotive doesn't support one time permissions, and thus
+ // won't show an "Ask every time" message
+ !waitFindObject(byTextRes(R.string.deny)).isChecked
} else {
!(waitFindObject(byTextRes(R.string.deny)).isChecked ||
(!isLegacyApp && hasAskButton(permission) &&
@@ -423,24 +481,34 @@
} else {
val button = waitFindObject(
byTextRes(
- when (state) {
- PermissionState.ALLOWED ->
- if (showsForegroundOnlyButton(permission)) {
- R.string.allow_foreground
- } else if (isMediaStorageButton(permission, targetSdk)) {
- R.string.allow_media_storage
- } else if (isAllStorageButton(permission, targetSdk)) {
- R.string.allow_external_storage
- } else {
- R.string.allow
- }
- PermissionState.DENIED ->
- if (!isLegacyApp && hasAskButton(permission)) {
- R.string.ask
- } else {
- R.string.deny
- }
- PermissionState.DENIED_WITH_PREJUDICE -> R.string.deny
+ if (isAutomotive) {
+ // Automotive doesn't support one time permissions, and thus
+ // won't show an "Ask every time" message
+ when (state) {
+ PermissionState.ALLOWED -> R.string.allow
+ PermissionState.DENIED -> R.string.deny
+ PermissionState.DENIED_WITH_PREJUDICE -> R.string.deny
+ }
+ } else {
+ when (state) {
+ PermissionState.ALLOWED ->
+ if (showsForegroundOnlyButton(permission)) {
+ R.string.allow_foreground
+ } else if (isMediaStorageButton(permission, targetSdk)) {
+ R.string.allow_media_storage
+ } else if (isAllStorageButton(permission, targetSdk)) {
+ R.string.allow_external_storage
+ } else {
+ R.string.allow
+ }
+ PermissionState.DENIED ->
+ if (!isLegacyApp && hasAskButton(permission)) {
+ R.string.ask
+ } else {
+ R.string.deny
+ }
+ PermissionState.DENIED_WITH_PREJUDICE -> R.string.deny
+ }
}
)
)
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
index 01b4058..f2f2a10 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
@@ -45,7 +45,7 @@
SystemUtil.eventually({
if (packageManager.checkPermission(ACCESS_FINE_LOCATION, APP_PACKAGE_NAME) ==
PackageManager.PERMISSION_DENIED) {
- waitFindObject(By.res(ALLOW_FOREGROUND_BUTTON), 100).click()
+ clickPermissionRequestAllowForegroundButton(100)
}
assertAppHasPermission(ACCESS_FINE_LOCATION, true)
}, 10000)
@@ -54,6 +54,10 @@
}
// Permission should not be granted and dialog should still be showing
assertAppHasPermission(ACCESS_FINE_LOCATION, false)
- waitFindObject(By.res(ALLOW_FOREGROUND_BUTTON), 1000)
+
+ // On Automotive the dialog gets closed by the tapjacking activity popping up
+ if (!isAutomotive) {
+ clickPermissionRequestAllowForegroundButton()
+ }
}
}
\ No newline at end of file
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest29.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest29.kt
index e5a5ede..5d13748 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest29.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest29.kt
@@ -161,7 +161,11 @@
clickPermissionRequestSettingsLink()
eventually {
pressBack()
- waitFindObject(By.res("com.android.permissioncontroller:id/grant_dialog"), 100)
+ if (isAutomotive) {
+ waitFindObject(By.textContains("Allow in settings."), 100)
+ } else {
+ waitFindObject(By.res("com.android.permissioncontroller:id/grant_dialog"), 100)
+ }
}
}
@@ -181,8 +185,9 @@
android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
) {
clickPermissionRequestSettingsLinkAndDeny()
+ waitForIdle()
+ pressBack()
}
- pressBack()
assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, false)
assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
index 0abae86..1272355 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
@@ -49,6 +49,7 @@
requestAppPermissionsAndAssertResult(ACCESS_BACKGROUND_LOCATION to true) {
clickAllowAlwaysInSettings()
+ waitForIdle()
pressBack()
}
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
index 76085fa..ced02b8 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -127,6 +127,8 @@
mMySync.pendingSync.set(MySync.NETWORK_INFO);
mMySync.expectedNetworkInfo = (NetworkInfo) intent.getExtra(
WifiP2pManager.EXTRA_NETWORK_INFO, null);
+ Log.d(TAG, "Get WIFI_P2P_CONNECTION_CHANGED_ACTION: "
+ + mMySync.expectedNetworkInfo);
mMySync.notify();
}
}
@@ -520,6 +522,10 @@
mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
+ assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+ assertNotNull(mMySync.expectedNetworkInfo);
+ assertEquals(NetworkInfo.DetailedState.DISCONNECTED,
+ mMySync.expectedNetworkInfo.getDetailedState());
}
private String getDeviceName() {
@@ -604,6 +610,10 @@
mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
+ assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+ assertNotNull(mMySync.expectedNetworkInfo);
+ assertEquals(NetworkInfo.DetailedState.DISCONNECTED,
+ mMySync.expectedNetworkInfo.getDetailedState());
WifiP2pGroupList persistentGroups = getPersistentGroups();
assertNotNull(persistentGroups);
@@ -636,6 +646,10 @@
mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
+ assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+ assertNotNull(mMySync.expectedNetworkInfo);
+ assertEquals(NetworkInfo.DetailedState.DISCONNECTED,
+ mMySync.expectedNetworkInfo.getDetailedState());
resetResponse(mMyResponse);
ShellIdentityUtils.invokeWithShellPermissions(() -> {
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiFeature.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiFeature.java
index 3e9fef4..4876ff0 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiFeature.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiFeature.java
@@ -29,4 +29,9 @@
PackageManager packageManager = context.getPackageManager();
return packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
}
+
+ public static boolean isTelephonySupported(Context context) {
+ final PackageManager pm = context.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+ }
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
index 0b548fb..8ad29c7 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -632,10 +632,10 @@
MacAddress lastBlockedClientMacAddress;
int lastBlockedClientReason;
boolean onStateChangedCalled = false;
- boolean onSoftapInfoChangedCalled = false;
boolean onSoftApCapabilityChangedCalled = false;
boolean onConnectedClientCalled = false;
boolean onBlockedClientConnectingCalled = false;
+ int onSoftapInfoChangedCalledCount = 0;
TestSoftApCallback(Object lock) {
softApLock = lock;
@@ -647,9 +647,9 @@
}
}
- public boolean getOnSoftapInfoChangedCalled() {
+ public int getOnSoftapInfoChangedCalledCount() {
synchronized(softApLock) {
- return onSoftapInfoChangedCalled;
+ return onSoftapInfoChangedCalledCount;
}
}
@@ -734,7 +734,7 @@
public void onInfoChanged(SoftApInfo softApInfo) {
synchronized(softApLock) {
currentSoftApInfo = softApInfo;
- onSoftapInfoChangedCalled = true;
+ onSoftapInfoChangedCalledCount++;
}
}
@@ -1472,7 +1472,7 @@
executor.runAll();
// Verify callback is run on the supplied executor and called
return callback.getOnStateChangedCalled() &&
- callback.getOnSoftapInfoChangedCalled() &&
+ callback.getOnSoftapInfoChangedCalledCount() > 0 &&
callback.getOnSoftApCapabilityChangedCalled() &&
callback.getOnConnectedClientCalled();
});
@@ -1633,8 +1633,9 @@
"SoftAp channel and state mismatch!!!", 5_000,
() -> {
executor.runAll();
- return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState() &&
- 2462 == callback.getCurrentSoftApInfo().getFrequency();
+ return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState()
+ && (callback.getOnSoftapInfoChangedCalledCount() > 1
+ ? 2462 == callback.getCurrentSoftApInfo().getFrequency() : true);
});
// stop tethering which used to verify stopSoftAp
@@ -2311,9 +2312,11 @@
// assert that the country code is all uppercase
assertEquals(wifiCountryCode.toUpperCase(Locale.US), wifiCountryCode);
- String telephonyCountryCode = getContext().getSystemService(TelephonyManager.class)
- .getNetworkCountryIso();
- assertEquals(telephonyCountryCode, wifiCountryCode.toLowerCase(Locale.US));
+ if (WifiFeature.isTelephonySupported(getContext())) {
+ String telephonyCountryCode = getContext().getSystemService(TelephonyManager.class)
+ .getNetworkCountryIso();
+ assertEquals(telephonyCountryCode, wifiCountryCode.toLowerCase(Locale.US));
+ }
}
/**