Merge stage-aosp-rvc-ts-dev into rvc-dev
Bug: 148878042
Exempt-From-Owner-Approval: All changes are already in master
Merged-In: Ie1f3e27e0c973e6410fcecfcee049d736481f0e6
Change-Id: I37b338f2cc04c30e49a6a29e89beb529f2e30824
diff --git a/apps/CameraITS/pymodules/its/caps.py b/apps/CameraITS/pymodules/its/caps.py
index ccbaa65..ddb3c95 100644
--- a/apps/CameraITS/pymodules/its/caps.py
+++ b/apps/CameraITS/pymodules/its/caps.py
@@ -555,12 +555,13 @@
0 in props["android.request.availableCapabilities"]
-def sensor_fusion_capable(props):
+def sensor_fusion_test_capable(props, cam):
"""Determine if test_sensor_fusion is run."""
return all([
its.caps.sensor_fusion(props),
its.caps.manual_sensor(props),
- props["android.lens.facing"] != FACING_EXTERNAL])
+ props["android.lens.facing"] != FACING_EXTERNAL,
+ cam.get_sensors().get("gyro")])
def multi_camera_frame_sync_capable(props):
diff --git a/apps/CameraITS/tests/scene1_1/test_dng_noise_model.py b/apps/CameraITS/tests/scene1_1/test_dng_noise_model.py
index 8afc41b..e257937 100644
--- a/apps/CameraITS/tests/scene1_1/test_dng_noise_model.py
+++ b/apps/CameraITS/tests/scene1_1/test_dng_noise_model.py
@@ -57,7 +57,7 @@
sens_min, _ = props['android.sensor.info.sensitivityRange']
sens_max_ana = props['android.sensor.maxAnalogSensitivity']
sens_step = (sens_max_ana - sens_min) / NUM_STEPS
- s_ae, e_ae, _, _, _ = cam.do_3a(get_results=True, do_af=False)
+ s_ae, e_ae, _, _, _ = cam.do_3a(get_results=True)
s_e_prod = s_ae * e_ae
# Focus at zero to intentionally blur the scene as much as possible.
f_dist = 0.0
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index f1b1d36..0caf148 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -425,7 +425,7 @@
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
props = cam.override_with_hidden_physical_camera_props(props)
- its.caps.skip_unless(its.caps.sensor_fusion_capable(props))
+ its.caps.skip_unless(its.caps.sensor_fusion_test_capable(props, cam))
print "Starting sensor event collection"
cam.start_sensor_events()
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index aa326a6..95eac99 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -307,7 +307,7 @@
with ItsSession(camera_id) as cam:
props = cam.get_camera_properties()
method = {'test_sensor_fusion': {
- 'flag': its.caps.sensor_fusion_capable(props),
+ 'flag': its.caps.sensor_fusion_test_capable(props, cam),
'runs': 10},
'test_multi_camera_frame_sync': {
'flag': its.caps.multi_camera_frame_sync_capable(props),
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 1981e03..11e1a55 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -3664,6 +3664,17 @@
android:value="android.software.leanback" />
</activity>
+ <activity android:name=".tv.audio.AudioCapabilitiesTestActivity"
+ android:label="@string/tv_audio_capabilities_test"
+ android:configChanges="orientation|screenSize|density|smallestScreenSize|screenLayout">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_tv" />
+ <meta-data android:name="test_required_features"
+ android:value="android.software.leanback" />
+ </activity>
<activity android:name=".tv.display.DisplayHdrCapabilitiesTestActivity"
android:label="@string/tv_hdr_capabilities_test"
android:configChanges="orientation|screenSize|density|smallestScreenSize|screenLayout">
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 2308863..4a0314c 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -4645,6 +4645,12 @@
<!-- Common strings for the TV Display tests -->
<string name="tv_start_test">Start Test</string>
+ <!-- Audio Capabilities test -->
+ <string name="tv_audio_capabilities_test">Audio Capabilities Test</string>
+ <string name="tv_audio_capabilities_test_info">This test checks if AudioDeviceInfo and
+ AudioTrack#isDirectPlaybackSupported APIs correctly report the Audio capabilities
+ of the connected audio devices.</string>
+
<!-- HDR Capabilities test -->
<string name="tv_hdr_capabilities_test">HDR Capabilities Test</string>
<string name="tv_hdr_capabilities_test_step_hdr_display">HDR Display</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
index 09d705f..d3fa699 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
@@ -808,6 +808,11 @@
@Override
protected void test() {
+ if (mSkipRingerTests) {
+ status = PASS;
+ return;
+ }
+
int volume, volumeDelta;
int[] streams = {STREAM_MUSIC,
AudioManager.STREAM_VOICE_CALL,
@@ -1000,6 +1005,7 @@
@Override
protected void test() {
if (mSkipRingerTests) {
+ status = PASS;
return;
}
int[] streams = { AudioManager.STREAM_RING };
@@ -1041,6 +1047,7 @@
@Override
protected void test() {
if (mSkipRingerTests) {
+ status = PASS;
return;
}
int[] streams = { AudioManager.STREAM_RING };
@@ -1125,6 +1132,7 @@
@Override
protected void test() {
if (mSkipRingerTests) {
+ status = PASS;
return;
}
int musicVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TestSequence.java
similarity index 97%
rename from apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java
rename to apps/CtsVerifier/src/com/android/cts/verifier/tv/TestSequence.java
index 7815dcf..7b80155 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TestSequence.java
@@ -1,4 +1,4 @@
-package com.android.cts.verifier.tv.display;
+package com.android.cts.verifier.tv;
import com.android.cts.verifier.tv.TvAppVerifierActivity;
@@ -63,4 +63,4 @@
boolean allTestStepsPass = steps.stream().allMatch(step -> step.hasPassed());
context.getPassButton().setEnabled(allTestStepsPass);
}
-}
\ No newline at end of file
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TestStepBase.java
similarity index 97%
rename from apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java
rename to apps/CtsVerifier/src/com/android/cts/verifier/tv/TestStepBase.java
index 050c549..6c9b5a1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TestStepBase.java
@@ -14,13 +14,12 @@
* limitations under the License
*/
-package com.android.cts.verifier.tv.display;
+package com.android.cts.verifier.tv;
import android.view.View;
import android.widget.TextView;
import com.android.cts.verifier.R;
-import com.android.cts.verifier.tv.TvAppVerifierActivity;
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.StandardSubjectBuilder;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/AudioCapabilitiesTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/AudioCapabilitiesTestActivity.java
new file mode 100644
index 0000000..544b3c5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/AudioCapabilitiesTestActivity.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv.audio;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.tv.TestSequence;
+import com.android.cts.verifier.tv.TestStepBase;
+import com.android.cts.verifier.tv.TvAppVerifierActivity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test to verify Audio Capabilities APIs are correctly implemented.
+ *
+ * <p>This test checks if the APIs return correct results when
+ *
+ * <ol>
+ * <li>No receiver or soundbar is connected
+ * <li>Receiver or soundbar is connected
+ * </ol>
+ *
+ * The test verifies the behavior of following APIs.
+ *
+ * <ul>
+ * <li><a
+ * href="https://developer.android.com/reference/android/media/AudioDeviceInfo#getEncodings()">
+ * AudioDeviceInfo.getEncodings()</a>
+ * <li><a
+ * href="https://developer.android.com/reference/android/media/AudioDeviceInfo#getChannelCounts()">
+ * AudioDeviceInfo.getChannelCounts()</a>
+ * <li><a
+ * href="https://developer.android.com/reference/android/media/AudioDeviceInfo#getSampleRates()">
+ * AudioDeviceInfo.getSampleRates()</a>
+ * <li><a
+ * href="https://developer.android.com/reference/android/media/AudioTrack#isDirectPlaybackSupported(android.media.AudioFormat,%20android.media.AudioAttributes)">
+ * AudioTrack.isDirectPlaybackSupported()</a>
+ * </ul>
+ */
+public class AudioCapabilitiesTestActivity extends TvAppVerifierActivity {
+ private TestSequence mTestSequence;
+
+ @Override
+ protected void setInfoResources() {
+ setInfoResources(
+ R.string.tv_audio_capabilities_test, R.string.tv_audio_capabilities_test_info, -1);
+ }
+
+ @Override
+ protected void createTestItems() {
+ List<TestStepBase> testSteps = new ArrayList<>();
+ testSteps.add(new TVTestStep(this));
+ testSteps.add(new ReceiverTestStep(this));
+ mTestSequence = new TestSequence(this, testSteps);
+ mTestSequence.init();
+ }
+
+ private static class TVTestStep extends TestStep {
+ public TVTestStep(TvAppVerifierActivity context) {
+ super(context, "", R.string.tv_start_test);
+ }
+
+ @Override
+ public boolean runTest() {
+ // TODO: Add test logic
+ return false;
+ }
+ }
+
+ private static class ReceiverTestStep extends TestStep {
+ public ReceiverTestStep(TvAppVerifierActivity context) {
+ super(context, "", R.string.tv_start_test);
+ }
+
+ @Override
+ public boolean runTest() {
+ // TODO: Add test logic
+ return false;
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/OWNERS
new file mode 100644
index 0000000..b19e6c1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/OWNERS
@@ -0,0 +1,2 @@
+kritidang@google.com
+blindahl@google.com
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/TestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/TestStep.java
new file mode 100644
index 0000000..cafc226
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/TestStep.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.cts.verifier.tv.audio;
+
+import android.view.View;
+
+import androidx.annotation.StringRes;
+
+import com.android.cts.verifier.tv.TestStepBase;
+import com.android.cts.verifier.tv.TvAppVerifierActivity;
+
+/** Test step containing instruction to the user and a button. */
+public abstract class TestStep extends TestStepBase {
+ protected View mButtonView;
+
+ @StringRes private int mButtonStringId;
+
+ /**
+ * Constructs a test step containing instruction to the user and a button.
+ *
+ * @param context The test activity which this test step is part of.
+ * @param instructionText Text of the test instruction visible to the user.
+ * @param buttonTextId Id of a string resource containing the text of the button.
+ */
+ public TestStep(
+ TvAppVerifierActivity context, String instructionText, @StringRes int buttonStringId) {
+ super(context, instructionText);
+ mButtonStringId = buttonStringId;
+ }
+
+ /** Creates the View for this test step in the context {@link TvAppVerifierActivity}. */
+ public void createUiElements() {
+ super.createUiElements();
+ mButtonView =
+ mContext.createButtonItem(
+ mButtonStringId,
+ (View view) -> {
+ onButtonClickRunTest();
+ });
+ }
+
+ @Override
+ public void enableInteractivity() {
+ TvAppVerifierActivity.setButtonEnabled(mButtonView, true);
+ }
+
+ @Override
+ public void disableInteractivity() {
+ TvAppVerifierActivity.setButtonEnabled(mButtonView, false);
+ }
+
+ public abstract boolean runTest();
+
+ private void onButtonClickRunTest() {
+ disableInteractivity();
+ super.done();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
index 1fd4bdd..7c6efbc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
@@ -24,6 +24,8 @@
import androidx.annotation.StringRes;
import com.android.cts.verifier.R;
+import com.android.cts.verifier.tv.TestSequence;
+import com.android.cts.verifier.tv.TestStepBase;
import com.android.cts.verifier.tv.TvAppVerifierActivity;
import com.android.cts.verifier.tv.TvUtil;
@@ -102,8 +104,7 @@
@Override
public void runTest() {
- DisplayManager displayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
getAsserter().withMessage("Display.isHdr()").that(display.isHdr()).isFalse();
getAsserter()
@@ -134,8 +135,7 @@
@Override
public void runTest() {
- DisplayManager displayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
getAsserter().withMessage("Display.isHdr()").that(display.isHdr()).isTrue();
@@ -206,8 +206,7 @@
private void runTest() {
try {
// Verify the display APIs do not crash when the display is disconnected
- DisplayManager displayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
display.isHdr();
display.getHdrCapabilities();
@@ -224,8 +223,7 @@
}
private static String getInstructionText(Context context) {
- DisplayManager displayManager =
- (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager = context.getSystemService(DisplayManager.class);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
int[] hdrTypes = display.getHdrCapabilities().getSupportedHdrTypes();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
index b663801..7c0f37f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
@@ -24,6 +24,8 @@
import androidx.annotation.StringRes;
import com.android.cts.verifier.R;
+import com.android.cts.verifier.tv.TestSequence;
+import com.android.cts.verifier.tv.TestStepBase;
import com.android.cts.verifier.tv.TvAppVerifierActivity;
import com.android.cts.verifier.tv.TvUtil;
@@ -130,8 +132,7 @@
private void runTest() {
try {
// Verify the display APIs do not crash when the display is disconnected
- DisplayManager displayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
display.getMode();
display.getSupportedModes();
@@ -163,8 +164,7 @@
@Override
public void runTest() {
- DisplayManager displayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
getAsserter()
.withMessage("Display.getMode()")
@@ -224,8 +224,7 @@
@Override
public void runTest() {
- DisplayManager displayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
getAsserter()
@@ -265,8 +264,7 @@
}
private static String getInstructionText(Context context) {
- DisplayManager displayManager =
- (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ DisplayManager displayManager = context.getSystemService(DisplayManager.class);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
String supportedModes =
Arrays.stream(display.getSupportedModes())
@@ -289,7 +287,7 @@
}
// We use a custom Mode class since the constructors of Display.Mode are hidden. Additionally,
- // we want to use fuzzy comparision for frame rates which is not used in Display.Mode.equals().
+ // we want to use fuzzy comparison for frame rates which is not used in Display.Mode.equals().
private static class Mode {
public int mWidth;
public int mHeight;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java
index c73fd53..6ee34f3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java
@@ -20,6 +20,7 @@
import androidx.annotation.StringRes;
+import com.android.cts.verifier.tv.TestStepBase;
import com.android.cts.verifier.tv.TvAppVerifierActivity;
/** Test step containing instruction to the user and a button. */
@@ -27,11 +28,9 @@
protected View mButtonView;
- @StringRes
- private int mButtonStringId;
+ @StringRes private int mButtonStringId;
- @StringRes
- private int mStepNameStringId;
+ @StringRes private int mStepNameStringId;
/**
* Constructs a test step containing instruction to the user and a button.
@@ -39,11 +38,14 @@
* @param context The test activity which this test step is part of.
* @param instructionText The text of the test instruction visible to the user.
* @param stepNameStringId Id of a string resource containing human readable name of this step
- * to be used in logs.
+ * to be used in logs.
* @param buttonStringId Id of a string resource containing the text of the button.
*/
- public OneButtonTestStep(TvAppVerifierActivity context, @StringRes int stepNameStringId,
- String instructionText, @StringRes int buttonStringId) {
+ public OneButtonTestStep(
+ TvAppVerifierActivity context,
+ @StringRes int stepNameStringId,
+ String instructionText,
+ @StringRes int buttonStringId) {
super(context, instructionText);
mStepNameStringId = stepNameStringId;
mButtonStringId = buttonStringId;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java
index 0281252..4da4d18 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java
@@ -19,6 +19,7 @@
import android.view.View;
import com.android.cts.verifier.R;
+import com.android.cts.verifier.tv.TestStepBase;
import com.android.cts.verifier.tv.TvAppVerifierActivity;
/**
@@ -30,8 +31,8 @@
private View noButton;
/**
- * Constructs a test step containing human instructions for a manual test and two buttons -
- * Yes and No.
+ * Constructs a test step containing human instructions for a manual test and two buttons - Yes
+ * and No.
*
* @param context The test activity which this test step is part of.
* @param instructionText The text of the test instruction visible to the user.
diff --git a/hostsidetests/adbmanager/Android.bp b/hostsidetests/adbmanager/Android.bp
new file mode 100644
index 0000000..54616e5
--- /dev/null
+++ b/hostsidetests/adbmanager/Android.bp
@@ -0,0 +1,25 @@
+// 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.
+
+java_test_host {
+ name: "CtsAdbManagerHostTestCases",
+ defaults: ["cts_defaults"],
+ srcs: ["src/**/*.java"],
+ test_suites: ["cts", "general-tests"],
+ libs: [
+ "compatibility-host-util",
+ "cts-tradefed",
+ "tradefed"
+ ],
+}
diff --git a/hostsidetests/adbmanager/AndroidTest.xml b/hostsidetests/adbmanager/AndroidTest.xml
new file mode 100644
index 0000000..2194a4c
--- /dev/null
+++ b/hostsidetests/adbmanager/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<configuration description="Config for CTS AdbManager host test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsAdbManagerHostTestCases.jar" />
+ </test>
+</configuration>
diff --git a/hostsidetests/adbmanager/OWNERS b/hostsidetests/adbmanager/OWNERS
new file mode 100644
index 0000000..e102aca
--- /dev/null
+++ b/hostsidetests/adbmanager/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1352
+joshuaduong@google.com
+mpgroover@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/hostsidetests/adbmanager/src/android/adbmanager/cts/AdbManagerHostDeviceTest.java b/hostsidetests/adbmanager/src/android/adbmanager/cts/AdbManagerHostDeviceTest.java
new file mode 100644
index 0000000..096f3fd
--- /dev/null
+++ b/hostsidetests/adbmanager/src/android/adbmanager/cts/AdbManagerHostDeviceTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.adbmanager.cts;
+
+import com.android.compatibility.common.util.CddTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests the AdbManager System APIs via shell commands.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class AdbManagerHostDeviceTest extends BaseHostJUnit4Test {
+ private static final String FEATURE_WIFI = "android.hardware.wifi";
+ private static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
+
+ private boolean hasFeature(String feature) throws Exception {
+ CommandResult result = getDevice().executeShellV2Command("pm has-feature " + feature);
+ Assert.assertTrue(new Integer(0).equals(result.getExitCode()));
+ return Boolean.parseBoolean(result.getStdout().trim());
+ }
+
+ @Test
+ @CddTest(requirement="6.1/C-1-1")
+ public void test_isadbWifiSupported() throws Exception {
+ boolean expected = hasFeature(FEATURE_WIFI);
+
+ CommandResult result = getDevice().executeShellV2Command("cmd adb is-wifi-supported");
+
+ Assert.assertTrue(new Integer(0).equals(result.getExitCode()));
+ Assert.assertEquals(expected, Boolean.parseBoolean(result.getStdout().trim()));
+ }
+
+ @Test
+ @CddTest(requirement="6.1/C-1-2")
+ public void test_isadbWifiQrSupported() throws Exception {
+ boolean expected = hasFeature(FEATURE_WIFI) && hasFeature(FEATURE_CAMERA_ANY);
+
+ CommandResult result = getDevice().executeShellV2Command("cmd adb is-wifi-qr-supported");
+
+ Assert.assertTrue(new Integer(0).equals(result.getExitCode()));
+ Assert.assertEquals(expected, Boolean.parseBoolean(result.getStdout().trim()));
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index a08a515..2a087dc 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -488,6 +488,8 @@
private void doMediaSandboxed(Config config, boolean sandboxed) throws Exception {
installPackage(config.apk);
installPackage(MEDIA_29.apk);
+ // Make sure user initialization is complete before updating permission
+ waitForBroadcastIdle();
for (int user : mUsers) {
updatePermissions(config.pkg, user, new String[] {
PERM_READ_EXTERNAL_STORAGE,
@@ -749,6 +751,11 @@
}
}
+ /** Wait until all broadcast queues are idle. */
+ private void waitForBroadcastIdle() throws Exception{
+ getDevice().executeShellCommand("am wait-for-broadcast-idle");
+ }
+
private void updateAppOp(String packageName, int userId, String appOp, boolean allow)
throws Exception {
updateAppOp(packageName, false, userId, appOp, allow);
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
index 5ced0e6..b565e32 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
@@ -238,6 +238,12 @@
requireRunningOnPrimaryProfile();
// Test the return cursor is correct when the all checks are met.
+ final String selection = "(" + CalendarContract.Calendars.ACCOUNT_TYPE + "=? AND "
+ + CalendarContract.Events.TITLE + " =? )";
+ final String[] selectionArgs = new String[] {
+ TEST_ACCOUNT_TYPE,
+ WORK_EVENT_TITLE
+ };
final String[] projection = new String[]{
CalendarContract.Instances.TITLE,
CalendarContract.Instances.DTSTART,
@@ -247,7 +253,7 @@
buildQueryInstancesUri(CalendarContract.Instances.ENTERPRISE_CONTENT_URI,
WORK_EVENT_DTSTART - DateUtils.YEAR_IN_MILLIS,
WORK_EVENT_DTEND + DateUtils.YEAR_IN_MILLIS, null),
- projection, null, null, null);
+ projection, selection, selectionArgs, null);
assertThat(cursor).isNotNull();
assertThat(cursor.getCount()).isEqualTo(1);
@@ -263,6 +269,12 @@
requireRunningOnPrimaryProfile();
// Test the return cursor is correct when the all checks are met.
+ final String selection = "(" + CalendarContract.Calendars.ACCOUNT_TYPE + "=? AND "
+ + CalendarContract.Events.TITLE + " =? )";
+ final String[] selectionArgs = new String[] {
+ TEST_ACCOUNT_TYPE,
+ WORK_EVENT_TITLE
+ };
final String[] projection = new String[]{
CalendarContract.Instances.TITLE,
CalendarContract.Instances.DTSTART,
@@ -272,7 +284,7 @@
buildQueryInstancesUri(CalendarContract.Instances.ENTERPRISE_CONTENT_BY_DAY_URI,
WORK_EVENT_DTSTART_JULIAN_DAY - 1,
WORK_EVENT_DTEND_JULIAN_DAY + 1, null),
- projection, null, null, null);
+ projection, selection, selectionArgs, null);
assertThat(cursor).isNotNull();
assertThat(cursor.getCount()).isEqualTo(1);
diff --git a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
index 83cdf0e..a2b7c08 100644
--- a/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
+++ b/hostsidetests/packagemanager/dynamicmime/app/manifests/AndroidManifest_preferred.xml
@@ -23,7 +23,7 @@
<uses-library android:name="android.test.runner" />
<activity android:name="android.dynamicmime.common.activity.FirstActivity">
<intent-filter android:label="TestApp.FirstActivity">
- <action android:name="android.intent.action.SEND"/>
+ <action android:name="android.dynamicmime.preferred.TEST_ACTION"/>
<action android:name="android.dynamicmime.preferred.FILTER_INFO_HOOK_group_first"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeGroup="group_first"/>
@@ -32,14 +32,34 @@
<activity android:name="android.dynamicmime.common.activity.TwoGroupsActivity">
<intent-filter android:label="TestApp.TwoGroupsActivity">
- <action android:name="android.intent.action.SEND"/>
- <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.dynamicmime.preferred.TEST_ACTION"/>
<action android:name="android.dynamicmime.preferred.FILTER_INFO_HOOK_group_both"/>
+ <category android:name="android.intent.category.DEFAULT"/>
<data android:mimeGroup="group_third"/>
<data android:mimeGroup="group_second"/>
</intent-filter>
</activity>
+ <activity android:name="android.dynamicmime.common.activity.StaticActivity1"
+ android:exported="true">
+ <intent-filter android:label="StaticActivity1">
+ <action android:name="android.dynamicmime.preferred.TEST_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="text/plain"/>
+ <data android:mimeType="image/png"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.dynamicmime.common.activity.StaticActivity2"
+ android:exported="true">
+ <intent-filter android:label="StaticActivity2">
+ <action android:name="android.dynamicmime.preferred.TEST_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="text/plain"/>
+ <data android:mimeType="image/png"/>
+ </intent-filter>
+ </activity>
+
<receiver android:name="android.dynamicmime.app.AppMimeGroupsReceiver"
android:exported="true">
<intent-filter>
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/StaticActivity1.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/StaticActivity1.java
new file mode 100644
index 0000000..a76f09e
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/StaticActivity1.java
@@ -0,0 +1,27 @@
+/*
+ * 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.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+/**
+ * Activity that has intent-filter to handle "text/plain" and "image/png" MIME types.
+ *
+ * Needed for {@link android.dynamicmime.testapp.preferred.PreferredActivitiesTest} to ensure that
+ * there is more than one activity, capable of handling certain MIME types.
+ */
+public class StaticActivity1 extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/StaticActivity2.java b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/StaticActivity2.java
new file mode 100644
index 0000000..ad536db
--- /dev/null
+++ b/hostsidetests/packagemanager/dynamicmime/common/src/android/dynamicmime/common/activity/StaticActivity2.java
@@ -0,0 +1,27 @@
+/*
+ * 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.dynamicmime.common.activity;
+
+import android.app.Activity;
+
+/**
+ * Activity that has intent-filter to handle "text/plain" and "image/png" MIME types.
+ *
+ * Needed for {@link android.dynamicmime.testapp.preferred.PreferredActivitiesTest} to ensure that
+ * there is more than one activity, capable of handling certain MIME types.
+ */
+public class StaticActivity2 extends Activity {
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
index c9e25b0..d37b0f5 100644
--- a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
@@ -54,11 +54,9 @@
@RunWith(AndroidJUnit4.class)
public class PreferredActivitiesTest extends BaseDynamicMimeTest {
- private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
- private static final int NAV_BAR_INTERACTION_MODE_GESTURAL = 2;
+ private static final String ACTION = "android.dynamicmime.preferred.TEST_ACTION";
private static final BySelector BUTTON_ALWAYS = By.res("android:id/button_always");
- private static final BySelector RESOLVER_DIALOG = By.res("android:id/contentPanel");
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(30L);
@@ -71,17 +69,6 @@
@Before
public void setUp() {
Utils.installApk(APK_PREFERRED_APP);
- assumeNavigationMode();
- }
-
- private void assumeNavigationMode() {
- Resources res = context().getResources();
- int navModeResId = res.getIdentifier(NAV_BAR_INTERACTION_MODE_RES_NAME, "integer",
- "android");
- int navMode = res.getInteger(navModeResId);
-
- assumeTrue("Non-gesture navigation mode required",
- navMode != NAV_BAR_INTERACTION_MODE_GESTURAL);
}
@After
@@ -288,10 +275,6 @@
}
private UiObject2 findActivityInDialog(String label) {
- getUiDevice()
- .wait(Until.findObject(RESOLVER_DIALOG), TIMEOUT)
- .swipe(Direction.UP, 1f);
-
return getUiDevice().findObject(By.text(label));
}
@@ -331,7 +314,7 @@
private static void sendIntent(String mimeType) {
Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.setAction(ACTION);
sendIntent.setType(mimeType);
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
targetContext().startActivity(sendIntent, null);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
index 0fd37c2..ece2fbf 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
@@ -19,6 +19,7 @@
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangeTypesAndWindowTitle;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.supportsMultiDisplay;
import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ADDED;
@@ -101,7 +102,7 @@
@Before
public void setUp() throws Exception {
mContext = sInstrumentation.getContext();
- assumeTrue(supportsMultiDisplay());
+ assumeTrue(supportsMultiDisplay(mContext));
mParentActivityTitle = mContext.getString(
R.string.accessibility_embedded_display_test_parent_activity);
@@ -246,11 +247,6 @@
assertNotNull(mEmbeddedDisplayActivity);
}
- private boolean supportsMultiDisplay() {
- return mContext.getPackageManager().hasSystemFeature(
- FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
- }
-
public static class EmbeddedDisplayParentActivity extends Activity {
private ActivityView mActivityView;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index 252fbcb..b32d5b8 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -21,6 +21,7 @@
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.supportsMultiDisplay;
import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
import static android.accessibilityservice.cts.utils.DisplayUtils.getStatusBarHeight;
import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession;
@@ -50,6 +51,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityService;
@@ -637,6 +639,8 @@
@Test
public void testGetWindowsOnAllDisplays_resultIsSortedByLayerDescending() throws Exception {
+ assumeTrue(supportsMultiDisplay(sInstrumentation.getContext()));
+
addTwoAppPanelWindows(mActivity);
// Creates a virtual display.
try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
@@ -655,9 +659,9 @@
SparseArray<List<AccessibilityWindowInfo>> allWindows =
sUiAutomation.getWindowsOnAllDisplays();
assertNotNull(allWindows);
- assertTrue(allWindows.size() == 2);
// Gets windows on default display.
+ assertTrue(allWindows.contains(Display.DEFAULT_DISPLAY));
List<AccessibilityWindowInfo> windowsOnDefaultDisplay =
allWindows.get(Display.DEFAULT_DISPLAY);
assertNotNull(windowsOnDefaultDisplay);
@@ -672,6 +676,7 @@
new IsSortedBy<>(w -> w.getLayer(), /* ascending */ false));
// Gets windows on virtual display.
+ assertTrue(allWindows.contains(virtualDisplayId));
List<AccessibilityWindowInfo> windowsOnVirtualDisplay =
allWindows.get(virtualDisplayId);
assertNotNull(windowsOnVirtualDisplay);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
index 3e253a7..2638c91 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
@@ -23,6 +23,7 @@
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.getActivityTitle;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.supportsMultiDisplay;
import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession;
import static android.accessibilityservice.cts.utils.DisplayUtils.getStatusBarHeight;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -41,6 +42,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -242,6 +244,8 @@
@Test
public void moveFocusToAnotherDisplay_movesActiveAndFocusWindow() throws Exception {
+ assumeTrue(supportsMultiDisplay(sInstrumentation.getContext()));
+
// Makes sure activityWindow on default display is focused
AccessibilityWindowInfo activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle);
assertTrue(activityWindow.isActive());
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index 83ac347..6885b71 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -16,6 +16,7 @@
import static android.accessibility.cts.common.ShellCommandBuilder.execShellCommand;
import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
+import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -154,6 +155,11 @@
}
}
+ public static boolean supportsMultiDisplay(Context context) {
+ return context.getPackageManager().hasSystemFeature(
+ FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
+ }
+
private static boolean isHomeScreenShowing(Context context, UiAutomation uiAutomation) {
final List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
final PackageManager packageManager = context.getPackageManager();
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index 980800c..e7d7394 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -16,6 +16,8 @@
package android.app.cts;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ApplicationExitInfo;
@@ -734,6 +736,60 @@
info.getRss() * 1024, new StringBuilder()));
}
+ // A clone of testPermissionChange using a different revoke api
+ public void testPermissionChangeWithReason() throws Exception {
+ String revokeReason = "test reason";
+ // Remove old records to avoid interference with the test.
+ clearHistoricalExitInfo();
+
+ // Grant the read calendar permission
+ mInstrumentation.getUiAutomation().grantRuntimePermission(
+ STUB_PACKAGE_NAME, android.Manifest.permission.READ_CALENDAR);
+ long now = System.currentTimeMillis();
+
+ // Start a process and do nothing
+ startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
+
+ // Enable high frequency memory sampling
+ executeShellCmd("dumpsys procstats --start-testing");
+ // Sleep for a while to wait for the sampling of memory info
+ sleep(10000);
+ // Stop the high frequency memory sampling
+ executeShellCmd("dumpsys procstats --stop-testing");
+ // Get the memory info from it.
+ String dump = executeShellCmd("dumpsys activity processes " + STUB_PACKAGE_NAME);
+ assertNotNull(dump);
+ final String lastPss = extractMemString(dump, " lastPss=", ' ');
+ final String lastRss = extractMemString(dump, " lastRss=", '\n');
+
+ // Revoke the read calendar permission
+ runWithShellPermissionIdentity(() -> {
+ mContext.getPackageManager().revokeRuntimePermission(STUB_PACKAGE_NAME,
+ android.Manifest.permission.READ_CALENDAR, Process.myUserHandle(),
+ revokeReason);
+ });
+ waitForGone(mWatcher);
+ long now2 = System.currentTimeMillis();
+
+ List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
+ STUB_PACKAGE_NAME, mStubPackagePid, 1,
+ mActivityManager::getHistoricalProcessExitReasons,
+ android.Manifest.permission.DUMP);
+
+ assertTrue(list != null && list.size() == 1);
+
+ ApplicationExitInfo info = list.get(0);
+ verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
+ ApplicationExitInfo.REASON_PERMISSION_CHANGE, null, null, now, now2);
+ assertEquals(revokeReason, info.getDescription());
+
+ // Also verify that we get the expected meminfo
+ assertEquals(lastPss, DebugUtils.sizeValueToString(
+ info.getPss() * 1024, new StringBuilder()));
+ assertEquals(lastRss, DebugUtils.sizeValueToString(
+ info.getRss() * 1024, new StringBuilder()));
+ }
+
public void testCrash() throws Exception {
// Remove old records to avoid interference with the test.
clearHistoricalExitInfo();
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index d4d5459..f690678 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -134,6 +134,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".augmented.AugmentedAuthActivity" />
<activity android:name=".SimpleAfterLoginActivity" />
<activity android:name=".SimpleBeforeLoginActivity" />
<activity android:name=".NonAutofillableActivity" />
diff --git a/tests/autofillservice/res/layout/authentication_activity.xml b/tests/autofillservice/res/layout/authentication_activity.xml
new file mode 100644
index 0000000..23e58e7
--- /dev/null
+++ b/tests/autofillservice/res/layout/authentication_activity.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Authenticate?" />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword"
+ android:autofillHints="password" />
+
+ <Button
+ android:id="@+id/yes"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Yes" />
+
+</LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
index b247ec2..7ddfccc 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
@@ -34,6 +34,8 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.autofill.AutofillManager;
+import android.widget.Button;
+import android.widget.EditText;
import com.google.common.base.Preconditions;
@@ -58,6 +60,7 @@
private static final int MSG_WAIT_FOR_LATCH = 1;
+ private static final int MSG_REQUEST_AUTOFILL = 2;
private static Bundle sData;
private static final SparseArray<CannedDataset> sDatasets = new SparseArray<>();
@@ -73,10 +76,18 @@
// Used to block response until it's counted down.
private static CountDownLatch sResponseLatch;
+ // Guarded by sLock
+ // Used to request autofill for a autofillable view in AuthenticationActivity
+ private static boolean sRequestAutofill;
+
private Handler mHandler;
+ private EditText mPasswordEditText;
+ private Button mYesButton;
+
static void resetStaticState() {
- setResultCode(RESULT_OK);
+ setResultCode(null, RESULT_OK);
+ setRequestAutofillForAuthenticationActivity(/* requestAutofill */ false);
sDatasets.clear();
sResponses.clear();
for (int i = 0; i < sPendingIntents.size(); i++) {
@@ -168,15 +179,30 @@
}
}
+ public static void setRequestAutofillForAuthenticationActivity(boolean requestAutofill) {
+ synchronized (sLock) {
+ sRequestAutofill = requestAutofill;
+ }
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.authentication_activity);
+
+ mPasswordEditText = findViewById(R.id.password);
+ mYesButton = findViewById(R.id.yes);
+ mYesButton.setOnClickListener(view -> doIt());
+
mHandler = new Handler(Looper.getMainLooper(), (m) -> {
switch (m.what) {
case MSG_WAIT_FOR_LATCH:
waitForLatchAndDoIt();
break;
+ case MSG_REQUEST_AUTOFILL:
+ requestFocusOnPassword();
+ break;
default:
throw new IllegalArgumentException("invalid message: " + m);
}
@@ -186,11 +212,17 @@
if (sResponseLatch != null) {
Log.d(TAG, "Delaying message until latch is counted down");
mHandler.dispatchMessage(mHandler.obtainMessage(MSG_WAIT_FOR_LATCH));
+ } else if (sRequestAutofill) {
+ mHandler.dispatchMessage(mHandler.obtainMessage(MSG_REQUEST_AUTOFILL));
} else {
doIt();
}
}
+ private void requestFocusOnPassword() {
+ syncRunOnUiThread(() -> mPasswordEditText.requestFocus());
+ }
+
private void waitForLatchAndDoIt() {
try {
final boolean called = sResponseLatch.await(5, TimeUnit.SECONDS);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 6c229e0..f0e1179 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -26,6 +26,7 @@
import android.app.PendingIntent;
import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
+import android.autofillservice.cts.augmented.AugmentedAuthActivity;
import android.autofillservice.cts.inline.InlineUiBot;
import android.content.ClipboardManager;
import android.content.Context;
@@ -100,6 +101,10 @@
return sDefaultUiBot2;
}
+ protected static UiBot getDropdownUiBot() {
+ return sDefaultUiBot;
+ }
+
@ClassRule
public static final SettingsStateKeeperRule sPublicServiceSettingsKeeper =
sTheRealServiceSettingsKeeper;
@@ -388,6 +393,7 @@
InstrumentedAutoFillService.resetStaticState();
AuthenticationActivity.resetStaticState();
+ AugmentedAuthActivity.resetStaticState();
sReplier.reset();
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAuthActivity.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAuthActivity.java
new file mode 100644
index 0000000..8de9eb7
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedAuthActivity.java
@@ -0,0 +1,90 @@
+/*
+ * 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.autofillservice.cts.augmented;
+
+import android.app.PendingIntent;
+import android.autofillservice.cts.AbstractAutoFillActivity;
+import android.autofillservice.cts.R;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.service.autofill.Dataset;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity for testing Augmented Autofill authentication flow. This activity shows a simple UI;
+ * when the UI is tapped, it returns whatever data was configured via the auth intent.
+ */
+public class AugmentedAuthActivity extends AbstractAutoFillActivity {
+ private static final String TAG = "AugmentedAuthActivity";
+
+ public static final String ID_AUTH_ACTIVITY_BUTTON = "button";
+
+ private static final String EXTRA_DATASET_TO_RETURN = "dataset_to_return";
+ private static final String EXTRA_CLIENT_STATE_TO_RETURN = "client_state_to_return";
+ private static final String EXTRA_RESULT_CODE_TO_RETURN = "result_code_to_return";
+
+ private static final List<PendingIntent> sPendingIntents = new ArrayList<>(1);
+
+ public static void resetStaticState() {
+ for (PendingIntent pendingIntent : sPendingIntents) {
+ pendingIntent.cancel();
+ }
+ sPendingIntents.clear();
+ }
+
+ public static IntentSender createSender(Context context, int requestCode,
+ Dataset datasetToReturn, Bundle clientStateToReturn, int resultCodeToReturn) {
+ Intent intent = new Intent(context, AugmentedAuthActivity.class);
+ intent.putExtra(EXTRA_DATASET_TO_RETURN, datasetToReturn);
+ intent.putExtra(EXTRA_CLIENT_STATE_TO_RETURN, clientStateToReturn);
+ intent.putExtra(EXTRA_RESULT_CODE_TO_RETURN, resultCodeToReturn);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, intent, 0);
+ sPendingIntents.add(pendingIntent);
+ return pendingIntent.getIntentSender();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Log.d(TAG, "Auth activity invoked, showing auth UI");
+ setContentView(R.layout.single_button_activity);
+ findViewById(R.id.button).setOnClickListener((v) -> {
+ Log.d(TAG, "Auth UI tapped, returning result");
+
+ Intent intent = getIntent();
+ Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET_TO_RETURN);
+ Bundle clientState = intent.getParcelableExtra(EXTRA_CLIENT_STATE_TO_RETURN);
+ int resultCode = intent.getIntExtra(EXTRA_RESULT_CODE_TO_RETURN, RESULT_OK);
+ Log.d(TAG, "Output: dataset=" + dataset + ", clientState=" + clientState
+ + ", resultCode=" + resultCode);
+
+ Intent result = new Intent();
+ result.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, dataset);
+ result.putExtra(AutofillManager.EXTRA_CLIENT_STATE, clientState);
+ setResult(resultCode, result);
+
+ finish();
+ });
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/CannedAugmentedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/CannedAugmentedFillResponse.java
index a8a26a3..af1229b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/CannedAugmentedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/CannedAugmentedFillResponse.java
@@ -19,6 +19,7 @@
import android.autofillservice.cts.R;
import android.content.Context;
+import android.content.IntentSender;
import android.os.Bundle;
import android.service.autofill.InlinePresentation;
import android.service.autofill.augmented.FillCallback;
@@ -189,6 +190,7 @@
final AutofillId id = pair.first;
datasetBuilder.setFieldInlinePresentation(id, pair.second, null,
dataset.mFieldPresentationById.get(id));
+ datasetBuilder.setAuthentication(dataset.mAuthentication);
}
list.add(datasetBuilder.build());
}
@@ -279,12 +281,14 @@
private final Map<AutofillId, InlinePresentation> mFieldPresentationById;
private final String mPresentation;
private final AutofillValue mOnlyFieldValue;
+ private final IntentSender mAuthentication;
private Dataset(@NonNull Builder builder) {
mFieldValuesById = builder.mFieldValuesById;
mPresentation = builder.mPresentation;
mOnlyFieldValue = builder.mOnlyFieldValue;
mFieldPresentationById = builder.mFieldPresentationById;
+ this.mAuthentication = builder.mAuthentication;
}
@NonNull
@@ -304,6 +308,7 @@
return "Dataset: [presentation=" + mPresentation
+ ", onlyField=" + mOnlyFieldValue
+ ", fields=" + mFieldValuesById
+ + ", auth=" + mAuthentication
+ "]";
}
@@ -314,6 +319,7 @@
private final String mPresentation;
private AutofillValue mOnlyFieldValue;
+ private IntentSender mAuthentication;
public Builder(@NonNull String presentation) {
mPresentation = Objects.requireNonNull(presentation);
@@ -356,6 +362,13 @@
return this;
}
+ /**
+ * Sets the authentication intent for this dataset.
+ */
+ public Builder setAuthentication(IntentSender authentication) {
+ mAuthentication = authentication;
+ return this;
+ }
public Dataset build() {
return new Dataset(this);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedAuthTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedAuthTest.java
new file mode 100644
index 0000000..bb81399
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedAuthTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.autofillservice.cts.inline;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static android.app.Activity.RESULT_OK;
+import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.augmented.AugmentedHelper.assertBasicRequestInfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.autofillservice.cts.AutofillActivityTestRule;
+import android.autofillservice.cts.augmented.AugmentedAuthActivity;
+import android.autofillservice.cts.augmented.AugmentedAutofillAutoActivityLaunchTestCase;
+import android.autofillservice.cts.augmented.AugmentedLoginActivity;
+import android.autofillservice.cts.augmented.CannedAugmentedFillResponse;
+import android.autofillservice.cts.augmented.CtsAugmentedAutofillService;
+import android.content.IntentSender;
+import android.service.autofill.Dataset;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.EditText;
+
+import org.junit.Test;
+
+public class InlineAugmentedAuthTest
+ extends AugmentedAutofillAutoActivityLaunchTestCase<AugmentedLoginActivity> {
+
+ protected AugmentedLoginActivity mActivity;
+
+ public InlineAugmentedAuthTest() {
+ super(getInlineUiBot());
+ }
+
+ @Override
+ protected AutofillActivityTestRule<AugmentedLoginActivity> getActivityRule() {
+ return new AutofillActivityTestRule<AugmentedLoginActivity>(AugmentedLoginActivity.class) {
+ @Override
+ protected void afterActivityLaunched() {
+ mActivity = getActivity();
+ }
+ };
+ }
+
+ @Test
+ public void testDatasetAuth_resultOk_validDataset() throws Exception {
+ // Set services
+ enableService();
+ enableAugmentedService();
+
+ // Set expectations
+ final EditText unField = mActivity.getUsername();
+ final AutofillId unFieldId = unField.getAutofillId();
+ final AutofillValue unValue = unField.getAutofillValue();
+ sReplier.addResponse(NO_RESPONSE);
+ Dataset authResult = new Dataset.Builder(createInlinePresentation("auth"))
+ .setId("dummyId")
+ .setValue(unFieldId, AutofillValue.forText("Auth Result"))
+ .build();
+ IntentSender authAction = AugmentedAuthActivity.createSender(mContext, 1,
+ authResult, null, RESULT_OK);
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("bla").build(),
+ unFieldId)
+ .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("inline")
+ .setField(unFieldId, "John Smith", createInlinePresentation("John"))
+ .setAuthentication(authAction)
+ .build())
+ .build());
+
+ // Trigger autofill request
+ mUiBot.selectByRelativeId(ID_USERNAME);
+ mUiBot.waitForIdle();
+ sReplier.getNextFillRequest();
+ CtsAugmentedAutofillService.AugmentedFillRequest request1 =
+ sAugmentedReplier.getNextFillRequest();
+
+ // Assert request
+ assertBasicRequestInfo(request1, mActivity, unFieldId, unValue);
+
+ // Confirm suggestions
+ mUiBot.assertDatasets("John");
+
+ // Tap on suggestion
+ mUiBot.selectDataset("John");
+ mUiBot.waitForIdle();
+
+ // Tap on the auth activity button and assert that the dataset from the auth activity is
+ // filled into the field.
+ mActivity.expectAutoFill("Auth Result");
+ mUiBot.selectByRelativeId(AugmentedAuthActivity.ID_AUTH_ACTIVITY_BUTTON);
+ mUiBot.waitForIdle();
+ mActivity.assertAutoFilled();
+ assertThat(unField.getText().toString()).isEqualTo("Auth Result");
+ mUiBot.assertNoDatasets();
+ }
+
+ @Test
+ public void testDatasetAuth_resultOk_nullDataset() throws Exception {
+ // Set services
+ enableService();
+ enableAugmentedService();
+
+ // Set expectations
+ final EditText unField = mActivity.getUsername();
+ final AutofillId unFieldId = unField.getAutofillId();
+ final AutofillValue unValue = unField.getAutofillValue();
+ sReplier.addResponse(NO_RESPONSE);
+ IntentSender authAction = AugmentedAuthActivity.createSender(mContext, 1,
+ null, null, RESULT_OK);
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("bla").build(),
+ unFieldId)
+ .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("inline")
+ .setField(unFieldId, "John Smith", createInlinePresentation("John"))
+ .setAuthentication(authAction)
+ .build())
+ .build());
+
+ // Trigger autofill request
+ mUiBot.selectByRelativeId(ID_USERNAME);
+ mUiBot.waitForIdle();
+ sReplier.getNextFillRequest();
+ CtsAugmentedAutofillService.AugmentedFillRequest request1 =
+ sAugmentedReplier.getNextFillRequest();
+
+ // Assert request
+ assertBasicRequestInfo(request1, mActivity, unFieldId, unValue);
+
+ // Confirm suggestions
+ mUiBot.assertDatasets("John");
+
+ // Tap on suggestion
+ mUiBot.selectDataset("John");
+ mUiBot.waitForIdle();
+
+ // Tap on the auth activity button and assert that the field is left unchanged (since the
+ // dataset returned from the auth activity is null).
+ mUiBot.selectByRelativeId(AugmentedAuthActivity.ID_AUTH_ACTIVITY_BUTTON);
+ mUiBot.waitForIdle();
+ assertThat(unField.getText().toString()).isEqualTo("");
+ }
+
+ @Test
+ public void testDatasetAuth_resultCancel() throws Exception {
+ // Set services
+ enableService();
+ enableAugmentedService();
+
+ // Set expectations
+ final EditText unField = mActivity.getUsername();
+ final AutofillId unFieldId = unField.getAutofillId();
+ final AutofillValue unValue = unField.getAutofillValue();
+ sReplier.addResponse(NO_RESPONSE);
+ Dataset authResult = new Dataset.Builder(createInlinePresentation("auth"))
+ .setId("dummyId")
+ .setValue(unFieldId, AutofillValue.forText("Auth Result"))
+ .build();
+ IntentSender authAction = AugmentedAuthActivity.createSender(mContext, 1,
+ authResult, null, RESULT_CANCELED);
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("bla").build(),
+ unFieldId)
+ .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("inline")
+ .setField(unFieldId, "John Smith", createInlinePresentation("John"))
+ .setAuthentication(authAction)
+ .build())
+ .build());
+
+ // Trigger autofill request
+ mUiBot.selectByRelativeId(ID_USERNAME);
+ mUiBot.waitForIdle();
+ sReplier.getNextFillRequest();
+ CtsAugmentedAutofillService.AugmentedFillRequest request1 =
+ sAugmentedReplier.getNextFillRequest();
+
+ // Assert request
+ assertBasicRequestInfo(request1, mActivity, unFieldId, unValue);
+
+ // Confirm suggestions
+ mUiBot.assertDatasets("John");
+
+ // Tap on suggestion
+ mUiBot.selectDataset("John");
+ mUiBot.waitForIdle();
+
+ // Tap on the auth activity button and assert that the field is left unchanged (since the
+ // result code returned by the auth activity is RESULT_CANCELED).
+ mUiBot.selectByRelativeId(AugmentedAuthActivity.ID_AUTH_ACTIVITY_BUTTON);
+ mUiBot.waitForIdle();
+ assertThat(unField.getText().toString()).isEqualTo("");
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedWebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedWebViewActivityTest.java
new file mode 100644
index 0000000..7ebe26d
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedWebViewActivityTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.autofillservice.cts.inline;
+
+import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
+import static android.autofillservice.cts.WebViewActivity.HTML_NAME_PASSWORD;
+import static android.autofillservice.cts.WebViewActivity.HTML_NAME_USERNAME;
+import static android.autofillservice.cts.augmented.CannedAugmentedFillResponse.NO_AUGMENTED_RESPONSE;
+
+import android.app.assist.AssistStructure.ViewNode;
+import android.autofillservice.cts.AutofillActivityTestRule;
+import android.autofillservice.cts.CannedFillResponse;
+import android.autofillservice.cts.Helper;
+import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
+import android.autofillservice.cts.MyWebView;
+import android.autofillservice.cts.WebViewActivity;
+import android.autofillservice.cts.augmented.AugmentedAutofillAutoActivityLaunchTestCase;
+import android.autofillservice.cts.augmented.CannedAugmentedFillResponse;
+import android.support.test.uiautomator.UiObject2;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.autofill.AutofillId;
+
+import org.junit.Test;
+
+public class InlineAugmentedWebViewActivityTest extends
+ AugmentedAutofillAutoActivityLaunchTestCase<WebViewActivity> {
+
+ private static final String TAG = "InlineAugmentedWebViewActivityTest";
+ private WebViewActivity mActivity;
+
+ public InlineAugmentedWebViewActivityTest() {
+ super(getInlineUiBot());
+ }
+
+ @Override
+ protected AutofillActivityTestRule<WebViewActivity> getActivityRule() {
+ return new AutofillActivityTestRule<WebViewActivity>(WebViewActivity.class) {
+ @Override
+ protected void beforeActivityLaunched() {
+ super.beforeActivityLaunched();
+ Log.i(TAG, "Setting service before launching the activity");
+ enableService();
+ }
+
+ @Override
+ protected void afterActivityLaunched() {
+ mActivity = getActivity();
+ }
+ };
+ }
+
+ @Test
+ public void testAugmentedAutoFillNoDatasets() throws Exception {
+ // Set service.
+ enableAutofillServices();
+
+ // Load WebView
+ mActivity.loadWebView(mUiBot);
+ mUiBot.waitForIdleSync();
+
+ // Set expectations.
+ sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+ sAugmentedReplier.addResponse(NO_AUGMENTED_RESPONSE);
+
+ // Trigger autofill.
+ mActivity.getUsernameInput().click();
+ sReplier.getNextFillRequest();
+ sAugmentedReplier.getNextFillRequest();
+
+ // Assert not shown.
+ mUiBot.assertNoDatasetsEver();
+ }
+
+ @Test
+ public void testAugmentedAutoFillOneDataset() throws Exception {
+ // Set service.
+ enableAutofillServices();
+
+ testBasicAugmentedAutofill();
+ }
+
+ @Test
+ public void testAugmentedAutoFill_startTypingHideInline() throws Exception {
+ // Set service.
+ enableAutofillServices();
+
+ testBasicAugmentedAutofill();
+
+ // Now pretend user typing something by updating the value in the input field.
+ mActivity.getUsernameInput().click();
+ mActivity.dispatchKeyPress(KeyEvent.KEYCODE_U);
+
+ // Expect the inline suggestion to disappear.
+ mUiBot.assertNoDatasets();
+ }
+
+ private void enableAutofillServices() throws Exception {
+ enableService();
+ enableAugmentedService();
+ }
+
+ private void testBasicAugmentedAutofill() throws Exception {
+ // Load WebView
+ MyWebView myWebView = mActivity.loadWebView(mUiBot);
+ mUiBot.waitForIdleSync();
+
+ // Set expectations
+ sReplier.addResponse(NO_RESPONSE);
+
+ // Trigger autofill.
+ mActivity.getUsernameInput().click();
+ mUiBot.waitForIdleSync();
+
+ // We cannot get webview field's AutofillId from AugmentedService FillRequest, we only can
+ // get these AutofillIds from AutofillService's AssistStructure by using html tag.
+ FillRequest autofillRequest = sReplier.getNextFillRequest();
+
+ // Set expectations for AugmentedService
+ AutofillId usernameId = getAutofillIdByWebViewTag(autofillRequest, HTML_NAME_USERNAME);
+ AutofillId passwordId = getAutofillIdByWebViewTag(autofillRequest, HTML_NAME_PASSWORD);
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+ .setField(usernameId, "dude", createInlinePresentation("dude"))
+ .setField(passwordId, "sweet", createInlinePresentation("sweet"))
+ .build())
+ .setOnlyDataset(new CannedAugmentedFillResponse.Dataset.Builder("req1")
+ .setOnlyField("dude")
+ .build())
+ .build());
+
+ sAugmentedReplier.getNextFillRequest();
+ final UiObject2 datasetPicker = mUiBot.assertDatasets("dude");
+
+ // Now Autofill it.
+ myWebView.expectAutofill("dude", "sweet");
+ mUiBot.selectDataset(datasetPicker, "dude");
+ myWebView.assertAutofilled();
+ mUiBot.assertNoDatasets();
+ }
+
+ private AutofillId getAutofillIdByWebViewTag(FillRequest autofillRequest, String tag) {
+ ViewNode viewNode = Helper.findNodeByHtmlName(autofillRequest.structure, tag);
+ return AutofillId.withoutSession(viewNode.getAutofillId());
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
index 5c63891..fcd18e4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
@@ -133,6 +133,79 @@
}
@Test
+ public void testDatasetAuthPinnedPresentationSelectedAndAutofilled() throws Exception {
+ // Set service.
+ enableService();
+
+ // Prepare the authenticated dataset
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ new CannedFillResponse.CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .build());
+
+ final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+ .addDataset(new CannedFillResponse.CannedDataset.Builder()
+ .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE, null,
+ Helper.createPinnedInlinePresentation("auth-pinned"))
+ .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE, null,
+ Helper.createInlinePresentation("auth-unpinned"))
+ .setPresentation(createPresentation("auth"))
+ .setAuthentication(authentication)
+ .build());
+ sReplier.addResponse(builder.build());
+
+ // Trigger auto-fill, verify seeing dataset.
+ assertSuggestionShownBySelectViewId(ID_USERNAME, "auth-pinned");
+ sReplier.getNextFillRequest();
+
+ // ...and select the dataset, then check the authentication result is autofilled.
+ mActivity.expectAutoFill("dude", "sweet");
+ AuthenticationActivity.setResultCode(RESULT_OK);
+ mUiBot.selectDataset("auth-pinned");
+ mUiBot.waitForIdle();
+ mActivity.assertAutoFilled();
+
+ // Clear the username field, and expect to see the pinned suggestion again, rather than
+ // the one returned from auth intent.
+ mActivity.onUsername((v) -> v.setText(""));
+ assertSuggestionShownBySelectViewId(ID_USERNAME, "auth-pinned");
+
+ // Now select the dataset again and verify that the same authentication flow happens.
+ mActivity.expectAutoFill("dude", "sweet");
+ AuthenticationActivity.setResultCode(RESULT_OK);
+ mUiBot.selectDataset("auth-pinned");
+ mUiBot.waitForIdle();
+ mActivity.assertAutoFilled();
+
+ // Clear the username field, put focus on password field, and then clear the password field,
+ // Expect to see unpinned suggestion.
+ mActivity.onUsername((v) -> v.setText(""));
+ mUiBot.selectByRelativeId(ID_PASSWORD);
+ mActivity.onPassword((v) -> v.setText(""));
+ assertSuggestionShownBySelectViewId(ID_PASSWORD, "auth-unpinned");
+
+ // Now select the dataset again and verify that the same authentication flow happens.
+ mActivity.expectAutoFill("dude", "sweet");
+ AuthenticationActivity.setResultCode(RESULT_OK);
+ mUiBot.selectDataset("auth-unpinned");
+ mUiBot.waitForIdle();
+ mActivity.assertAutoFilled();
+
+ // Clear the password field, and expect to see the unpinned suggestion again, rather than
+ // the one returned from auth intent.
+ mActivity.onPassword((v) -> v.setText(""));
+ assertSuggestionShownBySelectViewId(ID_PASSWORD, "auth-unpinned");
+
+ // Now select the dataset again and verify that the same authentication flow happens.
+ mActivity.expectAutoFill("dude", "sweet");
+ AuthenticationActivity.setResultCode(RESULT_OK);
+ mUiBot.selectDataset("auth-unpinned");
+ mUiBot.waitForIdle();
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
public void testDatasetAuthFilteringUsingRegex() throws Exception {
// Set service.
enableService();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
index 6aff0d5..eb18e96 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
@@ -23,8 +23,11 @@
import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.Helper.getContext;
+import static android.autofillservice.cts.Timeouts.MOCK_IME_TIMEOUT_MS;
import static android.autofillservice.cts.inline.InstrumentedAutoFillServiceInlineEnabled.SERVICE_NAME;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -45,6 +48,7 @@
import android.service.autofill.FillContext;
import android.support.test.uiautomator.Direction;
+import com.android.cts.mockime.ImeEventStream;
import com.android.cts.mockime.MockImeSession;
import org.junit.Test;
@@ -396,4 +400,61 @@
sReplier.getNextFillRequest();
mUiBot.waitForIdleSync();
}
+
+ @Test
+ public void testClickEventPassToIme() throws Exception {
+ testTouchEventPassToIme(/* longPress */ false);
+ }
+
+ @Test
+ public void testLongClickEventPassToIme() throws Exception {
+ testTouchEventPassToIme(/* longPress */ true);
+ }
+
+ private void testTouchEventPassToIme(boolean longPress) throws Exception {
+ final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
+ assumeTrue("MockIME not available", mockImeSession != null);
+
+ // Set service.
+ enableService();
+
+ Intent intent = new Intent(mContext, DummyActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+ .addDataset(new CannedFillResponse.CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setPresentation(createPresentation("The Username"))
+ .setInlinePresentation(longPress
+ ? createInlinePresentation("The Username", pendingIntent)
+ : createInlinePresentation("The Username"))
+ .build());
+
+ sReplier.addResponse(builder.build());
+
+ final ImeEventStream stream = mockImeSession.openEventStream();
+
+ // Trigger auto-fill.
+ mUiBot.selectByRelativeId(ID_USERNAME);
+ mUiBot.waitForIdleSync();
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertDatasets("The Username");
+
+ if (longPress) {
+ // Long click on suggestion
+ mUiBot.longPressSuggestion("The Username");
+
+ expectEvent(stream,
+ event -> "onInlineSuggestionLongClickedEvent".equals(event.getEventName()),
+ MOCK_IME_TIMEOUT_MS);
+ } else {
+ // Click on suggestion
+ mUiBot.selectDataset("The Username");
+
+ expectEvent(stream,
+ event -> "onInlineSuggestionClickedEvent".equals(event.getEventName()),
+ MOCK_IME_TIMEOUT_MS);
+ }
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
index 915a9dc..0824c46 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
@@ -332,7 +332,7 @@
mWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY, true);
// Launch a different activity on top.
- launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+ launchActivity(BROADCAST_RECEIVER_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
mWmState.waitForActivityState(MOVE_TASK_TO_BACK_ACTIVITY,STATE_STOPPED);
final boolean shouldBeVisible =
@@ -478,6 +478,7 @@
.setIntentExtra(
extra -> extra.putBoolean(Components.TestActivity.EXTRA_NO_IDLE, true))
.setTargetActivity(TEST_ACTIVITY)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
.execute();
}
@@ -552,7 +553,7 @@
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
lockScreenSession.sleepDevice();
separateTestJournal();
- launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY);
+ launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
mWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY, true);
assertTrue("Display turns on", isDisplayOn(DEFAULT_DISPLAY));
assertSingleLaunch(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY);
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index 7ddc020..3a71828 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -2201,8 +2201,15 @@
// Include stopped packages
.append(" -f 0x00000020");
} else {
+ // If new task flag isn't set the windowing mode of launcher activity will be the
+ // windowing mode of the target activity, so we need to launch launcher activity in
+ // it.
+ String amStartCmd =
+ (mWindowingMode == -1 || mNewTask)
+ ? getAmStartCmd(mLaunchingActivity)
+ : getAmStartCmd(mLaunchingActivity, mWindowingMode);
// Use launching activity to launch the target.
- commandBuilder.append(getAmStartCmd(mLaunchingActivity))
+ commandBuilder.append(amStartCmd)
.append(" -f 0x20000020");
}
diff --git a/tests/inputmethod/Android.bp b/tests/inputmethod/Android.bp
index 95d5fa1..6f07ac6 100644
--- a/tests/inputmethod/Android.bp
+++ b/tests/inputmethod/Android.bp
@@ -25,6 +25,7 @@
libs: ["android.test.runner"],
static_libs: [
"androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"CtsMockInputMethodLib",
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
index f1ec1e0..fd2ea5d 100644
--- a/tests/inputmethod/AndroidTest.xml
+++ b/tests/inputmethod/AndroidTest.xml
@@ -48,6 +48,24 @@
<option name="run-command" value="wm dismiss-keyguard" />
</target_preparer>
+ <!--
+ A (separate) standalone test app APK is needed to test implicit app-visibility from the IME
+ process to the IME target process, because if the IME target process is directly interacting
+ with MockIme process via MockImeSession, then the system would already give the MockIme an
+ implicit app-visibility back to the test app. To fully test app-visibility scenario,
+ MockImeSession cannot be used in the process where the focused Activity is hosted.
+ -->
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <!--
+ In order to simulate the scenario where the IME client process is normally
+ installed, explicitly set false here. Otherwise, the test APP will be installed under
+ force-queryable mode, which makes the test useless.
+ -->
+ <option name="force-queryable" value="false" />
+ <option name="test-file-name" value="CtsInputMethodStandaloneTestApp.apk" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsInputMethodTestCases.apk" />
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEvent.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEvent.java
index 808a972..abfae95 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEvent.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEvent.java
@@ -38,6 +38,7 @@
Integer,
String,
CharSequence,
+ Exception,
Parcelable,
}
@@ -74,6 +75,9 @@
if (object instanceof CharSequence) {
return ReturnType.CharSequence;
}
+ if (object instanceof Exception) {
+ return ReturnType.Exception;
+ }
if (object instanceof Parcelable) {
return ReturnType.Parcelable;
}
@@ -143,6 +147,9 @@
case CharSequence:
bundle.putCharSequence("mReturnValue", getReturnCharSequenceValue());
break;
+ case Exception:
+ bundle.putSerializable("mReturnValue", getReturnExceptionValue());
+ break;
case Parcelable:
bundle.putParcelable("mReturnValue", getReturnParcelableValue());
break;
@@ -186,6 +193,9 @@
case CharSequence:
result = bundle.getCharSequence("mReturnValue");
break;
+ case Exception:
+ result = bundle.getSerializable("mReturnValue");
+ break;
case Parcelable:
result = bundle.getParcelable("mReturnValue");
break;
@@ -364,6 +374,25 @@
return (String) mReturnValue;
}
+ /**
+ * Retrieves a result that is known to be {@link Exception} or its subclasses.
+ *
+ * @param <T> {@link Exception} or its subclass.
+ * @return {@link Exception} object returned as a result of the command.
+ * @throws NullPointerException if the return value is {@code null}
+ * @throws ClassCastException if the return value is non-{@code null} object that is different
+ * from {@link Exception}
+ */
+ public <T extends Exception> T getReturnExceptionValue() {
+ if (mReturnType == ReturnType.Null) {
+ throw new NullPointerException();
+ }
+ if (mReturnType != ReturnType.Exception) {
+ throw new ClassCastException();
+ }
+ return (T) mReturnValue;
+ }
+
/**
* @return result value of this event.
* @throws NullPointerException if the return value is {@code null}
@@ -380,6 +409,12 @@
return (T) mReturnValue;
}
+ /**
+ * @return {@code true} when the result value is an {@link Exception}.
+ */
+ public boolean isExceptionReturnValue() {
+ return mReturnType == ReturnType.Exception;
+ }
/**
* @return {@code true} when the result value is {@code null}.
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 ba7262e..779bba8 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.inputmethodservice.InputMethodService;
@@ -289,6 +290,15 @@
sendDownUpKeyEvents(keyEventCode);
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
}
+ case "getApplicationInfo": {
+ final String packageName = command.getExtras().getString("packageName");
+ final int flags = command.getExtras().getInt("flags");
+ try {
+ return getPackageManager().getApplicationInfo(packageName, flags);
+ } catch (PackageManager.NameNotFoundException e) {
+ return e;
+ }
+ }
case "getDisplayId":
return getDisplay().getDisplayId();
case "verifyLayoutInflaterContext":
@@ -809,6 +819,13 @@
suggestionView -> {
Log.d(TAG, "new inline suggestion view ready");
if (suggestionView != null) {
+ suggestionView.setOnClickListener((v) -> {
+ getTracer().onInlineSuggestionClickedEvent(() -> { });
+ });
+ suggestionView.setOnLongClickListener((v) -> {
+ getTracer().onInlineSuggestionLongClickedEvent(() -> { });
+ return true;
+ });
pendingInlineSuggestions.mViews[index] = suggestionView;
}
if (pendingInlineSuggestions.mInflatedViewCount.incrementAndGet()
@@ -1068,5 +1085,15 @@
return recordEventInternal("onInlineSuggestionsResponse", supplier::getAsBoolean,
arguments);
}
+
+ void onInlineSuggestionClickedEvent(@NonNull Runnable runnable) {
+ final Bundle arguments = new Bundle();
+ recordEventInternal("onInlineSuggestionClickedEvent", runnable, arguments);
+ }
+
+ void onInlineSuggestionLongClickedEvent(@NonNull Runnable runnable) {
+ final Bundle arguments = new Bundle();
+ recordEventInternal("onInlineSuggestionLongClickedEvent", 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 b495e36..9a5eba8 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -977,6 +977,27 @@
return callCommandInternal("sendDownUpKeyEvents", params);
}
+ /**
+ * Lets {@link MockIme} call
+ * {@link android.content.pm.PackageManager#getApplicationInfo(String, int)} with the given
+ * {@code packageName} and {@code flags}.
+ *
+ * @param packageName the package name to be passed to
+ * {@link android.content.pm.PackageManager#getApplicationInfo(String, int)}.
+ * @param flags the flags to be passed to
+ * {@link android.content.pm.PackageManager#getApplicationInfo(String, int)}.
+ * @return {@link ImeCommand} object that can be passed to
+ * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to
+ * wait until this event is handled by {@link MockIme}.
+ */
+ @NonNull
+ public ImeCommand callGetApplicationInfo(@NonNull String packageName, int flags) {
+ final Bundle params = new Bundle();
+ params.putString("packageName", packageName);
+ params.putInt("flags", flags);
+ return callCommandInternal("getApplicationInfo", params);
+ }
+
@NonNull
public ImeCommand callGetDisplayId() {
final Bundle params = new Bundle();
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStartInputLifecycleTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStartInputLifecycleTest.java
index 23f32ce..fc724f8 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStartInputLifecycleTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStartInputLifecycleTest.java
@@ -21,8 +21,10 @@
import static android.view.View.VISIBLE;
import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -30,14 +32,19 @@
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
+import android.os.Process;
import android.os.SystemClock;
import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.cts.util.DisableScreenDozeRule;
import android.view.inputmethod.cts.util.EndToEndImeTestBase;
import android.view.inputmethod.cts.util.TestActivity;
import android.view.inputmethod.cts.util.TestUtils;
import android.view.inputmethod.cts.util.UnlockScreenRule;
+import android.view.inputmethod.cts.util.WindowFocusStealer;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -140,6 +147,79 @@
}
}
+ /**
+ * Test case for Bug 158624922 and Bug 152373385.
+ *
+ * Test {@link android.inputmethodservice.InputMethodService#onStartInput(EditorInfo, boolean)}
+ * and {@link InputMethodService#onFinishInput()} won't be called and the input connection
+ * remains active, even when a non-IME focusable window hosted by a different process
+ * temporarily becomes the focused window.
+ */
+ @Test
+ public void testNoStartNewInputWhileOtherProcessHasWindowFocus() throws Exception {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ try (MockImeSession imeSession = MockImeSession.create(
+ instrumentation.getContext(),
+ instrumentation.getUiAutomation(),
+ new ImeSettings.Builder())) {
+ final ImeEventStream stream = imeSession.openEventStream();
+
+ final String marker = InputMethodStartInputLifecycleTest.class.getName() + "/"
+ + SystemClock.elapsedRealtimeNanos();
+ final EditText editText = launchTestActivity(marker);
+ TestUtils.runOnMainSync(() -> editText.requestFocus());
+
+ // Wait until the MockIme gets bound to the TestActivity.
+ expectBindInput(stream, Process.myPid(), TIMEOUT);
+
+ expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+
+ // Get app window token
+ final IBinder appWindowToken = TestUtils.getOnMainSync(
+ () -> editText.getApplicationWindowToken());
+
+ try (WindowFocusStealer focusStealer =
+ WindowFocusStealer.connect(instrumentation.getTargetContext(), TIMEOUT)) {
+
+ focusStealer.stealWindowFocus(appWindowToken, TIMEOUT);
+
+ // Wait until the edit text loses window focus.
+ TestUtils.waitOnMainUntil(() -> !editText.hasWindowFocus(), TIMEOUT);
+ }
+ // Wait until the edit text gains window focus again.
+ TestUtils.waitOnMainUntil(() -> editText.hasWindowFocus(), TIMEOUT);
+
+ // Not expect the input connection will be started or finished even gaining non-IME
+ // focusable window focus.
+ notExpectEvent(stream, event -> "onFinishInput".equals(event.getEventName())
+ || "onStartInput".equals(event.getEventName()), TIMEOUT);
+
+ // Verify the input connection of the EditText is still active and can accept text.
+ final InputMethodManager imm = editText.getContext().getSystemService(
+ InputMethodManager.class);
+ assertTrue(TestUtils.getOnMainSync(() -> imm.isActive(editText)));
+ assertTrue(TestUtils.getOnMainSync(() -> imm.isAcceptingText()));
+ }
+ }
+
+ private EditText launchTestActivity(String marker) {
+ final AtomicReference<EditText> editTextRef = new AtomicReference<>();
+ TestActivity.startSync(activity-> {
+ final LinearLayout layout = new LinearLayout(activity);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ final EditText editText = new EditText(activity);
+ editText.setPrivateImeOptions(marker);
+ editText.setHint("editText");
+ editText.requestFocus();
+ editTextRef.set(editText);
+
+ layout.addView(editText);
+ return layout;
+ });
+ return editTextRef.get();
+ }
+
private static Predicate<ImeEvent> onFinishInputMatcher() {
return event -> TextUtils.equals("onFinishInput", event.getEventName());
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/PackageVisibilityTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/PackageVisibilityTest.java
new file mode 100644
index 0000000..7705c63
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/PackageVisibilityTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.UnlockScreenRule;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import com.android.cts.mockime.ImeCommand;
+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.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.InvalidParameterException;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public final class PackageVisibilityTest extends EndToEndImeTestBase {
+ static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
+ @Rule
+ public final UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+
+ private static final ComponentName TEST_ACTIVITY = new ComponentName(
+ "android.view.inputmethod.ctstestapp",
+ "android.view.inputmethod.ctstestapp.MainActivity");
+
+ private static final Uri TEST_ACTIVITY_URI =
+ Uri.parse("https://example.com/android/view/inputmethod/ctstestapp");
+
+ private static final String EXTRA_KEY_PRIVATE_IME_OPTIONS =
+ "android.view.inputmethod.ctstestapp.EXTRA_KEY_PRIVATE_IME_OPTIONS";
+
+ private static final String TEST_MARKER_PREFIX =
+ "android.view.inputmethod.cts.PackageVisibilityTest";
+
+ private static String getTestMarker() {
+ return TEST_MARKER_PREFIX + "/" + SystemClock.elapsedRealtimeNanos();
+ }
+
+ @NonNull
+ private static Uri formatStringIntentParam(@NonNull Uri uri, @NonNull String key,
+ @Nullable String value) {
+ if (value == null) {
+ return uri;
+ }
+ return uri.buildUpon().appendQueryParameter(key, value).build();
+ }
+
+ @NonNull
+ private static String formatStringIntentParam(@NonNull String key, @Nullable String value) {
+ if (key.matches("[ \"']")) {
+ throw new InvalidParameterException("Unsupported character(s) in key=" + key);
+ }
+ if (value.matches("[ \"']")) {
+ throw new InvalidParameterException("Unsupported character(s) in value=" + value);
+ }
+ return value != null ? String.format(" --es %s %s", key, value) : "";
+ }
+
+ /**
+ * Launch the standalone version of the test {@link android.app.Activity} then wait for
+ * completions of launch.
+ *
+ * <p>Note: this method does not use
+ * {@link android.app.Instrumentation#startActivitySync(Intent)} because it does not work when
+ * both the calling process and the target process run under the instant app mode. Instead this
+ * method relies on adb command {@code adb shell am start} to work around that limitation.</p>
+ *
+ * @param instant {@code true} if the caller and the target is installed as instant apps.
+ * @param privateImeOptions If not {@code null},
+ * {@link android.view.inputmethod.EditorInfo#privateImeOptions} will
+ * in the test {@link android.app.Activity} will be set to this value.
+ * @param timeout timeout in milliseconds.
+ */
+ private void launchTestActivity(boolean instant, @Nullable String privateImeOptions,
+ long timeout) {
+ final String command;
+ if (instant) {
+ final Uri uri = formatStringIntentParam(
+ TEST_ACTIVITY_URI, EXTRA_KEY_PRIVATE_IME_OPTIONS, privateImeOptions);
+ command = String.format("am start -a %s -c %s %s",
+ Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE, uri.toString());
+ } else {
+ command = String.format("am start -n %s",
+ TEST_ACTIVITY.flattenToShortString())
+ + formatStringIntentParam(EXTRA_KEY_PRIVATE_IME_OPTIONS, privateImeOptions);
+ }
+ runShellCommand(command);
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .wait(Until.hasObject(By.pkg(TEST_ACTIVITY.getPackageName()).depth(0)), timeout);
+ }
+
+ @AppModeFull
+ @Test
+ public void testTargetPackageIsVisibleFromImeFull() throws Exception {
+ testTargetPackageIsVisibleFromIme(false /* instant */);
+ }
+
+ @AppModeInstant
+ @Test
+ public void testTargetPackageIsVisibleFromImeInstant() throws Exception {
+ // We need to explicitly check this condition in case tests are executed with atest command.
+ // See Bug 158617529 for details.
+ assumeTrue("This test should run when and only under the instant app mode.",
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager()
+ .isInstantApp());
+ testTargetPackageIsVisibleFromIme(true /* instant */);
+ }
+
+ private void testTargetPackageIsVisibleFromIme(boolean instant) throws Exception {
+ try (MockImeSession imeSession = MockImeSession.create(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder())) {
+ final ImeEventStream stream = imeSession.openEventStream();
+
+ final String marker = getTestMarker();
+ launchTestActivity(instant, marker, TIMEOUT);
+
+ expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+
+ final ImeCommand command = imeSession.callGetApplicationInfo(
+ TEST_ACTIVITY.getPackageName(), PackageManager.GET_META_DATA);
+ final ImeEvent event = expectCommand(stream, command, TIMEOUT);
+
+ if (event.isNullReturnValue()) {
+ fail("getApplicationInfo() returned null.");
+ }
+ if (event.isExceptionReturnValue()) {
+ final Exception exception = event.getReturnExceptionValue();
+ fail(exception.toString());
+ }
+ final ApplicationInfo applicationInfoFromIme = event.getReturnParcelableValue();
+ assertEquals(TEST_ACTIVITY.getPackageName(), applicationInfoFromIme.packageName);
+ }
+ }
+}
diff --git a/tests/inputmethod/testapp/Android.bp b/tests/inputmethod/testapp/Android.bp
new file mode 100644
index 0000000..1d55077
--- /dev/null
+++ b/tests/inputmethod/testapp/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsInputMethodStandaloneTestApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ ],
+ compile_multilib: "both",
+ static_libs: [
+ "androidx.annotation_annotation",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src/**/I*.aidl",
+ ],
+}
diff --git a/tests/inputmethod/testapp/AndroidManifest.xml b/tests/inputmethod/testapp/AndroidManifest.xml
new file mode 100644
index 0000000..0f474205
--- /dev/null
+++ b/tests/inputmethod/testapp/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?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.view.inputmethod.ctstestapp"
+ android:targetSandboxVersion="2">
+
+ <application
+ android:label="CtsInputMethodStandaloneTestApp"
+ android:multiArch="true"
+ android:supportsRtl="true">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:label="CtsInputMethodStandaloneTestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="https" />
+ <data android:host="example.com" />
+ <data android:path="/android/view/inputmethod/ctstestapp" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tests/inputmethod/testapp/src/android/view/inputmethod/ctstestapp/MainActivity.java b/tests/inputmethod/testapp/src/android/view/inputmethod/ctstestapp/MainActivity.java
new file mode 100644
index 0000000..58d5c42
--- /dev/null
+++ b/tests/inputmethod/testapp/src/android/view/inputmethod/ctstestapp/MainActivity.java
@@ -0,0 +1,65 @@
+/*
+ * 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.view.inputmethod.ctstestapp;
+
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A test {@link Activity} that automatically shows the input method.
+ */
+public final class MainActivity extends Activity {
+
+ private static final String EXTRA_KEY_PRIVATE_IME_OPTIONS =
+ "android.view.inputmethod.ctstestapp.EXTRA_KEY_PRIVATE_IME_OPTIONS";
+
+ @Nullable
+ private String getPrivateImeOptions() {
+ if (getPackageManager().isInstantApp()) {
+ final Uri uri = getIntent().getData();
+ if (uri == null || !uri.isHierarchical()) {
+ return null;
+ }
+ return uri.getQueryParameter(EXTRA_KEY_PRIVATE_IME_OPTIONS);
+ }
+ return getIntent().getStringExtra(EXTRA_KEY_PRIVATE_IME_OPTIONS);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ final EditText editText = new EditText(this);
+ editText.setHint("editText");
+ final String privateImeOptions = getPrivateImeOptions();
+ if (privateImeOptions != null) {
+ editText.setPrivateImeOptions(privateImeOptions);
+ }
+ editText.requestFocus();
+ layout.addView(editText);
+ getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ setContentView(layout);
+ }
+}
diff --git a/tests/tests/graphics/jni/VulkanTestHelpers.cpp b/tests/tests/graphics/jni/VulkanTestHelpers.cpp
index 7092b86..b549b97 100644
--- a/tests/tests/graphics/jni/VulkanTestHelpers.cpp
+++ b/tests/tests/graphics/jni/VulkanTestHelpers.cpp
@@ -69,6 +69,29 @@
} // namespace
+static bool enumerateDeviceExtensions(VkPhysicalDevice device,
+ std::vector<VkExtensionProperties>* extensions) {
+ VkResult result;
+
+ uint32_t count = 0;
+ result = vkEnumerateDeviceExtensionProperties(device, nullptr, &count, nullptr);
+ if (result != VK_SUCCESS) return false;
+
+ extensions->resize(count);
+ result = vkEnumerateDeviceExtensionProperties(device, nullptr, &count, extensions->data());
+ if (result != VK_SUCCESS) return false;
+
+ return true;
+}
+
+static bool hasExtension(const char* extension_name,
+ const std::vector<VkExtensionProperties>& extensions) {
+ return std::find_if(extensions.cbegin(), extensions.cend(),
+ [extension_name](const VkExtensionProperties& extension) {
+ return strcmp(extension.extensionName, extension_name) == 0;
+ }) != extensions.cend();
+}
+
bool VkInit::init() {
VkApplicationInfo appInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
@@ -108,6 +131,12 @@
ASSERT(status == VK_SUCCESS || status == VK_INCOMPLETE);
ASSERT(gpuCount > 0);
+ std::vector<VkExtensionProperties> supportedDeviceExtensions;
+ ASSERT(enumerateDeviceExtensions(mGpu, &supportedDeviceExtensions));
+ for (const auto extension : deviceExt) {
+ ASSERT(hasExtension(extension, supportedDeviceExtensions));
+ }
+
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(mGpu, &queueFamilyCount, nullptr);
ASSERT(queueFamilyCount != 0);
diff --git a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
index 167619c..d4df722 100644
--- a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
@@ -453,7 +453,7 @@
}
}
- // Not all devices support AC4
+ // Not all devices support AC4.
if (ac4TrackIndex == -1) {
List<AudioPresentation> presentations =
mExtractor.getAudioPresentations(0 /*trackIndex*/);
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 632acf5..af8767f 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -14,6 +14,8 @@
# platform version check (b/32056228)
# ============================================================
+LOCAL_PATH:= $(call my-dir)
+
include $(CLEAR_VARS)
LOCAL_MODULE := cts-platform-version-check
diff --git a/tests/tests/os/AutoRevokePreRApp/Android.bp b/tests/tests/os/AutoRevokePreRApp/Android.bp
new file mode 100644
index 0000000..254f9eb
--- /dev/null
+++ b/tests/tests/os/AutoRevokePreRApp/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test_helper_app {
+ name: "CtsAutoRevokePreRApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "test_current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "vts10",
+ "mts",
+ "general-tests",
+ ],
+ srcs: ["src/**/*.java", "src/**/*.kt"],
+}
diff --git a/tests/tests/os/AutoRevokePreRApp/AndroidManifest.xml b/tests/tests/os/AutoRevokePreRApp/AndroidManifest.xml
new file mode 100644
index 0000000..972a19f
--- /dev/null
+++ b/tests/tests/os/AutoRevokePreRApp/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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.os.cts.autorevokeprerapp">
+
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+ <application>
+ <activity android:name="android.os.cts.autorevokeprerapp.MainActivity"
+ android:exported="true"
+ android:visibleToInstantApps="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+
diff --git a/tests/tests/os/AutoRevokePreRApp/src/android/os/cts/autorevokeprerapp/MainActivity.kt b/tests/tests/os/AutoRevokePreRApp/src/android/os/cts/autorevokeprerapp/MainActivity.kt
new file mode 100644
index 0000000..ad4066b
--- /dev/null
+++ b/tests/tests/os/AutoRevokePreRApp/src/android/os/cts/autorevokeprerapp/MainActivity.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.os.cts.autorevokeprerapp
+
+import android.app.Activity
+import android.os.Bundle
+
+class MainActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ requestPermissions(arrayOf("android.permission.READ_CALENDAR"), 0)
+ }
+}
diff --git a/tests/tests/os/CtsOsTestCases.xml b/tests/tests/os/CtsOsTestCases.xml
index 193eb0f..72902e68 100644
--- a/tests/tests/os/CtsOsTestCases.xml
+++ b/tests/tests/os/CtsOsTestCases.xml
@@ -46,5 +46,6 @@
<!-- Load additional APKs onto device -->
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="push" value="CtsAutoRevokeDummyApp.apk->/data/local/tmp/cts/os/CtsAutoRevokeDummyApp.apk" />
+ <option name="push" value="CtsAutoRevokePreRApp.apk->/data/local/tmp/cts/os/CtsAutoRevokePreRApp.apk" />
</target_preparer>
</configuration>
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index 357e9c7..eb25347 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -31,10 +31,17 @@
import android.test.InstrumentationTestCase
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.Switch
-import com.android.compatibility.common.util.*
import com.android.compatibility.common.util.textAsString
import com.android.compatibility.common.util.MatcherUtils.hasTextThat
-import com.android.compatibility.common.util.SystemUtil.*
+import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.ThrowingSupplier
+import com.android.compatibility.common.util.UiAutomatorUtils
+import com.android.compatibility.common.util.click
+import com.android.compatibility.common.util.depthFirstSearch
+import com.android.compatibility.common.util.lowestCommonAncestor
+import com.android.compatibility.common.util.uiDump
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.containsStringIgnoringCase
import org.hamcrest.Matcher
@@ -46,6 +53,8 @@
private const val APK_PATH = "/data/local/tmp/cts/os/CtsAutoRevokeDummyApp.apk"
private const val APK_PACKAGE_NAME = "android.os.cts.autorevokedummyapp"
+private const val APK_PATH_2 = "/data/local/tmp/cts/os/CtsAutoRevokePreRApp.apk"
+private const val APK_PACKAGE_NAME_2 = "android.os.cts.autorevokeprerapp"
private const val READ_CALENDAR = "android.permission.READ_CALENDAR"
/**
@@ -105,7 +114,7 @@
// Run
runAutoRevoke()
- Thread.sleep(500)
+ Thread.sleep(1000)
// Verify
assertPermission(PERMISSION_GRANTED)
@@ -114,6 +123,47 @@
}
@AppModeFull(reason = "Uses separate apps for testing")
+ fun testPreRUnusedApp_doesntGetPermissionRevoked() {
+ wakeUpScreen()
+ withUnusedThresholdMs(3L) {
+ withDummyApp(APK_PATH_2, APK_PACKAGE_NAME_2) {
+ withDummyApp {
+ startApp(APK_PACKAGE_NAME_2)
+ clickPermissionAllow()
+ eventually {
+ assertPermission(PERMISSION_GRANTED, APK_PACKAGE_NAME_2)
+ }
+
+ goBack()
+ goHome()
+ goBack()
+
+ startApp()
+ clickPermissionAllow()
+ eventually {
+ assertPermission(PERMISSION_GRANTED)
+ }
+
+ goBack()
+ goHome()
+ goBack()
+ Thread.sleep(20)
+
+ // Run
+ runAutoRevoke()
+ Thread.sleep(500)
+
+ // Verify
+ eventually {
+ assertPermission(PERMISSION_DENIED)
+ assertPermission(PERMISSION_GRANTED, APK_PACKAGE_NAME_2)
+ }
+ }
+ }
+ }
+ }
+
+ @AppModeFull(reason = "Uses separate apps for testing")
fun testAutoRevoke_userWhitelisting() {
wakeUpScreen()
withUnusedThresholdMs(4L) {
@@ -284,7 +334,8 @@
runWithShellPermissionIdentity {
assertEquals(
permissionStateToString(state),
- permissionStateToString(context.packageManager.checkPermission(READ_CALENDAR, APK_PACKAGE_NAME)))
+ permissionStateToString(
+ context.packageManager.checkPermission(READ_CALENDAR, packageName)))
}
}
diff --git a/tests/tests/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java b/tests/tests/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java
index 4ae88c8..835ba12 100644
--- a/tests/tests/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java
@@ -53,21 +53,23 @@
ActivityManager manager = (ActivityManager) getActivity()
.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTasks = manager.getRunningTasks(10);
- // Current implementation should only return tasks for home and the caller.
- // We'll be done and task this to mean it shouldn't return more than 2.
+ // Current implementation should only return tasks for home and the caller. Since there can
+ // be multiple home tasks, we remove them from the list and then check that there is one or
+ // less task left in the list.
+ removeHomeRunningTasks(runningTasks);
assertTrue("Found tasks: " + runningTasks,
- runningTasks == null || runningTasks.size() <= 2);
+ runningTasks == null || runningTasks.size() <= 1);
List<ActivityManager.RecentTaskInfo> recentTasks = manager.getRecentTasks(10,
ActivityManager.RECENT_WITH_EXCLUDED);
// Current implementation should only return tasks for home and the caller. Since there can
// be multiple home tasks, we remove them from the list and then check that there is one or
// less task left in the list.
- removeHomeTasks(recentTasks);
+ removeHomeRecentsTasks(recentTasks);
assertTrue("Found tasks: " + recentTasks, recentTasks == null || recentTasks.size() <= 1);
}
- private void removeHomeTasks(List<ActivityManager.RecentTaskInfo> tasks) {
+ private void removeHomeRecentsTasks(List<ActivityManager.RecentTaskInfo> tasks) {
for (int i = tasks.size() -1; i >= 0; i--) {
ActivityManager.RecentTaskInfo task = tasks.get(i);
if (task.baseIntent != null && isHomeIntent(task.baseIntent)) {
@@ -76,9 +78,19 @@
}
}
+ private void removeHomeRunningTasks(List<ActivityManager.RunningTaskInfo> tasks) {
+ for (int i = tasks.size() -1; i >= 0; i--) {
+ ActivityManager.RunningTaskInfo task = tasks.get(i);
+ if (task.baseIntent != null && isHomeIntent(task.baseIntent)) {
+ tasks.remove(i);
+ }
+ }
+ }
+
private boolean isHomeIntent(Intent intent) {
return Intent.ACTION_MAIN.equals(intent.getAction())
- && intent.hasCategory(Intent.CATEGORY_HOME)
+ && (intent.hasCategory(Intent.CATEGORY_HOME)
+ || intent.hasCategory(Intent.CATEGORY_SECONDARY_HOME))
&& intent.getCategories().size() == 1
&& intent.getData() == null
&& intent.getType() == null;
diff --git a/tests/tests/permission/src/android/permission/cts/RevokePermissionTest.kt b/tests/tests/permission/src/android/permission/cts/RevokePermissionTest.kt
new file mode 100644
index 0000000..1d7e719
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/RevokePermissionTest.kt
@@ -0,0 +1,177 @@
+/*
+ * 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 android.Manifest.permission.CAMERA
+import android.Manifest.permission.READ_CALENDAR
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PERMISSION_GRANTED
+import android.os.Process
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+
+class RevokePermissionTest {
+
+ private val APP_PKG_NAME = "android.permission.cts.appthatrequestcustompermission"
+ private val APK = "/data/local/tmp/cts/permissions/" +
+ "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk"
+
+ @Before
+ fun installApp() {
+ runShellCommand("pm install -r -g $APK")
+ }
+
+ @Test
+ fun testRevokePermission() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = READ_CALENDAR,
+ isGranted = true)
+ }
+
+ @Test
+ fun testRevokePermissionNotRequested() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = CAMERA,
+ throwableType = SecurityException::class.java,
+ throwableMessage = "has not requested permission")
+ }
+
+ @Test
+ fun testRevokeFakePermission() {
+ val fakePermissionName = "FAKE_PERMISSION"
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = fakePermissionName,
+ throwableType = java.lang.IllegalArgumentException::class.java,
+ throwableMessage = "Unknown permission: $fakePermissionName")
+ }
+
+ @Test
+ fun testRevokeFakePackage() {
+ val fakePackageName = "fake.package.name.which.should.not.exist"
+ assertPackageNotInstalled(fakePackageName)
+ testRevoke(
+ packageName = fakePackageName,
+ permission = READ_CALENDAR)
+ }
+
+ @Test
+ fun testRevokePermissionWithReason() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = READ_CALENDAR,
+ reason = "test reason",
+ isGranted = true)
+ }
+
+ @Test
+ fun testRevokePermissionNotRequestedWithReason() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = CAMERA,
+ reason = "test reason",
+ throwableType = SecurityException::class.java,
+ throwableMessage = "has not requested permission")
+ }
+
+ @Test
+ fun testRevokeFakePermissionWithReason() {
+ val fakePermissionName = "FAKE_PERMISSION"
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = fakePermissionName,
+ reason = "test reason",
+ throwableType = java.lang.IllegalArgumentException::class.java,
+ throwableMessage = "Unknown permission: $fakePermissionName")
+ }
+
+ @Test
+ fun testRevokeFakePackageWithReason() {
+ val fakePackageName = "fake.package.name.which.should.not.exist"
+ assertPackageNotInstalled(fakePackageName)
+ testRevoke(
+ packageName = fakePackageName,
+ permission = READ_CALENDAR,
+ reason = "test reason")
+ }
+
+ @After
+ fun uninstallApp() {
+ runShellCommand("pm uninstall $APP_PKG_NAME")
+ }
+
+ private fun testRevoke(
+ packageName: String,
+ permission: String,
+ reason: String? = null,
+ isGranted: Boolean = false,
+ throwableType: Class<*>? = null,
+ throwableMessage: String = ""
+ ) {
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
+ val pm = context.packageManager
+
+ if (isGranted) {
+ assertEquals(PERMISSION_GRANTED, pm.checkPermission(READ_CALENDAR, APP_PKG_NAME))
+ }
+
+ runWithShellPermissionIdentity {
+ if (throwableType == null) {
+ if (reason == null) {
+ pm.revokeRuntimePermission(packageName, permission, Process.myUserHandle())
+ } else {
+ pm.revokeRuntimePermission(packageName, permission, Process.myUserHandle(),
+ reason)
+ }
+ } else {
+ try {
+ if (reason == null) {
+ pm.revokeRuntimePermission(packageName, permission, Process.myUserHandle())
+ } else {
+ pm.revokeRuntimePermission(packageName, permission, Process.myUserHandle(),
+ reason)
+ }
+ } catch (t: Throwable) {
+ if (t::class.java.name == throwableType.name &&
+ t.message!!.contains(throwableMessage)) {
+ return@runWithShellPermissionIdentity
+ }
+ throw RuntimeException("Unexpected throwable", t)
+ }
+ throw RuntimeException("revokeRuntimePermission expected to throw.")
+ }
+ }
+ }
+
+ private fun assertPackageNotInstalled(packageName: String) {
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
+ val pm = context.packageManager
+ try {
+ pm.getPackageInfo(packageName, 0)
+ throw RuntimeException("$packageName exists on this device")
+ } catch (e: PackageManager.NameNotFoundException) {
+ // Expected
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/view/res/layout/focus_finder_layout.xml b/tests/tests/view/res/layout/focus_finder_layout.xml
index 1dea684..4e2726c 100644
--- a/tests/tests/view/res/layout/focus_finder_layout.xml
+++ b/tests/tests/view/res/layout/focus_finder_layout.xml
@@ -46,7 +46,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/layout">
-
+ <android.view.cts.TestButton
+ android:id="@+id/bottom_button"
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:text="B" />
</LinearLayout>
</RelativeLayout>
diff --git a/tests/tests/view/src/android/view/cts/FocusFinderCtsActivity.java b/tests/tests/view/src/android/view/cts/FocusFinderCtsActivity.java
index ae0b4bf..300d3a5 100644
--- a/tests/tests/view/src/android/view/cts/FocusFinderCtsActivity.java
+++ b/tests/tests/view/src/android/view/cts/FocusFinderCtsActivity.java
@@ -25,6 +25,8 @@
public ViewGroup layout;
+ public ViewGroup inflateLayout;
+
public Button topLeftButton;
public Button topRightButton;
@@ -33,15 +35,19 @@
public Button bottomRightButton;
+ public Button bottomButton;
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.focus_finder_layout);
layout = (ViewGroup) findViewById(R.id.layout);
+ inflateLayout = (ViewGroup) findViewById(R.id.inflate_layout);
topLeftButton = (Button) findViewById(R.id.top_left_button);
topRightButton = (Button) findViewById(R.id.top_right_button);
bottomLeftButton = (Button) findViewById(R.id.bottom_left_button);
bottomRightButton = (Button) findViewById(R.id.bottom_right_button);
+ bottomButton = (Button) findViewById(R.id.bottom_button);
}
}
diff --git a/tests/tests/view/src/android/view/cts/FocusFinderTest.java b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
index 11e921a..53992ce 100644
--- a/tests/tests/view/src/android/view/cts/FocusFinderTest.java
+++ b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -44,10 +45,12 @@
public class FocusFinderTest {
private FocusFinder mFocusFinder;
private ViewGroup mLayout;
+ private ViewGroup mInflateLayout;
private Button mTopLeft;
private Button mTopRight;
private Button mBottomLeft;
private Button mBottomRight;
+ private Button mBottom;
@Rule
public ActivityTestRule<FocusFinderCtsActivity> mActivityRule =
@@ -59,14 +62,17 @@
mFocusFinder = FocusFinder.getInstance();
mLayout = activity.layout;
+ mInflateLayout = activity.inflateLayout;
mTopLeft = activity.topLeftButton;
mTopRight = activity.topRightButton;
mBottomLeft = activity.bottomLeftButton;
mBottomRight = activity.bottomRightButton;
+ mBottom = activity.bottomButton;
mTopLeft.setNextFocusLeftId(View.NO_ID);
mTopRight.setNextFocusLeftId(View.NO_ID);
mBottomLeft.setNextFocusLeftId(View.NO_ID);
mBottomRight.setNextFocusLeftId(View.NO_ID);
+ mBottom.setNextFocusLeftId(View.NO_ID);
}
@Test
@@ -456,4 +462,17 @@
view.setRight(right);
view.setBottom(bottom);
}
+
+ @Test
+ public void testFindNextFocusDoesNotReturnItself() {
+ View nextFocus = mFocusFinder.findNextFocus(mInflateLayout, mBottom, View.FOCUS_FORWARD);
+ assertNull(nextFocus);
+ }
+
+ @Test
+ public void testFindPreviousFocusDoesNotReturnItself() {
+ View previousFocus =
+ mFocusFinder.findNextFocus(mInflateLayout, mBottom, View.FOCUS_BACKWARD);
+ assertNull(previousFocus);
+ }
}
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index 9cab157..8736f36 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -81,7 +81,7 @@
mActivityRule.runOnUiThread(() -> {
View content = activity.findViewById(android.R.id.content);
int[] outLocation = new int[2];
- content.getLocationOnScreen(outLocation);
+ content.getLocationInWindow(outLocation);
center.x = outLocation[0] + (content.getWidth() / 2);
center.y = outLocation[1] + (content.getHeight() / 2);
windowRet[0] = activity.getWindow();
@@ -145,7 +145,7 @@
final Rect viewPos = new Rect();
mActivityRule.runOnUiThread(() -> {
int[] outLocation = new int[2];
- textureView.getLocationOnScreen(outLocation);
+ textureView.getLocationInWindow(outLocation);
viewPos.left = outLocation[0];
viewPos.top = outLocation[1];
viewPos.right = viewPos.left + textureView.getWidth();
@@ -187,7 +187,7 @@
final Rect viewPos = new Rect();
mActivityRule.runOnUiThread(() -> {
int[] outLocation = new int[2];
- textureView.getLocationOnScreen(outLocation);
+ textureView.getLocationInWindow(outLocation);
viewPos.left = outLocation[0];
viewPos.top = outLocation[1];
viewPos.right = viewPos.left + textureView.getWidth();
@@ -343,7 +343,7 @@
final Rect viewPos = new Rect();
mActivityRule.runOnUiThread(() -> {
int[] outLocation = new int[2];
- textureView.getLocationOnScreen(outLocation);
+ textureView.getLocationInWindow(outLocation);
viewPos.left = outLocation[0];
viewPos.top = outLocation[1];
viewPos.right = viewPos.left + textureView.getWidth();